diff --git a/M01_MasterData/M01_MasterData_UI_Side.ts b/M01_MasterData/M01_MasterData_UI_Side.ts index 4a98abae..2e31508d 100644 --- a/M01_MasterData/M01_MasterData_UI_Side.ts +++ b/M01_MasterData/M01_MasterData_UI_Side.ts @@ -173,7 +173,7 @@ export function buildSide( /** 트리 줄 — 부모는 접기/펴기 · 누를 일이 있는 줄만 밝게 */ const make: MakeRow = (node, caret, depth, onClick) => { const button = el("button", { - className: "m01-side__item", + className: "m01-side__item ui-sidebar-row", attrs: { type: "button" }, children: [ el("span", { @@ -407,7 +407,7 @@ ${first.file}` const section = (title: string, body: HTMLElement | null): HTMLElement => el("section", { - className: "m01-side__group ui-collapsible ui-sidebar-section is-collapsed", + className: "m01-side__group ui-collapsible ui-sidebar-section ui-sidebar-card is-collapsed", children: [ el("h3", { className: "ui-collapsible__title", text: title }), ...(body ? [body] : []), @@ -415,7 +415,9 @@ ${first.file}` }); const groups = GROUPS.map((group) => { - const body = LEAF.includes(group) ? null : el("div", { className: "m01-side__items" }); + const body = LEAF.includes(group) + ? null + : el("div", { className: "m01-side__items ui-sidebar-list" }); if (body) bodies.set(group, body); const root = section(L(GROUP_TITLE[group]), body); if (!body) { @@ -442,7 +444,7 @@ ${first.file}` return root; }); - const logicHost = el("div", { className: "m01-side__items" }); + const logicHost = el("div", { className: "m01-side__items ui-sidebar-list" }); const logic = section(tx("Lab_Title"), logicHost); logic.querySelector(".ui-collapsible__title")?.addEventListener("click", () => { if (!logic.classList.contains("is-collapsed")) return; // 닫을 때는 그대로 둠 @@ -450,7 +452,7 @@ ${first.file}` onLogic(); }); - const comboHost = el("div", { className: "m01-side__items" }); + const comboHost = el("div", { className: "m01-side__items ui-sidebar-list" }); const combo = section(tc("Title"), comboHost); combo.querySelector(".ui-collapsible__title")?.addEventListener("click", () => { if (!combo.classList.contains("is-collapsed")) return; @@ -458,7 +460,10 @@ ${first.file}` onCombo(); }); - const root = el("div", { className: "m01-side", children: [...groups, logic, combo] }); + const root = el("div", { + className: "m01-side ui-sidebar-panel", + children: [...groups, logic, combo], + }); attachCollapsible(root); void Promise.all(GROUPS.map(refresh)).catch((error) => showToast(error instanceof Error ? error.message : L("M01_LoadFailed"), "error"), diff --git a/M01_MasterData/M01_MasterData_UI_Style.css b/M01_MasterData/M01_MasterData_UI_Style.css index 2df3b62e..6d2983e3 100644 --- a/M01_MasterData/M01_MasterData_UI_Style.css +++ b/M01_MasterData/M01_MasterData_UI_Style.css @@ -7,25 +7,8 @@ padding-top: var(--spacing-8); } -/* 왼쪽 패널 — 공용 접기 컨테이너 여덟 */ -.m01-side { - display: flex; - flex-direction: column; - gap: var(--spacing-8); -} - -.m01-side__group { - padding: calc(var(--spacing-8) + var(--spacing-4)); - border-radius: var(--radius-cards); - background: var(--color-surface-raised); -} - -.m01-side__group > h3 { - margin: 0; - color: var(--color-text); - font-size: var(--text-body-sm); -} - +/* 왼쪽 패널 카드·목록·줄 모양은 공용 ui-sidebar-*(ui_template_overlay.css) — 여기는 + M01 전용(환율·요율처럼 하위 목록 없는 leaf 그룹) 만. */ .m01-side__leaf > h3::after { display: none; } @@ -34,39 +17,6 @@ color: var(--color-accent); } -.m01-side__items { - display: flex; - flex-direction: column; - gap: 2px; - max-height: 50vh; - margin-top: var(--spacing-8); - overflow: auto; -} - -.m01-side__item { - display: flex; - justify-content: space-between; - gap: var(--spacing-8); - padding: var(--spacing-4) var(--spacing-8); - border: 0; - border-radius: var(--radius-buttons); - background: transparent; - color: var(--color-text-body); - font: inherit; - font-size: var(--text-body-sm); - text-align: left; - cursor: pointer; -} - -.m01-side__item:hover { - background: var(--color-paper); -} - -.m01-side__item.is-active { - background: var(--color-mist-violet); - color: var(--color-accent); -} - .m01-tree__kids { display: flex; flex-direction: column; diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index 4150ba47..da78df92 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -79,17 +79,17 @@ export function buildSide(opt: SideOptions): SideHandle { const section = (title: string, body: HTMLElement, ...more: HTMLElement[]): HTMLElement => el("section", { - className: "m02-side__group ui-collapsible ui-sidebar-section", + className: "m02-side__group ui-collapsible ui-sidebar-section ui-sidebar-card is-collapsed", children: [el("h3", { className: "ui-collapsible__title", text: title }), body, ...more], }); const bodies = new Map( [...KINDS.map((k) => k.kind), STRUCTURE].map((k) => [ k, - el("div", { className: "m02-side__items" }), + el("div", { className: "m02-side__items ui-sidebar-list" }), ]), ); const listHost = el("div", { - className: "m02-side__lists", + className: "m02-side__lists ui-sidebar-panel", children: [ section(L("M02_KindTable"), bodies.get("table")!), section( @@ -104,9 +104,12 @@ export function buildSide(opt: SideOptions): SideHandle { const btnNew = createButton({ label: L("M02_New"), variant: "filled" }); const btnCopy = createButton({ label: L("M02_Copy"), variant: "ghost" }); const btnDel = createButton({ label: L("M02_Delete"), variant: "danger" }); - const editRow = el("div", { className: "m02-side__row", children: [btnNew, btnCopy, btnDel] }); + const editRow = el("div", { + className: "m02-side__row ui-sidebar-card", + children: [btnNew, btnCopy, btnDel], + }); - const root = el("div", { className: "m02-side", children: [listHost, editRow] }); + const root = el("div", { className: "m02-side ui-sidebar-panel", children: [listHost, editRow] }); const isOpen = (info: TemplateInfo): boolean => { if (picked) return picked.kind === info.종류 && picked.name === info.이름; @@ -126,7 +129,7 @@ export function buildSide(opt: SideOptions): SideHandle { ...(mine.length ? mine.map((info) => { const b = el("button", { - className: `m02-side__item${isOpen(info) ? " is-active" : ""}`, + className: `m02-side__item ui-sidebar-row${isOpen(info) ? " is-active" : ""}`, text: info.이름, attrs: { type: "button", title: info.수정일 }, }); diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css index b3c80cb2..cfaaab1f 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css @@ -1,44 +1,9 @@ -/* M02 마스터 템플릿 — 좌측 패널 + 메인. 색·간격은 공용 토큰. */ +/* M02 마스터 템플릿 — 좌측 패널 + 메인. 카드·목록·줄 모양은 공용 ui-sidebar-* + (ui_template_overlay.css, M01 과 한 벌) — 여기는 패널 위 여백만. */ .m02-side { - display: flex; - flex-direction: column; - gap: var(--spacing-12); padding-top: var(--spacing-8); } -.m02-side__group { - display: flex; - flex-direction: column; - gap: var(--spacing-4); -} - -.m02-side__items { - display: flex; - flex-direction: column; - gap: var(--spacing-4); -} - -.m02-side__item { - width: 100%; - padding: var(--spacing-4) var(--spacing-8); - border: 0; - border-radius: var(--radius-md); - background: none; - color: inherit; - font: inherit; - text-align: left; - cursor: pointer; -} - -.m02-side__item:hover { - background: var(--color-paper); -} - -.m02-side__item.is-active { - background: color-mix(in srgb, var(--color-accent) 14%, transparent); - font-weight: var(--font-weight-bold); -} - .m02-side__empty { margin: 0; color: var(--color-text-muted); diff --git a/ui_template/ui_template_overlay.css b/ui_template/ui_template_overlay.css index 236f5941..f7e5c05b 100644 --- a/ui_template/ui_template_overlay.css +++ b/ui_template/ui_template_overlay.css @@ -364,3 +364,62 @@ border-style: solid; border-color: color-mix(in srgb, var(--color-border) 25%, var(--color-text-secondary)); } + +/* 좌측 패널 공용 틀(M01·M02 공용) — 접기 카드 모양 · 목록 · 줄 · 고름 표시. + 전체를 감싸는 패널 간격은 .ui-sidebar-panel, 카드 하나는 .ui-sidebar-card, + 그 안 목록은 .ui-sidebar-list, 줄은 .ui-sidebar-row(선택 시 is-active). */ +.ui-sidebar-panel { + display: flex; + flex-direction: column; + gap: var(--spacing-8); +} + +.ui-sidebar-card { + padding: calc(var(--spacing-8) + var(--spacing-4)); + border-radius: var(--radius-cards); + background: var(--color-surface-raised); +} + +.ui-sidebar-card > .ui-collapsible__title { + margin: 0; + color: var(--color-text); + font-size: var(--text-body-sm); +} + +/* 카드 안에 다시 카드(M02 도면 안 구조물 도면 묶음) — 앞 목록과 간격 */ +.ui-sidebar-card .ui-sidebar-card { + margin-top: var(--spacing-8); +} + +.ui-sidebar-list { + display: flex; + flex-direction: column; + gap: 2px; + max-height: 50vh; + margin-top: var(--spacing-8); + overflow: auto; +} + +.ui-sidebar-row { + display: flex; + justify-content: space-between; + gap: var(--spacing-8); + padding: var(--spacing-4) var(--spacing-8); + border: 0; + border-radius: var(--radius-buttons); + background: transparent; + color: var(--color-text-body); + font: inherit; + font-size: var(--text-body-sm); + text-align: left; + cursor: pointer; +} + +.ui-sidebar-row:hover { + background: var(--color-paper); +} + +.ui-sidebar-row.is-active { + background: var(--color-mist-violet); + color: var(--color-accent); +}