/* ============================================================================= * B05_Profile_UI_Structures_Define_Form.ts * 손코딩 시설 폼(동작 「계곡통과시설」)에 정의를 붙임(PLAN 48-3 · 48-4) — 숨긴 선택지 · 규칙. * * · 칸은 `labeled(..., 옵션키)` 가 붙인 `data-option-keys` 표식으로 찾음(키 하나인 칸만) · 찾는 자리 = * 폼 본문 전체(그룹 상자로 옮겨 간 칸까지). * · 칸 배치 · 이름은 손코딩 그대로(동작이 그림) — 안 씀은 끈 옵션과 같은 길(`_Drainage_Facility_Hidden`). * · 선택지 = 정의 · 규칙 선택지 — 새 선택지는 보기를 더하고 밖 보기는 `hidden` 으로만 가림(옛 값은 그대로). * 여러 키를 함께 적는 칸(유입구 구조 · 본체 규격)은 키 하나 표식이 아니라 안 건드림. * · 규칙이 가린 칸 = `is-rule-hidden`(끈 옵션 표식 `is-option-hidden` 과 따로). * ========================================================================== */ import type { StructureType } from "./B05_Profile_Api_Structures"; import { ruleDefaults, ruleStates } from "./B05_Profile_UI_Structures_Define"; import { isOptionVisible } from "./B05_Profile_UI_Structures_Summary"; /** 시설 폼이 가릴 옵션 키 — 「구조물 옵션 보이기」에서 끈 것 + 정의 「안 씀」. */ export function hiddenOptionKeys(type: StructureType | null): string[] { return (type?.options ?? []).filter((o) => !isOptionVisible(o)).map((o) => o.key); } export interface FacilityDefinition { /** 지금 종류 정의로 칸 모양을 맞춤 — 시설 폼을 채운 뒤마다 부름. */ apply: (type: StructureType | null) => void; } export function createFacilityDefinition(root: HTMLElement): FacilityDefinition { let type: StructureType | null = null; const wrapOf = (key: string): HTMLElement | null => [...root.querySelectorAll("[data-option-keys]")].find( (node) => node.dataset.optionKeys === key, ) ?? null; const controlOf = (key: string): HTMLInputElement | HTMLSelectElement | null => wrapOf(key)?.querySelector("select, input") ?? null; const values = (): Record => Object.fromEntries((type?.options ?? []).map((o) => [o.key, controlOf(o.key)?.value ?? ""])); const paint = (): void => { if (!type) return; const states = ruleStates(type, values()); for (const option of type.options) { const wrap = wrapOf(option.key); const state = states.get(option.key); if (!wrap || !state) continue; wrap.classList.toggle("is-rule-hidden", !state.visible); const control = controlOf(option.key); if (!(control instanceof HTMLSelectElement)) continue; // 정의에 새로 든 선택지는 보기를 더하고, 정의 · 규칙 밖 보기는 가림(빈 보기 · 지금 값은 그대로) const have = new Set([...control.options].map((item) => item.value)); for (const choice of state.choices) if (!have.has(choice)) control.add(new Option(choice, choice)); for (const item of control.options) { item.hidden = !!item.value && !state.choices.includes(item.value) && item.value !== control.value; } } }; // 상위 칸이 바뀌면 걸린 규칙의 기본값을 하위 칸에 넣고, 그 칸의 change 로 폼 반영(시설 폼 emit)을 잇는다. root.addEventListener("change", (event) => { if (!type) return; const key = (event.target as HTMLElement).closest("[data-option-keys]")?.dataset .optionKeys; if (key) { for (const [child, value] of Object.entries(ruleDefaults(type, key, values()))) { const control = controlOf(child); if (!control || control.value === String(value)) continue; control.value = String(value); control.dispatchEvent(new Event("change", { bubbles: true })); } } paint(); }); return { apply(next) { type = next; root .querySelectorAll(".is-rule-hidden") .forEach((node) => node.classList.remove("is-rule-hidden")); root.querySelectorAll("option[hidden]").forEach((o) => (o.hidden = false)); paint(); }, }; }