// Make modal window function makeModal({ title = "Message", text = "No text", type = "info", parent = null, stylePos = "fixed", } = {}) { const overlay = document.createElement("div"); Object.assign(overlay.style, { display: "none", position: stylePos, background: "rgba(0 0 0 / 0.8)", opacity: 0, top: "0", left: "0", right: "0", bottom: "0", zIndex: "500", transition: "all .8s", cursor: "pointer", }); const boxModal = document.createElement("div"); Object.assign(boxModal.style, { transition: "all 0.5s", opacity: 0, display: "none", position: stylePos, top: "50%", left: "50%", transform: "translate(-50%,-50%)", background: "#525252", minWidth: "300px", fontFamily: "sans-serif", zIndex: "501", border: "1px solid rgb(255 255 255 / 45%)", }); boxModal.className = "alekpet_modal_window"; const boxModalBody = document.createElement("div"); Object.assign(boxModalBody.style, { display: "flex", flexDirection: "column", textAlign: "center", }); boxModalBody.className = "alekpet_modal_body"; const boxModalHtml = `
${title}
✕
${text}
`; boxModalBody.innerHTML = boxModalHtml; const alekpet_modal_header = boxModalBody.querySelector( ".alekpet_modal_header" ); Object.assign(alekpet_modal_header.style, { display: "flex", alignItems: "center", }); const close = boxModalBody.querySelector(".alekpet_modal_close"); Object.assign(close.style, { cursor: "pointer", }); let parentElement = document.body; if (parent && parent.nodeType === 1) { parentElement = parent; } boxModal.append(boxModalBody); parentElement.append(overlay, boxModal); const removeEvent = new Event("removeElements"); const remove = () => { animateTransitionProps(boxModal, { opacity: 0 }).then(() => animateTransitionProps(overlay, { opacity: 0 }).then(() => { parentElement.removeChild(boxModal); parentElement.removeChild(overlay); }) ); }; boxModal.addEventListener("removeElements", remove); overlay.addEventListener("removeElements", remove); animateTransitionProps(overlay) .then(() => { overlay.addEventListener("click", () => { overlay.dispatchEvent(removeEvent); }); animateTransitionProps(boxModal); }) .then(() => boxModal .querySelector(".alekpet_modal_close") .addEventListener("click", () => boxModal.dispatchEvent(removeEvent)) ); } function findWidget(node, value, attr = "name", func = "find") { return node?.widgets ? node.widgets[func]((w) => Array.isArray(value) ? value.includes(w[attr]) : w[attr] === value ) : null; } function animateTransitionProps( el, props = { opacity: 1 }, preStyles = { display: "block" } ) { Object.assign(el.style, preStyles); return new Promise((res) => { setTimeout(() => { Object.assign(el.style, props); const transstart = () => (el.isAnimating = true); const transchancel = () => (el.isAnimating = false); el.addEventListener("transitionstart", transstart); el.addEventListener("transitioncancel", transchancel); el.addEventListener("transitionend", function transend() { el.isAnimating = false; el.removeEventListener("transitionend", transend); el.removeEventListener("transitionend", transchancel); el.removeEventListener("transitionend", transstart); res(el); }); }, 100); }); } function animateClick(target, params = {}) { const { opacityVal = 0.9, callback = () => {} } = params; if (target?.isAnimating) return; const hide = +target.style.opacity === 0; return animateTransitionProps(target, { opacity: hide ? opacityVal : 0, }) .then(() => showHide({ elements: [target], hide: !hide })) .then(() => callback()); } function showHide({ elements = [], hide = null, displayProp = "block" } = {}) { Array.from(elements).forEach((el) => { if (hide !== null) { el.style.display = !hide ? displayProp : "none"; } else { el.style.display = !el.style.display || el.style.display === "none" ? displayProp : "none"; } }); } function isEmptyObject(obj) { if (!obj) return true; return Object.keys(obj).length === 0 && obj.constructor === Object; } function makeElement(tag, attrs = {}) { if (!tag) tag = "div"; const element = document.createElement(tag); Object.keys(attrs).forEach((key) => { const currValue = attrs[key]; if (key === "class") { if (Array.isArray(currValue)) { element.classList.add(...currValue); } else if (currValue instanceof String || typeof currValue === "string") { element.className = currValue; } } else if (key === "dataset") { try { if (Array.isArray(currValue)) { currValue.forEach((datasetArr) => { const [prop, propval] = Object.entries(datasetArr)[0]; element.dataset[prop] = propval; }); } else { Object.entries(currValue).forEach((datasetArr) => { const [prop, propval] = datasetArr; element.dataset[prop] = propval; }); } } catch (err) { console.log(err); } } else if (key === "style") { if ( typeof currValue === "object" && !Array.isArray(currValue) && Object.keys(currValue).length ) { Object.assign(element[key], currValue); } else if ( typeof currValue === "object" && Array.isArray(currValue) && currValue.length ) { element[key] = [...currValue]; } else if (currValue instanceof String || typeof currValue === "string") { element[key] = currValue; } } else if (["for"].includes(key)) { element.setAttribute(key, currValue); } else if (key === "children") { element.append(...(currValue instanceof Array ? currValue : [currValue])); } else if (key === "parent") { currValue.append(element); } else { element[key] = currValue; } }); return element; } function isValidStyle(opt, strColor) { let op = new Option().style; if (!op.hasOwnProperty(opt)) return { result: false, color: "", color_hex: "" }; op[opt] = strColor; return { result: op[opt] !== "", color_rgb: op[opt], color_hex: rgbToHex(op[opt]), }; } function rgbToHex(rgb) { const regEx = new RegExp(/\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/); if (regEx.test(rgb)) { let [, r, g, b] = regEx.exec(rgb); r = parseInt(r).toString(16); g = parseInt(g).toString(16); b = parseInt(b).toString(16); r = r.length === 1 ? r + "0" : r; g = g.length === 1 ? g + "0" : g; b = b.length === 1 ? b + "0" : b; return `#${r}${g}${b}`; } } async function getDataJSON(url) { try { const response = await fetch(url); const jsonData = await response.json(); return jsonData; } catch (err) { return new Error(err); } } function deepMerge(target, source) { if (source?.nodeType) return; for (let key in source) { if (source[key] instanceof Object && key in target) { Object.assign(source[key], deepMerge(target[key], source[key])); } } Object.assign(target || {}, source); return target; } const THEME_MODAL_WINDOW_BASE = { stylesTitle: { background: "auto", padding: "5px", borderRadius: "6px", marginBottom: "5px", alignSelf: "stretch", }, stylesWrapper: { display: "none", opacity: 0, minWidth: "220px", position: "absolute", left: "50%", top: "50%", transform: "translate(-50%, -50%)", opacity: 0, transition: "all .8s", fontFamily: "monospace", zIndex: 99999, }, stylesBox: { display: "flex", flexDirection: "column", background: "#0e0e0e", padding: "6px", justifyContent: "center", alignItems: "center", gap: "3px", textAlign: "center", borderRadius: "6px", color: "white", border: "2px solid silver", boxShadow: "2px 2px 4px silver", maxWidth: "300px", }, stylesClose: { position: "absolute", top: "-10px", right: "-10px", background: "silver", borderRadius: "50%", width: "20px", height: "20px", cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center", fontSize: "0.8rem", }, }; const THEMES_MODAL_WINDOW = { error: { stylesTitle: { ...THEME_MODAL_WINDOW_BASE.stylesTitle, background: "#8f210f", }, stylesBox: { ...THEME_MODAL_WINDOW_BASE.stylesBox, background: "#3b2222", boxShadow: "3px 3px 6px #141414", border: "1px solid #f91b1b", }, stylesWrapper: { ...THEME_MODAL_WINDOW_BASE.stylesWrapper }, stylesClose: { ...THEME_MODAL_WINDOW_BASE.stylesClose, background: "#3b2222", }, }, warning: { stylesTitle: { ...THEME_MODAL_WINDOW_BASE.stylesTitle, background: "#e99818", }, stylesBox: { ...THEME_MODAL_WINDOW_BASE.stylesBox, background: "#594e32", boxShadow: "3px 3px 6px #141414", border: "1px solid #e99818", }, stylesWrapper: { ...THEME_MODAL_WINDOW_BASE.stylesWrapper }, stylesClose: { ...THEME_MODAL_WINDOW_BASE.stylesClose, background: "#594e32", }, }, normal: { stylesTitle: { ...THEME_MODAL_WINDOW_BASE.stylesTitle, background: "#108f0f", }, stylesBox: { ...THEME_MODAL_WINDOW_BASE.stylesBox, background: "#223b2a", boxShadow: "3px 3px 6px #141414", border: "1px solid #108f0f", }, stylesWrapper: { ...THEME_MODAL_WINDOW_BASE.stylesWrapper }, stylesClose: { ...THEME_MODAL_WINDOW_BASE.stylesClose, background: "#223b2a", }, }, }; const defaultOptions = { auto: { autohide: false, autoshow: false, autoremove: false, propStyles: { opacity: 0 }, propPreStyles: {}, timewait: 2000, }, overlay: { overlay_enabled: false, overlayClasses: [], overlayStyles: {}, }, close: { closeRemove: false, showClose: true }, parent: null, }; function createWindowModal({ textTitle = "Message", textBody = "Hello world!", textFooter = null, classesWrapper = [], stylesWrapper = {}, classesBox = [], stylesBox = {}, classesTitle = [], stylesTitle = {}, classesBody = [], stylesBody = {}, classesClose = [], stylesClose = {}, classesFooter = [], stylesFooter = {}, options = defaultOptions, } = {}) { // Check all options exist const _options = deepMerge( JSON.parse(JSON.stringify(defaultOptions)), options ); const { parent, overlay: { overlay_enabled, overlayClasses, overlayStyles }, close: { closeRemove, showClose }, auto: { autohide, autoshow, autoremove, timewait, propStyles, propPreStyles, }, } = _options; // Function past text(html) function addText(text, parent) { if (!parent) return; switch (typeof text) { case "string": if (/^\<*.\/?\>$/.test(text)) { parent.innerHTML(text); } else { parent.textContent = text; } break; case "object": default: if (Array.isArray(text)) { text.forEach( (element) => (element.nodeType === 1 || element.nodeType === 3) && parent.append(element) ); } else if (text.nodeType === 1 || text.nodeType === 3) parent.append(text); } } // Overlay let overlayElement = null; if (overlay_enabled) { overlayElement = makeElement("div", { class: [...overlayClasses], style: { display: "none", position: "fixed", background: "rgba(0 0 0 / 0.8)", opacity: 0, top: 0, left: 0, right: 0, bottom: 0, zIndex: 99999, transition: "all .8s", cursor: "pointer", ...overlayStyles, }, }); } // Wrapper const wrapper_settings = makeElement("div", { class: ["alekpet__wrapper__window", ...classesWrapper], }); Object.assign(wrapper_settings.style, { ...THEME_MODAL_WINDOW_BASE.stylesWrapper, ...stylesWrapper, }); // Box const box__settings = makeElement("div", { class: ["alekpet__window__box", ...classesBox], }); Object.assign(box__settings.style, { ...THEME_MODAL_WINDOW_BASE.stylesBox, ...stylesBox, }); // Title let box_settings_title = ""; if (textTitle) { box_settings_title = makeElement("div", { class: ["alekpet__window__title", ...classesTitle], }); Object.assign(box_settings_title.style, { ...THEME_MODAL_WINDOW_BASE.stylesTitle, ...stylesTitle, }); // Add text (html) to title addText(textTitle, box_settings_title); } // Body let box_settings_body = ""; if (textBody) { box_settings_body = makeElement("div", { class: ["alekpet__window__body", ...classesBody], }); Object.assign(box_settings_body.style, { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "5px", textWrap: "wrap", ...stylesBody, }); // Add text (html) to body addText(textBody, box_settings_body); } // Close button const close__box__button = makeElement("div", { class: ["close__box__button", ...classesClose], textContent: "✖", }); Object.assign(close__box__button.style, { ...THEME_MODAL_WINDOW_BASE.stylesClose, ...stylesClose, }); if (!showClose) close__box__button.style.display = "none"; const closeEvent = new Event("closeModal"); const closeModalWindow = function () { overlay_enabled ? animateTransitionProps(overlayElement, { opacity: 0, }) .then(() => animateTransitionProps(wrapper_settings, { opacity: 0, }) ) .then(() => { if (closeRemove) { parent.removeChild(wrapper_settings); parent.removeChild(overlayElement); } else { showHide({ elements: [wrapper_settings, overlayElement] }); } }) : animateTransitionProps(wrapper_settings, { opacity: 0, }).then(() => { showHide({ elements: [wrapper_settings] }); }); }; close__box__button.addEventListener("closeModal", closeModalWindow); close__box__button.addEventListener("click", () => close__box__button.dispatchEvent(closeEvent) ); close__box__button.onmouseenter = () => { close__box__button.style.opacity = 0.8; }; close__box__button.onmouseleave = () => { close__box__button.style.opacity = 1; }; box__settings.append(box_settings_title, box_settings_body); // Footer if (textFooter) { const box_settings_footer = makeElement("div", { class: [...classesFooter], }); Object.assign(box_settings_footer.style, { ...stylesFooter, }); // Add text (html) to body addText(textFooter, box_settings_footer); box__settings.append(box_settings_footer); } wrapper_settings.append(close__box__button, box__settings); if (parent && parent.nodeType === 1) { if (overlay_enabled) parent.append(overlayElement); parent.append(wrapper_settings); if (autoshow) { overlay_enabled ? animateClick(overlayElement).then(() => animateClick(wrapper_settings).then( () => autohide && setTimeout( () => animateTransitionProps( wrapper_settings, { ...propStyles }, { ...propPreStyles } ) .then(() => animateTransitionProps( overlayElement, { ...propStyles }, { ...propPreStyles } ) ) .then(() => { if (autoremove) { parent.removeChild(wrapper_settings); parent.removeChild(overlayElement); } }), timewait ) ) ) : animateClick(wrapper_settings).then( () => autohide && setTimeout( () => animateTransitionProps( wrapper_settings, { ...propStyles }, { ...propPreStyles } ).then( () => autoremove && parent.removeChild(wrapper_settings) ), timewait ) ); } } return wrapper_settings; } export { makeModal, createWindowModal, animateTransitionProps, animateClick, showHide, makeElement, getDataJSON, isEmptyObject, isValidStyle, rgbToHex, findWidget, THEMES_MODAL_WINDOW, };