feat(M02): 좌측 표 양식 · 도면 양식 접히는 컨테이너 · 구조물 도면 묶음 · 롱기튜디널 두 양식 지움
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J53E6xKBaqhUZucMJ1wBYX
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
showConfirmDialog,
|
||||
showToast,
|
||||
} from "@ui/ui_template_elements";
|
||||
import { attachCollapsible } from "@ui/ui_template_collapsible";
|
||||
import { t as L } from "@ui/ui_template_locale";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import {
|
||||
@@ -24,6 +25,8 @@ import {
|
||||
} from "./M02_MasterTemplete_Api_Fetch";
|
||||
import { canEdit, type Selection } from "./M02_MasterTemplete_UI_Main";
|
||||
|
||||
/** 서버 종류 추가 전까지 `Kind` 에 없음 — 목록 응답에 있을 때만 그림 */
|
||||
const STRUCTURE = "structure" as Kind;
|
||||
const KINDS: { kind: Kind; label: () => string }[] = [
|
||||
{ kind: "table", label: () => L("M02_KindTable") },
|
||||
{ kind: "drawing", label: () => L("M02_KindDrawing") },
|
||||
@@ -55,6 +58,8 @@ export interface SideOptions {
|
||||
export interface SideHandle {
|
||||
root: HTMLElement;
|
||||
refresh: () => Promise<void>;
|
||||
/** 좌측 고름 표시만 옮김 — 화면은 열지 않음 */
|
||||
select: (kind: Kind, name: string) => void;
|
||||
}
|
||||
|
||||
/** 서버 이름 규칙과 같게 — 화면에서 먼저 막음 */
|
||||
@@ -70,7 +75,31 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
let items: TemplateInfo[] = [];
|
||||
let seq = 0;
|
||||
|
||||
const listHost = el("div", { className: "m02-side__lists" });
|
||||
let picked: { kind: Kind; name: string } | null = null;
|
||||
|
||||
const section = (title: string, body: HTMLElement, ...more: HTMLElement[]): HTMLElement =>
|
||||
el("section", {
|
||||
className: "m02-side__group ui-collapsible ui-sidebar-section",
|
||||
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" }),
|
||||
]),
|
||||
);
|
||||
const listHost = el("div", {
|
||||
className: "m02-side__lists",
|
||||
children: [
|
||||
section(L("M02_KindTable"), bodies.get("table")!),
|
||||
section(
|
||||
L("M02_KindDrawing"),
|
||||
bodies.get("drawing")!,
|
||||
section(L("M02_KindStructure"), bodies.get(STRUCTURE)!),
|
||||
),
|
||||
],
|
||||
});
|
||||
attachCollapsible(listHost);
|
||||
|
||||
const btnNew = createButton({ label: L("M02_New"), variant: "filled" });
|
||||
const btnCopy = createButton({ label: L("M02_Copy"), variant: "ghost" });
|
||||
@@ -80,6 +109,7 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
const root = el("div", { className: "m02-side", children: [listHost, editRow] });
|
||||
|
||||
const isOpen = (info: TemplateInfo): boolean => {
|
||||
if (picked) return picked.kind === info.종류 && picked.name === info.이름;
|
||||
const cur = opt.getOpen();
|
||||
return !!cur && cur.layer === layer && cur.kind === info.종류 && cur.name === info.이름;
|
||||
};
|
||||
@@ -90,28 +120,22 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
};
|
||||
|
||||
function paintList(): void {
|
||||
listHost.replaceChildren(
|
||||
...KINDS.map(({ kind, label }) => {
|
||||
const mine = items.filter((i) => i.종류 === kind);
|
||||
return el("div", {
|
||||
className: "m02-side__group",
|
||||
children: [
|
||||
el("h4", { text: label() }),
|
||||
...(mine.length
|
||||
? mine.map((info) => {
|
||||
const b = el("button", {
|
||||
className: `m02-side__item${isOpen(info) ? " is-active" : ""}`,
|
||||
text: info.이름,
|
||||
attrs: { type: "button", title: info.수정일 },
|
||||
});
|
||||
b.addEventListener("click", () => void select(info));
|
||||
return b;
|
||||
})
|
||||
: [el("p", { className: "m02-side__empty", text: L("M02_NoTemplates") })]),
|
||||
],
|
||||
});
|
||||
}),
|
||||
);
|
||||
for (const [kind, body] of bodies) {
|
||||
const mine = items.filter((i) => i.종류 === kind);
|
||||
body.replaceChildren(
|
||||
...(mine.length
|
||||
? mine.map((info) => {
|
||||
const b = el("button", {
|
||||
className: `m02-side__item${isOpen(info) ? " is-active" : ""}`,
|
||||
text: info.이름,
|
||||
attrs: { type: "button", title: info.수정일 },
|
||||
});
|
||||
b.addEventListener("click", () => void select(info));
|
||||
return b;
|
||||
})
|
||||
: [el("p", { className: "m02-side__empty", text: L("M02_NoTemplates") })]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function paintButtons(): void {
|
||||
@@ -123,6 +147,7 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
|
||||
async function select(info: TemplateInfo): Promise<void> {
|
||||
if (!isOpen(info) && !(await opt.confirmLeave())) return;
|
||||
picked = null;
|
||||
opt.onOpen({ layer, projectId: null, kind: info.종류, name: info.이름 });
|
||||
paintList();
|
||||
paintButtons();
|
||||
@@ -218,5 +243,12 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
});
|
||||
|
||||
void refresh();
|
||||
return { root, refresh };
|
||||
return {
|
||||
root,
|
||||
refresh,
|
||||
select: (kind, name) => {
|
||||
picked = { kind, name };
|
||||
paintList();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
.m02-side__group h4 {
|
||||
margin: 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--text-body-sm);
|
||||
.m02-side__items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
.m02-side__item {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ export const ui_locales_m2 = {
|
||||
M02_Title: ["마스터 템플릿", "Master Templates"],
|
||||
M02_KindTable: ["표 양식", "Table templates"],
|
||||
M02_KindDrawing: ["도면 양식", "Drawing templates"],
|
||||
M02_KindStructure: ["구조물 도면", "Structure drawings"],
|
||||
M02_NoTemplates: ["양식 없음", "None"],
|
||||
M02_New: ["새로", "New"],
|
||||
M02_Copy: ["본떠 만들기", "Copy as new"],
|
||||
|
||||
Reference in New Issue
Block a user