feat(B07): 그린 것을 블록으로 저장해 다시 놓고, 사진 넣기를 되살린다
조사표 4·7절 검토 결과를 반영한다. - 블록 라이브러리: 선택한 객체를 이름 붙여 저장(BLOCK)하고 팔레트에서 골라 클릭 한 번으로 놓는다(INSERT). 정의-참조 링크 없는 클론 방식이라 새 엔티티 타입 없이 기존 직렬화·groupId를 그대로 쓴다. 기준점은 선택 영역 중심. 라이브러리는 도면과 분리해 브라우저에 두고, 유실 대비로 파일 내보내기· 가져오기를 붙였다. - 사진 넣기: IMAGEATTACH가 파일 선택창을 띄우지 않아 도구가 FILE_SELECTED를 영원히 기다리고 있었다. 도구형 명령도 run()을 함께 부르게 하고 파일 선택을 붙여 되살렸다. - 조사표 4·7절의 반영 등급·반영 열을 실제 코드에 맞춰 정정하고, 반영하지 않기로 한 항목의 사유를 절마다 남겼다. 7절은 신규 구현 없음. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
/** 블록 라이브러리 팔레트 — 저장한 블록을 고르면 삽입 도구가 켜진다 (INSERT/BLOCK) */
|
||||
import type { FC } from 'react';
|
||||
import {
|
||||
deleteBlock,
|
||||
exportBlocksToFile,
|
||||
getBlocks,
|
||||
importBlocksFromFile,
|
||||
} from '../blocks/block-library';
|
||||
import { pickFile } from '../helpers/pick-file';
|
||||
import { startBlockInsert } from '../tools/insert-block-tool';
|
||||
import { isBlockLibraryVisible, setBlockLibraryVisible } from './ui-state';
|
||||
|
||||
export const BlockLibraryPanel: FC = () => {
|
||||
if (!isBlockLibraryVisible()) return null;
|
||||
|
||||
const blocks = getBlocks();
|
||||
|
||||
return (
|
||||
<div className="cad-block-library controls">
|
||||
<div className="cad-quick-properties__header">
|
||||
<span>블록 라이브러리</span>
|
||||
<button type="button" onClick={() => setBlockLibraryVisible(false)} title="닫기">
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{blocks.length === 0 ? (
|
||||
<p className="cad-block-library__empty">객체를 선택하고 BLOCK 명령으로 저장하십시오.</p>
|
||||
) : (
|
||||
<ul className="cad-block-library__list">
|
||||
{blocks.map((block) => (
|
||||
<li key={block.name}>
|
||||
<button
|
||||
type="button"
|
||||
className="cad-block-library__name"
|
||||
title={`객체 ${block.entities.length}개 — 클릭하면 삽입`}
|
||||
onClick={() => {
|
||||
setBlockLibraryVisible(false);
|
||||
void startBlockInsert(block);
|
||||
}}
|
||||
>
|
||||
{block.name}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
title="삭제"
|
||||
onClick={() => {
|
||||
if (window.confirm(`블록 '${block.name}'을 삭제하시겠습니까?`)) {
|
||||
deleteBlock(block.name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
<div className="cad-block-library__actions">
|
||||
<button type="button" onClick={() => exportBlocksToFile()}>
|
||||
파일로 내보내기
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => pickFile('application/json', (file) => importBlocksFromFile(file))}
|
||||
>
|
||||
파일에서 가져오기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import { type FC, useEffect, useState } from 'react';
|
||||
import { CommandLine } from './CommandLine';
|
||||
import { InspectorPanel } from './InspectorPanel';
|
||||
import { QuickProperties } from './QuickProperties';
|
||||
import { BlockLibraryPanel } from './BlockLibraryPanel';
|
||||
import { LayersWidget, PropertiesWidget, TextStyleWidget } from './RibbonWidgets';
|
||||
import { QuickAccessBar } from './QuickAccessBar';
|
||||
import { Ribbon } from '../ribbon/Ribbon';
|
||||
@@ -51,6 +52,7 @@ export const Toolbar: FC = () => {
|
||||
/>
|
||||
<ViewControls />
|
||||
<QuickProperties />
|
||||
<BlockLibraryPanel />
|
||||
{commandLineVisible && <CommandLine />}
|
||||
<StatusBar
|
||||
commandLineVisible={commandLineVisible}
|
||||
|
||||
@@ -10,6 +10,7 @@ let inspectorTab: InspectorTab = 'layers';
|
||||
let inspectorCollapsed = false;
|
||||
let quickPropertiesVisible = false;
|
||||
let activeRibbonTab = 'home';
|
||||
let blockLibraryVisible = false;
|
||||
|
||||
const notify = () => window.dispatchEvent(new CustomEvent(HtmlEvent.UPDATE_STATE));
|
||||
|
||||
@@ -17,6 +18,7 @@ export const getInspectorTab = () => inspectorTab;
|
||||
export const isInspectorCollapsed = () => inspectorCollapsed;
|
||||
export const isQuickPropertiesVisible = () => quickPropertiesVisible;
|
||||
export const getActiveRibbonTab = () => activeRibbonTab;
|
||||
export const isBlockLibraryVisible = () => blockLibraryVisible;
|
||||
|
||||
export function setActiveRibbonTab(tabId: string): void {
|
||||
activeRibbonTab = tabId;
|
||||
@@ -38,3 +40,8 @@ export function setQuickPropertiesVisible(visible: boolean): void {
|
||||
quickPropertiesVisible = visible;
|
||||
notify();
|
||||
}
|
||||
|
||||
export function setBlockLibraryVisible(visible: boolean): void {
|
||||
blockLibraryVisible = visible;
|
||||
notify();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user