Merge remote-tracking branch 'origin/sub_laptop_8' into main_laptop_1
This commit is contained in:
@@ -41,10 +41,13 @@ function rows(warnings: FillSlopeWarning[], stationInterval: number): NoticeChip
|
||||
});
|
||||
}
|
||||
|
||||
export function createFillSlopeNotice(onPick: (stationId: string) => void): FillSlopeNotice {
|
||||
export function createFillSlopeNotice(
|
||||
onPick: (stationId: string) => void,
|
||||
selected: () => string | null,
|
||||
): FillSlopeNotice {
|
||||
const root = document.createElement("div");
|
||||
root.className = "b06-section__notice-bar";
|
||||
const chips = createNoticeChips(onPick);
|
||||
const chips = createNoticeChips(onPick, selected);
|
||||
root.append(chips.root);
|
||||
|
||||
return {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* 횡단 알림 줄 칩 꼴(196-1) — 항목 = 짧은 이름 + 측점 수 칩 한 줄 · 긴 설명 = 공용 호버.
|
||||
* 줄 아래 내용 칸 하나를 같이 씀 — 누른 칩의 측점 목록만 · 같은 칩 다시 누르면 닫힘 ·
|
||||
* 다른 칩 누르면 바뀜(한 번에 하나). 측점 단추 누름 = `onPick`(그 카드로).
|
||||
* ⭐ 207-1 — 내용 칸은 줄 아래 카드 위에 겹쳐 뜸(흐름 밖 · CSS) · 측점 누르면 접힘 ·
|
||||
* 열 때 지금 고른 측점(`selected`) 단추 강조.
|
||||
* 무엇을 셀지는 부르는 쪽(`_FillSlope_Notice`) — 여기는 그리기만.
|
||||
* ========================================================================== */
|
||||
|
||||
@@ -29,7 +31,10 @@ export interface NoticeChips {
|
||||
update: (items: NoticeChipItem[]) => void;
|
||||
}
|
||||
|
||||
export function createNoticeChips(onPick: (stationId: string) => void): NoticeChips {
|
||||
export function createNoticeChips(
|
||||
onPick: (stationId: string) => void,
|
||||
selected: () => string | null = () => null,
|
||||
): NoticeChips {
|
||||
const root = document.createElement("div");
|
||||
root.className = "b06-section__notice-blocks";
|
||||
const chips = document.createElement("div");
|
||||
@@ -57,12 +62,14 @@ export function createNoticeChips(onPick: (stationId: string) => void): NoticeCh
|
||||
);
|
||||
panel.hidden = !open;
|
||||
panel.classList.toggle("is-info", open?.tone === "info");
|
||||
const picked = open ? selected() : null;
|
||||
panel.replaceChildren(
|
||||
...(open?.rows ?? []).map((row) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.textContent = row.text;
|
||||
button.dataset.stationId = row.stationId;
|
||||
button.classList.toggle("is-selected", row.stationId === picked);
|
||||
return button;
|
||||
}),
|
||||
);
|
||||
@@ -77,7 +84,10 @@ export function createNoticeChips(onPick: (stationId: string) => void): NoticeCh
|
||||
});
|
||||
panel.addEventListener("click", (event) => {
|
||||
const id = (event.target as HTMLElement).closest("button")?.dataset.stationId;
|
||||
if (id) onPick(id);
|
||||
if (!id) return;
|
||||
openKey = null;
|
||||
render();
|
||||
onPick(id);
|
||||
});
|
||||
attachHoverInfo(
|
||||
chips,
|
||||
|
||||
@@ -124,8 +124,9 @@ export function createSectionView(
|
||||
} = { balloonScope: "-" };
|
||||
|
||||
// 성토사면 5m 초과 경고 줄(2026-09-14 브레인 (나)) — 카드 위 · 측점 누르면 그 카드로.
|
||||
const fillSlopeNotice = createFillSlopeNotice((id) =>
|
||||
selectedStationId === id ? revealCard(id, "smooth") : selectStation(id, true),
|
||||
const fillSlopeNotice = createFillSlopeNotice(
|
||||
(id) => (selectedStationId === id ? revealCard(id, "smooth") : selectStation(id, true)),
|
||||
() => selectedStationId,
|
||||
);
|
||||
// 열 수 칸(138-3) — 고정 알림 줄 오른쪽 끝 · 1~6 · 바꾸면 카드 전부 다시 그림
|
||||
const columnsField = createInputField({
|
||||
|
||||
@@ -172,9 +172,10 @@
|
||||
}
|
||||
|
||||
.b06-section__notice-chip {
|
||||
padding: 0 var(--spacing-8);
|
||||
padding: 2px var(--spacing-8);
|
||||
border: 1px solid currentColor;
|
||||
border-radius: var(--radius-sm);
|
||||
/* 둥근 사각(207-1) — 공용 단추 반경 · 알약 아님 */
|
||||
border-radius: var(--radius-buttons);
|
||||
background: transparent;
|
||||
color: var(--color-warning);
|
||||
font-size: var(--text-caption);
|
||||
@@ -193,14 +194,24 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 내용 칸 하나(누른 칩의 측점 목록) — 길면 줄 안에서 스크롤 · 고정 줄이 카드 칸을 다 덮지 않게 */
|
||||
/* 내용 칸 하나(누른 칩의 측점 목록) — 길면 칸 안에서 스크롤 · 고정 줄이 카드 칸을 다 덮지 않게.
|
||||
207-1 — 줄 아래 카드 위에 겹쳐 뜸(흐름 밖) · 열고 닫아도 줄 높이 · 카드 자리 그대로 */
|
||||
.b06-section__notice-list {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-4);
|
||||
max-height: 30vh;
|
||||
overflow: auto;
|
||||
padding-top: var(--spacing-4);
|
||||
padding: var(--spacing-8);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-cards);
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: var(--shadow-lg);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
@@ -213,6 +224,13 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 지금 고른 측점(207-1) — 카드 고름 색과 같음 */
|
||||
.b06-section__notice-list > button.is-selected {
|
||||
outline: 2px solid var(--color-danger);
|
||||
outline-offset: -1px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.b06-cross-card {
|
||||
cursor: pointer;
|
||||
transition:
|
||||
|
||||
@@ -144,9 +144,10 @@ def test_측점_누르면_그_카드로_다시_그려도_연_칩_유지_0이면_
|
||||
"""
|
||||
bar.update(items(["a", "b"], [], []));
|
||||
chip("fill").click();
|
||||
panel.children[1].click();
|
||||
panel.children[1].click(); // 207-1 — 고르면 내용 칸 접힘
|
||||
chipsRow.children[0].click(); chipsRow.children[0].click(); // 칩 누름은 측점 고름 아님
|
||||
out.picks = picks.slice();
|
||||
chip("fill").click();
|
||||
bar.update(items(["a", "b", "x"], [], []));
|
||||
out.kept = view();
|
||||
bar.update(items([], ["p"], []));
|
||||
@@ -165,7 +166,7 @@ out.gone = view();
|
||||
def test_알림_줄은_칩을_쓰고_긴_설명은_호버_짧은_이름은_사전():
|
||||
src = (B06 / "B06_Section_UI_Cross_FillSlope_Notice.ts").read_text(encoding="utf-8")
|
||||
assert "<details" not in src and '"details"' not in src and "summary" not in src
|
||||
assert "createNoticeChips(onPick)" in src
|
||||
assert "createNoticeChips(onPick, selected)" in src # 207-1 고른 측점 넘김
|
||||
for label, hint in (
|
||||
('L("B06_Notice_FillSlope")', "FILL_SLOPE_WARN_TEXT"),
|
||||
('L("B06_Notice_PaveSuggested")', 'L("B06_Design_Paved_Suggested")'),
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"""207-1 B06 알림 칩 다듬기 — 둥근 사각 · 내용 칸 겹쳐 띄움 · 고르면 접힘 · 고른 측점 강조.
|
||||
|
||||
동작은 `B06_Section_UI_Cross_Notice_Chips.ts` 를 Node 로 그대로 돌림(196-1 가짜 DOM 재사용).
|
||||
모양 · 흐름 밖(겹침) · 줄 높이만 재는 scroll-margin 은 CSS · 부르는 쪽 원문 대조.
|
||||
"""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
from test_196_1_b06_notice_chips import B06, PRELUDE
|
||||
|
||||
CSS = (B06 / "B06_Section_UI_Style_Cross.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _rule(selector: str) -> str:
|
||||
body = CSS[CSS.index(selector + " {") :]
|
||||
return body[: body.index("}")]
|
||||
|
||||
|
||||
def _node(body: str):
|
||||
script = (
|
||||
PRELUDE
|
||||
+ """
|
||||
const sel = { id: null };
|
||||
const picked = [];
|
||||
const bar2 = mod.exports.createNoticeChips((id) => { picked.push(id); sel.id = id; }, () => sel.id);
|
||||
const [chips2, panel2] = bar2.root.children;
|
||||
const chip2 = (key) => chips2.children.find((c) => c.dataset.noticeKey === key);
|
||||
const marks = () => panel2.children.map((b) => [b.dataset.stationId, b.classList.contains("is-selected")]);
|
||||
"""
|
||||
+ body
|
||||
+ "\nconsole.log(JSON.stringify(out));"
|
||||
)
|
||||
got = subprocess.run(
|
||||
["node", "-e", script], capture_output=True, text=True, encoding="utf-8", timeout=60
|
||||
)
|
||||
assert got.returncode == 0, got.stderr
|
||||
return json.loads(got.stdout)
|
||||
|
||||
|
||||
def test_칩은_둥근_사각_공용_단추_반경_알약_아님():
|
||||
chip = _rule(".b06-section__notice-chip")
|
||||
assert "border-radius: var(--radius-buttons)" in chip
|
||||
assert "radius-pills" not in chip and "radius-sm" not in chip
|
||||
|
||||
|
||||
def test_내용_칸은_흐름_밖_카드_위에_겹침():
|
||||
panel = _rule(".b06-section__notice-list")
|
||||
assert "position: absolute" in panel and "top: 100%" in panel
|
||||
assert "z-index" in panel and "box-shadow: var(--shadow-lg)" in panel
|
||||
assert "background: var(--color-surface-raised)" in panel
|
||||
# 기준 = 고정 줄(sticky 는 absolute 의 기준 상자) · 그 사이에 기준을 바꾸는 position 없음
|
||||
assert "position: sticky" in _rule(".b06-section__notice-bar")
|
||||
assert "position" not in _rule(".b06-section__notice-blocks")
|
||||
|
||||
|
||||
def test_열고_닫아도_카드_자리_그대로_scroll_margin은_칩_줄_높이만():
|
||||
view = (B06 / "B06_Section_UI_Section_View.ts").read_text(encoding="utf-8")
|
||||
# 줄 높이 = 고정 줄 offsetHeight — 흐름 밖 내용 칸은 높이에 안 듦
|
||||
assert '"--b06-notice-h", `${fillSlopeNotice.root.offsetHeight}px`' in view
|
||||
assert "scroll-margin-top: calc(var(--b06-notice-h, 0px) + var(--spacing-8))" in CSS
|
||||
# 내용 칸만 흐름 밖 — 칩 줄은 흐름 안
|
||||
assert "position" not in _rule(".b06-section__notice-chips")
|
||||
|
||||
|
||||
def test_측점_고르면_그_카드로_가며_내용_칸_접힘():
|
||||
got = _node(
|
||||
"""
|
||||
bar2.update(items(["a", "b"], ["p"], []));
|
||||
chip2("fill").click();
|
||||
out.before = panel2.hidden;
|
||||
panel2.children[1].click();
|
||||
out.picked = picked.slice();
|
||||
out.after = panel2.hidden;
|
||||
out.rows = panel2.children.length;
|
||||
out.expanded = chips2.children.map((c) => c.getAttribute("aria-expanded"));
|
||||
"""
|
||||
)
|
||||
assert got["before"] is False
|
||||
assert got["picked"] == ["b"]
|
||||
assert got["after"] is True and got["rows"] == 0
|
||||
assert got["expanded"] == ["false", "false"]
|
||||
|
||||
|
||||
def test_고른_측점이_있으면_열_때_그_단추_강조_없으면_강조_없음():
|
||||
got = _node(
|
||||
"""
|
||||
bar2.update(items(["a", "b", "c"], ["b"], []));
|
||||
chip2("fill").click();
|
||||
out.none = marks();
|
||||
chip2("fill").click();
|
||||
sel.id = "b"; // 측점 횡단도를 고른 상태
|
||||
chip2("fill").click();
|
||||
out.fill = marks();
|
||||
chip2("pave").click();
|
||||
out.pave = marks();
|
||||
sel.id = "zz"; // 목록에 없는 측점
|
||||
chip2("fill").click();
|
||||
out.absent = marks();
|
||||
"""
|
||||
)
|
||||
assert got["none"] == [["a", False], ["b", False], ["c", False]]
|
||||
assert got["fill"] == [["a", False], ["b", True], ["c", False]]
|
||||
assert got["pave"] == [["b", True]]
|
||||
assert got["absent"] == [["a", False], ["b", False], ["c", False]]
|
||||
|
||||
|
||||
def test_고른_측점_넘김_부르는_쪽_이음과_강조_색():
|
||||
notice = (B06 / "B06_Section_UI_Cross_FillSlope_Notice.ts").read_text(encoding="utf-8")
|
||||
assert "createNoticeChips(onPick, selected)" in notice
|
||||
view = (B06 / "B06_Section_UI_Section_View.ts").read_text(encoding="utf-8")
|
||||
assert "() => selectedStationId," in view
|
||||
mark = _rule(".b06-section__notice-list > button.is-selected")
|
||||
assert "var(--color-danger)" in mark # 카드 고름 색과 같음
|
||||
assert "border-color: var(--color-danger)" in _rule(".b06-cross-card--selected")
|
||||
Reference in New Issue
Block a user