diff --git a/A00_Common/map/map_draw_kind.css b/A00_Common/map/map_draw_kind.css new file mode 100644 index 000000000..d2d892999 --- /dev/null +++ b/A00_Common/map/map_draw_kind.css @@ -0,0 +1,28 @@ +/* 공용 긋기 방식 칸(626-3 · `map_draw_kind.ts`) — 고름 아래 길이 · 기울기 두 열 */ +.map-draw-kind { + display: flex; + flex-direction: column; + gap: 6px; +} + +.map-draw-kind__fields { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--spacing-8); + align-items: start; +} + +.map-draw-kind__fields[hidden] { + display: none; +} + +.map-draw-kind__fields .ui-field { + margin: 0; + min-width: 0; +} + +.map-draw-kind__fields .ui-input-unit, +.map-draw-kind__fields .ui-input { + width: 100%; + min-width: 0; +} diff --git a/A00_Common/map/map_draw_kind.ts b/A00_Common/map/map_draw_kind.ts new file mode 100644 index 000000000..024e2d33c --- /dev/null +++ b/A00_Common/map/map_draw_kind.ts @@ -0,0 +1,146 @@ +/* ============================================================================= + * map_draw_kind.ts + * 공용 긋기 방식 칸(PLAN 626-3 · 599-1 · 625-1) — 예정노선(D03) 그리기 칸 · 노드 판 · B05 평면 예정노선 그리기가 같이 씀. + * 꼴 = 마디 고름 2 × 2(자유지정 · 길이·기울기 고정 / 길이 고정 · 기울기 고정) + 길이 · 기울기 두 열(기울기 아래 ≈ °) · + * 방식에 안 쓰는 칸은 숨김(막힘 = 권한 없음만) · 설명 글 없음(요구 463-6). + * 쓰는 법 = `createDrawKindFields({ value, onChange })` → { root, get, set, setDisabled }. + * ========================================================================== */ + +import type { UiSize } from "@ui/ui_template_button"; +import { createInputField } from "@ui/ui_template_input"; +import { createSegment } from "@ui/ui_template_segment"; +import { createText } from "@ui/ui_template_text"; +import "./map_draw_kind.css"; + +/** 기울기 % → 각(°) 한 자리 글. */ +const slopeDegText1 = (pct: number): string => ((Math.atan(pct / 100) * 180) / Math.PI).toFixed(1); + +/** 제약 칸 처음 값 — 길이 = 측점 간격(지식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: "자유지정", + both: "길이·기울기 고정", + length: "길이 고정", + slope: "기울기 고정", +}; + +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 = `≈ ${slopeDegText1(Math.abs(value.slopePct))}°`; + }; + const slopeColumn = document.createElement("div"); + slopeColumn.append(slopeField.root, slopeDegText); + const fields = document.createElement("div"); + fields.className = "map-draw-kind__fields"; + fields.append(lengthField.root, slopeColumn); + const root = document.createElement("div"); + root.className = "map-draw-kind"; + root.append(segment.root, fields); + + function fieldsState(): void { + for (const button of segment.root.querySelectorAll("button")) button.disabled = disabled; + lengthField.input.disabled = slopeField.input.disabled = disabled; + fields.hidden = value.kind === "free"; + lengthField.root.hidden = value.kind === "slope"; + slopeColumn.hidden = 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(); + }, + }; +} diff --git a/B05_Profile/B05_Profile_UI_RouteEdit.ts b/B05_Profile/B05_Profile_UI_RouteEdit.ts index 80130828c..43681bd41 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit.ts @@ -22,7 +22,6 @@ import { type ViewState, } from "../B04_PreProcess/B04_PreProcess_UI_MapRender"; import type { VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; -import { showToast } from "@ui/ui_template_elements"; import { bindMinorContourToggle, createSheetFollow, @@ -41,12 +40,12 @@ import { import { bindRouteApply } from "./B05_Profile_UI_RouteEdit_Apply"; import type { EditedCurve, EditedNode } from "./logic/B05_Profile_UI_RouteEdit_Curve"; import { drawRouteEditScene, routeHeadText, watchTheme } from "./B05_Profile_UI_RouteEdit_Render"; -import { bindNodeSplice, bindRouteEditNavigation } from "./B05_Profile_UI_RouteEdit_Input"; +import { bindRouteEditMouse } from "./B05_Profile_UI_RouteEdit_Mouse"; import { bindRouteEditPointer } from "./B05_Profile_UI_RouteEdit_Pointer"; import { createCrossPreview } from "./B05_Profile_UI_RouteEdit_Cross"; import { createMapRotation } from "./B05_Profile_UI_RouteEdit_Rotate"; import { createRouteEditChrome } from "./B05_Profile_UI_RouteEdit_Chrome"; -import { bindMeasureControls, createMeasureTool } from "./B05_Profile_UI_RouteEdit_Measure"; +import { createRouteMeasure } from "./B05_Profile_UI_RouteEdit_Measure"; import { createSlopeLabels } from "./B05_Profile_UI_RouteEdit_Slope"; import { captureView, @@ -191,22 +190,13 @@ export async function mountRouteEditor( // 횡단이 뜨면 그 측점 반폭 선을 그린다 — 새로 고침 뒤 고른 측점은 만드는 도중 불려 아래 선언이 아직이라 한 박자 뒤에(505-4). onShown: () => queueMicrotask(draw), }); - /** 구간 재기 — 계획·예상 노선 위 두 점. 셈·서버 묻기는 `_Measure` 몫(계획서 0-9 ⑤). */ - const measure = createMeasureTool({ + /** 구간 재기 — 계획·예상 노선 위 두 점 · 재기 단추·작은 창. 셈·서버 묻기 · 배선은 `_Measure` 몫(계획서 0-9 ⑤). */ + const { measure, measureControls } = createRouteMeasure({ projectId, stationIntervalM, lines: () => ({ planned: plannedLine.length ? plannedLine : planned, expected }), toScreen: (vertex) => toScreen(vertex), isClosed: () => closed, - onChange: () => { - measureControls.sync(); - draw(); - }, - onHint: () => measureControls.sync(), - }); - /** 재기 단추·작은 창 — 배선은 `_Measure` 몫. */ - const measureControls = bindMeasureControls({ - measure, box: measureBox, text: measureText, button: measureButton, @@ -547,37 +537,18 @@ export async function mountRouteEditor( offStation, }); - bindNodeSplice({ - canvas, - unrotate: rotation.unrotate, - segmentAt: pointer.segmentAt, - nodeAt: pointer.nodeAt, - insert: (segment, px, py) => { - edits.spliceNode(snapshotNow(), segment + 1, toMetric(px, py)); // 편집값도 같은 자리에 끼운다. - picked = segment + 1; - applyEdit("새 노드를 넣었습니다(직선 추가)."); - }, - remove: (index) => { - if (planned.length <= 2) return showToast("노선은 노드가 2개 이상이어야 합니다.", "error"); - if (apexLock[index]) - return showToast("고정한 점은 못 지웁니다 — 고정을 먼저 푸세요.", "error"); - edits.spliceNode(snapshotNow(), index); - picked = -1; - applyEdit("노드를 지웠습니다(직선 삭제)."); - }, - }); - - bindRouteEditNavigation({ + // 두 번 클릭 삽입 · 오른쪽 클릭 삭제 · 지도 이동 · 확대 · 돌리기 — 배선은 `_Mouse` 몫. + bindRouteEditMouse({ canvas, + rotation, + pointer, + toMetric, getView: () => view, - setView: (next) => { - view = next; - }, + setView: (next) => (view = next), getMeta: () => meta, - // 돌린 지도에서는 손이 민 방향과 그림이 움직일 방향이 다르다 — 거꾸로 돌려 넘긴다. - unrotateDelta: rotation.unrotateDelta, - rotateWheel: rotation.wheel, // Ctrl+휠 = 5° 돌리기(390-3) - claimed: pointer.claimed, // 왼쪽 옮기기 = 노드 · 손잡이 · 마커 · 재기 아닌 누름만(588-4) + snapshot: snapshotNow, + setPicked: (index) => (picked = index), + applyEdit: (message) => applyEdit(message), draw, }); diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Measure.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Measure.ts index 2e4a90118..995a068bb 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Measure.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Measure.ts @@ -183,6 +183,22 @@ export function bindMeasureControls(params: MeasureControlsParams): { return { sync, mode: () => mode }; } +/** 재기 도구 + 단추 · 작은 창 한 벌(626-3 · `_RouteEdit` 에서 뗌) — 상태 바뀜 = 창 맞춤 · 다시 그림 · 미리보기 = 창 글만. */ +export function createRouteMeasure( + params: Omit & Omit, +): { measure: MeasureTool; measureControls: ReturnType } { + const measure = createMeasureTool({ + ...params, + onChange: () => { + measureControls.sync(); + params.redraw(); + }, + onHint: () => measureControls.sync(), + }); + const measureControls = bindMeasureControls({ ...params, measure }); + return { measure, measureControls }; +} + export function createMeasureTool(params: MeasureToolParams): MeasureTool { /** 찍은 두 점. 셋째를 찍으면 새 구간의 시작이 된다. */ let picked: MeasurePoint[] = []; diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Mouse.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Mouse.ts new file mode 100644 index 000000000..386837255 --- /dev/null +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Mouse.ts @@ -0,0 +1,72 @@ +/* ============================================================================= + * B05_Profile_UI_RouteEdit_Mouse.ts + * 계획노선 편집(평면) 마우스 배선 — 선 두 번 클릭 = 노드 넣기 · 노드 오른쪽 클릭 = 지움 · 가운데(휠) 버튼 끌기 = + * 지도 이동 · 휠 = 확대 · Ctrl+휠 = 돌리기. `B05_Profile_UI_RouteEdit.ts` 에서 뗌(626-3 · 동작 그대로). + * 노드 끌기 · 집기는 `_Pointer` 몫 · 집기 셈 · 듣기는 `_Input` 몫. + * ========================================================================== */ + +import type { ViewState } from "../B04_PreProcess/B04_PreProcess_UI_MapRender"; +import type { VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; +import { showToast } from "@ui/ui_template_elements"; +import { bindNodeSplice, bindRouteEditNavigation } from "./B05_Profile_UI_RouteEdit_Input"; +import type { RouteEditSnapshot } from "./B05_Profile_UI_RouteEdit_History"; +import type { MapRotation } from "./B05_Profile_UI_RouteEdit_Rotate"; +import * as edits from "./logic/B05_Profile_UI_RouteEdit_Edits"; + +type Vertex = [number, number]; + +export interface RouteEditMouseParams { + canvas: HTMLCanvasElement; + rotation: Pick; + pointer: { + segmentAt: (px: number, py: number) => number; + nodeAt: (px: number, py: number) => number; + claimed: () => boolean; + }; + toMetric: (px: number, py: number) => Vertex; + getView: () => ViewState; + setView: (next: ViewState) => void; + getMeta: () => VWorldMeta | null; + /** 지금 편집값(같은 배열 — 넣고 빼면 그대로 바뀜). */ + snapshot: () => RouteEditSnapshot; + setPicked: (index: number) => void; + applyEdit: (message: string) => void; + draw: () => void; +} + +export function bindRouteEditMouse(params: RouteEditMouseParams): void { + const { canvas, rotation, pointer } = params; + bindNodeSplice({ + canvas, + unrotate: rotation.unrotate, + segmentAt: pointer.segmentAt, + nodeAt: pointer.nodeAt, + insert: (segment, px, py) => { + edits.spliceNode(params.snapshot(), segment + 1, params.toMetric(px, py)); // 편집값도 같은 자리에 끼운다. + params.setPicked(segment + 1); + params.applyEdit("새 노드를 넣었습니다(직선 추가)."); + }, + remove: (index) => { + const snapshot = params.snapshot(); + if (snapshot.planned.length <= 2) + return showToast("노선은 노드가 2개 이상이어야 합니다.", "error"); + if (snapshot.apexLock[index]) + return showToast("고정한 점은 못 지웁니다 — 고정을 먼저 푸세요.", "error"); + edits.spliceNode(snapshot, index); + params.setPicked(-1); + params.applyEdit("노드를 지웠습니다(직선 삭제)."); + }, + }); + + bindRouteEditNavigation({ + canvas, + getView: params.getView, + setView: params.setView, + getMeta: params.getMeta, + // 돌린 지도에서는 손이 민 방향과 그림이 움직일 방향이 다르다 — 거꾸로 돌려 넘긴다. + unrotateDelta: rotation.unrotateDelta, + rotateWheel: rotation.wheel, // Ctrl+휠 = 5° 돌리기(390-3) + claimed: pointer.claimed, // 왼쪽 옮기기 = 노드 · 손잡이 · 마커 · 재기 아닌 누름만(588-4) + draw: params.draw, + }); +} diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts index e75e1096f..c584139bd 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPaint.ts @@ -5,7 +5,7 @@ * 미리보기(거리 · 가상 종단 기울기 % · °) · 집기 = 화면 px 둘레 선 위 자리. * 길이·기울기 고정 부채꼴(625-2) — 정방향 = 채움 · 역방향 = 테두리 · 노드 · 놓인 꺾임점 · 미리보기 고리 = * 기울기 부호 색(+ 오르막 `--draw-map-slope-up` 따뜻한 색 · − 내리막 `--draw-map-slope-down` 찬 색 · 막힘 = 경고 빨강). - * `paintFan` = ScreenMap 의 `toScreen` 만 씀 — B05 평면(626-3)이 그대로 씀. + * `paintFan` · `paintPreview` = ScreenMap 의 `toScreen` · `pxPerMeter` · `upright` 만 씀 — B05 평면(626-3)이 그대로 씀. * 지점 핀(585-1) = 따로 겹(`pointLayer` · 겹 목록 맨 끝 = 늘 맨 위) · 색 = 목록 줄과 같은 `pointColor(i)` · 끌기 · 메뉴 = `D03_PlanAnalysis_UI_Points`. * ========================================================================== */ @@ -330,7 +330,7 @@ export function paintMark( * 고리 = 고른 점 기울기 부호 색(놓인 뒤 꺾임점과 같은 색). */ export function paintPreview( context: CanvasRenderingContext2D, - map: ScreenMap, + map: Pick, from: Vertex, preview: DrawPreview, compass: { lengthM: number; fan: Fan } | null, @@ -368,7 +368,7 @@ export function paintPreview( export function planLabel( context: CanvasRenderingContext2D, - map: ScreenMap, + map: Pick, x: number, y: number, text: string, diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts b/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts index 20c2de8cc..5a8ed38ef 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_DrawPanel.ts @@ -2,150 +2,26 @@ * D03_PlanAnalysis_UI_DrawPanel.ts * D03 좌측 「그리기」 칸 몸(PLAN 599-1 · 599-4 · 요구 463-10 · 22) — [긋기] · 연도 구역 마커 · 긋기 방식 칸. * - * 방식 칸 `createDrawKindFields` = 마디 고름 2 × 2(자유지정 · 길이·기울기 고정 / 길이 고정 · 기울기 고정 · - * 625-1) + 길이 · 기울기 두 열(기울기 아래 ≈ °) — 방식에 안 쓰는 칸은 숨김(막힘 = 권한 없음만) · - * 그리기 칸 · 노드 판(598-4)이 같은 부품을 씀. + * 방식 칸 `createDrawKindFields` = 공용 `A00_Common/map/map_draw_kind`(626-3) — 그리기 칸 · 노드 판(598-4) · + * B05 평면 예정노선 그리기가 같은 부품을 씀(여기서 다시 내보냄). * 연도 구역 마커 = 끌어 선 위에 놓기(`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"; +import type { DrawKindFields } from "../A00_Common/map/map_draw_kind"; -/** 제약 칸 처음 값 — 길이 = 측점 간격(지식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: "자유지정", - both: "길이·기울기 고정", - length: "길이 고정", - slope: "기울기 고정", -}; - -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 = slopeField.input.disabled = disabled; - fields.hidden = value.kind === "free"; - lengthField.root.hidden = value.kind === "slope"; - slopeColumn.hidden = 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(); - }, - }; -} +// 방식 칸 = 공용(`A00_Common/map/map_draw_kind` · 626-3 · B05 평면 예정노선 그리기도 씀) +export { + createDrawKindFields, + DRAW_DEFAULT_LENGTH_M, + DRAW_DEFAULT_SLOPE_PCT, + DRAW_KIND_LABELS, + type DrawConstraint, + type DrawKindFields, + type DrawKindValue, +} from "../A00_Common/map/map_draw_kind"; export interface DrawPanel { root: HTMLElement; diff --git a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css index 0ab8d4118..c08519723 100644 --- a/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css +++ b/D03_PlanAnalysis/D03_PlanAnalysis_UI_Style.css @@ -185,34 +185,7 @@ margin: 0; } -/* 599 그리기 칸 — 방식 칸(마디 고름 2 × 2 · 길이 · 기울기 두 열) */ -.d03-plan__draw-kind { - display: flex; - flex-direction: column; - gap: 6px; -} - -.d03-plan__draw-fields { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: var(--spacing-8); - align-items: start; -} - -.d03-plan__draw-fields[hidden] { - display: none; -} - -.d03-plan__draw-fields .ui-field { - margin: 0; - min-width: 0; -} - -.d03-plan__draw-fields .ui-input-unit, -.d03-plan__draw-fields .ui-input { - width: 100%; - min-width: 0; -} +/* 599 그리기 칸 방식 칸 = 공용 `A00_Common/map/map_draw_kind.css`(626-3) */ /* 597 구역 · 연도 목록 — 구역 줄(이름 칸 · 연도 고름 · 지움) · 구간 줄(측점 글 작게) */ .d03-plan__line-tail { diff --git a/resources/tester/test_553_4_de_parts.py b/resources/tester/test_553_4_de_parts.py index be0296afe..92f3777ee 100644 --- a/resources/tester/test_553_4_de_parts.py +++ b/resources/tester/test_553_4_de_parts.py @@ -98,10 +98,14 @@ def test_d03_parts(): ], keys assert ".d03-plan__layers" in css and ".d03-plan__legend" in css # 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 + # 626-3 — 방식 칸은 공용 `A00_Common/map/map_draw_kind`(B05 평면도 씀) · D03 칸은 다시 내보냄 + kind = _src("A00_Common/map/map_draw_kind.ts") + kind_css = _src("A00_Common/map/map_draw_kind.css") + assert "createSegment({" in kind and kind.count("createInputField(") == 2 + assert "columns: 2" in kind and "export function createDrawKindFields(" in kind + assert "createDrawKindFields," in draw_panel and 'from "../A00_Common/map/map_draw_kind"' in draw_panel + assert ".map-draw-kind__fields" in kind_css and "createDrawKindFields(" in draw + assert '"길이·기울기 고정"' in kind and '"둘 다"' not in kind assert "d03-plan__hint" not in draw # 칸 설명 글 걷음(요구 463-6) # 599-4 — 연도 구역 마커 = 그리기 칸 단추(끌어 놓기) · 지도 도구줄에서 뺌 assert "YEAR_MARK_DRAG" in draw_panel and '"year-mark"' not in tools