feat(b09): 단가연결 모달 — 연결 하위 메뉴 다섯 → 한 창(종류 고르기 · 검색) · 이름 단가연결 (251-3 · 251-6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLCk2y5xE8YGVgjbTntWsY
This commit is contained in:
2026-10-11 01:18:31 +09:00
co-authored by Claude Opus 5.5
parent 0e6da95c95
commit 8e7a795abc
10 changed files with 178 additions and 127 deletions
@@ -3,6 +3,8 @@
* [로직 더하기] 창 — 한 목록에 구분(단가산출 · 단가승격 · 일위조합) 칸 · 치는 대로 걸러짐(찾기 단추 없음) ·
* 단가승격 · 일위조합 줄은 층 단추(위 층만 · 짝 `M02_Labor_Ref.readable`) 층의 서고 · 고르면 참조 {층 · 주인 · 키} ·
* 프로젝트 · 카드엔 서고 줄 없음 · 거름 = 로직 품셈 분류 + 사전조합 새 분류(149-2 · 구조물군 › 공통 + 종류)
* 단가연결(251 · B08 거리이정표 · B09 설계내역서) = 종류 고르기(단가산출 · 사전조합 · 덧붙인 줄 종류) +
* 덧붙일 줄(구조물 조합 · B09 호표) — `lib.extra` · `lib.kindFilter`
* ========================================================================== */
import { createButton, createSelectField, el, showToast } from "@ui/ui_template_elements";
@@ -38,6 +40,18 @@ export interface LibraryPick {
/** 서고 줄만(로직 목록 안 받음 · B09 설계내역서 연결 204-2) · 창 제목 */
libOnly?: boolean;
title?: string;
/** 목록 끝 덧붙일 줄(251 · 구조물 조합 · B09 호표) — kind = 종류 고르기 값 */
extra?: Array<{
kind: string;
key: string;
tag: string;
name: string;
sub: string;
unit: string;
onPick: () => void;
}>;
/** 종류 고르기 칸(251 · 값 logic · precombo · 덧붙인 줄 kind) */
kindFilter?: Array<{ value: string; text: string }>;
}
/** 고를 수 있는 층 — 위 층만(마스터 → 마스터 · 회사 → 그 회사 · 마스터 · 개인 → 그 개인 · 그 회사 · 마스터) */
@@ -72,12 +86,21 @@ export function openLogicPicker(done: (logic: LogicSummary) => void, lib?: Libra
let scopes: Scope[] = [];
let scope: Scope | null = null;
let libRows: { kind: "Kind_Combo"; row: LibraryRow }[] = [];
// 251 종류 고르기 — 값 "" = 전체 · logic · precombo · 덧붙인 줄 kind
const kindSel = lib?.kindFilter
? createSelectField({
options: [{ value: "", text: L("M01_All") }, ...lib.kindFilter],
compact: true,
label: "종류",
})
: null;
const kindOk = (k: string): boolean => !kindSel?.select.value || kindSel.select.value === k;
let loading = false;
let seq = 0;
/* 공용 찾기 목록 — 구분 · 상세구분 2열 · 이름 왼쪽 맞춤 · ↑ ↓ Enter */
const finder = createFindList({
placeholder: tc("Pick_Search"),
filters: [sub.root, detail.root],
filters: [...(kindSel ? [kindSel.root] : []), sub.root, detail.root],
extra: [layerBar],
});
@@ -85,18 +108,26 @@ export function openLogicPicker(done: (logic: LogicSummary) => void, lib?: Libra
const q = finder.search.value.trim().toLowerCase();
const found = all.filter(
(x) =>
kindOk("logic") &&
(!sub.select.value || x.구분 === sub.select.value) &&
(!detail.select.value || x.상세구분 === detail.select.value) &&
matchLogic(finder.search.value, x).ok, // 이름 · 키 · 별칭 …(공용 · 208-1)
);
const libFound = libRows.filter(
(x) =>
kindOk("precombo") &&
(!sub.select.value || x.row.구분 === sub.select.value) &&
(!detail.select.value || x.row.상세구분 === detail.select.value) &&
(!q || `${x.row.이름} ${x.row.제목 ?? ""}`.toLowerCase().includes(q)),
);
const total = all.length + libRows.length;
const shown = found.length + libFound.length;
const extra = (lib?.extra ?? []).filter(
(x) =>
kindOk(x.kind) &&
!sub.select.value &&
(!q || `${x.tag} ${x.name} ${x.sub}`.toLowerCase().includes(q)),
);
const total = all.length + libRows.length + (lib?.extra?.length ?? 0);
const shown = found.length + libFound.length + extra.length;
finder.hint.textContent = loading
? tc("Loading")
: shown
@@ -132,6 +163,13 @@ export function openLogicPicker(done: (logic: LogicSummary) => void, lib?: Libra
},
};
}),
...extra.map((x) => ({
...x,
onPick: () => {
close();
x.onPick();
},
})),
]);
};
const loadLib = async (): Promise<void> => {
@@ -192,6 +230,7 @@ export function openLogicPicker(done: (logic: LogicSummary) => void, lib?: Libra
find();
});
detail.select.addEventListener("change", find);
kindSel?.select.addEventListener("change", find);
finder.search.addEventListener("input", find);
layerBar.style.display = "none";