- ui_template_help: [?] 뒤 [<] [>] · 다섯 주소 목록만(b09-print · B03 · B04 · M01 · M02 뺌) · B09 [>] 흐림 · ui-step-nav 이벤트 - ui_template_workflow_layout: ui-step-nav 듣개 — 켜진 칸 ±1 단계 단추 click(onStepClick 한 길) · 꺼진 단계 = 그 단추 title 알림 - CSS: [<] [>] = [?] 같은 정사각 · 늘어남 규칙에서 뺌(overlay · B09 도크 한 줄) - design_dock · design_shared · locale_b2: switchView 단추 · 글 지움 - 시험: test_280_2_step_nav(새) · test_design_view_parity(단추 셋) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
/* =============================================================================
|
|
* design_dock.ts
|
|
* B05 · B06 한 페이지 두 보기의 **좌측 패널 맨 아래 고정 영역**(계획서 24-1 · 24-2b).
|
|
*
|
|
* 구성 = [위에 놓을 것(구조물 목록)][구분선][단추 행]. 단추 셋은 두 보기가 **같다**:
|
|
* [초기화] [저장] [확정](보기 오가기는 공용 [<] [>] · 요구 280-2). 누르면 무엇을 하는지는 페이지가 `DesignDockActions` 로 건넨다 — 이 파일은
|
|
* 단추를 만들고 켜고 끄기만 한다(저장 흐름은 `design_save.ts` 몫).
|
|
* ========================================================================== */
|
|
|
|
import { createButton } from "@ui/ui_template_elements";
|
|
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
|
import "./design_dock.css";
|
|
|
|
/** 보기 이름 — 종단 보기(3D + 종단도) · 횡단 보기(종단도 + 횡단 카드). */
|
|
export type DesignView = "profile" | "cross";
|
|
|
|
/** 단추 하나의 동작 — `enabled` 가 거짓이면 눌리지 않고 `title` 이 까닭을 알린다. */
|
|
export interface DesignDockAction {
|
|
run: () => void | Promise<void>;
|
|
enabled?: boolean;
|
|
title?: string;
|
|
}
|
|
|
|
export interface DesignDockActions {
|
|
reset: DesignDockAction;
|
|
save: DesignDockAction;
|
|
confirm: DesignDockAction;
|
|
}
|
|
|
|
export interface DesignDock {
|
|
root: HTMLElement;
|
|
/** 단추 셋의 켬·끔·설명을 다시 준다(자료가 늦게 오는 페이지가 부름). */
|
|
setActions: (actions: Partial<DesignDockActions>) => void;
|
|
/** 보기가 바뀌면 단추 줄 class 가 따라 바뀐다. */
|
|
setView: (view: DesignView) => void;
|
|
/** 「계산 중」 점 — 묶음 차례가 떠 있는 동안 켬(386-2). */
|
|
setBusy: (busy: boolean) => void;
|
|
}
|
|
|
|
function label(key: keyof typeof ui_locales): string {
|
|
return ui_locales[key][currentLanguageIndex];
|
|
}
|
|
|
|
/**
|
|
* 아래 고정 영역을 만든다. `above` 는 단추 행 위에 놓을 것(구조물 목록) — 목록을 「구조물 배치」
|
|
* 본문에 두면 고른 구조물의 폼 길이에 밀려 화면 밖으로 나갔다(2026-08-18 사용자 지시).
|
|
*/
|
|
export function createDesignDock(
|
|
view: DesignView,
|
|
above: ReadonlyArray<HTMLElement>,
|
|
initial: DesignDockActions,
|
|
): DesignDock {
|
|
let current = view;
|
|
let actions = { ...initial };
|
|
|
|
const root = document.createElement("div");
|
|
root.className = "b05-route__dock ui-sidebar-actions design-dock";
|
|
const busy = document.createElement("span");
|
|
busy.className = "design-dock__busy";
|
|
busy.title = label("Design_Busy");
|
|
busy.setAttribute("role", "status");
|
|
busy.setAttribute("aria-label", label("Design_Busy"));
|
|
busy.hidden = true;
|
|
const divider = document.createElement("hr");
|
|
divider.className = "b05-structure__divider";
|
|
const row = document.createElement("div");
|
|
row.className = view === "profile" ? "b05-route__actions" : "b06-profile__actions";
|
|
row.dataset.helpRow = "";
|
|
|
|
const press = (name: keyof DesignDockActions) => (): void => {
|
|
void Promise.resolve(actions[name].run());
|
|
};
|
|
const resetButton = createButton({
|
|
label: label("Common_Btn_Reset"),
|
|
variant: "danger",
|
|
onClick: press("reset"),
|
|
});
|
|
const saveButton = createButton({
|
|
label: label("B06_Profile_Btn_Save"),
|
|
variant: "ghost",
|
|
onClick: press("save"),
|
|
});
|
|
const confirmButton = createButton({
|
|
label: label("B06_Profile_Btn_Confirm"),
|
|
variant: "filled",
|
|
onClick: press("confirm"),
|
|
});
|
|
resetButton.dataset.dockAction = "reset";
|
|
saveButton.dataset.dockAction = "save";
|
|
confirmButton.dataset.dockAction = "confirm";
|
|
const buttons = {
|
|
reset: resetButton,
|
|
save: saveButton,
|
|
confirm: confirmButton,
|
|
};
|
|
|
|
const sync = (): void => {
|
|
(Object.keys(buttons) as Array<keyof DesignDockActions>).forEach((name) => {
|
|
const action = actions[name];
|
|
buttons[name].disabled = action.enabled === false;
|
|
if (action.title) buttons[name].title = action.title;
|
|
else buttons[name].removeAttribute("title");
|
|
});
|
|
row.className = current === "profile" ? "b05-route__actions" : "b06-profile__actions";
|
|
};
|
|
|
|
row.append(resetButton, saveButton, confirmButton);
|
|
root.append(...above, divider, row, busy);
|
|
sync();
|
|
|
|
return {
|
|
root,
|
|
setActions: (next) => {
|
|
actions = { ...actions, ...next };
|
|
sync();
|
|
},
|
|
setView: (next) => {
|
|
current = next;
|
|
sync();
|
|
},
|
|
setBusy: (next) => {
|
|
busy.hidden = !next;
|
|
},
|
|
};
|
|
}
|