Files
Aislo/B06_Section/B06_Section_UI_Cross_Notice_Chips.ts
T
eomsangdonandClaude Opus 5.5 6e8692d133 feat(B06): 횡단 알림 줄 칩 꼴 · 내용 칸 하나 공유 · 긴 설명 = 호버 (196-1)
- 항목 = 짧은 이름 + 측점 수 칩(성토 5m↑ · 포장 권장 · 소단 검토) · 0 이면 숨김
- 펼침 = 줄 아래 내용 칸 하나(누른 칩 목록만 · 다시 누르면 닫힘 · 한 번에 하나)
- 긴 법령 설명 · 측점 없는 구조물 줄 까닭 글 = 공용 호버
- sticky · 열 수 칸 · 카드 scroll-margin 그대로 · 새 시험 test_196_1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-10 13:54:45 +09:00

96 lines
3.3 KiB
TypeScript

/* =============================================================================
* B06_Section_UI_Cross_Notice_Chips.ts
* 횡단 알림 줄 칩 꼴(196-1) — 항목 = 짧은 이름 + 측점 수 칩 한 줄 · 긴 설명 = 공용 호버.
* 줄 아래 내용 칸 하나를 같이 씀 — 누른 칩의 측점 목록만 · 같은 칩 다시 누르면 닫힘 ·
* 다른 칩 누르면 바뀜(한 번에 하나). 측점 단추 누름 = `onPick`(그 카드로).
* 무엇을 셀지는 부르는 쪽(`_FillSlope_Notice`) — 여기는 그리기만.
* ========================================================================== */
import { attachHoverInfo } from "@ui/ui_template_hover_info";
export interface NoticeChipRow {
stationId: string;
text: string;
}
export interface NoticeChipItem {
key: string;
/** 짧은 이름 — 칩 글 = 이름 + 측점 수 */
label: string;
/** 긴 설명 — 호버 */
hint: string;
tone: "warn" | "info";
rows: NoticeChipRow[];
}
export interface NoticeChips {
root: HTMLElement;
/** 측점 설계가 바뀔 때마다 — 연 칩은 그대로(측점이 0 이 되면 닫힘) */
update: (items: NoticeChipItem[]) => void;
}
export function createNoticeChips(onPick: (stationId: string) => void): NoticeChips {
const root = document.createElement("div");
root.className = "b06-section__notice-blocks";
const chips = document.createElement("div");
chips.className = "b06-section__notice-chips";
const panel = document.createElement("div");
panel.className = "b06-section__notice-list";
panel.hidden = true;
root.append(chips, panel);
let items: NoticeChipItem[] = [];
let openKey: string | null = null;
const render = (): void => {
const open = items.find((item) => item.key === openKey);
if (!open) openKey = null;
chips.replaceChildren(
...items.map((item) => {
const chip = document.createElement("button");
chip.type = "button";
chip.className = `b06-section__notice-chip b06-section__notice-chip--${item.tone}`;
chip.dataset.noticeKey = item.key;
chip.setAttribute("aria-expanded", String(item === open));
chip.textContent = `${item.label} ${item.rows.length}`;
return chip;
}),
);
panel.hidden = !open;
panel.classList.toggle("is-info", open?.tone === "info");
panel.replaceChildren(
...(open?.rows ?? []).map((row) => {
const button = document.createElement("button");
button.type = "button";
button.textContent = row.text;
button.dataset.stationId = row.stationId;
return button;
}),
);
};
chips.addEventListener("click", (event) => {
const key = (event.target as HTMLElement).closest<HTMLElement>(".b06-section__notice-chip")
?.dataset.noticeKey;
if (!key) return;
openKey = openKey === key ? null : key;
render();
});
panel.addEventListener("click", (event) => {
const id = (event.target as HTMLElement).closest("button")?.dataset.stationId;
if (id) onPick(id);
});
attachHoverInfo(
chips,
(target) => items.find((item) => item.key === target.dataset.noticeKey)?.hint,
".b06-section__notice-chip",
);
return {
root,
update(next) {
items = next.filter((item) => item.rows.length);
render();
},
};
}