This commit is contained in:
2026-07-10 19:01:33 +09:00
parent e3d66e717c
commit b98affbf99
22 changed files with 1681 additions and 752 deletions
+55 -15
View File
@@ -7,11 +7,13 @@ export interface WorkflowLayoutOptions {
leftPanel: HTMLElement;
mainContent: HTMLElement;
onMenuToggle?: (isOpen: boolean) => void;
onHeaderToggle?: (isVisible: boolean) => void;
}
export interface WorkflowLayoutHandle {
root: HTMLElement;
setMenuOpen: (isOpen: boolean) => void;
setHeaderVisible: (isVisible: boolean) => void;
}
function createStepBar(steps: readonly string[], activeStep: number): HTMLElement {
@@ -37,8 +39,8 @@ export function createWorkflowLayout(options: WorkflowLayoutOptions): WorkflowLa
const menuButton = document.createElement("button");
menuButton.type = "button";
menuButton.className = "ui-workflow-layout__menu-button";
menuButton.setAttribute("aria-label", "Toggle workflow menu");
menuButton.textContent = "";
menuButton.setAttribute("aria-label", "Toggle settings panel");
menuButton.innerHTML = "🛠️ <span>Option ▾</span>";
const titleWrap = document.createElement("div");
titleWrap.className = "ui-workflow-layout__title-wrap";
@@ -47,33 +49,71 @@ export function createWorkflowLayout(options: WorkflowLayoutOptions): WorkflowLa
title.textContent = options.title;
titleWrap.append(title, createStepBar(options.steps, options.activeStep));
header.append(menuButton, titleWrap);
const headerHideButton = document.createElement("button");
headerHideButton.type = "button";
headerHideButton.className = "ui-workflow-layout__header-hide";
headerHideButton.setAttribute("aria-label", "Hide header");
headerHideButton.textContent = "▲";
header.append(titleWrap, headerHideButton);
// 헤더 숨김 시 표시되는 복원 화살표
const headerRevealButton = document.createElement("button");
headerRevealButton.type = "button";
headerRevealButton.className = "ui-workflow-layout__header-reveal";
headerRevealButton.setAttribute("aria-label", "Show header");
headerRevealButton.textContent = "▼";
const body = document.createElement("div");
body.className = "ui-workflow-layout__body";
const aside = document.createElement("aside");
aside.className = "ui-workflow-layout__panel";
aside.append(options.leftPanel);
const dropdownPanel = document.createElement("div");
dropdownPanel.className = "ui-workflow-layout__dropdown-panel";
dropdownPanel.append(options.leftPanel);
const main = document.createElement("main");
main.className = "ui-workflow-layout__main";
main.append(options.mainContent);
main.style.position = "relative";
main.append(menuButton, options.mainContent);
body.append(aside, main);
root.append(header, body);
body.append(dropdownPanel, main);
root.append(header, headerRevealButton, body);
function setMenuOpen(isOpen: boolean): void {
root.classList.toggle("is-menu-open", isOpen);
menuButton.setAttribute("aria-expanded", String(isOpen));
const icon = menuButton.querySelector("span");
if (icon) {
icon.textContent = isOpen ? "Option ▴" : "Option ▾";
}
options.onMenuToggle?.(isOpen);
}
menuButton.addEventListener("click", () => setMenuOpen(!root.classList.contains("is-menu-open")));
main.addEventListener("click", () => {
if (root.classList.contains("is-menu-open")) setMenuOpen(false);
});
setMenuOpen(true);
function setHeaderVisible(isVisible: boolean): void {
root.classList.toggle("is-header-hidden", !isVisible);
headerHideButton.setAttribute("aria-expanded", String(isVisible));
options.onHeaderToggle?.(isVisible);
}
return { root, setMenuOpen };
menuButton.addEventListener("click", (e) => {
e.stopPropagation();
setMenuOpen(!root.classList.contains("is-menu-open"));
});
document.addEventListener("click", (e) => {
if (root.classList.contains("is-menu-open")) {
const target = e.target as HTMLElement;
if (!dropdownPanel.contains(target) && !menuButton.contains(target)) {
setMenuOpen(false);
}
}
});
headerHideButton.addEventListener("click", () => setHeaderVisible(false));
headerRevealButton.addEventListener("click", () => setHeaderVisible(true));
setMenuOpen(false);
setHeaderVisible(true);
return { root, setMenuOpen, setHeaderVisible };
}