Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Sangeun_Tabs.ts
T
eomsangdonandClaude Opus 5.5 c11baa441c feat(804-2): 단가산출 「줄 쌓기」 새 탭 — M01 단가산출 기초 · M02 단가산출 · 기계 시간당 경비 수식 펼침 · KaTeX
- 공용 화면 A00_Common/line_stack_* — 위 옵션 · 왼쪽 줄 쌓기 편집 · 오른쪽 산출근거(경쟁사 시트 꼴) · 맨 아래 합계 상자
- 모델 common_util_line_stack.ts — 수식 문서 ↔ 줄 쌓기 한 벌 왕복(차림 = 문서 칸 `쌓기` · 기본과 다를 때만)
- 기계 · 단가산출 줄 단가 수 누름 = 그 문서를 같은 계산기로 셈해 펼침(손료 식 · 운전경비 · 노 · 재 · 경 · 계)
- 공식 묶음 · 틀 풀기(단가산출 결과 → 이 문서 식) · 내 틀(이 브라우저) · 「내 틀 (… 에서 변경)」
- 수식 탭과 같은 길 · 같은 캐시 · 탭 바꾸면 다시 읽음 · [수식 저장] = 서버 정본 셈
- KaTeX(MIT) — vite 미리 묶기에서 뺌(rolldown 이 글자 규칙 서로게이트 범위를 깨뜨림)
- 시험 test_804_2 6(왕복 1,518 · 금액 · 기계 펼침 = hourly · 틀 풀기 · 저장 · 이음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:34:29 +09:00

101 lines
4.0 KiB
TypeScript

/* =============================================================================
* M02_MasterTemplete_Sangeun_Tabs.ts
* M02 단가산출(SK) 상세 보기 고르기(PLAN 799-2 · 802-4b-2 · 804-2 · 요구 655-8 · 660~665 · 677) — 수식(첫 탭) · 줄 쌓기 · 단가산출 상세.
* 줄 쌓기 = 공용 줄 쌓기 화면(`A00_Common/line_stack_screen.ts`) — 같은 SK 수식 길 · 같은 캐시 · 탭을 바꾸면 고른 쪽이 다시 읽음
* 수식 = 공용 수식 문서 화면(`A00_Common/formula_doc_screen.ts` · M01 과 같은 꼴) — 길 앞머리만 M02 사본 길
* (`/m02/layers/{층}/sangeun/{키}/doc` · 802-4a) · 층 쓰기 권한 없으면 읽기만 · 저장 = 그 탭 [수식 저장](페이지 [저장] 아님)
* 옛 M02 수식 나무 화면(796-2b · 2c · 2e · 800 · 801)은 걷음 · 단가산출 상세 = 상세가 쥔 `editor`(수식일 때 숨김)
* 저장 안 한 SK(일위대가 줄 모달 · 새 SK) = 「저장한 단가산출만」 글
* ========================================================================== */
import { createSegment } from "@ui/ui_template_segment";
import { createDocScreen } from "../A00_Common/formula_doc_screen";
import { createStackScreen } from "../A00_Common/line_stack_screen";
import { lt } from "../A00_Common/line_stack_text";
import { el } from "@ui/ui_template_elements";
import { tl } from "../M01_MasterData/M01_MasterData_UI_LogicLab_Text";
import type { Layer } from "./M02_MasterTemplete_Api_Fetch";
type View = "equation" | "stack" | "ho";
export interface SkEquationOptions {
scope: { layer: Layer; owner: string | null };
edit: boolean;
/** [수식 저장] · [단가 갱신] · [재생성] 뒤 그 SK 새 판 */
onSaved: (version: string) => void;
}
/** SK 수식 길 — 꼬리 물음(`/table?key=…`)에 주인 물음을 이어 붙임 */
export function skDocPath(scope: SkEquationOptions["scope"], key: string, path: string): string {
const [tail, query] = path.split("?");
const ask = [query, scope.owner ? `owner=${encodeURIComponent(scope.owner)}` : ""]
.filter(Boolean)
.join("&");
const at = `/m02/layers/${scope.layer}/sangeun/${encodeURIComponent(key)}/doc${tail}`;
return ask ? `${at}?${ask}` : at;
}
/** 보기 고르기 · 수식 그릇 — `editor` = 단가산출 상세 그릇(수식일 때 숨김) */
export function createSangeunTabs(
editor: HTMLElement,
opt: SkEquationOptions,
): {
picker: HTMLElement;
host: HTMLElement;
/** 저장한 SK 키(빈 키 = 저장 전) */
draw: (key: string) => void;
canLeave: () => Promise<boolean>;
} {
const screen = createDocScreen({
base: "",
admin: opt.edit,
cache: "m02_sk_doc_drafts",
copy: { route: (k, p) => skDocPath(opt.scope, k, p), onSaved: opt.onSaved },
});
const stack = createStackScreen({
base: "",
admin: opt.edit,
cache: "m02_sk_doc_drafts",
copy: { route: (k, p) => skDocPath(opt.scope, k, p), onSaved: opt.onSaved },
});
let key = "";
let view: View = "equation";
const show = (v: View): void => {
view = v;
screen.root.hidden = v !== "equation";
stack.root.hidden = v !== "stack";
editor.hidden = v !== "ho";
if (!editor.hidden) editor.dataset.view = v;
};
const picker = createSegment({
options: [
{ value: "equation", label: tl("View_Equation") },
{ value: "stack", label: lt("Tab") },
{ value: "ho", label: tl("Ho_Title") },
],
value: "equation",
size: 32,
ariaLabel: tl("View_Label"),
onChange: (v) => {
show(v as View);
if (v === "stack") stack.draw(key, true);
else if (v === "equation" && key) {
screen.draw("");
screen.draw(key);
}
},
});
picker.root.dataset.skViews = "";
show("equation");
return {
picker: picker.root,
host: el("div", { className: "m02-sk__views", children: [screen.root, stack.root] }),
draw: (k: string) => {
key = k;
screen.draw(k);
if (view === "stack") stack.draw(k);
},
canLeave: () => screen.canLeave().then((ok) => ok && stack.canLeave()),
};
}