Files
Aislo/M01_MasterData/M01_MasterData_UI_LogicLab_SourceFind.ts
T
eomsangdonandClaude Sonnet 5.5 ad9a50c584 feat(813-0b): 원문 패널 — 제목 한 줄로 통합 · 표 글자 16
- 제목 = 책 부문 + 절 번호 + 절 제목(개정 꼬리 포함) 한 줄 · 본문 안 원문 제목 줄은 화면에서만 숨김(절 여럿이면 절마다 번호 이름 줄)
- 기초 찾기로 고른 뒤 남던 작은 글 줄 지움
- 표 머리 · 칸 글자 = 본문과 같은 --text-body(16) · [주] 는 14 그대로

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:43:15 +09:00

96 lines
3.5 KiB
TypeScript

/* =============================================================================
* M01_MasterData_UI_LogicLab_SourceFind.ts
* 원문 패널 맨 위 「단가산출 기초 찾기」(PLAN 807-1 · 요구 699) — M01 로직 상세 · M02 단가산출 원문 패널이 같이 씀.
* M01 로직 목록(로그인 길 `/m02/logic-lib/logics` · 처음 칠 때 한 번 읽음) · 낱말 맞춤 = 공용 `matchLogic`
* (키 · 원문번호 · 이름 · 구분 · 별칭) · 30 줄까지 · 고르면 `onPick`(부르는 쪽이 그 출처 원문을 보임 · 기억 안 함)
* 빈 칸 = 목록 비움 + `onClear`(부르는 쪽이 처음 화면으로)
* ========================================================================== */
import { API_BASE_URL } from "@config/config_frontend";
import { currentLanguageIndex } from "@ui/ui_template_locale";
import { createFindList } from "@ui/ui_template_find_list";
import { matchLogic } from "@ui/ui_template_logic_find";
const TEXT = {
Find: ["단가산출 기초 찾기 — 이름 · 원문번호", "Find base build-up — name · clause"],
Many: ["{n} 개 중 30 개 — 낱말을 더 침", "Showing 30 of {n} — type more"],
None: ["맞는 기초 없음", "No match"],
Failed: ["기초 목록을 못 읽음", "Failed to load"],
} as const;
const ts = (key: keyof typeof TEXT, n = 0): string =>
TEXT[key][currentLanguageIndex === 1 ? 1 : 0].replace("{n}", String(n));
export interface FoundLogic {
키: string;
원문번호?: string;
이름: string;
구분?: string;
상세구분?: string;
별칭?: string | null;
결과단위?: string;
출처?: string;
}
const MAX = 30;
let all: Promise<FoundLogic[]> | null = null; // 한 화면 수명 동안 한 번(M01 로직 목록)
const logics = (): Promise<FoundLogic[]> =>
(all ??= fetch(`${API_BASE_URL}/m02/logic-lib/logics`, { credentials: "include" })
.then((r) => (r.ok ? (r.json() as Promise<{ logics: FoundLogic[] }>) : Promise.reject()))
.then((d) => d.logics)
.catch((e: unknown) => {
all = null;
throw e;
}));
export function createSourceFind(opts: {
onPick: (logic: FoundLogic) => void;
onClear: () => void;
}): { root: HTMLElement; clear: () => void } {
const find = createFindList({ placeholder: ts("Find") });
find.root.classList.add("m01lab-src__find");
find.root.setAttribute("data-source-find", "");
let seq = 0;
const run = async (): Promise<void> => {
const q = find.search.value.trim();
const mine = ++seq;
if (!q) {
find.setRows([]);
find.hint.textContent = "";
return opts.onClear();
}
let rows: FoundLogic[];
try {
rows = await logics();
} catch {
find.hint.textContent = ts("Failed");
return;
}
if (mine !== seq) return;
const hit = rows.filter((x) => matchLogic(q, x).ok);
find.hint.textContent = hit.length > MAX ? ts("Many", hit.length) : hit.length ? "" : ts("None"); // prettier-ignore
find.setRows(
hit.slice(0, MAX).map((x) => ({
key: x.키,
tag: x.원문번호 || x.키,
name: x.이름,
sub: [x.구분, x.상세구분].filter(Boolean).join(" · "),
unit: x.결과단위 ?? "",
onPick: () => {
find.setRows([]);
find.hint.textContent = ""; // 고른 기초 이름은 원문 제목 한 줄이 보임
opts.onPick(x);
},
})),
);
};
find.search.addEventListener("input", () => void run());
return {
root: find.root,
clear: () => {
find.search.value = "";
find.setRows([]);
find.hint.textContent = "";
},
};
}