99 lines
2.5 KiB
TypeScript
99 lines
2.5 KiB
TypeScript
export class Modal {
|
|
public triggers?: NodeListOf<HTMLElement>;
|
|
public modal?: HTMLElement;
|
|
protected focusLock: HTMLElement[] = [];
|
|
protected trigger?: HTMLElement;
|
|
|
|
constructor(public name: string) {}
|
|
|
|
public reset(): void {
|
|
if (!this.modal) {
|
|
return;
|
|
}
|
|
|
|
this.modal.style.display = 'none';
|
|
this.removeFocusLock();
|
|
}
|
|
|
|
public open(): void {
|
|
if (!this.modal) {
|
|
return;
|
|
}
|
|
|
|
this.modal.style.display = 'block';
|
|
this.createFocusLock();
|
|
}
|
|
|
|
public initialize(): void {
|
|
this.triggers = document.querySelectorAll(
|
|
`[data-modal-trigger="${this.name}"]`,
|
|
) as NodeListOf<HTMLElement>;
|
|
|
|
this.modal = document.querySelector(
|
|
`[data-modal="${this.name}"]`,
|
|
) as HTMLElement;
|
|
|
|
this.triggers.forEach((item) =>
|
|
item.addEventListener('click', (evt) => {
|
|
evt.preventDefault();
|
|
this.trigger = item;
|
|
this.open();
|
|
}),
|
|
);
|
|
|
|
if (this.modal) {
|
|
const attrLabel = `modal_${this.name}_label`;
|
|
const label = this.modal.querySelector('.modal__title');
|
|
this.modal.setAttribute('aria-modal', 'true');
|
|
this.modal.setAttribute('aria-labelledby', attrLabel);
|
|
label.setAttribute('id', attrLabel);
|
|
}
|
|
}
|
|
|
|
private getFocusable(): HTMLElement[] {
|
|
const focusable = Array.from(
|
|
this.modal.querySelectorAll(
|
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
|
|
) as NodeListOf<HTMLElement>,
|
|
).filter(
|
|
(item) => item.offsetParent !== null && !this.focusLock.includes(item),
|
|
);
|
|
|
|
const firstFocusable = focusable[0];
|
|
const lastFocusable = focusable[focusable.length - 1];
|
|
|
|
return [firstFocusable, lastFocusable];
|
|
}
|
|
|
|
private createFocusLock(): void {
|
|
const startFocus = document.createElement('div');
|
|
startFocus.setAttribute('tabindex', '0');
|
|
|
|
const stopFocus = document.createElement('div');
|
|
stopFocus.setAttribute('tabindex', '0');
|
|
|
|
this.modal.prepend(startFocus);
|
|
this.modal.appendChild(stopFocus);
|
|
this.focusLock = [startFocus, stopFocus];
|
|
|
|
stopFocus.addEventListener('focus', (event) => {
|
|
event.preventDefault();
|
|
this.getFocusable()[0].focus();
|
|
});
|
|
|
|
startFocus.addEventListener('focus', (event) => {
|
|
event.preventDefault();
|
|
this.getFocusable()[1].focus();
|
|
});
|
|
|
|
this.getFocusable()[0].focus();
|
|
}
|
|
|
|
private removeFocusLock(): void {
|
|
this.focusLock.forEach((item) => {
|
|
item.parentElement.removeChild(item);
|
|
});
|
|
this.trigger?.focus();
|
|
}
|
|
}
|