import { lang } from './constants.js'; /** * @typedef {Object} ModalResult * @property {boolean} cancelled Was dialog cancelled * @property {string} [action] Click action name * @property {Object} [data] Additional data */ /** * @callback ModalCallback * @param {ModalResult} result */ export default class uModal { /** * Builds modal dialog * Positive click handlers bound to elements with class 'button-resolve'. * Negative click handlers bound to elements with class 'button-reject'. * Optional attribute 'data-action' value is returned in {@link ModalResult.action} * @param {(string|Node|NodeList|Array.)} content */ constructor(content) { const modal = document.createElement('div'); modal.setAttribute('id', 'modal'); const modalHeader = document.createElement('div'); modalHeader.setAttribute('id', 'modal-header'); const buttonClose = document.createElement('button'); buttonClose.setAttribute('id', 'modal-close'); buttonClose.setAttribute('type', 'button'); buttonClose.setAttribute('class', 'button-reject'); const img = document.createElement('img'); img.setAttribute('src', 'images/close.svg'); img.setAttribute('alt', lang.strings['close']); buttonClose.append(img); modalHeader.append(buttonClose); modal.append(modalHeader); const modalBody = document.createElement('div'); modalBody.setAttribute('id', 'modal-body'); if (typeof content === 'string') { modalBody.innerHTML = content; } else if (content instanceof NodeList || content instanceof Array) { for (const node of content) { modalBody.append(node); } } else { modalBody.append(content); } modal.append(modalBody); this._modal = modal; this.visible = false; } /** * @return {HTMLDivElement} */ get modal() { return this._modal; } /** * Show modal dialog * @returns {Promise} */ show() { return new Promise((resolve) => { this.addListeners(resolve); if (!this.visible) { document.body.append(this._modal); } }); } /** * Add listeners * @param {ModalCallback} resolve callback */ addListeners(resolve) { this._modal.querySelectorAll('.button-resolve').forEach((el) => { el.addEventListener('click', () => { uModal.onClick(el, resolve, { cancelled: false, action: el.getAttribute('data-action') }); }); }); this._modal.querySelectorAll('.button-reject').forEach((el) => { el.addEventListener('click', () => { uModal.onClick(el, resolve, { cancelled: true }); }); }); } /** * On click action * Handles optional confirmation dialog * @param {Element} el Clicked element * @param {ModalCallback} resolve callback * @param {ModalResult} result */ static onClick(el, resolve, result) { const confirm = el.getAttribute('data-confirm'); let proceed = true; if (confirm) { proceed = this.isConfirmed(confirm); } if (proceed) { resolve(result); } } /** * Show confirmation dialog and return user decision * @param {string} message * @return {boolean} True if confirmed, false otherwise */ static isConfirmed(message) { return confirm(message); } /** * Remove modal dialog */ hide() { document.body.removeChild(this._modal); this.visible = false } }