- 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>
161 lines
4.2 KiB
TypeScript
161 lines
4.2 KiB
TypeScript
/** 특성 팔레트 본문 — 선택 객체의 값을 읽고 바로 고친다 (PROPERTIES) */
|
||
import type { FC } from 'react';
|
||
import type { Entity } from '../entities/Entity';
|
||
import { polylineLength, sampleEntityPoints } from '../helpers/geometry/sample-entity';
|
||
import {
|
||
commonValue,
|
||
LINE_TYPES,
|
||
LINE_WIDTHS,
|
||
paintNowCommitLater,
|
||
restyle,
|
||
selectionLineStyle,
|
||
styleTargets,
|
||
} from '../helpers/selection-style';
|
||
import { getLayers, getSelectedEntities } from '../state';
|
||
|
||
interface PropertiesEditorProps {
|
||
compact?: boolean;
|
||
}
|
||
|
||
/** 고른 것을 사본으로 바꿈 — 되돌리기 한 칸(488) · 선 특성은 글 뺌 */
|
||
const applyToSelection = (mutate: (entity: Entity) => void) => restyle(getSelectedEntities(), mutate);
|
||
const applyToLines = (mutate: (entity: Entity) => void) =>
|
||
restyle(styleTargets(getSelectedEntities()), mutate);
|
||
|
||
/** 여러 객체가 값이 다르면 빈 칸(488 · 리본과 같은 규칙 · 고르면 모두에 적용) */
|
||
export const PropertiesEditor: FC<PropertiesEditorProps> = ({ compact = false }) => {
|
||
const selected = getSelectedEntities();
|
||
const layers = getLayers();
|
||
const first = selected[0];
|
||
const view = selectionLineStyle();
|
||
|
||
if (!first) {
|
||
return <p className="cad-properties__empty">선택된 객체가 없습니다.</p>;
|
||
}
|
||
|
||
const points = sampleEntityPoints(first);
|
||
const box = first.getBoundingBox();
|
||
|
||
return (
|
||
<div className="cad-properties-editor" data-compact={compact}>
|
||
<div className="cad-properties-editor__title">
|
||
{selected.length === 1 ? first.getType() : `여러 객체 (${selected.length})`}
|
||
</div>
|
||
|
||
<label>
|
||
<span>색상</span>
|
||
<input
|
||
type="color"
|
||
value={view.color || '#000000'}
|
||
data-mixed={view.color ? undefined : 'true'}
|
||
onChange={(event) => {
|
||
// 끄는 동안 = 화면만 · 멎은 뒤 되돌리기 한 칸(리본 색 칸과 같은 길)
|
||
const color = event.target.value;
|
||
paintNowCommitLater(styleTargets(getSelectedEntities()), (entity) => {
|
||
entity.lineColor = color;
|
||
});
|
||
}}
|
||
/>
|
||
</label>
|
||
|
||
<label>
|
||
<span>선 두께</span>
|
||
<select
|
||
value={view.width}
|
||
onChange={(event) =>
|
||
applyToLines((entity) => {
|
||
entity.lineWidth = Number(event.target.value);
|
||
})
|
||
}
|
||
>
|
||
<option value="" hidden />
|
||
{LINE_WIDTHS.map((width) => (
|
||
<option key={width} value={width}>
|
||
{width}px
|
||
</option>
|
||
))}
|
||
</select>
|
||
</label>
|
||
|
||
<label>
|
||
<span>선종류</span>
|
||
<select
|
||
value={view.lineType}
|
||
onChange={(event) => {
|
||
const dash = LINE_TYPES.find((type) => type.value === event.target.value)?.dash;
|
||
applyToLines((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>
|
||
|
||
<label>
|
||
<span>레이어</span>
|
||
<select
|
||
value={commonValue(selected, (entity) => entity.layerId)}
|
||
onChange={(event) =>
|
||
applyToSelection((entity) => {
|
||
entity.layerId = event.target.value;
|
||
})
|
||
}
|
||
>
|
||
<option value="" hidden />
|
||
{layers.map((layer) => (
|
||
<option key={layer.id} value={layer.id}>
|
||
{layer.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</label>
|
||
|
||
<label>
|
||
<span>투명도</span>
|
||
<input
|
||
type="number"
|
||
min={0}
|
||
max={90}
|
||
step={5}
|
||
value={Math.round((1 - (first.opacity ?? 1)) * 100)}
|
||
onChange={(event) => {
|
||
const percent = Math.min(90, Math.max(0, Number(event.target.value)));
|
||
applyToSelection((entity) => {
|
||
entity.opacity = 1 - percent / 100;
|
||
});
|
||
}}
|
||
/>
|
||
</label>
|
||
|
||
{!compact && (
|
||
<dl className="cad-properties-editor__readout">
|
||
<div>
|
||
<dt>길이</dt>
|
||
<dd>{polylineLength(points).toFixed(3)}</dd>
|
||
</div>
|
||
<div>
|
||
<dt>크기</dt>
|
||
<dd>
|
||
{(box.xmax - box.xmin).toFixed(3)} × {(box.ymax - box.ymin).toFixed(3)}
|
||
</dd>
|
||
</div>
|
||
<div>
|
||
<dt>시작점</dt>
|
||
<dd>{points[0] ? `${points[0].x.toFixed(2)}, ${points[0].y.toFixed(2)}` : '-'}</dd>
|
||
</div>
|
||
<div>
|
||
<dt>그룹</dt>
|
||
<dd>{first.groupId ? '있음' : '없음'}</dd>
|
||
</div>
|
||
</dl>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|