Files
Aislo/A00_Common/design/design_dock.ts
T
eomsangdonandClaude Sonnet 5.5 7d92298b03 test(B05·B06): 좌측 패널 같음 지킴 시험 · 아래 단추 한 줄 배치(24-2f)
- test_design_view_parity.py 10건 — 두 보기 공용 칸 DOM · 단추 넷 차례 · 보기 전환 글자 · 페이지 파일이 좌측 칸 · 단추를 직접 만들면 빨강
- 아래 단추 글자가 두 줄로 꺾이지 않게 design_dock.css

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EauA2ATmqNRbeV48s9CcBr
2026-09-29 20:41:02 +09:00

131 lines
4.7 KiB
TypeScript

/* =============================================================================
* design_dock.ts
* B05 · B06 한 페이지 두 보기의 **좌측 패널 맨 아래 고정 영역**(계획서 24-1 · 24-2b).
*
* 구성 = [위에 놓을 것(구조물 목록)][구분선][단추 행]. 단추 넷은 두 보기가 **같다**:
* [초기화] [저장] [확정] [보기 전환](종단 보기에서는 「횡단 보기」 · 횡단 보기에서는
* 「종단 보기」). 누르면 무엇을 하는지는 페이지가 `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;
/** [보기 전환] — 지금은 주소 이동이다(2단계에서 같은 페이지 안 전환으로 바뀜). */
switchView: DesignDockAction;
}
export interface DesignDock {
root: HTMLElement;
/** 단추 넷의 켬·끔·설명을 다시 준다(자료가 늦게 오는 페이지가 부름). */
setActions: (actions: Partial<DesignDockActions>) => void;
/** 보기가 바뀌면 [보기 전환] 글자가 따라 바뀐다. */
setView: (view: DesignView) => void;
}
function label(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
}
/** 보기 전환 단추 글자 — 가려는 **쪽** 이름이다. */
export function switchViewLabel(view: DesignView): string {
return label(view === "profile" ? "Design_Btn_ToCross" : "Design_Btn_ToProfile");
}
/**
* 아래 고정 영역을 만든다. `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";
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";
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"),
});
const switchButton = createButton({
label: switchViewLabel(view),
variant: "ghost",
onClick: press("switchView"),
});
resetButton.dataset.dockAction = "reset";
saveButton.dataset.dockAction = "save";
confirmButton.dataset.dockAction = "confirm";
switchButton.dataset.dockAction = "switchView";
const buttons = {
reset: resetButton,
save: saveButton,
confirm: confirmButton,
switchView: switchButton,
};
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");
});
(switchButton.querySelector(".ui-btn__label") ?? switchButton).textContent =
switchViewLabel(current);
row.className = current === "profile" ? "b05-route__actions" : "b06-profile__actions";
};
row.append(resetButton, saveButton, confirmButton, switchButton);
root.append(...above, divider, row);
sync();
return {
root,
setActions: (next) => {
actions = { ...actions, ...next };
sync();
},
setView: (next) => {
current = next;
sync();
},
};
}