import "./ui_template_overlay.css"; import { t } from "./ui_template_locale"; const TITLE_OVERLAY_STATE_KEY = "frd_workflow_title_overlay_open"; const PROGRESS_OVERLAY_STATE_KEY = "frd_workflow_progress_overlay_open"; export interface WorkflowOverlayOptions { title: string; progressContent: HTMLElement; optionsContent?: HTMLElement; showTitlePanel?: boolean; onOptionsOpenChange?: (isOpen: boolean) => void; } export interface WorkflowOverlayHandle { root: HTMLElement; setTitleOpen: (isOpen: boolean) => void; setProgressOpen: (isOpen: boolean) => void; } export interface WorkflowPanelHandle { root: HTMLButtonElement; setOpen: (isOpen: boolean) => void; } export function createWorkflowPanelHandle(placement: "side" | "bottom"): WorkflowPanelHandle { const root = document.createElement("button"); root.type = "button"; root.className = `ui-workflow-overlay__handle ui-workflow-overlay__handle--${placement}`; const icon = document.createElement("span"); icon.className = "ui-workflow-overlay__handle-icon"; root.append(icon); function setOpen(isOpen: boolean): void { const rotation = placement === "side" ? (isOpen ? 180 : 0) : isOpen ? 90 : -90; icon.style.transform = `rotate(${rotation}deg)`; root.title = t(isOpen ? "Workflow_Overlay_Collapse" : "Workflow_Overlay_Expand"); root.setAttribute("aria-label", root.title); root.setAttribute("aria-expanded", String(isOpen)); } return { root, setOpen }; } function readOpenState(key: string): boolean { return sessionStorage.getItem(key) !== "false"; } function createPanel( variant: "title" | "progress", titleText: string, body: HTMLElement, storageKey: string, onOpenChange?: (isOpen: boolean) => void, ): { root: HTMLElement; setOpen: (isOpen: boolean) => void } { const root = document.createElement("aside"); root.className = `ui-workflow-overlay__panel ui-workflow-overlay__panel--${variant}`; // title 패널은 사이드바 도킹형 — 토글이 패널 우측 가장자리 세로 중앙 핸들로 나감 const isSidebar = variant === "title"; const header = document.createElement("div"); header.className = "ui-workflow-overlay__header"; const title = document.createElement("h2"); title.className = "ui-workflow-overlay__title"; title.textContent = titleText; const panelHandle = isSidebar ? createWorkflowPanelHandle("side") : null; const toggle = panelHandle?.root ?? document.createElement("button"); if (!panelHandle) { toggle.type = "button"; toggle.className = "ui-workflow-overlay__toggle"; } if (isSidebar) { header.append(title); root.append(header, toggle); } else { header.append(title, toggle); root.append(header); } if (body.childElementCount > 0) { body.classList.add("ui-workflow-overlay__body"); root.append(body); } function setOpen(isOpen: boolean): void { root.classList.toggle("is-collapsed", !isOpen); if (panelHandle) panelHandle.setOpen(isOpen); else { toggle.textContent = isOpen ? "−" : "+"; toggle.title = t(isOpen ? "Workflow_Overlay_Collapse" : "Workflow_Overlay_Expand"); toggle.setAttribute("aria-label", toggle.title); toggle.setAttribute("aria-expanded", String(isOpen)); } sessionStorage.setItem(storageKey, String(isOpen)); onOpenChange?.(isOpen); } function toggleOpen(): void { setOpen(root.classList.contains("is-collapsed")); } title.tabIndex = 0; title.setAttribute("role", "button"); title.addEventListener("click", toggleOpen); title.addEventListener("keydown", (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); toggleOpen(); } }); toggle.addEventListener("click", toggleOpen); setOpen(readOpenState(storageKey)); return { root, setOpen }; } export function createWorkflowOverlays(options: WorkflowOverlayOptions): WorkflowOverlayHandle { const root = document.createElement("div"); root.className = "ui-workflow-overlay"; const progressBody = document.createElement("div"); progressBody.append(options.progressContent); const progressPanel = createPanel( "progress", t("Workflow_Progress_Title"), progressBody, PROGRESS_OVERLAY_STATE_KEY, ); let setTitleOpen = (_isOpen: boolean): void => {}; if (options.showTitlePanel !== false) { const titleBody = document.createElement("div"); if (options.optionsContent) titleBody.append(options.optionsContent); const titlePanel = createPanel( "title", options.title, titleBody, TITLE_OVERLAY_STATE_KEY, options.onOptionsOpenChange, ); root.append(titlePanel.root); setTitleOpen = titlePanel.setOpen; } root.append(progressPanel.root); return { root, setTitleOpen, setProgressOpen: progressPanel.setOpen, }; }