diff --git a/A00_Common/design/design_shared.ts b/A00_Common/design/design_shared.ts index 622ceb954..36c04ff22 100644 --- a/A00_Common/design/design_shared.ts +++ b/A00_Common/design/design_shared.ts @@ -190,6 +190,7 @@ export function sharedStructures(shared: DesignPageShared): StructuresSection { onPipeUpdate: (from, to, attributes) => on().onPipeUpdate?.(from, to, attributes), onPipeRemove: (chainageM) => on().onPipeRemove?.(chainageM), onPipeSelect: (chainageM) => on().onPipeSelect?.(chainageM), + onListRelease: () => on().onListRelease?.(), onReveal: () => shared.revealLeft(), }, { includeDetail: () => shared.view() === "cross", projectId: shared.projectId }, diff --git a/A00_Common/design/design_view_cross.ts b/A00_Common/design/design_view_cross.ts index e98171105..577ce2226 100644 --- a/A00_Common/design/design_view_cross.ts +++ b/A00_Common/design/design_view_cross.ts @@ -179,6 +179,7 @@ export async function mountCrossView( focusChainage: (chainageM) => { if (!isWallSelecting()) focusStationAt(chainageM); }, + releaseCard: () => sectionView.focusStation(null), markStructure: (structureId) => sectionView.selectMark(structureId), applyPipeOptions: (chainageM, patch) => applyPipeOptionsToCache(pipeOptionsContext, chainageM, patch), diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel.ts b/B05_Profile/B05_Profile_UI_Structures_Panel.ts index c09b500c0..150f47e9f 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel.ts @@ -433,8 +433,10 @@ export function createStructuresSection( selectedPipeChainage, // 재선택 = 해제(2026-08-18 복구) — 해제는 리셋과 같은 전체 초기화다. onSelectStructure: (structure) => { - if (structure.structure_id !== null && structure.structure_id === editingId) resetForm(); - else loadForm(structure); + if (structure.structure_id !== null && structure.structure_id === editingId) { + resetForm(); + callbacks.onListRelease?.(); + } else loadForm(structure); }, onSelectPipe: (pipe) => { const isSame = @@ -442,6 +444,7 @@ export function createStructuresSection( Math.abs(selectedPipeChainage - pipe.chainage_m) < PIPE_MATCH_M; if (isSame) { resetForm(); + callbacks.onListRelease?.(); } else { loadPipeForm(pipe); callbacks.onPipeSelect(pipe.chainage_m); diff --git a/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts b/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts index 86e12199c..e667ce5ae 100644 --- a/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts +++ b/B05_Profile/B05_Profile_UI_Structures_Panel_Types.ts @@ -164,6 +164,8 @@ export interface StructuresCallbacks { * 목록에서 직접 고른 경우엔 이미 펼쳐져 있어 아무 일도 안 한다. */ onReveal?: () => void; + /** 목록에서 고른 줄을 다시 눌러 풂 — 다른 화면의 고름(횡단 카드)도 함께 끔(54-8). */ + onListRelease?: () => void; } type OptionEntry = { key: string; read: () => string | number; isEmpty: () => boolean }; diff --git a/B06_Section/B06_Section_UI_Page_Structures_Panel.ts b/B06_Section/B06_Section_UI_Page_Structures_Panel.ts index 0002fef70..3bda06669 100644 --- a/B06_Section/B06_Section_UI_Page_Structures_Panel.ts +++ b/B06_Section/B06_Section_UI_Page_Structures_Panel.ts @@ -87,6 +87,8 @@ export interface B06StructuresPanelDeps { stationInterval: () => number; /** 목록·폼에서 고른 시설의 측점 카드를 선택·스크롤한다. */ focusChainage: (chainageM: number) => void; + /** 목록에서 고른 줄을 다시 눌러 풂 — 횡단 카드 선택도 끔(54-8). */ + releaseCard?: () => void; /** 종단 알약 강조를 목록에서 고른 것에 맞춘다(null = 관 · 해제 — 측점선 짝을 따름). */ markStructure?: (structureId: string | null) => void; /** 목록이 바뀔 때마다 종단 알약 레인에 같은 목록을 넘긴다(2026-09-07 표시 통일). */ @@ -298,6 +300,7 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc // 알약 = 그 관 알약(같은 측점 구조물 말고 · 측점선 없는 물넘이포장도) — 카드 뒤에(53-2) deps.markStructure?.(chainageM === null ? null : `pipe-${chainageM.toFixed(2)}`); }, + onListRelease: () => deps.releaseCard?.(), }, }); diff --git a/B06_Section/B06_Section_UI_Section_View.ts b/B06_Section/B06_Section_UI_Section_View.ts index b03fa52ba..47097869c 100644 --- a/B06_Section/B06_Section_UI_Section_View.ts +++ b/B06_Section/B06_Section_UI_Section_View.ts @@ -445,7 +445,8 @@ export function createSectionView( refreshCard, refreshCards, focusStation(stationId) { - if (selectedStationId !== stationId) selectStation(stationId, true); + if (stationId === null) clearSelection(); + else if (selectedStationId !== stationId) selectStation(stationId, true); else { revealCard(stationId, "smooth"); // 같은 카드라도 알약 강조는 다시 맞춘다 — 목록에서 같은 측점 다른 항목을 고른 경우(52-15). diff --git a/B06_Section/B06_Section_UI_Section_View_Types.ts b/B06_Section/B06_Section_UI_Section_View_Types.ts index 811c93dad..24c05dbc1 100644 --- a/B06_Section/B06_Section_UI_Section_View_Types.ts +++ b/B06_Section/B06_Section_UI_Section_View_Types.ts @@ -38,8 +38,8 @@ export interface SectionViewController { /** 여러 측점을 한꺼번에 교체한다 — 상단 패널은 **마지막에 한 번만** 다시 그린다. */ refreshCards: (chainages: ReadonlyArray) => void; /** 좌측 구조물 목록에서 고른 측점 카드를 선택하고 화면에 드러낸다 — 재클릭 토글 없음 - * (2026-08-29 B05/B06 일원화: 목록 클릭 → 해당 카드 스크롤·강조). */ - focusStation: (stationId: string) => void; + * (2026-08-29 B05/B06 일원화: 목록 클릭 → 해당 카드 스크롤·강조). null = 카드 선택 풂(54-8). */ + focusStation: (stationId: string | null) => void; /** 종단 알약 강조를 그 구조물로(null = 풀기 — 카드 측점선 짝을 따름). */ selectMark: (structureId: string | null) => void; /** 카드(측점) 선택이 바뀔 때 알림 — 좌측 「구조물 배치」 폼이 그 측점 구조물을 diff --git a/resources/tester/ui_checks/53-2_목록_강조.js b/resources/tester/ui_checks/53-2_목록_강조.js index 76c58b674..9f89c8798 100644 --- a/resources/tester/ui_checks/53-2_목록_강조.js +++ b/resources/tester/ui_checks/53-2_목록_강조.js @@ -153,8 +153,14 @@ } items()[i].click(); // 다시 누름 = 풂 await w(900); - if (selRows().length || litPills().length || (!isB06 && lit3D().length)) - fail(`${r.text}: 풂 뒤 남음 ${selRows().length}/${litPills()}/${lit3D()}`); + // 풂 = 카드 선택도 풀림(54-8) + if ( + selRows().length || + litPills().length || + (!isB06 && lit3D().length) || + (isB06 && cardSel().length) + ) + fail(`${r.text}: 풂 뒤 남음 ${selRows().length}/${litPills()}/${lit3D()}/${cardSel()}`); } out.checked = rows.length; } else if (MODE === "pill") {