/* ============================================================================= * 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"; 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 => 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(); 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: "m02-dfip__muted is-bad", text: t("NoPill") })] : [el("p", { className: "m02-dfip__muted", text: t("NoSlot") })]), ); }; tpl.input.addEventListener("input", () => (draw(), onChange())); draw(); return { root: el("div", { className: "m02-dfip__stack", children: [el("p", { className: "m02-dfip__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 }] : []; }), }), }; }