breakout71/src/asyncAlert.ts
Renan LE CARO 94ffb80f49 wip
2025-05-03 16:42:03 +02:00

237 lines
5.9 KiB
TypeScript

import { t } from "./i18n/i18n";
import { isOptionOn } from "./options";
import { hideAnyTooltip } from "./tooltip";
export let alertsOpen = 0,
closeModal: null | (() => void) = null;
export type AsyncAlertAction<t> = {
text?: string;
value?: t;
help?: string;
disabled?: boolean;
icon?: string;
className?: string;
actionLabel?: string;
};
const popupWrap = document.getElementById("popup") as HTMLDivElement;
const closeModaleButton = document.getElementById(
"close-modale",
) as HTMLButtonElement;
closeModaleButton.addEventListener("click", (e) => {
e.preventDefault();
if (closeModal) closeModal();
});
closeModaleButton.title = t("play.close_modale_window_tooltip");
let lastClickedItemIndex = -1;
export function requiredAsyncAlert<t>(p: {
title?: string;
content: (string | AsyncAlertAction<t>)[];
className?: string;
}): Promise<t> {
return asyncAlert({ ...p, allowClose: false });
}
export async function asyncAlert<t>({
title,
content = [],
allowClose = true,
className = "",
}: {
title?: string;
content: (string | AsyncAlertAction<t>)[];
allowClose?: boolean;
className?: string;
}): Promise<t | void> {
hideAnyTooltip();
updateAlertsOpen(+1);
return new Promise((resolve) => {
popupWrap.className = className;
closeModaleButton.style.display = allowClose ? "" : "none";
const popup = document.createElement("div");
let closed = false;
function closeWithResult(value: t | undefined) {
if (closed) return;
closed = true;
Array.prototype.forEach.call(
popup.querySelectorAll("button:not([disabled])"),
(b) => (b.disabled = true),
);
document.body.style.minHeight = document.body.scrollHeight + "px";
setTimeout(() => (document.body.style.minHeight = ""), 0);
popup.remove();
resolve(value);
}
if (allowClose) {
closeModal = () => {
closeWithResult(undefined);
};
} else {
closeModal = null;
}
if (title) {
const h1 = document.createElement("h1");
h1.innerHTML = title;
popup.appendChild(h1);
}
content
?.filter((i) => i)
.forEach((entry, index) => {
if (!entry) return;
if (typeof entry == "string") {
const p = document.createElement("div");
p.innerHTML = entry;
popup.appendChild(p);
return;
}
let addto: HTMLElement;
if (popup.lastChild?.nodeName == "SECTION") {
addto = popup.lastChild as HTMLElement;
} else {
addto = document.createElement("section");
addto.className = "actions";
popup.appendChild(addto);
}
addButton(entry, index, addto, closeWithResult);
});
popup.addEventListener(
"click",
(e) => {
const target = e.target as HTMLElement;
if (target.getAttribute("data-resolve-to")) {
closeWithResult(target.getAttribute("data-resolve-to") as t);
}
},
true,
);
popupWrap.appendChild(popup);
(
popupWrap.querySelector(
`section.actions > button.needs-focus`,
) as HTMLButtonElement
)?.focus();
lastClickedItemIndex = -1;
}).then(
(v: unknown) => {
updateAlertsOpen(-1);
closeModal = null;
return v as t | undefined;
},
() => {
closeModal = null;
updateAlertsOpen(-1);
},
);
}
function updateAlertsOpen(delta: number) {
alertsOpen += delta;
if (alertsOpen > 1) {
alert("Two alerts where opened at once");
}
document.body.classList[alertsOpen ? "add" : "remove"]("has-alert-open");
}
function addButton<t>(
entry: AsyncAlertAction<t>,
index: number,
addto: HTMLElement,
closeWithResult: (r: t) => void,
) {
const {
text = "",
value = null,
help = "",
disabled = false,
className = "",
icon = "",
tooltip = "",
actionLabel = "",
} = entry;
const buttonWrap = document.createElement("div");
addto.appendChild(buttonWrap);
if (actionLabel) {
buttonWrap.className = className + " upgrade";
buttonWrap.innerHTML = icon;
const description = document.createElement("p");
description.innerHTML = `
<strong>${text}</strong>
${help}
`;
description.setAttribute("data-tooltip", tooltip);
description.setAttribute("data-help-content", tooltip);
buttonWrap.appendChild(description);
const button = document.createElement("button");
button.textContent = actionLabel;
button.disabled = disabled;
if (!disabled)
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
closeWithResult(value);
// Focus "same" button if it's still there
lastClickedItemIndex = index;
});
button.className = lastClickedItemIndex === index ? " needs-focus" : "";
buttonWrap.appendChild(button);
return;
}
const button = document.createElement("button");
button.innerHTML = `
${icon}
<div>
<strong>${text}</strong>
<em>${help || ""}</em>
</div>`;
if (disabled) {
button.setAttribute("disabled", "disabled");
} else {
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
closeWithResult(value);
// Focus "same" button if it's still there
lastClickedItemIndex = index;
});
}
button.className =
className +
(lastClickedItemIndex === index ? " needs-focus" : "") +
" choice-button";
buttonWrap.appendChild(button);
if (tooltip) {
button.setAttribute("data-tooltip", tooltip);
// if (!isOptionOn("mobile-mode")) {
// // const helpBtn = document.createElement("button");
// // helpBtn.innerText = "?";
// // helpBtn.setAttribute("data-help-content", tooltip);
// // buttonWrap.appendChild(helpBtn);
// // } else {
// button.setAttribute("data-tooltip", tooltip);
// }
}
}