-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
189 additions
and
213 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,190 +1,171 @@ | ||
import { StateManager } from './StateManager'; | ||
|
||
export class HierarchyManager { | ||
private readonly stateManager = new StateManager(); | ||
private readonly stateManager = new StateManager(); | ||
|
||
private readonly titleSelector = '.js-category-title'; | ||
private readonly titleSelector = `.js-category-title`; | ||
|
||
private readonly listSelector = '.js-category-list'; | ||
private readonly listSelector = `.js-category-list`; | ||
|
||
/** | ||
* Инициализирует иерархию. | ||
*/ | ||
public init(): void { | ||
this.addListeners(); | ||
this.initSaved(); | ||
this.openCurrentPath(); | ||
} | ||
public init(): void { | ||
this.addListeners(); | ||
this.initSaved(); | ||
this.openCurrentPath(); | ||
} | ||
|
||
private openPathAndSave(id: string): void { | ||
this.openPath(id); | ||
private openPathAndSave(id: string): void { | ||
this.openPath(id); | ||
|
||
this.stateManager.addOpenedPath(id); | ||
} | ||
this.stateManager.addOpenedPath(id); | ||
} | ||
|
||
private openPath(id: string): void { | ||
const list = document.querySelector( | ||
`${this.listSelector}[data-id="${id}"]`, | ||
); | ||
private openPath(id: string): void { | ||
const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); | ||
|
||
if (!list) { | ||
return; | ||
} | ||
if (!list) { | ||
return; | ||
} | ||
|
||
list.classList.add('_open'); | ||
list.parentNode?.querySelector(this.titleSelector)?.classList.add('_open'); | ||
} | ||
list.classList.add(`_open`); | ||
list.parentNode?.querySelector(this.titleSelector)?.classList.add(`_open`); | ||
} | ||
|
||
private closePath(id: string): void { | ||
const list = document.querySelector( | ||
`${this.listSelector}[data-id="${id}"]`, | ||
); | ||
private closePath(id: string): void { | ||
const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); | ||
|
||
if (!list) { | ||
return; | ||
} | ||
if (!list) { | ||
return; | ||
} | ||
|
||
list.classList.remove('_open'); | ||
list.parentNode | ||
?.querySelector(this.titleSelector) | ||
?.classList.remove('_open'); | ||
list.classList.remove(`_open`); | ||
list.parentNode?.querySelector(this.titleSelector)?.classList.remove(`_open`); | ||
|
||
this.stateManager.removeOpenedPath(id); | ||
} | ||
this.stateManager.removeOpenedPath(id); | ||
} | ||
|
||
private closePathWithChildren(id: string): void { | ||
this.closePath(id); | ||
private closePathWithChildren(id: string): void { | ||
this.closePath(id); | ||
|
||
const list = document.querySelector( | ||
`${this.listSelector}[data-id="${id}"]`, | ||
); | ||
const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); | ||
|
||
if (!list) { | ||
return; | ||
} | ||
if (!list) { | ||
return; | ||
} | ||
|
||
const childLists = list.querySelectorAll<HTMLElement>(this.listSelector); | ||
const childLists = list.querySelectorAll<HTMLElement>(this.listSelector); | ||
|
||
for (const item of childLists) { | ||
this.closePath(item.dataset.id || ''); | ||
} | ||
} | ||
for (const item of childLists) { | ||
this.closePath(item.dataset.id || ``); | ||
} | ||
} | ||
|
||
private togglePath(id: string): void { | ||
const list = document.querySelector( | ||
`${this.listSelector}[data-id="${id}"]`, | ||
); | ||
private togglePath(id: string): void { | ||
const list = document.querySelector(`${this.listSelector}[data-id="${id}"]`); | ||
|
||
if (!list) { | ||
return; | ||
} | ||
if (!list) { | ||
return; | ||
} | ||
|
||
if (list.classList.contains('_open')) { | ||
this.closePathWithChildren(id); | ||
if (list.classList.contains(`_open`)) { | ||
this.closePathWithChildren(id); | ||
|
||
return; | ||
} | ||
return; | ||
} | ||
|
||
this.openPathAndSave(id); | ||
} | ||
this.openPathAndSave(id); | ||
} | ||
|
||
private addListeners(): void { | ||
const items = document.querySelectorAll<HTMLElement>( | ||
'.js-category-title:not([data-id="root"])', | ||
); | ||
private addListeners(): void { | ||
const items = document.querySelectorAll<HTMLElement>(`.js-category-title:not([data-id="root"])`); | ||
|
||
for (const item of items) { | ||
item.addEventListener('click', () => { | ||
const id = item.dataset.id || ''; | ||
for (const item of items) { | ||
item.addEventListener(`click`, () => { | ||
const id = item.dataset.id || ``; | ||
|
||
this.togglePath(id); | ||
}); | ||
} | ||
this.togglePath(id); | ||
}); | ||
} | ||
|
||
this.addExpandListener(); | ||
this.addCollapseListener(); | ||
this.addTargetListener(); | ||
} | ||
this.addExpandListener(); | ||
this.addCollapseListener(); | ||
this.addTargetListener(); | ||
} | ||
|
||
private addExpandListener(): void { | ||
const expandButton = document.querySelector('.js-tree-expand'); | ||
private addExpandListener(): void { | ||
const expandButton = document.querySelector(`.js-tree-expand`); | ||
|
||
expandButton?.addEventListener('click', () => { | ||
const items = document.querySelectorAll<HTMLElement>(this.listSelector); | ||
expandButton?.addEventListener(`click`, () => { | ||
const items = document.querySelectorAll<HTMLElement>(this.listSelector); | ||
|
||
for (const item of items) { | ||
const id = item.dataset.id || ''; | ||
for (const item of items) { | ||
const id = item.dataset.id || ``; | ||
|
||
this.openPathAndSave(id); | ||
} | ||
}); | ||
} | ||
this.openPathAndSave(id); | ||
} | ||
}); | ||
} | ||
|
||
private addCollapseListener(): void { | ||
const collapseButton = document.querySelector('.js-tree-collapse'); | ||
private addCollapseListener(): void { | ||
const collapseButton = document.querySelector(`.js-tree-collapse`); | ||
|
||
collapseButton?.addEventListener('click', () => { | ||
const items = document.querySelectorAll<HTMLElement>(this.listSelector); | ||
collapseButton?.addEventListener(`click`, () => { | ||
const items = document.querySelectorAll<HTMLElement>(this.listSelector); | ||
|
||
for (const item of items) { | ||
const id = item.dataset.id || ''; | ||
for (const item of items) { | ||
const id = item.dataset.id || ``; | ||
|
||
this.closePath(id); | ||
} | ||
}); | ||
} | ||
this.closePath(id); | ||
} | ||
}); | ||
} | ||
|
||
private addTargetListener(): void { | ||
const targetButton = document.querySelector('.js-tree-target'); | ||
private addTargetListener(): void { | ||
const targetButton = document.querySelector(`.js-tree-target`); | ||
|
||
targetButton?.addEventListener('click', () => { | ||
const targetElement = this.openCurrentPath(); | ||
targetButton?.addEventListener(`click`, () => { | ||
const targetElement = this.openCurrentPath(); | ||
|
||
targetElement?.scrollIntoView(); | ||
}); | ||
} | ||
targetElement?.scrollIntoView(); | ||
}); | ||
} | ||
|
||
private initSaved(): void { | ||
const savedPaths = this.stateManager.getOpenedPaths(); | ||
private initSaved(): void { | ||
const savedPaths = this.stateManager.getOpenedPaths(); | ||
|
||
for (const id of savedPaths) { | ||
this.openPath(id); | ||
} | ||
} | ||
for (const id of savedPaths) { | ||
this.openPath(id); | ||
} | ||
} | ||
|
||
private openCurrentPath(): Element | null { | ||
const pathnameSplit = window.location.pathname.split('/'); | ||
const pathname = `/${pathnameSplit[pathnameSplit.length - 2] || ''}/${ | ||
pathnameSplit[pathnameSplit.length - 1] || '' | ||
}`; | ||
private openCurrentPath(): Element | null { | ||
const pathnameSplit = window.location.pathname.split(`/`); | ||
const pathname = `/${pathnameSplit[pathnameSplit.length - 2] || ``}/${ | ||
pathnameSplit[pathnameSplit.length - 1] || `` | ||
}`; | ||
|
||
const activeElement = document.querySelector( | ||
`.js-category-link[data-id="${pathname}"]`, | ||
); | ||
const activeElement = document.querySelector(`.js-category-link[data-id="${pathname}"]`); | ||
|
||
if (!activeElement) { | ||
return null; | ||
} | ||
if (!activeElement) { | ||
return null; | ||
} | ||
|
||
activeElement.classList.add('_active'); | ||
activeElement.classList.add(`_active`); | ||
|
||
let parent = activeElement.closest<HTMLElement>(this.listSelector); | ||
let parent = activeElement.closest<HTMLElement>(this.listSelector); | ||
|
||
// eslint-disable-next-line no-constant-condition,@typescript-eslint/no-unnecessary-condition | ||
while (true) { | ||
if (!parent) { | ||
break; | ||
} | ||
// eslint-disable-next-line no-constant-condition,@typescript-eslint/no-unnecessary-condition | ||
while (true) { | ||
if (!parent) { | ||
break; | ||
} | ||
|
||
const id = parent.dataset.id || ''; | ||
const id = parent.dataset.id || ``; | ||
|
||
this.openPath(id); | ||
this.openPath(id); | ||
|
||
parent = (parent.parentNode as HTMLElement).closest<HTMLElement>( | ||
this.listSelector, | ||
); | ||
} | ||
parent = (parent.parentNode as HTMLElement).closest<HTMLElement>(this.listSelector); | ||
} | ||
|
||
return activeElement; | ||
} | ||
return activeElement; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,38 @@ | ||
export class StateManager { | ||
private readonly openedPathLsKey = 'opened-path-state'; | ||
private readonly openedPathLsKey = `opened-path-state`; | ||
|
||
private openedPaths: string[] = []; | ||
private openedPaths: string[] = []; | ||
|
||
public constructor() { | ||
const lsOpenedPathState = localStorage.getItem('opened-path-state'); | ||
public constructor() { | ||
const lsOpenedPathState = localStorage.getItem(`opened-path-state`); | ||
|
||
this.openedPaths = lsOpenedPathState | ||
? (JSON.parse(lsOpenedPathState) as string[]) | ||
: []; | ||
} | ||
this.openedPaths = lsOpenedPathState ? (JSON.parse(lsOpenedPathState) as string[]) : []; | ||
} | ||
|
||
/** | ||
* Добавляет path в стейт. | ||
*/ | ||
public addOpenedPath(path: string): void { | ||
this.openedPaths.push(path); | ||
this.updateState(); | ||
} | ||
/** | ||
* Добавляет path в стейт. | ||
*/ | ||
public addOpenedPath(path: string): void { | ||
this.openedPaths.push(path); | ||
this.updateState(); | ||
} | ||
|
||
/** | ||
* Удаляет path из стейта. | ||
*/ | ||
public removeOpenedPath(path: string): void { | ||
this.openedPaths = this.openedPaths.filter( | ||
(savedPath) => savedPath !== path, | ||
); | ||
this.updateState(); | ||
} | ||
/** | ||
* Удаляет path из стейта. | ||
*/ | ||
public removeOpenedPath(path: string): void { | ||
this.openedPaths = this.openedPaths.filter(savedPath => savedPath !== path); | ||
this.updateState(); | ||
} | ||
|
||
/** | ||
* Получает все открытые paths. | ||
*/ | ||
public getOpenedPaths(): string[] { | ||
return this.openedPaths; | ||
} | ||
/** | ||
* Получает все открытые paths. | ||
*/ | ||
public getOpenedPaths(): string[] { | ||
return this.openedPaths; | ||
} | ||
|
||
private updateState(): void { | ||
localStorage.setItem( | ||
this.openedPathLsKey, | ||
JSON.stringify(this.openedPaths), | ||
); | ||
} | ||
private updateState(): void { | ||
localStorage.setItem(this.openedPathLsKey, JSON.stringify(this.openedPaths)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import '../css/custom.css'; | ||
import { HierarchyManager } from './HierarchyManager'; | ||
|
||
import '../css/custom.css'; | ||
|
||
const hierarchyManager = new HierarchyManager(); | ||
|
||
hierarchyManager.init(); |
Oops, something went wrong.