WIP
This commit is contained in:
parent
34ac200437
commit
39b0160064
136 changed files with 7129 additions and 1950 deletions
|
|
@ -1,5 +1,8 @@
|
|||
export class CollapsiblePanelAnimator {
|
||||
private static nextPanelId = 0;
|
||||
|
||||
private _isOpen = false;
|
||||
private focusBeforeOpen: HTMLElement | null = null;
|
||||
|
||||
public onOpen: () => unknown = () => {};
|
||||
public onClose: () => unknown = () => {};
|
||||
|
|
@ -9,25 +12,64 @@ export class CollapsiblePanelAnimator {
|
|||
private readonly collapsibleContent: HTMLElement,
|
||||
ignoreForCloseOnClick: HTMLElement
|
||||
) {
|
||||
const panelId =
|
||||
collapsibleContent.id ||
|
||||
`collapsible-panel-${CollapsiblePanelAnimator.nextPanelId++}`;
|
||||
collapsibleContent.id = panelId;
|
||||
|
||||
toggleButton.setAttribute('aria-controls', panelId);
|
||||
if (!collapsibleContent.hasAttribute('role')) {
|
||||
collapsibleContent.setAttribute('role', 'region');
|
||||
}
|
||||
if (!collapsibleContent.hasAttribute('aria-label')) {
|
||||
const label =
|
||||
toggleButton.getAttribute('aria-label') || toggleButton.textContent?.trim();
|
||||
collapsibleContent.setAttribute('aria-label', `${label || 'Panel'} panel`);
|
||||
}
|
||||
if (!collapsibleContent.hasAttribute('tabindex')) {
|
||||
collapsibleContent.tabIndex = -1;
|
||||
}
|
||||
|
||||
toggleButton.addEventListener('click', this.toggle.bind(this));
|
||||
window.addEventListener(
|
||||
'click',
|
||||
(event) => !ignoreForCloseOnClick.contains(event.target as Node) && this.close()
|
||||
);
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (this._isOpen && event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
this.syncAccessibility();
|
||||
}
|
||||
|
||||
public open() {
|
||||
if (this._isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusBeforeOpen =
|
||||
document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
||||
this._isOpen = true;
|
||||
this.collapsibleContent.classList.remove('hidden');
|
||||
this.toggleButton.classList.add('active');
|
||||
this.syncAccessibility();
|
||||
this.onOpen();
|
||||
this.focusPanel();
|
||||
}
|
||||
|
||||
public close() {
|
||||
if (!this._isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusWasInside = this.collapsibleContent.contains(document.activeElement);
|
||||
this._isOpen = false;
|
||||
this.collapsibleContent.classList.add('hidden');
|
||||
this.toggleButton.classList.remove('active');
|
||||
this.syncAccessibility();
|
||||
this.onClose();
|
||||
|
||||
if (focusWasInside) {
|
||||
(this.focusBeforeOpen ?? this.toggleButton).focus({ preventScroll: true });
|
||||
}
|
||||
}
|
||||
|
||||
public toggle() {
|
||||
|
|
@ -41,4 +83,20 @@ export class CollapsiblePanelAnimator {
|
|||
public get isOpen() {
|
||||
return this._isOpen;
|
||||
}
|
||||
|
||||
private syncAccessibility() {
|
||||
this.collapsibleContent.classList.toggle('hidden', !this._isOpen);
|
||||
this.toggleButton.classList.toggle('active', this._isOpen);
|
||||
this.toggleButton.setAttribute('aria-expanded', String(this._isOpen));
|
||||
this.collapsibleContent.setAttribute('aria-hidden', String(!this._isOpen));
|
||||
this.collapsibleContent.inert = !this._isOpen;
|
||||
}
|
||||
|
||||
private focusPanel() {
|
||||
requestAnimationFrame(() => {
|
||||
if (this._isOpen) {
|
||||
this.collapsibleContent.focus({ preventScroll: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue