107 lines
4.1 KiB
TypeScript
107 lines
4.1 KiB
TypeScript
/**
|
|
* 오토캐드 도형 붙이기 — 두 길 모두 서버가 DXF 를 도형으로 바꿔 현재 도면에 원좌표 그대로 붙이고
|
|
* 고른 상태로 둔다(되돌리기 한 번에 빠짐).
|
|
* - 끌어다 놓기: 오토캐드 WBLOCK(DWG) · DXFOUT(DXF) 로 저장한 파일, 또는 도면 파일 통째.
|
|
* 끄는 동안 `body.cad-file-dragging` 으로 흐린 테두리.
|
|
* - Ctrl+V: 시스템 클립보드 글이 DXF 면 같은 길 · 아니면 앱 안 붙여넣기(PASTECLIP).
|
|
* 키 처리(`input-controller.ts`)는 Ctrl+V 의 기본 동작을 막지 않아 브라우저가 `paste` 를 쏜다 —
|
|
* `paste` 이벤트의 글은 권한 없이 읽힘(iframe 안에서도).
|
|
*/
|
|
import { toast } from 'react-toastify';
|
|
import { runCommandInput } from '../commands/run-command';
|
|
import { hasClipboardContent } from '../helpers/cad-clipboard';
|
|
import { getEntitiesAndLayersFromJsonObject } from '../helpers/import-export-handlers/import-entities-from-json';
|
|
import {
|
|
addEntities,
|
|
getLayers,
|
|
isDrawingReadOnly,
|
|
setLayers,
|
|
setSelectedEntityIds,
|
|
} from '../state';
|
|
import type { JsonEntity } from '../entities/Entity';
|
|
import { type ClipLayer, isDxfText, prepareClip } from './dxf-clip';
|
|
|
|
const DXF_CLIP_URL = '/api/projects/dxf-clip';
|
|
|
|
export async function pasteDxfText(text: string): Promise<number> {
|
|
return pasteServerClip(
|
|
await fetch(DXF_CLIP_URL, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ text }),
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function pasteDrawingFile(file: File): Promise<number> {
|
|
const form = new FormData();
|
|
form.append('file', file);
|
|
return pasteServerClip(
|
|
await fetch(`${DXF_CLIP_URL}/file`, { method: 'POST', credentials: 'include', body: form })
|
|
);
|
|
}
|
|
|
|
async function pasteServerClip(response: Response): Promise<number> {
|
|
const payload = (await response.json().catch(() => ({}))) as {
|
|
entities?: JsonEntity[];
|
|
layers?: ClipLayer[];
|
|
message?: string;
|
|
};
|
|
if (!response.ok || !payload.entities) {
|
|
throw new Error(payload.message ?? `HTTP ${response.status}`);
|
|
}
|
|
const { entities, newLayers } = prepareClip(payload.entities, payload.layers ?? [], getLayers());
|
|
const drawing = await getEntitiesAndLayersFromJsonObject({ entities, layers: getLayers() });
|
|
if (newLayers.length) setLayers([...getLayers(), ...newLayers]);
|
|
addEntities(drawing.entities, true);
|
|
setSelectedEntityIds(drawing.entities.map((entity) => entity.id));
|
|
return drawing.entities.length;
|
|
}
|
|
|
|
function reportPaste(job: Promise<number>): void {
|
|
job
|
|
.then((count) => toast.success(`오토캐드 도형 ${count}개를 붙여넣었습니다.`))
|
|
.catch((error: unknown) =>
|
|
toast.error(error instanceof Error ? error.message : '도형을 붙여넣지 못했습니다.')
|
|
);
|
|
}
|
|
|
|
function refuseReadOnly(): boolean {
|
|
if (!isDrawingReadOnly()) return false;
|
|
toast.info('볼 수만 있는 도면입니다 — 붙여넣을 수 없습니다.');
|
|
return true;
|
|
}
|
|
|
|
export function registerDxfClipPaste(): void {
|
|
document.addEventListener('paste', (event: ClipboardEvent) => {
|
|
if ((event.target as HTMLElement | null)?.closest?.('input, textarea, select')) return;
|
|
const text = event.clipboardData?.getData('text/plain') ?? '';
|
|
event.preventDefault();
|
|
if (!isDxfText(text)) {
|
|
if (hasClipboardContent()) runCommandInput('PASTECLIP');
|
|
return;
|
|
}
|
|
if (!refuseReadOnly()) reportPaste(pasteDxfText(text));
|
|
});
|
|
|
|
// 파일을 끌 때만 — 앱 안 끌기(글 · 패널)는 건드리지 않음. 기본 동작(파일 열기로 화면 이동)은 막음.
|
|
const dragging = (event: DragEvent) => event.dataTransfer?.types.includes('Files') ?? false;
|
|
const setDragging = (on: boolean) => document.body.classList.toggle('cad-file-dragging', on);
|
|
document.addEventListener('dragover', (event) => {
|
|
if (!dragging(event)) return;
|
|
event.preventDefault();
|
|
setDragging(true);
|
|
});
|
|
document.addEventListener('dragleave', (event) => {
|
|
if (!event.relatedTarget) setDragging(false);
|
|
});
|
|
document.addEventListener('drop', (event) => {
|
|
if (!dragging(event)) return;
|
|
event.preventDefault();
|
|
setDragging(false);
|
|
const file = event.dataTransfer?.files[0];
|
|
if (file && !refuseReadOnly()) reportPaste(pasteDrawingFile(file));
|
|
});
|
|
}
|