diff --git a/B05_Profile/B05_Profile_UI_Page_Structures.ts b/B05_Profile/B05_Profile_UI_Page_Structures.ts index 49cc98731..ef571f0bc 100644 --- a/B05_Profile/B05_Profile_UI_Page_Structures.ts +++ b/B05_Profile/B05_Profile_UI_Page_Structures.ts @@ -313,15 +313,14 @@ export function createStructuresBridge(deps: StructuresBridgeDeps) { async function loadStructures(): Promise { try { // 레지스트리는 **한 번만** 받고 쓰는 자리에 따라 나눈다(2026-09-07). - // · 추가 메뉴 — 켜진 종류만(B05 는 종전대로 여섯 군). - // · 알약 레인·군 판정 — **전부**. B06 에서 넣은 B군(측구·맹암거 등)은 레지스트리에서 - // 꺼져 있는데, 켜진 것만 주면 이름·색·군을 못 찾아 종단에 「?」 회색 알약으로 뜬다. + // · 고르기 목록 — 켜진 종류만(B05 는 종전대로 여섯 군). + // · 목록 이름 · 폼 · 알약 레인 · 군 판정 — **전부**. B06 에서 넣은 B군(측구·맹암거 등)은 + // 레지스트리에서 꺼져 있는데, 켜진 것만 주면 이름을 못 찾아 영문 키 · 「?」 알약으로 뜬다. const [allTypes, stored] = await Promise.all([ fetchStructureTypes(true), fetchStructures(deps.projectId), ]); - const menuTypes = allTypes.filter((type) => type.enabled); - deps.panel().structures.setTypes(menuTypes); + deps.panel().structures.setTypes(allTypes, (type) => type.enabled); deps.profilePanel().setStructureTypes(allTypes); // A군 판정에 쓸 타입 정보 — 횡단배수만 그래프 세로선·틸팅 대상이다. structureTypeMap = new Map( diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel.ts b/B05_Profile/B05_Profile_UI_Structures_Panel.ts index 2a4adbb4f..5eec636e4 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel.ts @@ -53,9 +53,9 @@ export { GROUP_LABELS } from "./B05_Profile_UI_Structures_Panel_Types"; export type { PipeFacilityItem, StructuresSection } from "./B05_Profile_UI_Structures_Panel_Types"; import { DEFAULT_INTERVAL_LENGTH_M, - GROUP_LABELS, - groupOption, + groupOptions, hasComputedRange, + pickOption, readBeforeOption, readLengthOption, type PipeFacilityItem, @@ -165,6 +165,7 @@ export function createStructuresSection( } let types: StructureType[] = []; + let canPick: (type: StructureType) => boolean = () => true; // 배열 자체를 바꾸지 않는다(조각들이 참조로 들고 있음) — 내용만 갈아끼운다. const structures: StructureInstance[] = []; let pipeFacilities: PipeFacilityItem[] = []; @@ -348,7 +349,7 @@ export function createStructuresSection( const candidates = group ? types.filter((t) => t.group === group && pickable(t)) : []; typeSelect.replaceChildren( new Option("— 선택 —", ""), - ...candidates.map((type) => new Option(type.name, type.type_id)), + ...candidates.map((type) => pickOption(type.name, type.type_id, !canPick(type))), ); typeSelect.value = keepTypeId && candidates.some((type) => type.type_id === keepTypeId) ? keepTypeId : ""; @@ -539,14 +540,11 @@ export function createStructuresSection( facility: facilityOptions, listRoot: list, hasSelection: () => editingId !== null || selectedPipeChainage !== null, - setTypes(next) { + setTypes(next, pick = () => true) { types = next; - const groups = new Map(next.map((t) => [t.group, t.group_name] as const)); - // 첫 항목은 빈칸 — 진입·리셋 상태에서 군·종류를 강제로 고르게 두지 않는다. 이름은 정의(48) 먼저. - groupSelect.replaceChildren( - new Option("— 선택 —", ""), - ...[...groups].map(([g, name]) => groupOption(g, name ?? GROUP_LABELS[g] ?? g)), - ); + canPick = pick; + // 첫 항목은 빈칸 — 진입·리셋 상태에서 군·종류를 강제로 고르게 두지 않는다. + groupSelect.replaceChildren(new Option("— 선택 —", ""), ...groupOptions(next, pick)); groupSelect.value = ""; syncTypeOptions(); }, diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts b/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts index 04fa037cd..86e12199c 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts @@ -38,14 +38,29 @@ export const GROUP_LABELS: Record = { * 옛 값 · 도면 · 집계는 그대로이고 데이터도 지우지 않는다. */ export const HIDDEN_PICK_GROUPS: ReadonlySet = new Set(["B", "D"]); -/** 군 드롭다운 항목 — 뺄 군은 가림(옛 항목을 열 때는 값으로 그대로 고를 수 있음). */ -export function groupOption(group: string, name: string): HTMLOptionElement { - const option = new Option(name, group); - option.hidden = HIDDEN_PICK_GROUPS.has(group); - if (option.hidden) option.dataset.pickHidden = ""; // 정의 폼의 「가림 풀기」가 건드리지 않게 +/** 고르기 목록 항목 — 가릴 항목은 가림(옛 항목을 열 때는 값으로 그대로 고를 수 있음). */ +export function pickOption(text: string, value: string, hidden: boolean): HTMLOptionElement { + const option = new Option(text, value); + option.hidden = hidden; + if (hidden) option.dataset.pickHidden = ""; // 정의 폼의 「가림 풀기」가 건드리지 않게 return option; } +/** 군 드롭다운 항목들 — 이름은 정의(48) 먼저 · 뺄 군과 고를 종류가 없는 군은 가림. */ +export function groupOptions( + types: StructureType[], + pick: (type: StructureType) => boolean, +): HTMLOptionElement[] { + const groups = new Map(types.map((t) => [t.group, t.group_name] as const)); + return [...groups].map(([g, name]) => + pickOption( + name ?? GROUP_LABELS[g] ?? g, + g, + HIDDEN_PICK_GROUPS.has(g) || !types.some((t) => t.group === g && pick(t)), + ), + ); +} + /** 구간형 신규 추가 시 기본 구간 길이(m). 사용자가 종점을 바로 고칠 수 있게 짧게 잡는다. */ export const DEFAULT_INTERVAL_LENGTH_M = 15; @@ -95,8 +110,9 @@ export interface StructuresSection { /** 배치된 구조물 목록(
    ) — 섹션 본문이 아니라 사이드 하단 고정 영역에 붙인다 * (2026-08-18 사용자 지시: 폼 길이에 밀려 목록이 화면 밖으로 나가던 문제). */ listRoot: HTMLElement; - /** 서버에서 받은 타입 목록을 채운다(최초 1회). */ - setTypes: (types: StructureType[]) => void; + /** 서버에서 받은 타입 목록을 채운다(최초 1회). 이름·폼은 넘긴 종류 전부로 풀고, + * 고르기 목록엔 `pick` 이 고른 종류만 보임(빼면 전부). */ + setTypes: (types: StructureType[], pick?: (type: StructureType) => boolean) => void; /** 서버 정본으로 목록을 교체한다(진입·복원·저장 후). */ setStructures: (structures: StructureInstance[]) => void; getStructures: () => StructureInstance[];