246 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const DIALOG_STYLE_ID = "sces-batch-name-dialog-style";
const activeDialogs = new WeakMap<
Document,
{
input: HTMLInputElement;
promise: Promise<string | null>;
}
>();
export function promptForBatchName(document: Document): Promise<string | null> {
const existingDialog = activeDialogs.get(document);
if (existingDialog) {
existingDialog.input.focus();
existingDialog.input.select();
return existingDialog.promise;
}
ensureDialogStyles(document);
const dialogRoot = document.createElement("div");
dialogRoot.dataset.pluginBatchNameDialog = "root";
dialogRoot.setAttribute("role", "dialog");
dialogRoot.setAttribute("aria-modal", "true");
dialogRoot.setAttribute("aria-labelledby", "sces-batch-name-title");
applyOverlayStyles(dialogRoot);
const dialogPanel = document.createElement("div");
applyPanelStyles(dialogPanel);
const title = document.createElement("h2");
title.id = "sces-batch-name-title";
title.textContent = "提交批次";
applyTitleStyles(title);
const description = document.createElement("p");
description.textContent = "请输入批次名称,便于后续在系统中识别和追踪。";
applyDescriptionStyles(description);
const input = document.createElement("input");
input.type = "text";
input.dataset.pluginBatchNameInput = "input";
input.placeholder = "例如618达人筛选第一批";
input.maxLength = 60;
applyInputStyles(input);
const errorText = document.createElement("p");
errorText.dataset.pluginBatchNameError = "text";
applyErrorStyles(errorText);
const buttonRow = document.createElement("div");
applyButtonRowStyles(buttonRow);
const cancelButton = document.createElement("button");
cancelButton.type = "button";
cancelButton.dataset.pluginBatchNameCancel = "button";
cancelButton.textContent = "取消";
applySecondaryButtonStyles(cancelButton);
const confirmButton = document.createElement("button");
confirmButton.type = "button";
confirmButton.dataset.pluginBatchNameConfirm = "button";
confirmButton.textContent = "确认提交";
applyPrimaryButtonStyles(confirmButton);
buttonRow.append(cancelButton, confirmButton);
dialogPanel.append(title, description, input, errorText, buttonRow);
dialogRoot.appendChild(dialogPanel);
document.body.appendChild(dialogRoot);
const dialogPromise = new Promise<string | null>((resolve) => {
const closeDialog = (value: string | null) => {
activeDialogs.delete(document);
dialogRoot.remove();
document.removeEventListener("keydown", handleDocumentKeydown, true);
resolve(value);
};
const submitValue = () => {
const value = input.value.trim();
if (!value) {
errorText.textContent = "请输入批次名称";
input.setAttribute("aria-invalid", "true");
input.focus();
return;
}
closeDialog(value);
};
const handleDocumentKeydown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
event.preventDefault();
closeDialog(null);
return;
}
if (event.key === "Enter") {
event.preventDefault();
submitValue();
}
};
input.addEventListener("input", () => {
if (!input.value.trim()) {
return;
}
errorText.textContent = "";
input.removeAttribute("aria-invalid");
});
cancelButton.addEventListener("click", () => {
closeDialog(null);
});
confirmButton.addEventListener("click", () => {
submitValue();
});
dialogRoot.addEventListener("click", (event) => {
if (event.target === dialogRoot) {
closeDialog(null);
}
});
document.addEventListener("keydown", handleDocumentKeydown, true);
});
activeDialogs.set(document, {
input,
promise: dialogPromise
});
input.focus();
return dialogPromise;
}
function ensureDialogStyles(document: Document): void {
if (document.getElementById(DIALOG_STYLE_ID)) {
return;
}
const style = document.createElement("style");
style.id = DIALOG_STYLE_ID;
style.textContent = `
[data-plugin-batch-name-dialog="root"] {
animation: sces-batch-name-fade-in 0.16s ease;
}
@keyframes sces-batch-name-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`;
document.head.appendChild(style);
}
function applyOverlayStyles(root: HTMLElement): void {
root.style.position = "fixed";
root.style.inset = "0";
root.style.background = "rgba(15, 23, 42, 0.38)";
root.style.display = "flex";
root.style.alignItems = "center";
root.style.justifyContent = "center";
root.style.padding = "24px";
root.style.zIndex = "2147483647";
}
function applyPanelStyles(panel: HTMLElement): void {
panel.style.width = "min(420px, calc(100vw - 32px))";
panel.style.background = "#fffaf9";
panel.style.border = "1px solid rgba(127, 29, 45, 0.14)";
panel.style.borderRadius = "18px";
panel.style.boxShadow = "0 28px 70px rgba(15, 23, 42, 0.22)";
panel.style.padding = "24px";
panel.style.boxSizing = "border-box";
}
function applyTitleStyles(title: HTMLElement): void {
title.style.margin = "0";
title.style.color = "#4c0519";
title.style.fontSize = "20px";
title.style.fontWeight = "700";
title.style.lineHeight = "28px";
}
function applyDescriptionStyles(description: HTMLElement): void {
description.style.margin = "10px 0 0";
description.style.color = "#64748b";
description.style.fontSize = "13px";
description.style.lineHeight = "20px";
}
function applyInputStyles(input: HTMLInputElement): void {
input.style.width = "100%";
input.style.height = "42px";
input.style.marginTop = "18px";
input.style.padding = "0 14px";
input.style.boxSizing = "border-box";
input.style.border = "1px solid #d8c1c6";
input.style.borderRadius = "12px";
input.style.background = "#ffffff";
input.style.color = "#1f2937";
input.style.fontSize = "14px";
input.style.outline = "none";
}
function applyErrorStyles(errorText: HTMLElement): void {
errorText.style.minHeight = "20px";
errorText.style.margin = "8px 0 0";
errorText.style.color = "#b91c1c";
errorText.style.fontSize = "12px";
errorText.style.lineHeight = "18px";
}
function applyButtonRowStyles(buttonRow: HTMLElement): void {
buttonRow.style.display = "flex";
buttonRow.style.justifyContent = "flex-end";
buttonRow.style.gap = "10px";
buttonRow.style.marginTop = "18px";
}
function applySecondaryButtonStyles(button: HTMLButtonElement): void {
button.style.height = "36px";
button.style.padding = "0 16px";
button.style.border = "1px solid #d7dde6";
button.style.borderRadius = "10px";
button.style.background = "#ffffff";
button.style.color = "#334155";
button.style.fontWeight = "600";
button.style.cursor = "pointer";
}
function applyPrimaryButtonStyles(button: HTMLButtonElement): void {
button.style.height = "36px";
button.style.padding = "0 16px";
button.style.border = "1px solid #7f1d2d";
button.style.borderRadius = "10px";
button.style.background = "#7f1d2d";
button.style.color = "#ffffff";
button.style.fontWeight = "600";
button.style.cursor = "pointer";
}