/** 리본 안에 들어가는 위젯 패널 — 특성(색·굵기·선종류), 도면층, 문자 스타일 */ import type { FC } from 'react'; import { runCommandInput } from '../commands/run-command'; import type { Entity } from '../entities/Entity'; import { EntityName } from '../entities/Entity'; import type { TextEntity } from '../entities/TextEntity'; import { getActiveLayerId, getActiveLineColor, getActiveLineDash, getActiveLineWidth, getActiveTextStyle, getEntities, getLayers, getSelectedEntities, setActiveLayerId, setActiveLineColor, setActiveLineDash, setActiveLineWidth, setActiveTextStyle, setEntities, } from '../state'; export const LINE_TYPES: { value: string; label: string; dash: number[] | undefined }[] = [ { value: 'solid', label: '실선', dash: undefined }, { value: 'dashed', label: '파선', dash: [10, 5] }, { value: 'dashdot', label: '1점쇄선', dash: [12, 4, 2, 4] }, { value: 'dotted', label: '점선', dash: [2, 4] }, ]; export const LINE_WIDTHS = [1, 2, 3, 4, 5]; const FONT_FAMILIES = ['Noto Sans KR', 'Malgun Gothic', 'Pretendard', 'Arial', 'monospace']; export const dashToLineType = (dash: number[] | undefined): string => LINE_TYPES.find((type) => JSON.stringify(type.dash) === JSON.stringify(dash))?.value ?? 'solid'; /** 선택 객체가 있으면 즉시 적용하고, 없으면 이후 그리기 기본값만 바꾼다. */ function applyToSelection(mutate: (entity: Entity) => void, trackInUndoStack = true): boolean { const selected = getSelectedEntities(); if (!selected.length) return false; for (const entity of selected) { mutate(entity); } setEntities([...getEntities()], trackInUndoStack); return true; } /** * 색 선택기는 드래그하는 내내 값 변경을 쏜다. 그 하나하나를 되돌리기·변경통지에 실으면 * 색 한 번 고르는 동안 수십 건이 나간다(2026-09-01 지적). 끄는 동안에는 화면만 칠하고, * 멎은 뒤 한 번만 되돌리기·통지에 싣는다. */ const COLOR_COMMIT_DELAY_MS = 350; let colorCommitTimer: ReturnType | undefined; function paintNowCommitLater(mutate: (entity: Entity) => void): void { applyToSelection(mutate, false); // 화면만 — 되돌리기·미저장 표시는 아직 clearTimeout(colorCommitTimer); colorCommitTimer = setTimeout(() => { applyToSelection(() => {}, true); // 멎었다 — 이번 한 번만 실어 보낸다 }, COLOR_COMMIT_DELAY_MS); } export const PropertiesWidget: FC = () => { const lineColor = getActiveLineColor(); const lineWidth = getActiveLineWidth(); const lineType = dashToLineType(getActiveLineDash()); return (
); }; export const LayersWidget: FC = () => { const layers = getLayers(); const activeLayerId = getActiveLayerId(); return (
); }; export const TextStyleWidget: FC = () => { const textStyle = getActiveTextStyle(); const handle = (patch: Partial, live = false) => { setActiveTextStyle(patch); const mutate = (entity: Entity) => { if (entity.getType() === EntityName.Text) { (entity as TextEntity).setTextOptions(patch); } }; // 색만 드래그 중 연발한다 — 나머지 항목은 한 번 고르면 끝이라 바로 싣는다. if (live) paintNowCommitLater(mutate); else applyToSelection(mutate); }; return (
); };