Files
Aislo/B06_Section/B06_Section_UI_Adjust_Dock.ts
T
eomsangdon 8e8849e4ac feat(B05,B06): 배관 조정 항목을 유입구·유출구 폼으로 모은다
2026-08-29 사용자 지시(좌측 패널 항목 정리 — 배관 자리):
- 길이·높이·기준측점 전/후 입력칸에 [-][+] 묶음 추가(stepper). 눈금은 조정창과
  같다(길이 1m·전후 0.5m·높이 0.1m). 래퍼 여백 0이라 2열 격자는 그대로다.
- 형태·구조물 형식 행은 [횡단 조정] 사본에서 숨김 — 폼의 형태·구조와 중복.
- 단 수·옵션 행은 고른 벽 쪽(유입구/유출구) 그룹으로 DOM 이식. 배선을 옮기지 않아
  값·동기는 그대로고, 사본이 갈릴 때마다 다시 이식한다.
- 유입구 '구조' 드롭다운에 조정창 형식(자동·집수정 I/ㄴ/ㄷ)을 병합. 고른 값이
  정본 둘(inlet_type·inlet_structure)을 함께 정하고, B06에서는 유입측 형식 제어와
  양방향으로 맞춘다. 형태·형식 연결은 미연결 상태 그대로 둔다.

검증(공용 브라우저): 유입구 스텝 5·유출구 4개, 구조 목록 6종 병합, 벽 선택 시
단 수·옵션이 유출구 그룹으로 이동(dock에는 높이·구간값만 남음). B05도 동일 폼이
정상 표시되고 [횡단 조정]은 뜨지 않음. typecheck 통과.
2026-08-29 15:30:55 +09:00

103 lines
4.4 KiB
TypeScript

/* =============================================================================
* B06_Section_UI_Adjust_Dock.ts
* 좌측 [구조물 배치] 섹션 안 **[횡단 조정] 하위 컨테이너**(2026-08-29 사용자:
* B05/B06 일원화 — 오버레이 조정창 항목을 좌측에도 보이되, 기존 폼과 구분되게
* 접이식 하위 컨테이너에 담는다. 9키·십자 위치제어는 오버레이 전용).
*
* 카드가 조정창을 만들 때 같은 deps로 **사본 창**을 하나 더 만들어 이 슬롯에
* 세운다(adopt). 값·동작이 같은 함수라 오버레이·좌측 어느 쪽을 만져도 카드
* 재렌더에서 두 창이 함께 새 값으로 그려진다 — 별도 동기화 코드가 없다.
* 카드가 여럿이어도 슬롯은 하나 — 마지막으로 고른 구조물의 사본만 선다.
* ========================================================================== */
let dockRoot: HTMLDetailsElement | null = null;
let slot: HTMLElement | null = null;
let placeholder: HTMLElement | null = null;
/** 유입구·유출구 그룹의 이식 자리(2026-08-29 사용자 지시 4). B06 패널이 등록한다. */
let sideSlots: { inlet: HTMLElement; outlet: HTMLElement } | null = null;
/** 단 수·옵션 행을 옮겨 붙일 자리를 등록한다(계곡 통과 시설 폼의 유입구·유출구). */
export function setAdjustSideSlots(next: { inlet: HTMLElement; outlet: HTMLElement }): void {
sideSlots = next;
}
/** 이식 자리가 지금 화면에 서 있는지 — 숨겨진 그룹(다른 시설)이면 옮기지 않는다. */
function slotUsable(target: HTMLElement | undefined): target is HTMLElement {
if (!target) return false;
const group = target.closest("fieldset");
return !!group && !group.hidden;
}
/** 옮겨 놨던 행을 비운다 — 사본이 갈릴 때마다 옛 행을 남기지 않는다. */
function clearSideSlots(): void {
sideSlots?.inlet.replaceChildren();
sideSlots?.outlet.replaceChildren();
}
/**
* 조정창 사본의 단 수·옵션 행을 고른 벽 쪽(유입/유출) 그룹으로 옮긴다. 행 DOM을
* 그대로 옮기므로 배선·값 동기는 손대지 않는다. 자리가 없으면 사본에 그대로 둔다.
*/
function relocateSideRows(panelRoot: HTMLElement): void {
clearSideSlots();
const side = panelRoot.dataset.side;
if (!sideSlots || (side !== "inlet" && side !== "outlet")) return;
const target = sideSlots[side];
if (!slotUsable(target)) return;
const rows = panelRoot.querySelectorAll<HTMLElement>(
".b06-structure-panel__struct--tier, .b06-structure-panel__struct--options",
);
rows.forEach((row) => {
if (row.classList.contains("is-hidden")) return;
target.append(row);
});
}
function ensureDock(): void {
if (dockRoot) return;
dockRoot = document.createElement("details");
dockRoot.className = "b06-adjust-dock";
dockRoot.open = true;
const summary = document.createElement("summary");
summary.textContent = "횡단 조정";
placeholder = document.createElement("p");
placeholder.className = "b06-adjust-dock__empty";
placeholder.textContent = "횡단도에서 구조물(벽·구체)을 고르면 조정 항목이 여기에 뜹니다.";
slot = document.createElement("div");
slot.className = "b06-adjust-dock__slot";
dockRoot.append(summary, placeholder, slot);
}
/** 하위 컨테이너 루트 — 페이지가 [구조물 배치] 섹션 본문에 붙인다. */
export function adjustDockRoot(): HTMLElement {
ensureDock();
return dockRoot!;
}
/** 페이지 진입 시 초기 상태(빈 슬롯 + 안내문)로 되돌린다. */
export function resetAdjustDock(): void {
ensureDock();
slot!.replaceChildren();
clearSideSlots();
placeholder!.hidden = false;
}
/** 조정창 사본을 슬롯에 세운다 — 이미 다른 사본이 있으면 갈아 끼운다. */
export function adoptAdjustPanel(panelRoot: HTMLElement): void {
ensureDock();
panelRoot.classList.add("b06-structure-panel--dock");
if (slot!.firstChild !== panelRoot || slot!.childNodes.length !== 1) {
slot!.replaceChildren(panelRoot);
}
placeholder!.hidden = true;
relocateSideRows(panelRoot);
}
/** 이 사본이 슬롯에 서 있으면 내린다 — 다른 사본이 이미 대신 서 있으면 손대지 않는다. */
export function releaseAdjustPanel(panelRoot: HTMLElement): void {
if (!slot || !slot.contains(panelRoot)) return;
slot.replaceChildren();
clearSideSlots();
placeholder!.hidden = false;
}