icynet-auth-server/src/fe/ts/modal/modal.ts

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();
}
}