fix(M02): 좌측 패널 공용 틀 M01 과 맞춤

- ui_template_overlay.css 에 ui-sidebar-panel · ui-sidebar-card · ui-sidebar-list ·
  ui-sidebar-row 공용 클래스 추가
- M01·M02 좌측 코드가 공용 클래스를 씀 — 카드 여백·둥글기·바탕 · 목록 간격 ·
  줄 선택 표시 한 벌
- M02 좌측 전용 CSS 중복 규칙 제거(없는 토큰 --radius-md 로 둥글기 안 먹던 것도 해소)
- M02 표/도면/구조물 카드 처음 접힘으로 M01 과 맞춤 · 새로·본떠·지우기 단추 줄 카드 안으로

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K7JS47SacjPxZ718QsZKyr
This commit is contained in:
2026-09-27 18:24:34 +09:00
co-authored by Claude Sonnet 5
parent 243d00c218
commit ef5a7e9771
5 changed files with 83 additions and 101 deletions
+11 -6
View File
@@ -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"),
+2 -52
View File
@@ -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;
@@ -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<Kind, HTMLElement>(
[...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.수정일 },
});
@@ -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);
+59
View File
@@ -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);
}