Files
Aislo/M02_MasterTemplete/DataFlow/M02_DF_InputPanel_Template.ts
eomsangdonandClaude Opus 5.5 09134bab84 refactor(M02): 띠 알림 · 빈 상태 · 보조 글 · 배지 · 탭 · 표 · 목록 줄 · 도구줄 · 체크를 부품으로 (PLAN 553-5)
- DataFlow: 표 넷 = 데이터표 · 함수 판 알림 · 선/알약 경고 · 끊긴 곳 = 띠 알림 · 알약 · 칸 종류 = 배지 · 고르는 줄 = 목록 줄 · 입력 넷 = 입력 · 도구줄 여섯 · 날 색 → 공통 값
- 구조물 옵션 · 구조물 · 도면 · 면적 · 품목 고르기: 판 띠 = 띠 알림 · 한눈/종류별/부품 = 탭 · 표 둘 = 데이터표 · 체크 둘 = 체크 · 카드 · 번호 = 카드 · 배지 · 도면 도구줄
- 일위대가 · 호표 · 실무자료 · 본 화면 · 서고: 알림 = 띠 알림 · 빈 상태 · 보조 글 · 도구줄 여섯 · 옛 보라 별칭 0
- M01 로직 입력칸 = 입력 부품(작은 칸) · 일위대가 상세 줄 수 고정 시험 693 · 지킴 시험 M02 몫 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 23:44:19 +09:00

99 lines
4.1 KiB
TypeScript

/* =============================================================================
* M02_DF_InputPanel_Template.ts
* 입력 판 — 재료 조건 찾기 「검색어 틀」: 틀(예: 「레미콘 {굵은골재최대치수}-{강도}-{슬럼프}」) 입력 · 틀 자리 {이름} 마다
* 값을 줄 변수 알약 고르기 · 글 [한, 영] 짝은 아래 · 재료 판(`M02_DF_InputPanel_Material.ts`)이 부름
* ========================================================================== */
import { currentLanguageIndex } from "@ui/ui_template_locale";
import { createInputField, createSelectField, el } from "@ui/ui_template_elements";
import type { CfLogic, CfPort } from "./M02_DF_Model";
import { templateSlots, variablePills } from "./M02_DF_InputPanel_Build";
import "@ui/ui_template_text.css";
import "@ui/ui_template_badge.css";
import "@ui/ui_template_list_row.css";
import "@ui/ui_template_toolbar.css";
const TEXT = {
Template: ["검색어 틀 — 자리는 {이름} 으로", "Search template — slots as {name}"],
Placeholder: ["예: 레미콘 {굵은골재최대치수}-{강도}-{슬럼프}", "e.g. Ready-mix {a}-{b}-{c}"],
Help: [
"틀의 자리마다 변수 알약을 고르면 그 값이 끼워진 글로 재료를 찾음(띄어쓴 말은 모두 들어간 것)",
"Each slot takes a variable pill's value; words must all match",
],
Slot: ["자리 「{n}」 ← 알약", "Slot “{n}” ← pill"],
NoSlot: ["틀에 자리 {이름} 을 넣어 주세요", "Add a {slot} to the template"],
NoPill: ["변수 알약이 없음 — 변수 입력 상자를 먼저 만듦", "No variable pill yet"],
} as const;
type TKey = keyof typeof TEXT;
const t = (key: TKey, v: Record<string, string> = {}): string =>
Object.entries(v).reduce(
(s, [k, x]) => s.replace(`{${k}}`, x),
TEXT[key][currentLanguageIndex] as string,
);
export interface TemplateBox {
root: HTMLElement;
/** 지금 틀 · 자리별 알약 — 틀이 비었으면 null */
get(): { template: string; slots: { name: string; from: CfPort }[] };
}
/** 틀 칸 + 자리 고르기 칸 — 바뀌면 onChange */
export function templateBox(lg: CfLogic, onChange: () => void): TemplateBox {
const pills = variablePills(lg);
const tpl = createInputField({
type: "text",
label: t("Template"),
compact: true,
placeholder: t("Placeholder"),
});
const slotBox = el("div", { className: "m02-dfip__stack" });
const picks = new Map<string, HTMLSelectElement>();
const draw = (): void => {
const names = templateSlots(tpl.input.value);
const before = new Map([...picks].map(([k, s]) => [k, s.value]));
picks.clear();
slotBox.replaceChildren(
...(names.length
? pills.length
? names.map((n) => {
// 자리 이름과 같은 알약이 있으면 먼저 고름
const same = pills.findIndex((p) => p.port.name === n);
const sel = createSelectField({
label: t("Slot", { n }),
options: pills.map((p, i) => ({
value: String(i),
text: `${p.port.name}${p.example !== null ? ` = ${p.example}` : ""}`,
})),
value: before.get(n) ?? String(same >= 0 ? same : 0),
compact: true,
onChange: () => onChange(),
});
picks.set(n, sel.select);
return sel.root;
})
: [el("p", { className: "ui-text ui-text--error", text: t("NoPill") })]
: [el("p", { className: "ui-text ui-text--muted", text: t("NoSlot") })]),
);
};
tpl.input.addEventListener("input", () => (draw(), onChange()));
draw();
return {
root: el("div", {
className: "m02-dfip__stack",
children: [
el("p", { className: "ui-text ui-text--muted", text: t("Help") }),
tpl.root,
slotBox,
],
}),
get: () => ({
template: tpl.input.value.trim(),
slots: templateSlots(tpl.input.value).flatMap((n) => {
const p = pills[Number(picks.get(n)?.value)];
return p ? [{ name: n, from: p.port }] : [];
}),
}),
};
}