- 부품 상자 CSS · 단추 기본 꼴을 CAD iframe 에 실음(integration/ui-kit.ts) · 자리 · 폭은 cad-* 그대로 - 상태막대 = ui-toggle--chip 22 · 특성/레이어 = ui-tabs 36 · ↶↷ · 리본 접기 · 탐색 막대 = ui-icon-btn 20 · 32 - 빠른 특성 · 블록 라이브러리 · 자리표 = ui-float-panel · 리본 넘침 · 스냅 · 명령 자동완성 = ui-ctx-menu - window.confirm · window.prompt → askConfirm · askText · 빈 안내 = ui-empty 작은 꼴 - 높이 단: 리본 칸 19→18 · 명령 칸 26→28 · 레이어 줄 40→42 · 문서 탭 25→24 · 도각 편집 단추 24 - 마우스 바탕 = --color-hover · 켜짐 = --color-accent-soft · 도면 그림 색(--draw-*) 안 건드림 - 안 쓰는 DropdownButton 지움(쓰는 곳 0 · grep) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
166 lines
4.3 KiB
TypeScript
166 lines
4.3 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 (
|
||
<div className="ui-empty ui-empty--small">
|
||
<p className="ui-empty__text">선택된 객체가 없습니다.</p>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
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>
|
||
);
|
||
};
|