diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts index 25439f9da..c6bb83bee 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts @@ -11,18 +11,15 @@ * 자유 · 길이 고정 · 기울기 고정(기울기 고정 · 길이 자유 · 마우스 방향 첫 자리) · 둘 다(양각기 원 위 후보 중 * 마우스에 가까운 것) · 제약 기울기 = 오르내림 같은 크기(562-1 짝이 |기울기|로 셈). * 긋기는 해를 안 물음(585-3) — 새 선 = 사업연도 시작 해 · 갈래 = 붙은 자리 구간 해 · 이어 긋기 = 끝 구간이 늘어남 · - * 여러 해 = 다 그은 뒤 도구줄 「연도 마커」를 선 위로 끌어 구역을 나누고 속성 판에서 해를 고름. + * 여러 해 = 다 그은 뒤 그리기 칸 「연도 구역 마커」를 선 위로 끌어 구역을 나누고 속성 판에서 해를 고름. * 고르기 · 고치기 · 마커(쉴 때) = `D03_PlanAnalysis_UI_Edit`. * 키 = Enter 끝 · Esc 이번 긋기 취소 · Backspace 마지막 점 빼기(설정 창 단축키 표 D03 줄). * 셈 = 562-1 짝(`logic/D03_PlanAnalysis_Draw`) · 꼴 다루기 = `logic/D03_PlanAnalysis_Plan` · * 값을 바꾸면 `commit(다음 꼴)` 한 번 = 되돌리기 한 걸음(스택 · 캐시 = 페이지). * ========================================================================== */ -import { createButton, showToast } from "@ui/ui_template_elements"; +import { showToast } from "@ui/ui_template_elements"; import { keyIs } from "../A00_Common/key_registry"; -import { createInputField } from "@ui/ui_template_input"; -import { createSegment } from "@ui/ui_template_segment"; -import { createText } from "@ui/ui_template_text"; import { compassCandidates, compassPoint, @@ -50,17 +47,19 @@ import { paintNetwork, paintPreview, screenGap, - slopeDeg, SNAP_PX, } from "./D03_PlanAnalysis_UI_DrawPaint"; +import { + createDrawKindFields, + createDrawPanel, + DRAW_DEFAULT_LENGTH_M, + DRAW_DEFAULT_SLOPE_PCT, + type DrawConstraint, +} from "./D03_PlanAnalysis_UI_DrawPanel"; import type { PlanMapLayer, ScreenMap } from "./D03_PlanAnalysis_UI_Map"; -/** 제약 칸 처음 값 — 길이 = 측점 간격(지식DB 설계제원_총괄) · 기울기 = 간선 20 km/h 일반지형 상한(종단선형). */ -export const DRAW_DEFAULT_LENGTH_M = 20; -export const DRAW_DEFAULT_SLOPE_PCT = 9; const START_HINT = "지점 · 선 끝 · 선 위를 눌러 시작합니다 — 빈 땅은 우클릭 「여기서 긋기 시작」."; -export type DrawConstraint = "free" | "length" | "slope" | "both"; export type DrawMode = "idle" | "draw"; export interface DrawPreview { @@ -87,7 +86,7 @@ export interface PlanDrawParams { export interface PlanDraw { layer: PlanMapLayer; - /** 좌측 「그리기」 칸 몸(모드 단추 · 제약 칸 · 안내). */ + /** 좌측 「그리기」 칸 몸(`D03_PlanAnalysis_UI_DrawPanel` — 긋기 · 연도 구역 마커 · 방식 칸). */ panel: HTMLElement; mode: () => DrawMode; setMode: (mode: DrawMode) => void; @@ -119,94 +118,23 @@ export function createPlanDraw(params: PlanDrawParams): PlanDraw { let slopePct = DRAW_DEFAULT_SLOPE_PCT; // ── 좌측 칸 ───────────────────────────────────────────── - const drawButton = createButton({ - label: "긋기", - variant: "filled", - size: 28, - onClick: () => setMode(mode === "draw" ? "idle" : "draw"), + const kindFields = createDrawKindFields({ + value: { kind, lengthM, slopePct }, + onChange: (value) => ({ kind, lengthM, slopePct } = value), }); - const modeRow = document.createElement("div"); - modeRow.className = "d03-plan__row"; - modeRow.append(drawButton); - const kindSegment = createSegment({ - options: [ - { value: "free", label: "자유" }, - { value: "length", label: "길이 고정" }, - { value: "slope", label: "기울기 고정" }, - { value: "both", label: "둘 다" }, - ], - value: kind, - size: 24, - ariaLabel: "긋기 제약", - onChange: (value) => { - kind = value as DrawConstraint; - fieldsState(); - }, + const drawPanel = createDrawPanel({ + kind: kindFields, + onDraw: () => setMode(mode === "draw" ? "idle" : "draw"), }); - kindSegment.root.dataset.role = "draw-constraint"; - const lengthField = createInputField({ - label: "길이", - type: "number", - value: String(lengthM), - min: 1, - unit: "m", - size: 24, - onInput: (value) => { - const next = Number(value); - if (next > 0) lengthM = next; - lengthField.setError(next > 0 ? undefined : "0 보다 큰 수"); - }, - }); - lengthField.input.dataset.role = "draw-length"; - const slopeField = createInputField({ - label: "기울기(± 같음)", - type: "number", - value: String(slopePct), - unit: "%", - size: 24, - onInput: (value) => { - const next = Number(value); - const ok = value.trim() !== "" && Number.isFinite(next); - if (ok) slopePct = next; - slopeField.setError(ok ? undefined : "숫자"); - degText(); - }, - }); - slopeField.input.dataset.role = "draw-slope"; - const slopeDegText = createText("", "muted"); - slopeDegText.dataset.role = "draw-slope-deg"; - const degText = (): void => { - slopeDegText.textContent = `≈ ${fmt(slopeDeg(Math.abs(slopePct)))}°`; - }; - degText(); - const fieldRow = document.createElement("div"); - fieldRow.className = "d03-plan__row"; - fieldRow.append(lengthField.root, slopeField.root, slopeDegText); - const hint = createText("", "muted"); - hint.classList.add("d03-plan__hint"); - const panel = document.createElement("div"); - panel.className = "d03-plan__draw"; - panel.append(modeRow, kindSegment.root, fieldRow, hint); - - function fieldsState(): void { - lengthField.input.disabled = kind === "free" || kind === "slope"; - slopeField.input.disabled = kind === "free" || kind === "length"; - } + const panel = drawPanel.root; function panelState(): void { - drawButton.querySelector(".ui-btn__label")!.textContent = mode === "draw" ? "긋기 끝" : "긋기"; - drawButton.disabled = !editable; - drawButton.setAttribute("aria-pressed", String(mode === "draw")); - panel.dataset.mode = mode; - hint.textContent = !editable - ? "보기만 — 산림청 계정에서 긋습니다." - : mode === "draw" - ? active === null - ? START_HINT - : "누름 = 점 · Enter 끝 · Esc 취소 · Backspace 마지막 점 빼기" - : "[긋기]로 지점 · 선에서 긋습니다 · 지도 우클릭 = 긋기 시작 · 지점 놓기 · 선 누름 = 고름 · 「연도 마커」를 선 위로 끌면 구역을 나눕니다."; + drawPanel.state({ + drawing: mode === "draw", + editable, + canMark: params.network().lines.some((line) => line.vertices.length > 1), + }); } - fieldsState(); // ── 셈 ────────────────────────────────────────────────── const lastOf = (line: number): Vertex | null => { @@ -524,13 +452,9 @@ export function createPlanDraw(params: PlanDrawParams): PlanDraw { constraint: () => ({ kind, lengthM, slopePct }), setConstraint(next, length, slope) { kind = next; - kindSegment.set(next); - if (length !== undefined) [lengthM, lengthField.input.value] = [length, String(length)]; - if (slope !== undefined) { - [slopePct, slopeField.input.value] = [slope, String(slope)]; - degText(); - } - fieldsState(); + if (length !== undefined) lengthM = length; + if (slope !== undefined) slopePct = slope; + kindFields.set({ kind, lengthM, slopePct }); }, }; } diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts new file mode 100644 index 000000000..18b8214d7 --- /dev/null +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts @@ -0,0 +1,190 @@ +/* ============================================================================= + * D03_PlanAnalysis_UI_DrawPanel.ts + * D03 좌측 「그리기」 칸 몸(PLAN 599-1 · 599-4 · 요구 463-10 · 22) — [긋기] · 연도 구역 마커 · 긋기 방식 칸. + * + * 방식 칸 `createDrawKindFields` = 마디 고름 2 × 2(자유 · 길이 고정 · 기울기 고정 · 길이·기울기 고정) + + * 길이 · 기울기 두 열(기울기 아래 ≈ °) — 그리기 칸 · 노드 판(598-4)이 같은 부품을 씀. + * 연도 구역 마커 = 끌어 선 위에 놓기(`YEAR_MARK_DRAG` · 받음 = `D03_PlanAnalysis_UI_Edit` drop). + * 설명 글 없음(요구 463-6) — 칸 오류 · ≈ ° 값만. + * ========================================================================== */ + +import type { UiSize } from "@ui/ui_template_button"; +import { createButton } from "@ui/ui_template_elements"; +import { createInputField } from "@ui/ui_template_input"; +import { createSegment } from "@ui/ui_template_segment"; +import { createText } from "@ui/ui_template_text"; +import { fmt, slopeDeg } from "./D03_PlanAnalysis_UI_DrawPaint"; +import { YEAR_MARK_DRAG } from "./D03_PlanAnalysis_UI_Map"; + +/** 제약 칸 처음 값 — 길이 = 측점 간격(지식DB 설계제원_총괄) · 기울기 = 간선 20 km/h 일반지형 상한(종단선형). */ +export const DRAW_DEFAULT_LENGTH_M = 20; +export const DRAW_DEFAULT_SLOPE_PCT = 9; + +export type DrawConstraint = "free" | "length" | "slope" | "both"; + +export interface DrawKindValue { + kind: DrawConstraint; + lengthM: number; + slopePct: number; +} + +export const DRAW_KIND_LABELS: Record = { + free: "자유", + length: "길이 고정", + slope: "기울기 고정", + both: "길이·기울기 고정", +}; + +export interface DrawKindFields { + root: HTMLElement; + get: () => DrawKindValue; + /** 값을 바꿈(onChange 는 안 부름). */ + set: (value: Partial) => void; + setDisabled: (disabled: boolean) => void; +} + +/** 긋기 방식 칸 — 고름 · 길이 · 기울기가 바뀔 때마다 `onChange(지금 값)`(빈 · 틀린 수는 칸 오류만). */ +export function createDrawKindFields(opts: { + value: DrawKindValue; + onChange: (value: DrawKindValue) => void; + size?: UiSize; +}): DrawKindFields { + const size = opts.size ?? 24; + let value = { ...opts.value }; + let disabled = false; + const changed = (): void => opts.onChange({ ...value }); + const segment = createSegment({ + options: (Object.keys(DRAW_KIND_LABELS) as DrawConstraint[]).map((kind) => ({ + value: kind, + label: DRAW_KIND_LABELS[kind], + })), + value: value.kind, + size, + columns: 2, + ariaLabel: "긋기 방식", + onChange: (next) => { + value.kind = next as DrawConstraint; + fieldsState(); + changed(); + }, + }); + segment.root.dataset.role = "draw-constraint"; + const lengthField = createInputField({ + label: "길이", + type: "number", + value: String(value.lengthM), + min: 1, + unit: "m", + size, + onInput: (text) => { + const next = Number(text); + lengthField.setError(next > 0 ? undefined : "0 보다 큰 수"); + if (!(next > 0)) return; + value.lengthM = next; + changed(); + }, + }); + lengthField.input.dataset.role = "draw-length"; + const slopeField = createInputField({ + label: "기울기", + type: "number", + value: String(value.slopePct), + unit: "%", + size, + onInput: (text) => { + const next = Number(text); + const ok = text.trim() !== "" && Number.isFinite(next); + slopeField.setError(ok ? undefined : "숫자"); + if (!ok) return; + value.slopePct = next; + degText(); + changed(); + }, + }); + slopeField.input.dataset.role = "draw-slope"; + const slopeDegText = createText("", "muted"); + slopeDegText.dataset.role = "draw-slope-deg"; + const degText = (): void => { + slopeDegText.textContent = `≈ ${fmt(slopeDeg(Math.abs(value.slopePct)))}°`; + }; + const slopeColumn = document.createElement("div"); + slopeColumn.append(slopeField.root, slopeDegText); + const fields = document.createElement("div"); + fields.className = "d03-plan__draw-fields"; + fields.append(lengthField.root, slopeColumn); + const root = document.createElement("div"); + root.className = "d03-plan__draw-kind"; + root.append(segment.root, fields); + + function fieldsState(): void { + for (const button of segment.root.querySelectorAll("button")) button.disabled = disabled; + lengthField.input.disabled = disabled || value.kind === "free" || value.kind === "slope"; + slopeField.input.disabled = disabled || value.kind === "free" || value.kind === "length"; + } + degText(); + fieldsState(); + + return { + root, + get: () => ({ ...value }), + set(next) { + value = { ...value, ...next }; + segment.set(value.kind); + lengthField.input.value = String(value.lengthM); + slopeField.input.value = String(value.slopePct); + lengthField.setError(undefined); + slopeField.setError(undefined); + degText(); + fieldsState(); + }, + setDisabled(next) { + disabled = next; + fieldsState(); + }, + }; +} + +export interface DrawPanel { + root: HTMLElement; + /** 단추 글 · 막힘 — 긋는 중 · 고칠 수 있음 · 마커 놓을 선 있음. */ + state: (state: { drawing: boolean; editable: boolean; canMark: boolean }) => void; +} + +/** 좌측 「그리기」 칸 — [긋기] · 연도 구역 마커(끌기) · 방식 칸. */ +export function createDrawPanel(opts: { kind: DrawKindFields; onDraw: () => void }): DrawPanel { + const drawButton = createButton({ + label: "긋기", + variant: "filled", + size: 28, + onClick: opts.onDraw, + }); + const markButton = createButton({ + label: "연도 구역 마커", + variant: "ghost", + size: 28, + iconStart: "flag", + }); + markButton.title = "선 위로 끌어 놓으면 구역을 나눕니다"; + markButton.dataset.role = "draw-year-mark"; + markButton.draggable = true; + markButton.addEventListener("dragstart", (event) => { + event.dataTransfer?.setData(YEAR_MARK_DRAG, "1"); + if (event.dataTransfer) event.dataTransfer.effectAllowed = "copy"; + }); + const row = document.createElement("div"); + row.className = "d03-plan__row"; + row.append(drawButton, markButton); + const root = document.createElement("div"); + root.className = "d03-plan__draw"; + root.append(row, opts.kind.root); + return { + root, + state({ drawing, editable, canMark }) { + drawButton.querySelector(".ui-btn__label")!.textContent = drawing ? "긋기 끝" : "긋기"; + drawButton.disabled = !editable; + drawButton.setAttribute("aria-pressed", String(drawing)); + markButton.disabled = !editable || drawing || !canMark; + root.dataset.mode = drawing ? "draw" : "idle"; + }, + }; +} diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts index fc69c07cd..4a3aa2551 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts @@ -35,7 +35,7 @@ import type { GlBase } from "./D03_PlanAnalysis_UI_MapGl"; import type { Vertex } from "./logic/D03_PlanAnalysis_Plan"; import { localTmFrame, tmParams } from "./logic/D03_PlanAnalysis_Tm"; -/** 도구줄 「연도 마커」 끌어 놓기 자료 꼴(585-3). */ +/** 「연도 구역 마커」 끌어 놓기 자료 꼴(585-3 · 단추 = 그리기 칸 599-4). */ export const YEAR_MARK_DRAG = "application/x-aislo-year-mark"; /** 측점 간격(m) — B 규칙 측점과 같음. */ @@ -124,7 +124,7 @@ export interface PlanTools { view3d: boolean; /** 거리재기 켜짐(눌림). */ measure: boolean; - /** 「연도 마커」 끌 수 있음(고칠 수 있고 선 있음). */ + /** 받기만 — 연도 구역 마커 단추는 그리기 칸(599-4 · `D03_PlanAnalysis_UI_DrawPanel`)이 켬끔. */ yearMark: boolean; } @@ -197,12 +197,6 @@ export function createPlanMap(events: PlanMapEvents): PlanMap { const redoButton = mapToolButton("redo", { onClick: events.onRedo }); const viewButton = mapToolButton("view3d", { onClick: events.on3d }); const measureButton = mapToolButton("measure", { onClick: events.onMeasure }); - const yearMarkButton = mapToolButton("year-mark", { disabled: true }); - yearMarkButton.draggable = true; - yearMarkButton.addEventListener("dragstart", (event) => { - event.dataTransfer?.setData(YEAR_MARK_DRAG, "1"); - if (event.dataTransfer) event.dataTransfer.effectAllowed = "copy"; - }); const tools = createMapTools({ label: "평면 도구", hint: { @@ -216,7 +210,7 @@ export function createPlanMap(events: PlanMapEvents): PlanMap { "Shift+누름 = 구간 더함 · 뺌", "빈 곳 오른쪽으로 끌기 = 창 안 구간 · 왼쪽으로 = 걸친 구간까지", "고른 선 위 끌기 = 꺾임점 더해 옮김", - "「연도 마커」 선 위로 끌어 놓기 = 구역 나눔", + "그리기 칸 「연도 구역 마커」 선 위로 끌어 놓기 = 구역 나눔", "꺾임점 · 마크 끌기 = 옮김 · 마크를 선 밖으로 = 지움(앞 구간과 합침)", "오른쪽 누름 = 꺾임점 · 마크 지움", "Delete = 고른 선 · 마크 지움", @@ -229,7 +223,6 @@ export function createPlanMap(events: PlanMapEvents): PlanMap { [undoButton, redoButton], [viewButton, "rotate-ccw", "rotate-cw"], [markButton("stations", "측점"), markButton("slopes", "구간 길이 · 기울기"), measureButton], - [yearMarkButton], ], }); const toolbar = tools.root; @@ -526,7 +519,6 @@ export function createPlanMap(events: PlanMapEvents): PlanMap { if (tools.view3d !== undefined) viewButton.disabled = !tools.view3d; if (tools.measure !== undefined) measureButton.setAttribute("aria-pressed", String(tools.measure)); - if (tools.yearMark !== undefined) yearMarkButton.disabled = !tools.yearMark; }, redraw: drawTop, redrawBase: draw, diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css index 14f869430..ed11d04f3 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css @@ -195,3 +195,21 @@ .d03-plan__piece-year { font-weight: 600; } + +/* 599 그리기 칸 — 방식 칸(마디 고름 2 × 2 · 길이 · 기울기 두 열) */ +.d03-plan__draw-kind { + display: flex; + flex-direction: column; + gap: 6px; +} + +.d03-plan__draw-fields { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--spacing-8); + align-items: start; +} + +.d03-plan__draw-fields .ui-field { + margin: 0; +} diff --git a/resources/tester/test_553_4_de_parts.py b/resources/tester/test_553_4_de_parts.py index b6ff5235e..749bcbca4 100644 --- a/resources/tester/test_553_4_de_parts.py +++ b/resources/tester/test_553_4_de_parts.py @@ -30,11 +30,12 @@ def test_d03_parts(): three = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_3D.ts") css = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css") draw = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Draw.ts") + draw_panel = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts") tools = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Map.ts") layers = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Layers.ts") assert 'createElement("progress")' not in ts and "createProgressBar()" in ts # 562-3 — 날 단추 · 입력 · 고르기 없음 · 도구줄 · 제약 칸 · 연도 고름은 부품 - for src in (ts, draw, tools, layers): + for src in (ts, draw, draw_panel, tools, layers): for raw in ('createElement("button")', 'createElement("input")', 'createElement("select")'): assert raw not in src, raw # 568-3 — 도구줄 = 공용 지도 도구줄 · 아홉([?] · 되돌리기 · 다시하기 · 3D · 돌리기 둘 · 측점 · 기울기 · 재기) @@ -76,7 +77,14 @@ def test_d03_parts(): ("place", "false"), ], keys assert ".d03-plan__layers" in css and ".d03-plan__legend" in css - assert "createSegment({" in draw and draw.count("createInputField(") == 2 + # 599-1 — 방식 칸 = 마디 고름 2 × 2 · 길이 · 기울기 두 열 · 노드 판(598-4)과 같은 함수 + assert "createSegment({" in draw_panel and draw_panel.count("createInputField(") == 2 + assert "columns: 2" in draw_panel and "export function createDrawKindFields(" in draw_panel + assert ".d03-plan__draw-fields" in css and "createDrawKindFields(" in draw + assert '"길이·기울기 고정"' in draw_panel and '"둘 다"' not in draw_panel + assert "d03-plan__hint" not in draw # 칸 설명 글 걷음(요구 463-6) + # 599-4 — 연도 구역 마커 = 그리기 칸 단추(끌어 놓기) · 지도 도구줄에서 뺌 + assert "YEAR_MARK_DRAG" in draw_panel and '"year-mark"' not in tools props = _src("D03_PlanAnalysis/D03_PlanAnalysis_UI_Props.ts") # 585-2 조각 연도 = 속성 판 assert "createSelectField(" in props and "createFloatPanel(" in props assert 'createElement("button")' not in props and 'createElement("select")' not in props diff --git a/resources/tester/test_568_1_map_tools.py b/resources/tester/test_568_1_map_tools.py index c14681ef0..9cfd9653f 100644 --- a/resources/tester/test_568_1_map_tools.py +++ b/resources/tester/test_568_1_map_tools.py @@ -55,7 +55,6 @@ def test_단추_목록_아이콘_차례(): "footprint", "measure", "station-snap", - "year-mark", ] names = _icon_names() assert all(icon in names for _, icon, _ in rows) diff --git a/ui_template/ui_template_map_tools.ts b/ui_template/ui_template_map_tools.ts index afacf9875..47c7e28cd 100644 --- a/ui_template/ui_template_map_tools.ts +++ b/ui_template/ui_template_map_tools.ts @@ -31,7 +31,6 @@ export const MAP_TOOLS = { footprint: { icon: "road", title: "절/성토선", toggle: true }, measure: { icon: "ruler", title: "거리 재기", toggle: true }, "station-snap": { icon: "magnet", title: "측점 스냅", toggle: true }, - "year-mark": { icon: "flag", title: "연도 구역 마커 — 선 위로 끌어 놓기" }, } satisfies Record; export type MapToolAct = keyof typeof MAP_TOOLS; diff --git a/ui_template/ui_template_segment.css b/ui_template/ui_template_segment.css index 984b571b9..0c721748d 100644 --- a/ui_template/ui_template_segment.css +++ b/ui_template/ui_template_segment.css @@ -48,6 +48,19 @@ cursor: not-allowed; } +/* 여러 줄(columns) — 칸 사이 1px 틈에 틀 색 바탕이 보여 줄 · 칸 나눔선 */ +.ui-segment[data-columns] { + display: grid; + grid-template-columns: repeat(var(--ui-segment-columns), 1fr); + gap: 1px; + height: auto; + background: var(--color-border); +} +.ui-segment[data-columns] > .ui-segment__item { + height: var(--ui-size-h); + border-left: 0; +} + /* 숨김 — display 가 [hidden] 을 덮지 않게(공용 기본 꼴이 안 실린 곳 · 페이지 꾸밈 위에서도) */ :is(.ui-segment, .ui-segment__item)[hidden] { display: none !important; diff --git a/ui_template/ui_template_segment.ts b/ui_template/ui_template_segment.ts index 81a591655..1ba81b868 100644 --- a/ui_template/ui_template_segment.ts +++ b/ui_template/ui_template_segment.ts @@ -4,6 +4,7 @@ * * 꼴 = 글 · 아이콘(+ title) · 글 + 아이콘 * 크기 = data-size 20 · 24 · 28 · 32(기본 28) + * 줄 = 한 줄(기본) · `columns: 2` = 2 × 2 처럼 여러 줄 칸(긴 이름 넷 · 599-1 D03 긋기 방식) * 상태 = 마우스 `:hover` · 눌림 `:active` · 막힘 `disabled` · 고름 = aria-checked — CSS 는 `[data-state]` 로도 받음 * 쓰기 = `createSegment({ options: [{ value: "list", label: "목록" }, { value: "card", label: "카드" }], value: "list", onChange })` * 대신 = B01 `.b01-sys-seg` · `.b01-home__seg` · `.b01-proj-seg` · B04 `b04-surface__sheet-method` · B05 `b05-routerec__pills` · @@ -37,12 +38,18 @@ export function createSegment(opts: { /** 20 · 24 · 28 · 32 권함 */ size?: UiSize; ariaLabel?: string; + /** 한 줄 칸 수 — 넘으면 다음 줄(없음 = 한 줄) */ + columns?: number; }): SegmentHandle { const root = document.createElement("div"); root.className = "ui-segment"; root.setAttribute("role", "radiogroup"); if (opts.ariaLabel) root.setAttribute("aria-label", opts.ariaLabel); setUiSize(root, opts.size ?? 28); + if (opts.columns) { + root.dataset.columns = String(opts.columns); + root.style.setProperty("--ui-segment-columns", String(opts.columns)); + } let current = opts.value; const items = opts.options.map((o) => { const b = document.createElement("button");