interface OnionElement extends HTMLElement {
onion?: {
abort?: () => void;
}
}
interface ClassList {
[key: string]: boolean;
}
function setClasses(el: Element, classes: ClassList) {
Object.entries(classes)
.filter(([key]) => key.length)
.forEach(([key, value]) => el.classList.toggle(key, value));
}
function addEndListener(el: Element, listener: () => void) {
el.addEventListener("transitionend", listener);
el.addEventListener("animationend", listener);
}
function removeEndListener(el: Element, listener: () => void) {
el.removeEventListener("transitionend", listener);
el.removeEventListener("animationend", listener);
}
function show(el: OnionElement, token: string = "") {
if (!(el instanceof HTMLElement)) return;
if (el.classList.contains("is-opening"))
return;
el.onion?.abort?.();
if (token === "is-opening")
token = "";
const handleEnd = (event?: Event) => {
if (event && event.target !== el) return;
setClasses(el, {
"is-open": true,
"is-opening": true,
"is-closing": false,
[token]: false,
});
el.onion?.abort?.();
}
const timeoutID = setTimeout(handleEnd, 2000);
el.onion = Object.assign(el.onion || {}, {
abort: () => {
removeEndListener(el, handleEnd);
clearTimeout(timeoutID);
setClasses(el, { [token]: false });
delete el.onion?.abort;
}
});
addEndListener(el, handleEnd);
setClasses(el, {
"is-open": true,
"is-opening": false,
"is-closing": false,
[token]: false,
});
setTimeout(() => setClasses(el, {
"is-open": true,
"is-opening": true,
"is-closing": false,
[token]: true,
}));
}
function hide(el: OnionElement, token: string = "") {
if (!(el instanceof HTMLElement)) return;
if (el.classList.contains("is-closing") || !el.classList.contains("is-open"))
return;
el.onion?.abort?.();
if (token === "is-closing")
token = "";
const handleEnd = (event?: Event) => {
if (event && event.target !== el) return;
setClasses(el, {
"is-open": false,
"is-opening": false,
"is-closing": false,
[token]: false,
});
el.onion?.abort?.();
}
const timeoutID = setTimeout(handleEnd, 2000);
el.onion = Object.assign(el.onion || {}, {
abort: () => {
removeEndListener(el, handleEnd);
clearTimeout(timeoutID);
setClasses(el, { [token]: false });
delete el.onion?.abort;
}
});
addEndListener(el, handleEnd);
setClasses(el, {
"is-open": true,
"is-opening": false,
"is-closing": true,
[token]: true,
});
}
function toggle(el: OnionElement, force?: boolean, openingToken: string = "", closingToken: string = "") {
if (!(el instanceof HTMLElement)) return;
if (typeof force === 'undefined') {
if (el.classList.contains('is-opening')) {
hide(el, closingToken);
} else {
show(el, openingToken);
}
} else {
if (force) show(el, openingToken);
else hide(el, closingToken);
}
}
export default {
show,
hide,
toggle
};