@siberiaweb/components
Version:
269 lines (268 loc) • 9.85 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import CSS from "./CSS";
import "./ModalForm.css";
import HTMLElementUtils from "../utils/HTMLElementUtils";
/**
* Модальная форма.
*/
export default class ModalForm {
/**
* Конструктор.
*
* @param template Шаблон содержания.
* @param properties Свойства.
*/
constructor(template, properties = {}) {
/**
* Функция модального разрешения.
*/
this.modalResolve = null;
/**
* Модальный результат.
*/
this.modalResult = "undefined";
/**
* Элемент для установки фокуса.
*/
this._focusElement = null;
/**
* Предотвращение получения событий от клавиатуры элементами модальной формы.
*/
this.preventKeyboardEvents = false;
this.host = this.createHost(template.dataset["modalFormId"]);
this.overlay = this.createOverlay();
this.closeOnClickOutOfBounds = (properties.closeOnClickOutOfBounds === undefined) ||
properties.closeOnClickOutOfBounds;
this.closeOnEscape = (properties.closeOnEscape === undefined) || properties.closeOnEscape;
this.host.appendChild(template.content.cloneNode(true));
this.overlay.appendChild(this.host);
this.initControls();
if (!ModalForm.globalEventListenersAssigned) {
document.addEventListener("keydown", ModalForm.documentKeyDownEventListener);
document.addEventListener("focusin", ModalForm.documentFocusInEventListener);
}
}
/**
* Создание хоста.
*/
createHost(id) {
let container = document.createElement("div");
container.classList.add(CSS.MODAL_FORM);
if (id) {
container.id = id;
}
container.addEventListener("keydown", (event) => {
if (this.preventKeyboardEvents) {
event.preventDefault();
event.stopImmediatePropagation();
}
}, {
capture: true
});
return container;
}
/**
* Создание подложки.
*/
createOverlay() {
let container = document.createElement("div");
container.classList.add(CSS.OVERLAY);
container.addEventListener("click", (event) => {
if ((event.target === container) && this.closeOnClickOutOfBounds) {
this.close("cancel");
}
});
return container;
}
/**
* Инициализация элементов управления.
*/
initControls() {
let modalOkElements = Array.from(this.host.querySelectorAll("[data-mr-ok]"));
for (const element of modalOkElements) {
element.addEventListener("click", () => {
this.close("ok");
});
}
let modalCancelElements = Array.from(this.host.querySelectorAll("[data-mr-cancel]"));
for (const element of modalCancelElements) {
element.addEventListener("click", () => {
this.close("cancel");
});
}
}
/**
* Вывод.
*/
show() {
return new Promise((resolve => {
this.modalResolve = resolve;
if (ModalForm.shawnForms.length === 0) {
document.body.classList.add(CSS.OVERFLOW_HIDDEN);
}
else {
ModalForm.shawnForms[ModalForm.shawnForms.length - 1].getOverlay().classList.add(CSS.OVERFLOW_HIDDEN);
}
ModalForm.shawnForms.push(this);
document.body.appendChild(this.overlay);
window.requestAnimationFrame(() => {
let enabledControls = this.getEnabledControls();
if (enabledControls.length > 0) {
enabledControls[0].focus();
}
});
}));
}
/**
* Закрытие.
*/
close(result = "undefined") {
(() => __awaiter(this, void 0, void 0, function* () {
this.preventKeyboardEvents = true;
this.overlay.classList.add(CSS.CLOSING);
yield HTMLElementUtils.waitForAnimation(this.overlay);
this.overlay.classList.remove(CSS.CLOSING);
this.preventKeyboardEvents = false;
this.modalResult = result;
this.overlay.remove();
this.overlay.classList.remove(CSS.OVERFLOW_HIDDEN);
ModalForm.shawnForms.splice(ModalForm.shawnForms.indexOf(this), 1);
if (ModalForm.shawnForms.length === 0) {
document.body.classList.remove(CSS.OVERFLOW_HIDDEN);
}
else {
let topModalForm = ModalForm.shawnForms[ModalForm.shawnForms.length - 1];
if (topModalForm.focusElement !== null) {
topModalForm.focusElement.focus();
}
}
if (this.modalResolve !== null) {
this.modalResolve();
}
}))();
}
/**
* Получение модального результата.
*/
getModalResult() {
return this.modalResult;
}
/**
* Получение элемента формы по идентификатору.
*
* @param id Идентификатор.
*/
getElementById(id) {
return this.getHost().querySelector(`[data-mf-id="${id}"]`);
}
/**
* Получение хоста.
*/
getHost() {
return this.host;
}
/**
* Получение подложки.
*/
getOverlay() {
return this.overlay;
}
/**
* Получение признака закрытия модальной формы при клике вне границ.
*/
isCloseOnClickOutOfBounds() {
return this.closeOnClickOutOfBounds;
}
/**
* Получение признака закрытия модальной формы по клавише Escape.
*/
isCloseOnEscape() {
return this.closeOnEscape;
}
/**
* Получение активных элементов управления.
*/
getEnabledControls() {
let controls = Array.from(this.host.querySelectorAll('a, button, input, select, textarea, [ tabindex ]:not( [ tabindex="-1" ] )'));
return controls.filter((control) => {
return !control.hasAttribute("disabled");
});
}
/**
* Получение элемента для фокусировки.
*/
get focusElement() {
return this._focusElement;
}
/**
* Установка элемента для фокусировки.
*
* @param value Значение.
*/
set focusElement(value) {
this._focusElement = value;
}
/**
* Глобальный обработчик нажатия клавиш.
*
* @param event Событие.
*/
static documentKeyDownEventListener(event) {
if ((event.code === "Escape") && (ModalForm.shawnForms.length > 0)) {
let modalForm = ModalForm.shawnForms[ModalForm.shawnForms.length - 1];
if (modalForm.isCloseOnEscape()) {
modalForm.close("cancel");
}
}
}
/**
* Глобальный обработчик установки фокуса.
*
* @param event Событие.
*/
static documentFocusInEventListener(event) {
if (!(event.target instanceof HTMLElement) || (ModalForm.shawnForms.length === 0)) {
return;
}
let modalForm = ModalForm.shawnForms[ModalForm.shawnForms.length - 1];
if (modalForm.getHost().contains(event.target)) {
modalForm.focusElement = event.target;
return;
}
let enabledControls = modalForm.getEnabledControls();
if (enabledControls.length === 0) {
return;
}
if (modalForm.focusElement === null) {
event.preventDefault();
enabledControls[0].focus();
return;
}
if (enabledControls[0] === modalForm.focusElement) {
event.preventDefault();
enabledControls[enabledControls.length - 1].focus();
return;
}
if (enabledControls[enabledControls.length - 1] === modalForm.focusElement) {
event.preventDefault();
enabledControls[0].focus();
return;
}
}
}
/**
* Выведенные на экран формы.
*/
ModalForm.shawnForms = [];
/**
* Признак установки глобальных обработчиков.
*/
ModalForm.globalEventListenersAssigned = false;