- 1-A 도면 GET · 저장 PUT 한도 60 s · 저장 중 미리받기 쉼 · 끊긴 저장 = 목록 confirmed 로 판정
- 2 CAD 자동백업 열쇠 = b07:{프로젝트}:{도면} · 도각 편집 = b07-frame:{프로젝트} · [초기화] 같은 열쇠
- 3-1 [초기화] 물음 글 = 프로젝트 도각 그대로 · [도각 편집] → [기본 도각 복원] 안내
- 3-2 풀린 장 저장본 json · 수량표 찌꺼기 걷음(_write_manifest 한 곳)
- 5-1 확정 풀기 · 배치 · 축척 = mark_stage(save)
- 7-3 start_stage 가 DEV_UNLOCK 표식 남김
- 6-a navigateDrawing · onNavigate · hasPrev · hasNext 걷음 · 6-b cad-host-save 토글 걷음 · 4-2 B08 안 쓰는 글 11 걷음
- 6-f 특성 판 색 끌기 = 되돌리기 한 칸(paintNowCommitLater 한 벌) · 6-g LAYMRG 사본 길
- 4-1 B09 [인쇄 미리보기] = 저장 뒤 이동 · 단계 그대로 · 6-e M02 카드 도움말 DWG 한 줄
- 시험 test_506_3_* 둘 · cad/undo-506-3 · test_298_2_b07_buttons 열쇠 맞춤
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
200 lines
5.8 KiB
TypeScript
200 lines
5.8 KiB
TypeScript
/** 리본 안에 들어가는 위젯 패널 — 특성(색·굵기·선종류), 도면층, 문자 스타일 */
|
|
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 {
|
|
LINE_TYPES,
|
|
LINE_WIDTHS,
|
|
paintNowCommitLater,
|
|
restyle,
|
|
selectionLineStyle,
|
|
styleTargets,
|
|
} from '../helpers/selection-style';
|
|
import {
|
|
getActiveLayerId,
|
|
getActiveTextStyle,
|
|
getLayers,
|
|
getSelectedEntities,
|
|
setActiveLayerId,
|
|
setActiveLineColor,
|
|
setActiveLineDash,
|
|
setActiveLineWidth,
|
|
setActiveTextStyle,
|
|
} from '../state';
|
|
|
|
const FONT_FAMILIES = ['Noto Sans KR', 'Malgun Gothic', 'Pretendard', 'Arial', 'monospace'];
|
|
|
|
/** 고른 선 있으면 그것만 바꿈(488 — 그리기 기본값 그대로) · 없으면 그리기 기본값만 */
|
|
const selectedLines = () => styleTargets(getSelectedEntities());
|
|
|
|
export const PropertiesWidget: FC = () => {
|
|
const view = selectionLineStyle();
|
|
|
|
return (
|
|
<div className="cad-ribbon-props">
|
|
<label className="cad-prop" title="객체 색상 (COLOR)">
|
|
<span>색상</span>
|
|
<input
|
|
type="color"
|
|
value={view.color || '#000000'}
|
|
data-mixed={view.color ? undefined : 'true'}
|
|
onChange={(event) => {
|
|
const color = event.target.value;
|
|
const lines = selectedLines();
|
|
if (!lines.length) return setActiveLineColor(color);
|
|
paintNowCommitLater(lines, (entity) => {
|
|
entity.lineColor = color;
|
|
});
|
|
}}
|
|
/>
|
|
</label>
|
|
<label className="cad-prop" title="선 두께 (LWEIGHT)">
|
|
<span>선 두께</span>
|
|
<select
|
|
value={view.width}
|
|
onChange={(event) => {
|
|
const width = Number(event.target.value);
|
|
const lines = selectedLines();
|
|
if (!lines.length) return setActiveLineWidth(width);
|
|
restyle(lines, (entity) => {
|
|
entity.lineWidth = width;
|
|
});
|
|
}}
|
|
>
|
|
<option value="" hidden />
|
|
{LINE_WIDTHS.map((width) => (
|
|
<option key={width} value={width}>
|
|
{width}px
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label className="cad-prop" title="선종류 (LINETYPE)">
|
|
<span>선종류</span>
|
|
<select
|
|
value={view.lineType}
|
|
onChange={(event) => {
|
|
const dash = LINE_TYPES.find((type) => type.value === event.target.value)?.dash;
|
|
const lines = selectedLines();
|
|
if (!lines.length) return setActiveLineDash(dash ? [...dash] : undefined);
|
|
restyle(lines, (entity) => {
|
|
entity.lineDash = dash ? [...dash] : undefined;
|
|
});
|
|
}}
|
|
>
|
|
<option value="" hidden />
|
|
{LINE_TYPES.map((type) => (
|
|
<option key={type.value} value={type.value}>
|
|
{type.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const LayersWidget: FC = () => {
|
|
const layers = getLayers();
|
|
const activeLayerId = getActiveLayerId();
|
|
|
|
return (
|
|
<div className="cad-ribbon-props">
|
|
<label className="cad-prop cad-prop--wide" title="현재 레이어">
|
|
<span>현재 레이어</span>
|
|
<select value={activeLayerId} onChange={(event) => setActiveLayerId(event.target.value)}>
|
|
{/* 잠금 레이어(배경)는 고를 수 없으니 보여 주지 않는다. 현재 층이 잠금이면
|
|
(전부 잠금인 도면) 표시가 비지 않게 그 항목만 남긴다. */}
|
|
{layers
|
|
.filter((layer) => !layer.isLocked || layer.id === activeLayerId)
|
|
.map((layer) => (
|
|
<option key={layer.id} value={layer.id}>
|
|
{layer.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<button
|
|
type="button"
|
|
className="cad-tool"
|
|
data-size="small"
|
|
title="레이어 특성 관리자 (LAYER)"
|
|
onClick={() => runCommandInput('LAYER')}
|
|
>
|
|
<span className="cad-tool__glyph">▤</span>
|
|
<span className="cad-tool__label">레이어</span>
|
|
</button>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const TextStyleWidget: FC = () => {
|
|
const textStyle = getActiveTextStyle();
|
|
|
|
const handle = (patch: Partial<typeof textStyle>, live = false) => {
|
|
setActiveTextStyle(patch);
|
|
const texts = getSelectedEntities().filter((entity) => entity.getType() === EntityName.Text);
|
|
const mutate = (entity: Entity) => (entity as TextEntity).setTextOptions(patch);
|
|
// 색만 드래그 중 연발한다 — 나머지 항목은 한 번 고르면 끝이라 바로 싣는다.
|
|
if (live) paintNowCommitLater(texts, mutate);
|
|
else restyle(texts, mutate);
|
|
};
|
|
|
|
return (
|
|
<div className="cad-ribbon-props">
|
|
<label className="cad-prop cad-prop--wide" title="문자 글꼴 (STYLE)">
|
|
<span>글꼴</span>
|
|
<select
|
|
value={textStyle.fontFamily}
|
|
onChange={(event) => handle({ fontFamily: event.target.value })}
|
|
>
|
|
{FONT_FAMILIES.map((family) => (
|
|
<option key={family} value={family}>
|
|
{family}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label className="cad-prop" title="문자 높이">
|
|
<span>크기</span>
|
|
<input
|
|
type="number"
|
|
min={4}
|
|
max={120}
|
|
value={textStyle.fontSize}
|
|
onChange={(event) => {
|
|
const size = Number(event.target.value);
|
|
if (Number.isFinite(size) && size > 0) handle({ fontSize: size });
|
|
}}
|
|
/>
|
|
</label>
|
|
<label className="cad-prop" title="굵게">
|
|
<span>굵게</span>
|
|
<input
|
|
type="checkbox"
|
|
checked={textStyle.bold}
|
|
onChange={(event) => handle({ bold: event.target.checked })}
|
|
/>
|
|
</label>
|
|
<label className="cad-prop" title="기울임">
|
|
<span>기울임</span>
|
|
<input
|
|
type="checkbox"
|
|
checked={textStyle.italic}
|
|
onChange={(event) => handle({ italic: event.target.checked })}
|
|
/>
|
|
</label>
|
|
<label className="cad-prop" title="문자 색상">
|
|
<span>색상</span>
|
|
<input
|
|
type="color"
|
|
value={textStyle.textColor}
|
|
onChange={(event) => handle({ textColor: event.target.value }, true)}
|
|
/>
|
|
</label>
|
|
</div>
|
|
);
|
|
};
|