feat(M02): 면적산출 칸 지정 모달 AreaLink — 지금 고른 칸으로 · 주소로 잇기 · 풀기 · 정본은 면적 문서 연결(setLink) (PLAN 39-4 D5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_AreaLink.ts
|
||||
* 면적산출 상세 — 칸 지정 모달(PLAN 39-4 D5). 오른쪽 패널의 담은 로직 · 자재 줄을 누르면 엶.
|
||||
* 그 줄 알약 키(로직 물량 「이름|단위」 · 자재 「자재|MT키」)를 면적산출 칸에 잇기 · 옮기기 · 풀기.
|
||||
* 정본 = 면적산출 문서 `연결` 한 곳 — 스프레드시트 손잡이 `setLink`(알약 누름과 같은 규칙 · 되돌리기 됨)라
|
||||
* 아래 알약 줄 · 칸 세모 · 패널 수량이 같이 따라감. 손 입력 수량은 일위대가 몫(여기 없음) · 저장은 [저장].
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton, createInputField, el } from "@ui/ui_template_elements";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import type { LinkInfo, SpreadsheetHandle } from "../A00_Common/spreadsheet/spreadsheet";
|
||||
|
||||
const TEXT = {
|
||||
title: "칸 지정 — {value}",
|
||||
close: "닫기",
|
||||
now: "지금 연결",
|
||||
none: "연결 없음",
|
||||
picked: "지금 고른 칸으로 ({value})",
|
||||
address: "칸 주소",
|
||||
hint: "예 B6",
|
||||
link: "잇기",
|
||||
unlink: "풀기",
|
||||
bad: "칸 주소가 틀림 — 예 B6",
|
||||
readOnly: "읽기만 — 고칠 수 없음",
|
||||
};
|
||||
|
||||
const fill = (text: string, value: string): string => text.replace("{value}", value);
|
||||
|
||||
export interface AreaLinkOptions {
|
||||
/** 줄 이름(모달 제목) */
|
||||
label: string;
|
||||
/** 알약 키 */
|
||||
key: string;
|
||||
/** 연결에 같이 적을 이름 · 단위 · 자재 */
|
||||
info: LinkInfo;
|
||||
sheet: SpreadsheetHandle;
|
||||
/** 시트에서 지금 고른 칸(A1) */
|
||||
picked: () => string;
|
||||
readOnly: boolean;
|
||||
}
|
||||
|
||||
/** 지금 연결 칸 · 값 글 — 「B6 = 12.5 ㎡」 · 없으면 null */
|
||||
function linkedText(opts: AreaLinkOptions): string | null {
|
||||
const link = opts.sheet
|
||||
.getDoc()
|
||||
.시트.flatMap((s) => s.연결 ?? [])
|
||||
.find((l) => l.키 === opts.key);
|
||||
if (!link) return null;
|
||||
const value = opts.sheet.getLinkedValues()[opts.key];
|
||||
return [link.칸, "=", String(value ?? ""), opts.info.단위 ?? ""].join(" ").trim();
|
||||
}
|
||||
|
||||
export function openAreaLink(opts: AreaLinkOptions): void {
|
||||
const { close } = openModal({
|
||||
title: fill(TEXT.title, opts.label),
|
||||
closeLabel: TEXT.close,
|
||||
dialogClass: "m02-modal m02-area-link",
|
||||
mount: (body) => {
|
||||
const now = el("p", { className: "m02-area-link__now" });
|
||||
const paint = (): void => {
|
||||
const text = linkedText(opts);
|
||||
now.textContent = `${TEXT.now} · ${text ?? TEXT.none}`;
|
||||
unlink.disabled = opts.readOnly || !text;
|
||||
};
|
||||
const done = (cell: string | null): boolean => {
|
||||
if (!opts.sheet.setLink(opts.key, cell, opts.info)) return false;
|
||||
close();
|
||||
return true;
|
||||
};
|
||||
const here = opts.picked();
|
||||
const pick = createButton({
|
||||
label: fill(TEXT.picked, here),
|
||||
variant: "filled",
|
||||
size: "sm",
|
||||
onClick: () => void done(here),
|
||||
});
|
||||
const address = createInputField({ label: TEXT.address, placeholder: TEXT.hint });
|
||||
const link = createButton({
|
||||
label: TEXT.link,
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
onClick: () => {
|
||||
if (!done(address.input.value)) address.setError(TEXT.bad);
|
||||
},
|
||||
});
|
||||
const unlink = createButton({
|
||||
label: TEXT.unlink,
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
onClick: () => void done(null),
|
||||
});
|
||||
address.input.addEventListener("input", () => address.setError(undefined));
|
||||
address.input.addEventListener("keydown", (ev) => ev.key === "Enter" && link.click());
|
||||
pick.disabled = link.disabled = address.input.disabled = opts.readOnly;
|
||||
paint();
|
||||
body.append(
|
||||
now,
|
||||
...(opts.readOnly
|
||||
? [el("p", { className: "m02-struct__empty", text: TEXT.readOnly })]
|
||||
: []),
|
||||
el("div", { className: "m02-area-link__row", children: [pick, unlink] }),
|
||||
el("div", { className: "m02-area-link__row", children: [address.root, link] }),
|
||||
);
|
||||
if (!opts.readOnly) address.input.focus();
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user