/* ============================================================================= * B05_Profile_UI_Structures_Form.ts * 「구조물 배치」 폼의 **뼈대**만 세우는 조립기 (2026-09-03 · 700줄 제한). * * 패널 본체(`_UI_Structures_Panel`)는 값 읽기·검증·저장·목록 갱신을 맡고, 여기서는 * 화면 요소를 만들어 넘기기만 한다. 배치 규칙(행 순서·구분선·버튼 묶음)은 사용자 지시로 * 굳은 것이라 그대로 옮겼다. * * 화면 구성(2026-08-17~29 사용자 지시): * [구조물군][종류] ← 2열 격자 * [측점 범위] [시작 측점] [기준 측점] [종료 측점] ← 타입에 따라 보이는 행이 갈린다 * ──────── 구분선 * [옵션 격자] / [계곡 통과 시설 서브폼] * [추가·삭제·리셋] * 목록(`list`)은 본문에 붙이지 않는다 — 폼이 길어지면 스크롤 밖으로 밀려 안 보였다. * ========================================================================== */ import { field, select, type StationFields, stationFields, } from "./B05_Profile_UI_Structures_Fields"; import { createFacilityOptionsForm } from "./B05_Profile_UI_Drainage_Facility"; export interface StructuresFormElements { root: HTMLElement; body: HTMLElement; groupSelect: HTMLSelectElement; typeSelect: HTMLSelectElement; startFields: StationFields; anchorFields: StationFields; endFields: StationFields; memoField: HTMLInputElement; /** 계산으로 채우는 측점 범위 표시(직접 입력이 아니다). */ rangeValue: HTMLElement; rangeWrap: HTMLElement; positionRow: HTMLElement; /** 측점 그룹과 옵션 사이 구분선 — 타입이 없으면 함께 숨긴다. */ positionDivider: HTMLElement; optionRow: HTMLElement; /** 버튼 묶음 — 패널이 그 앞에 메모 칸을 끼워 넣는다. */ actions: HTMLElement; facilityOptions: ReturnType; primary: HTMLButtonElement; removeButton: HTMLButtonElement; resetButton: HTMLButtonElement; /** 하단 고정 영역에 패널이 따로 붙이는 구조물 목록. */ list: HTMLElement; } /** 폼 뼈대를 세운다. 값 채우기·이벤트 배선은 호출한 쪽(패널)이 한다. */ export function buildStructuresForm(options: { /** 측점 잔여거리 정규화에 쓰는 현재 측점 간격(m). */ getInterval: () => number; /** 계곡 통과 시설 서브폼 값이 바뀔 때 — 패널이 즉시 반영을 건다. */ onFacilityChange: () => void; }): StructuresFormElements { const root = document.createElement("section"); // b05-structure-section: 이 섹션 안의 입력·버튼 높이를 한 값(--b05-control-h)으로 // 묶는 스코프(2026-08-29 사용자 지시 — 조작 컨트롤 높이 통일). root.className = "b05-route__panel-section b05-structure-section ui-collapsible ui-sidebar-section"; const heading = document.createElement("h3"); heading.className = "ui-collapsible__title"; heading.textContent = "구조물 배치"; const body = document.createElement("div"); body.className = "b05-route__panel-body"; root.append(heading, body); const groupSelect = select([]); const typeSelect = select([]); // 위치 입력 순서 = 시작 측점 → 기준 측점 → 종료 측점 (2026-08-17 사용자 확정). // 점형은 기준 측점만 보인다. 잔여거리는 [0, 간격) — 넘치면 측점번호로 올려 표시한다. const startFields = stationFields("시작 측점", options.getInterval); const anchorFields = stationFields("기준 측점", options.getInterval); const endFields = stationFields("종료 측점", options.getInterval); const memoField = document.createElement("input"); memoField.type = "text"; memoField.placeholder = "메모(선택)"; // 측점 범위(계산 표시) — C군 사면안정처럼 길이 옵션으로 구간이 정해지는 타입은 // 시작·종료를 직접 입력하지 않고 기준 측점 + 길이로 계산해 보여만 준다 // (2026-08-19 사용자 지시). 표기는 하단 구조물 목록과 같은 시작 ~ 종료 측점. const rangeValue = document.createElement("span"); rangeValue.className = "b05-structure__range-value"; const rangeWrap = field("측점 범위", rangeValue); // 측점은 3행 — 한 행이 [라벨][측점번호][잔여거리](2026-08-17 사용자 지시 2). const positionRow = document.createElement("div"); positionRow.className = "b05-structure__position-row"; positionRow.append(rangeWrap, startFields.wrap, anchorFields.wrap, endFields.wrap); // 기본 인터페이스는 2열 격자(2026-08-17 사용자 지시 1). const typeRow = document.createElement("div"); typeRow.className = "b05-structure__grid"; typeRow.append(field("구조물군", groupSelect), field("종류", typeSelect)); // 옵션 칸은 타입마다 다르므로 선택할 때마다 새로 그린다. const optionRow = document.createElement("div"); optionRow.className = "b05-structure__grid"; // 계곡 통과 시설(배관 등)의 부속 옵션 서브폼 — 일반 구조물의 옵션 칸과 같은 자리에서 // 같은 흐름(종류 선택 → 옵션 → 추가/수정)으로 편집한다(2026-08-17 사용자 지시 — // 자동·수동 배관은 같은 구조물, 별도 UI 금지). const facilityOptions = createFacilityOptionsForm({ onChange: options.onFacilityChange }); const primary = document.createElement("button"); primary.type = "button"; primary.className = "b05-route__irregular-btn is-primary"; const removeButton = document.createElement("button"); removeButton.type = "button"; removeButton.className = "b05-route__irregular-btn is-danger"; removeButton.textContent = "삭제"; const resetButton = document.createElement("button"); resetButton.type = "button"; resetButton.className = "b05-route__irregular-btn"; resetButton.textContent = "리셋"; const actions = document.createElement("div"); actions.className = "b05-route__irregular-actions"; actions.append(primary, removeButton, resetButton); // 목록은 이 섹션 본문에 붙이지 않는다 — 폼이 길어지면 스크롤 밖으로 밀려 안 보였다. // 패널(B05_Profile_UI_Panel)이 하단 고정 영역에 배치한다(2026-08-18 사용자 지시). const list = document.createElement("ul"); list.className = "b05-route__irregular-list"; // 측점 그룹과 옵션 나열 사이 구분선(2026-08-17 사용자 지시 2). const positionDivider = document.createElement("hr"); positionDivider.className = "b05-structure__divider"; body.append(typeRow, positionRow, positionDivider, optionRow, facilityOptions.root, actions); return { root, body, groupSelect, typeSelect, startFields, anchorFields, endFields, memoField, rangeValue, rangeWrap, positionRow, positionDivider, optionRow, actions, facilityOptions, primary, removeButton, resetButton, list, }; }