|
@@ -4,20 +4,33 @@ import { getOS, resourceAsString, setInnerHtml, t } from "../utils/index.js";
|
|
|
import { BytmDialog, type BytmDialogEvents } from "../components/index.js";
|
|
|
import "./prompt.css";
|
|
|
|
|
|
-export type PromptDialogRenderProps = {
|
|
|
- type: "confirm" | "alert";
|
|
|
+export type PromptDialogRenderProps = ConfirmRenderProps | AlertRenderProps | PromptRenderProps;
|
|
|
+
|
|
|
+type ConfirmRenderProps = BaseRenderProps & {
|
|
|
+ type: "confirm";
|
|
|
+};
|
|
|
+
|
|
|
+type AlertRenderProps = BaseRenderProps & {
|
|
|
+ type: "alert";
|
|
|
+};
|
|
|
+
|
|
|
+type PromptRenderProps = BaseRenderProps & {
|
|
|
+ type: "prompt";
|
|
|
+ defaultValue?: string;
|
|
|
+};
|
|
|
+
|
|
|
+type BaseRenderProps = {
|
|
|
message: Stringifiable;
|
|
|
};
|
|
|
|
|
|
export type ShowPromptProps = Partial<PromptDialogRenderProps> & Required<Pick<PromptDialogRenderProps, "message">>;
|
|
|
|
|
|
export type PromptDialogEmitter = Emitter<BytmDialogEvents & {
|
|
|
- resolve: (result: boolean) => void;
|
|
|
+ resolve: (result: boolean | string | null) => void;
|
|
|
}>;
|
|
|
|
|
|
let promptDialog: PromptDialog | null = null;
|
|
|
|
|
|
-// TODO: implement prompt() equivalent for text input
|
|
|
class PromptDialog extends BytmDialog {
|
|
|
constructor(props: PromptDialogRenderProps) {
|
|
|
super({
|
|
@@ -44,8 +57,8 @@ class PromptDialog extends BytmDialog {
|
|
|
return headerEl;
|
|
|
}
|
|
|
|
|
|
- async renderBody({ type, message }: PromptDialogRenderProps) {
|
|
|
- const resolve = (val: boolean) => (this.events as PromptDialogEmitter).emit("resolve", val);
|
|
|
+ async renderBody({ type, message, ...rest }: PromptDialogRenderProps) {
|
|
|
+ const resolve = (val: boolean | string | null) => (this.events as PromptDialogEmitter).emit("resolve", val);
|
|
|
|
|
|
const contElem = document.createElement("div");
|
|
|
|
|
@@ -56,6 +69,28 @@ class PromptDialog extends BytmDialog {
|
|
|
messageElem.textContent = String(message);
|
|
|
contElem.appendChild(messageElem);
|
|
|
|
|
|
+ if(type === "prompt") {
|
|
|
+ const inputCont = document.createElement("div");
|
|
|
+ inputCont.id = "bytm-prompt-dialog-input-cont";
|
|
|
+
|
|
|
+ const inputLabel = document.createElement("label");
|
|
|
+ inputLabel.id = "bytm-prompt-dialog-input-label";
|
|
|
+ inputLabel.htmlFor = "bytm-prompt-dialog-input";
|
|
|
+ inputLabel.textContent = t("prompt_input_label");
|
|
|
+ inputCont.appendChild(inputLabel);
|
|
|
+
|
|
|
+ const inputElem = document.createElement("input");
|
|
|
+ inputElem.id = "bytm-prompt-dialog-input";
|
|
|
+ inputElem.type = "text";
|
|
|
+ inputElem.autocomplete = "off";
|
|
|
+ inputElem.spellcheck = false;
|
|
|
+ inputElem.value = "defaultValue" in rest ? rest.defaultValue ?? "" : "";
|
|
|
+ inputElem.autofocus = true;
|
|
|
+ inputCont.appendChild(inputElem);
|
|
|
+
|
|
|
+ contElem.appendChild(inputCont);
|
|
|
+ }
|
|
|
+
|
|
|
const buttonsWrapper = document.createElement("div");
|
|
|
buttonsWrapper.id = "bytm-prompt-dialog-button-wrapper";
|
|
|
|
|
@@ -63,16 +98,16 @@ class PromptDialog extends BytmDialog {
|
|
|
buttonsCont.id = "bytm-prompt-dialog-buttons-cont";
|
|
|
|
|
|
let confirmBtn: HTMLButtonElement | undefined;
|
|
|
- if(type === "confirm") {
|
|
|
+ if(type === "confirm" || type === "prompt") {
|
|
|
confirmBtn = document.createElement("button");
|
|
|
confirmBtn.id = "bytm-prompt-dialog-confirm";
|
|
|
confirmBtn.classList.add("bytm-prompt-dialog-button");
|
|
|
confirmBtn.textContent = t("prompt_confirm");
|
|
|
confirmBtn.ariaLabel = confirmBtn.title = t("click_to_confirm_tooltip");
|
|
|
confirmBtn.tabIndex = 0;
|
|
|
- confirmBtn.autofocus = true;
|
|
|
+ confirmBtn.autofocus = type === "confirm";
|
|
|
confirmBtn.addEventListener("click", () => {
|
|
|
- resolve(true);
|
|
|
+ resolve(type === "confirm" ? true : (document.querySelector<HTMLInputElement>("#bytm-prompt-dialog-input"))?.value?.trim() ?? null);
|
|
|
promptDialog?.close();
|
|
|
}, { once: true });
|
|
|
}
|
|
@@ -86,7 +121,12 @@ class PromptDialog extends BytmDialog {
|
|
|
if(type === "alert")
|
|
|
closeBtn.autofocus = true;
|
|
|
closeBtn.addEventListener("click", () => {
|
|
|
- resolve(type === "alert");
|
|
|
+ const resVals: Record<PromptDialogRenderProps["type"], boolean | null> = {
|
|
|
+ alert: true,
|
|
|
+ confirm: false,
|
|
|
+ prompt: null,
|
|
|
+ };
|
|
|
+ resolve(resVals[type]);
|
|
|
promptDialog?.close();
|
|
|
}, { once: true });
|
|
|
|
|
@@ -101,30 +141,27 @@ class PromptDialog extends BytmDialog {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/** Shows a prompt dialog of the specified type and resolves true if the user confirms it or false if they cancel it - always resolves true with type "alert" */
|
|
|
-export function showPrompt({
|
|
|
- type = "alert",
|
|
|
- message,
|
|
|
-}: ShowPromptProps) {
|
|
|
- return new Promise<boolean>((resolve) => {
|
|
|
+/** Shows a confirm() or alert() prompt dialog of the specified type and resolves true if the user confirms it or false if they cancel it - always resolves true with type "alert" */
|
|
|
+export function showPrompt(props: ConfirmRenderProps | AlertRenderProps): Promise<boolean>;
|
|
|
+/** Shows a prompt() dialog with the specified message and default value and resolves the entered value if the user confirms it or null if they cancel it */
|
|
|
+export function showPrompt(props: PromptRenderProps): Promise<string | null>;
|
|
|
+export function showPrompt({ type, ...rest }: PromptDialogRenderProps): Promise<boolean | string | null> {
|
|
|
+ return new Promise<boolean | string | null>((resolve) => {
|
|
|
if(BytmDialog.getOpenDialogs().includes("prompt-dialog"))
|
|
|
promptDialog?.close();
|
|
|
|
|
|
- promptDialog = new PromptDialog({
|
|
|
- type,
|
|
|
- message,
|
|
|
- });
|
|
|
+ promptDialog = new PromptDialog({ type, ...rest });
|
|
|
|
|
|
// make config menu inert while prompt dialog is open
|
|
|
promptDialog.once("open", () => document.querySelector("#bytm-cfg-menu")?.setAttribute("inert", "true"));
|
|
|
promptDialog.once("close", () => document.querySelector("#bytm-cfg-menu")?.removeAttribute("inert"));
|
|
|
|
|
|
- let resolveVal: boolean | undefined;
|
|
|
- const tryResolve = () => resolve(typeof resolveVal === "boolean" ? resolveVal : false);
|
|
|
+ let resolveVal: boolean | string | null | undefined;
|
|
|
+ const tryResolve = () => resolve(typeof resolveVal !== "undefined" ? resolveVal : false);
|
|
|
|
|
|
- const resolveUnsub = promptDialog.on("resolve" as "_", (val: boolean) => {
|
|
|
+ const resolveUnsub = promptDialog.on("resolve" as "_", (val: boolean | string | null) => {
|
|
|
resolveUnsub();
|
|
|
- if(resolveVal)
|
|
|
+ if(resolveVal !== undefined)
|
|
|
return;
|
|
|
resolveVal = val;
|
|
|
tryResolve();
|
|
@@ -132,15 +169,14 @@ export function showPrompt({
|
|
|
|
|
|
const closeUnsub = promptDialog.on("close", () => {
|
|
|
closeUnsub();
|
|
|
- if(resolveVal)
|
|
|
+ if(resolveVal !== undefined)
|
|
|
return;
|
|
|
resolveVal = type === "alert";
|
|
|
+ if(type === "prompt")
|
|
|
+ resolveVal = null;
|
|
|
tryResolve();
|
|
|
});
|
|
|
|
|
|
promptDialog.open();
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
-//@ts-ignore
|
|
|
-unsafeWindow.showPrompt = showPrompt;
|