|
|
|
|
|
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 = ` |
|
|
<div class="alekpet_modal_header" style="display: flex; align-items: center; background: #222; width: 100%;justify-content: center;"> |
|
|
<div class="alekpet_modal_title" style="flex-basis: 85%; text-align: center;padding: 5px;">${title}</div> |
|
|
<div class="alekpet_modal_close">✕</div> |
|
|
</div> |
|
|
<div class="alekpet_modal_description" style="padding: 8px;">${text}</div>`; |
|
|
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) => 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, opacityVal = 0.9) { |
|
|
if (target?.isAnimating) return; |
|
|
|
|
|
const hide = +target.style.opacity === 0; |
|
|
return animateTransitionProps(target, { |
|
|
opacity: hide ? opacityVal : 0, |
|
|
}).then(() => showHide({ elements: [target], hide: !hide })); |
|
|
} |
|
|
|
|
|
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 { |
|
|
const [prop, propval] = Object.entries(currValue)[0]; |
|
|
element.dataset[prop] = propval; |
|
|
} |
|
|
} catch (err) { |
|
|
console.log(err); |
|
|
} |
|
|
} 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: "#8f210f", |
|
|
padding: "5px", |
|
|
borderRadius: "6px", |
|
|
marginBottom: "5px", |
|
|
alignSelf: "stretch", |
|
|
}, |
|
|
stylesBox: { |
|
|
background: "auto", |
|
|
color: "auto", |
|
|
border: 0, |
|
|
boxShadow: "none", |
|
|
padding: 0, |
|
|
fontFamily: "sans-serif", |
|
|
}, |
|
|
stylesWrapper: { |
|
|
display: "flex", |
|
|
background: "#3b2222", |
|
|
justifyContent: "center", |
|
|
flexDirection: "column", |
|
|
alignItems: "stretch", |
|
|
textAlign: "center", |
|
|
borderRadius: "6px", |
|
|
boxShadow: "3px 3px 6px #141414", |
|
|
border: "1px solid #f91b1b", |
|
|
color: "white", |
|
|
padding: "6px", |
|
|
fontFamily: "sans-serif", |
|
|
lineHeight: 1.5, |
|
|
maxWidth: "300px", |
|
|
}, |
|
|
stylesClose: { background: "#3b2222" }, |
|
|
}; |
|
|
|
|
|
const THEMES_MODAL_WINDOW = { |
|
|
error: { ...THEME_MODAL_WINDOW_BASE }, |
|
|
warning: { |
|
|
stylesTitle: { |
|
|
...THEME_MODAL_WINDOW_BASE.stylesTitle, |
|
|
background: "#e99818", |
|
|
}, |
|
|
stylesBox: { ...THEME_MODAL_WINDOW_BASE.stylesBox }, |
|
|
stylesWrapper: { |
|
|
...THEME_MODAL_WINDOW_BASE.stylesWrapper, |
|
|
background: "#594e32", |
|
|
boxShadow: "3px 3px 6px #141414", |
|
|
border: "1px solid #e99818", |
|
|
}, |
|
|
stylesClose: { |
|
|
...THEME_MODAL_WINDOW_BASE.stylesClose, |
|
|
background: "#594e32", |
|
|
}, |
|
|
}, |
|
|
normal: { |
|
|
stylesTitle: { |
|
|
...THEME_MODAL_WINDOW_BASE.stylesTitle, |
|
|
background: "#108f0f", |
|
|
}, |
|
|
stylesBox: { ...THEME_MODAL_WINDOW_BASE.stylesBox }, |
|
|
stylesWrapper: { |
|
|
...THEME_MODAL_WINDOW_BASE.stylesWrapper, |
|
|
background: "#223b2a", |
|
|
boxShadow: "3px 3px 6px #141414", |
|
|
border: "1px solid #108f0f", |
|
|
}, |
|
|
stylesClose: { |
|
|
...THEME_MODAL_WINDOW_BASE.stylesClose, |
|
|
background: "#223b2a", |
|
|
}, |
|
|
}, |
|
|
}; |
|
|
|
|
|
const defaultOptions = { |
|
|
auto: { |
|
|
autohide: false, |
|
|
autoshow: false, |
|
|
autoremove: false, |
|
|
propStyles: { opacity: 0 }, |
|
|
propPreStyles: {}, |
|
|
timewait: 2000, |
|
|
}, |
|
|
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, |
|
|
} = {}) { |
|
|
|
|
|
options = deepMerge({ ...defaultOptions }, options); |
|
|
|
|
|
const { |
|
|
parent, |
|
|
auto: { |
|
|
autohide, |
|
|
autoshow, |
|
|
autoremove, |
|
|
timewait, |
|
|
propStyles, |
|
|
propPreStyles, |
|
|
}, |
|
|
} = options; |
|
|
|
|
|
|
|
|
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) => parent.append(element)); |
|
|
} else if (text.nodeType === 1 || text.nodeType === 3) |
|
|
parent.append(text); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const wrapper_settings = makeElement("div", { |
|
|
class: ["alekpet__wrapper__window", ...classesWrapper], |
|
|
}); |
|
|
|
|
|
Object.assign(wrapper_settings.style, { |
|
|
display: "none", |
|
|
opacity: 0, |
|
|
minWidth: "220px", |
|
|
position: "absolute", |
|
|
left: "50%", |
|
|
top: "50%", |
|
|
transform: "translate(-50%, -50%)", |
|
|
opacity: 0, |
|
|
transition: "all .8s", |
|
|
...stylesWrapper, |
|
|
}); |
|
|
|
|
|
|
|
|
const box__settings = makeElement("div", { |
|
|
class: ["alekpet__window__box", ...classesBox], |
|
|
}); |
|
|
Object.assign(box__settings.style, { |
|
|
display: "flex", |
|
|
flexDirection: "column", |
|
|
background: "#0e0e0e", |
|
|
padding: "10px", |
|
|
justifyContent: "center", |
|
|
alignItems: "center", |
|
|
gap: "10px", |
|
|
textAlign: "center", |
|
|
borderRadius: "6px", |
|
|
color: "white", |
|
|
border: "2px solid silver", |
|
|
fontFamily: "monospace", |
|
|
boxShadow: "2px 2px 4px silver", |
|
|
...stylesBox, |
|
|
}); |
|
|
|
|
|
|
|
|
const box_settings_title = makeElement("div", { |
|
|
class: ["alekpet__window__title", , ...classesTitle], |
|
|
}); |
|
|
Object.assign(box_settings_title.style, { |
|
|
...stylesTitle, |
|
|
}); |
|
|
|
|
|
addText(textTitle, box_settings_title); |
|
|
|
|
|
|
|
|
const 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, |
|
|
}); |
|
|
|
|
|
addText(textBody, box_settings_body); |
|
|
|
|
|
|
|
|
const close__box__button = makeElement("div", { |
|
|
class: ["close__box__button", ...classesClose], |
|
|
textContent: "✖", |
|
|
}); |
|
|
Object.assign(close__box__button.style, { |
|
|
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", |
|
|
...stylesClose, |
|
|
}); |
|
|
|
|
|
close__box__button.addEventListener("click", () => |
|
|
animateTransitionProps(wrapper_settings, { |
|
|
opacity: 0, |
|
|
}).then(() => { |
|
|
showHide({ elements: [wrapper_settings] }); |
|
|
}) |
|
|
); |
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
if (textFooter) { |
|
|
const box_settings_footer = makeElement("div", { |
|
|
class: [...classesFooter], |
|
|
}); |
|
|
Object.assign(box_settings_footer.style, { |
|
|
...stylesFooter, |
|
|
}); |
|
|
|
|
|
|
|
|
addText(textFooter, box_settings_footer); |
|
|
|
|
|
box__settings.append(box_settings_footer); |
|
|
} |
|
|
|
|
|
wrapper_settings.append(close__box__button, box__settings); |
|
|
|
|
|
if (parent && parent.nodeType === 1) { |
|
|
parent.append(wrapper_settings); |
|
|
|
|
|
if (autoshow) { |
|
|
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, |
|
|
}; |
|
|
|