diff --git a/B05_Profile/B05_Profile_UI_Profile_Structures.ts b/B05_Profile/B05_Profile_UI_Profile_Structures.ts index c22646ad4..4eb2380a8 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Structures.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Structures.ts @@ -17,6 +17,7 @@ import { } from "./B05_Profile_UI_IrregularStations"; import { structureAnchorM, type StructureInstance } from "./B05_Profile_Api_Structures"; import { GROUP_LABELS } from "./B05_Profile_UI_Structures_Panel"; +import { HIDDEN_PICK_GROUPS } from "./B05_Profile_UI_Structures_Panel_Types"; /** 우클릭 메뉴용 최소 타입 정보 — 사이드 「구조물 배치」 종류 목록과 같은 원천. */ export interface StructureMenuType { @@ -32,7 +33,9 @@ export function structureGroupMenuItems( types: ReadonlyArray, onPick: (typeId: string) => void, ): MapContextMenuItem[] { - const groups = [...new Set(types.map((type) => type.group))]; + const groups = [...new Set(types.map((type) => type.group))].filter( + (group) => !HIDDEN_PICK_GROUPS.has(group), + ); return groups.map((group) => ({ label: GROUP_LABELS[group] ?? group, children: types diff --git a/B05_Profile/B05_Profile_UI_Structures_Define_Form.ts b/B05_Profile/B05_Profile_UI_Structures_Define_Form.ts index e3e44d790..7f321904a 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Define_Form.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Define_Form.ts @@ -78,7 +78,9 @@ export function createFacilityDefinition(root: HTMLElement): FacilityDefinition root .querySelectorAll(".is-rule-hidden") .forEach((node) => node.classList.remove("is-rule-hidden")); - root.querySelectorAll("option[hidden]").forEach((o) => (o.hidden = false)); + root + .querySelectorAll("option[hidden]:not([data-pick-hidden])") + .forEach((o) => (o.hidden = false)); paint(); }, }; diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel.ts b/B05_Profile/B05_Profile_UI_Structures_Panel.ts index 7f1c2ffe0..ffae71d9d 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel.ts @@ -24,7 +24,7 @@ import type { PipeFacility } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { type FacilityAttributes } from "./B05_Profile_UI_Drainage_Facility"; import { field, optionDefaults } from "./B05_Profile_UI_Structures_Fields"; import { - hasSpecContainers, + hidesTypeSummary, paintStale, renderOptionFields as renderOptionFieldsInto, } from "./B05_Profile_UI_Structures_Panel_Options"; @@ -54,6 +54,7 @@ export type { PipeFacilityItem, StructuresSection } from "./B05_Profile_UI_Struc import { DEFAULT_INTERVAL_LENGTH_M, GROUP_LABELS, + groupOption, hasComputedRange, readBeforeOption, readLengthOption, @@ -227,7 +228,7 @@ export function createStructuresSection( !!type && !managed && type.placement === "interval" && !hasComputedRange(type); positionRow.hidden = !type; positionDivider.hidden = !type; - summary.root.hidden = !type || hasSpecContainers(type); // 컨테이너 종류는 컨테이너마다 잇기(52-1) + summary.root.hidden = !type || hidesTypeSummary(type); // 컨테이너 종류는 컨테이너마다 잇기(52-1) startFields.wrap.hidden = !isInterval; endFields.wrap.hidden = !isInterval; primary.disabled = !type; @@ -544,7 +545,7 @@ export function createStructuresSection( // 첫 항목은 빈칸 — 진입·리셋 상태에서 군·종류를 강제로 고르게 두지 않는다. 이름은 정의(48) 먼저. groupSelect.replaceChildren( new Option("— 선택 —", ""), - ...[...groups].map(([g, name]) => new Option(name ?? GROUP_LABELS[g] ?? g, g)), + ...[...groups].map(([g, name]) => groupOption(g, name ?? GROUP_LABELS[g] ?? g)), ); groupSelect.value = ""; syncTypeOptions(); diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel_Options.ts b/B05_Profile/B05_Profile_UI_Structures_Panel_Options.ts index e35163c76..af789a5dd 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel_Options.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel_Options.ts @@ -23,10 +23,10 @@ import { FORM_OPTION_KEYS } from "./B05_Profile_UI_Drainage_Facility_Merge"; import { STRUCTURE_SPECS } from "../A00_Common/design/design_structure_pipe"; import { specKeys } from "../A00_Common/design/design_structure_spec"; -/** 구조물 명세 컨테이너 · 정의 그룹 상자(그룹마다 잇기 줄)로 그리는 종류인지 — 그때 종류 단위 - * 「구조물 조합」 줄은 가림(PLAN 52-1 · 52-6b). */ -export const hasSpecContainers = (type: StructureType): boolean => - !!STRUCTURE_SPECS[type.type_id]?.containers.length || !!type.groups?.length; +/** 종류 단위 「구조물 조합」 줄을 가릴 종류인지 — 구조물 명세 컨테이너 · 정의 그룹 상자(그룹마다 잇기 줄)로 + * 그리는 종류(PLAN 52-1 · 52-6b) · 종단배수(B군 · 조합 잇기 없음 · 표 07). */ +export const hidesTypeSummary = (type: StructureType): boolean => + !!STRUCTURE_SPECS[type.type_id]?.containers.length || !!type.groups?.length || type.group === "B"; /** 패널이 쥔 값 중 옵션 그리기에 필요한 것들. 이름은 분리 전 지역변수와 같다. */ export interface OptionRenderContext { diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts b/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts index ff1daad7a..04fa037cd 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts @@ -34,6 +34,18 @@ export const GROUP_LABELS: Record = { 호환: "기타", }; +/** 새로 놓을 때 고르기 목록(군 드롭다운 · 우클릭 메뉴)에서 뺄 군 — 종단배수·계류사방(표 07). + * 옛 값 · 도면 · 집계는 그대로이고 데이터도 지우지 않는다. */ +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 = ""; // 정의 폼의 「가림 풀기」가 건드리지 않게 + return option; +} + /** 구간형 신규 추가 시 기본 구간 길이(m). 사용자가 종점을 바로 고칠 수 있게 짧게 잡는다. */ export const DEFAULT_INTERVAL_LENGTH_M = 15; diff --git a/resources/tester/ui_checks/52-8_종단_계류_숨김.js b/resources/tester/ui_checks/52-8_종단_계류_숨김.js new file mode 100644 index 000000000..d77b951a7 --- /dev/null +++ b/resources/tester/ui_checks/52-8_종단_계류_숨김.js @@ -0,0 +1,99 @@ +/* 52-8 — B05 · B06 좌측 「구조물 배치」 종단배수(B) · 계류사방(D) 고르기 목록에서 숨김 + * 시작 화면: B05 종단설계(#/b05-profile) · 검증 프로젝트(sub2_검증) + * 부르는 법: 앞에 `window.__m528 = "단계";` 를 붙여 + * orca eval --expression "window.__m528='seed'; $(cat 이 파일)" → 옛 값 둘(골막이 D · 측구 B)을 세션 초안에만 둠 + * (orca reload) + * orca eval --expression "window.__m528='verify'; $(cat 이 파일)" → 군 목록 · 우클릭 메뉴 · 옛 값 열기 (B05) + * (#/b06-section 으로 가서 reload 후 verify 한 번 더) + * orca eval --expression "window.__m528='clear'; $(cat 이 파일)" → 세션 초안 비움(서버 저장 안 함) + * 기대 수치(verify): visibleGroups 에 B · D 없음(0) · hiddenGroups 가림만 · menuHasBD 0 · + * 옛 값 목록 둘 그대로 · 옛 값을 열면 군 = D / B 로 열림(editGroup) · 잇기 칸 pickLinks 골막이(D) 1 · 측구(B) 0 + */ +(async () => { + const MODE = window.__m528 || "verify"; + const w = (ms) => new Promise((r) => setTimeout(r, ms)); + const projectId = localStorage.getItem("frd_current_project_id"); + const modUrl = (part) => + performance + .getEntriesByType("resource") + .map((e) => e.name) + .filter((n) => n.includes(part)) + .pop(); + const api = await import(modUrl("/B05_Profile/B05_Profile_Api_Structures.ts")); + const out = { mode: MODE, hash: location.hash }; + if (MODE === "seed") { + const base = { + placement_source: "manual", + status: "draft", + revision: 0, + geometry: null, + memo: "", + options: {}, + }; + api.writePendingStructures(projectId, [ + { + ...base, + structure_id: "m528d", + type_id: "erosion_check", + placement: "point", + chainage_m: 40, + }, + { + ...base, + structure_id: "m528b", + type_id: "ditch_side", + placement: "interval", + start_m: 60, + end_m: 80, + }, + ]); + out.seeded = api.readPendingStructures(projectId).length; + return JSON.stringify(out); + } + if (MODE === "clear") { + api.writePendingStructures(projectId, null); + out.cleared = api.readPendingStructures(projectId) === null; + return JSON.stringify(out); + } + const groupSel = [...document.querySelectorAll("select")].find((s) => + [...s.options].some((o) => o.text.includes("A 횡단배수")), + ); + const isBD = (o) => /^(B|D) /.test(o.text); + out.visibleGroups = [...groupSel.options].filter((o) => !o.hidden && o.value).map((o) => o.text); + out.visibleBD = [...groupSel.options].filter((o) => !o.hidden && isBD(o)).length; + out.hiddenGroups = [...groupSel.options].filter((o) => o.hidden).map((o) => o.text); + // 우클릭 메뉴 목록(공용 만들기) — 종류 전부를 줘도 B · D 군이 안 나옴 + const menuMod = await import(modUrl("/B05_Profile/B05_Profile_UI_Profile_Structures.ts")); + const types = (await api.fetchStructureTypes(true)).map((t) => ({ + type_id: t.type_id, + group: t.group, + name: t.name, + })); + const menu = menuMod.structureGroupMenuItems(types, () => {}); + out.menuGroups = menu.map((m) => m.label); + out.menuHasBD = menu.filter((m) => /^(B|D) /.test(m.label)).length; + // 옛 값 — 목록에 그대로 · 열면 그 군으로 열림 + const rows = [...document.querySelectorAll(".b05-route__irregular-list li")]; + out.rows = rows.map((r) => r.textContent.trim().replace(/\s+/g, " ").slice(0, 40)); + const old = rows.find((r) => + (window.__m528row ? new RegExp(window.__m528row) : /골막이/).test(r.textContent), + ); + if (old) { + (old.querySelector("button") ?? old).click(); + await w(1500); + const sels = [...document.querySelectorAll("select")].filter((s) => + [...s.options].some((o) => o.text.includes("A 횡단배수")), + ); + // 다시 열면 고른 것이 복원돼 있음 — 누르면 풀리므로 풀렸으면 한 번 더 + if (!sels[0]?.value) { + (old.querySelector("button") ?? old).click(); + await w(1500); + } + out.editGroup = sels[0]?.value; + // 종단배수(B)는 조합 잇기 칸이 없음(0) · 계류사방(D)은 옛 그대로(1) + out.pickLinks = [...document.querySelectorAll('[data-b05-summary="pick"]')].filter( + (n) => n.offsetParent, + ).length; + } + return JSON.stringify(out); +})();