Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
/* =============================================================================
|
||||
* 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<typeof createFacilityOptionsForm>;
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -23,11 +23,11 @@ import {
|
||||
} from "./B05_Profile_Api_Structures";
|
||||
import type { PipeFacility, PipeSource } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
|
||||
import {
|
||||
createFacilityOptionsForm,
|
||||
type FacilityAttributes,
|
||||
type FacilityOptionsForm,
|
||||
} from "./B05_Profile_UI_Drainage_Facility";
|
||||
import { field, numberInput, select, stationFields } from "./B05_Profile_UI_Structures_Fields";
|
||||
import { field, numberInput, select } from "./B05_Profile_UI_Structures_Fields";
|
||||
import { buildStructuresForm } from "./B05_Profile_UI_Structures_Form";
|
||||
import { renderStructureList } from "./B05_Profile_UI_Structures_List";
|
||||
import { splitPavementRange } from "./B05_Profile_UI_Structures_Pavement";
|
||||
import { formatStation } from "./B05_Profile_Util_Station";
|
||||
@@ -115,58 +115,32 @@ interface StructuresCallbacks {
|
||||
}
|
||||
|
||||
export function createStructuresSection(callbacks: StructuresCallbacks): StructuresSection {
|
||||
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 사용자 확정).
|
||||
// 점형은 기준 측점만 보인다.
|
||||
// 측점 표기 전역 규칙(2026-08-18): 잔여거리는 [0, 간격) — 넘치면 측점번호로
|
||||
// 올려 표시한다(1+30 → 2+10). 정규화는 stationFields가 입력 확정 때 처리.
|
||||
const startFields = stationFields("시작 측점", () => callbacks.getInterval());
|
||||
const anchorFields = stationFields("기준 측점", () => callbacks.getInterval());
|
||||
const endFields = stationFields("종료 측점", () => callbacks.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: () => liveCommit() });
|
||||
// 폼 뼈대는 전용 조립기가 세운다(2026-09-03 · 700줄 제한) — 여기서는 값·검증·저장만.
|
||||
const form = buildStructuresForm({
|
||||
getInterval: () => callbacks.getInterval(),
|
||||
onFacilityChange: () => liveCommit(),
|
||||
});
|
||||
const {
|
||||
root,
|
||||
body,
|
||||
groupSelect,
|
||||
typeSelect,
|
||||
startFields,
|
||||
anchorFields,
|
||||
endFields,
|
||||
memoField,
|
||||
rangeValue,
|
||||
rangeWrap,
|
||||
positionRow,
|
||||
positionDivider,
|
||||
optionRow,
|
||||
actions,
|
||||
facilityOptions,
|
||||
primary,
|
||||
removeButton,
|
||||
resetButton,
|
||||
list,
|
||||
} = form;
|
||||
|
||||
/** 이 타입이 서브폼(계곡 통과 시설 부속)을 쓰는지 — 쓰면 레지스트리 옵션 칸 대신
|
||||
* 서브폼이 그린다. 독립 기슭막이(D4)는 서브폼을 떠나 C군과 같은 레지스트리 옵션
|
||||
@@ -248,34 +222,6 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu
|
||||
: "—";
|
||||
}
|
||||
|
||||
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 사용자 지시,
|
||||
// 필요 시 별도 설명 페이지로).
|
||||
|
||||
// 측점 그룹과 옵션 나열 사이 구분선(2026-08-17 사용자 지시 2).
|
||||
const positionDivider = document.createElement("hr");
|
||||
positionDivider.className = "b05-structure__divider";
|
||||
|
||||
body.append(typeRow, positionRow, positionDivider, optionRow, facilityOptions.root, actions);
|
||||
|
||||
let types: StructureType[] = [];
|
||||
let structures: StructureInstance[] = [];
|
||||
let pipeFacilities: PipeFacilityItem[] = [];
|
||||
|
||||
Reference in New Issue
Block a user