260720_1
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
:root {
|
||||
--cad-title-height: 42px;
|
||||
--cad-ribbon-height: 82px;
|
||||
--cad-command-height: 58px;
|
||||
/* 하단 명령어 입력창 제거 → 높이 0으로 캔버스가 공간을 회수 (추후 사용성 개선 예정) */
|
||||
--cad-command-height: 0px;
|
||||
--cad-status-height: 28px;
|
||||
--cad-panel-width: 248px;
|
||||
font-family: Inter, Pretendard, "Noto Sans KR", system-ui, sans-serif;
|
||||
@@ -343,16 +344,9 @@ body > canvas[data-id="canvas"] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 하단 명령어 입력창은 숨김 처리 (마우스 커서 단축키 입력으로 대체) */
|
||||
.cad-command-area {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
right: 0;
|
||||
bottom: var(--cad-status-height);
|
||||
left: var(--cad-panel-width);
|
||||
height: var(--cad-command-height);
|
||||
padding: 5px 10px;
|
||||
background: #151c24f2;
|
||||
border-top: 1px solid #3b4b5a;
|
||||
display: none;
|
||||
}
|
||||
.cad-command-prompt {
|
||||
display: flex;
|
||||
@@ -435,6 +429,130 @@ body > canvas[data-id="canvas"] {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 수량 산출표 패널 (CAD 화면 하단 중심, 접이식) — 첨부 양식
|
||||
* -------------------------------------------------------------------------- */
|
||||
.cad-qtable {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
bottom: calc(var(--cad-status-height) + 8px);
|
||||
left: calc(var(--cad-panel-width) + (100vw - var(--cad-panel-width)) / 2);
|
||||
transform: translateX(-50%);
|
||||
max-width: min(720px, calc(100vw - var(--cad-panel-width) - 24px));
|
||||
border: 1px solid #465565;
|
||||
border-radius: 6px;
|
||||
background: #202b37f2;
|
||||
box-shadow: 0 4px 16px #0009;
|
||||
color: #dce6f2;
|
||||
}
|
||||
|
||||
.cad-qtable__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
.cad-qtable__title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.cad-qtable__title strong {
|
||||
color: #f7fbff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.cad-qtable__title span {
|
||||
overflow: hidden;
|
||||
color: #91a2b5;
|
||||
font-size: 11px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cad-qtable__status {
|
||||
padding: 1px 7px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
}
|
||||
.cad-qtable__status[data-confirmed="true"] {
|
||||
background: #1f4d33;
|
||||
color: #7fdca4;
|
||||
}
|
||||
.cad-qtable__status[data-confirmed="false"] {
|
||||
background: #4a3a1c;
|
||||
color: #e6c07a;
|
||||
}
|
||||
.cad-qtable__nav,
|
||||
.cad-qtable__collapse {
|
||||
width: 26px;
|
||||
height: 24px;
|
||||
border: 1px solid #40505f;
|
||||
border-radius: 4px;
|
||||
background: #2a3745;
|
||||
color: #cdd8e4;
|
||||
font-size: 13px;
|
||||
}
|
||||
.cad-qtable__nav:hover:not(:disabled),
|
||||
.cad-qtable__collapse:hover {
|
||||
background: #31516b;
|
||||
}
|
||||
.cad-qtable__nav:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.cad-qtable__body {
|
||||
padding: 0 8px 8px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.cad-qtable table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.cad-qtable th,
|
||||
.cad-qtable td {
|
||||
padding: 0;
|
||||
border: 1px solid #40505f;
|
||||
font-size: 11px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.cad-qtable__label {
|
||||
padding: 2px 5px;
|
||||
color: #a9bccd;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
background: #26333f;
|
||||
}
|
||||
.cad-qtable__label--vertical {
|
||||
width: 18px;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.cad-qtable__value input {
|
||||
width: 48px;
|
||||
padding: 3px 2px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #eaf1f8;
|
||||
font: inherit;
|
||||
text-align: center;
|
||||
}
|
||||
.cad-qtable__value input:focus {
|
||||
outline: 2px solid #4ca6e8;
|
||||
outline-offset: -2px;
|
||||
background: #12324a;
|
||||
}
|
||||
.cad-qtable__value--derived input {
|
||||
color: #8fa3b5;
|
||||
background: #1a242e;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
:root {
|
||||
--cad-panel-width: 200px;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import './App.css';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { QuantityPanel } from './components/QuantityPanel.tsx';
|
||||
import { Toolbar } from './components/Toolbar.tsx';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="cad-app">
|
||||
<Toolbar />
|
||||
<QuantityPanel />
|
||||
<ToastContainer position="bottom-right" theme="light" />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,6 +38,21 @@ export enum HtmlEvent {
|
||||
DRAWING_CHANGED = 'DRAWING_CHANGED',
|
||||
}
|
||||
|
||||
/** 부모(B07 페이지)가 도면과 함께 넘기는 설계 컨텍스트 (수량 패널 표시용). */
|
||||
export interface DesignMeta {
|
||||
kind: 'cross' | 'longitudinal';
|
||||
/** 패널 제목 (측점 라벨, 예: "2+0.0" 또는 "종단도 전체") */
|
||||
title: string;
|
||||
/** 측점 부가 정보 (예: "STA.0+050.000") */
|
||||
info: string;
|
||||
confirmed: boolean;
|
||||
/** 수량 산출표 값 (횡단도만). 미산정 항목은 null. */
|
||||
quantityTable: Record<string, number | null> | null;
|
||||
/** 이전/다음 도면 존재 여부 (경계에서 버튼 비활성화) */
|
||||
hasPrev: boolean;
|
||||
hasNext: boolean;
|
||||
}
|
||||
|
||||
export interface StateMetaData {
|
||||
instructions: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
import { type FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { HtmlEvent } from '../App.types';
|
||||
import {
|
||||
notifyDrawingChangedByTable,
|
||||
requestDrawingNavigation,
|
||||
} from '../integration/aislo-drawing-bridge';
|
||||
import { getDesignMeta, setDesignQuantityTable } from '../state';
|
||||
|
||||
/** 편집 가능한 항목 키 (cut/fill 은 지반고·계획고에서 파생되는 읽기전용). */
|
||||
const EDITABLE_KEYS = [
|
||||
'ground',
|
||||
'planned',
|
||||
'cut_soil',
|
||||
'cut_soft_rock',
|
||||
'cut_rock',
|
||||
'tree_removal',
|
||||
'fill_slope_protection',
|
||||
'cut_slope_protection',
|
||||
'ditch_soil',
|
||||
'ditch_soft_rock',
|
||||
'ditch_rock',
|
||||
'embankment',
|
||||
'grubbing',
|
||||
'surface_grading',
|
||||
] as const;
|
||||
|
||||
function round2(value: number): number {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
|
||||
function formatValue(value: number | null | undefined): string {
|
||||
return typeof value === 'number' && Number.isFinite(value) ? String(round2(value)) : '';
|
||||
}
|
||||
|
||||
function parseValue(raw: string): number | null {
|
||||
const text = raw.trim();
|
||||
if (!text) return null;
|
||||
const parsed = Number(text);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
/** 편집 값 문자열 → 저장용 수치 테이블 (파생 cut/fill 포함) */
|
||||
function toNumericTable(values: Record<string, string>): Record<string, number | null> {
|
||||
const table: Record<string, number | null> = {};
|
||||
for (const key of EDITABLE_KEYS) table[key] = parseValue(values[key] ?? '');
|
||||
const ground = table.ground;
|
||||
const planned = table.planned;
|
||||
if (typeof ground === 'number' && typeof planned === 'number') {
|
||||
table.cut = Math.max(ground - planned, 0);
|
||||
table.fill = Math.max(planned - ground, 0);
|
||||
} else {
|
||||
table.cut = null;
|
||||
table.fill = null;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
interface ValueInputProps {
|
||||
fieldKey: (typeof EDITABLE_KEYS)[number];
|
||||
colSpan: number;
|
||||
values: Record<string, string>;
|
||||
onEdit: (key: string, value: string) => void;
|
||||
}
|
||||
|
||||
const ValueInput: FC<ValueInputProps> = ({ fieldKey, colSpan, values, onEdit }) => (
|
||||
<td className="cad-qtable__value" colSpan={colSpan}>
|
||||
<input
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
autoComplete="off"
|
||||
value={values[fieldKey] ?? ''}
|
||||
onChange={(event) => onEdit(fieldKey, event.target.value)}
|
||||
/>
|
||||
</td>
|
||||
);
|
||||
|
||||
const DerivedCell: FC<{ value: string }> = ({ value }) => (
|
||||
<td className="cad-qtable__value cad-qtable__value--derived" colSpan={1}>
|
||||
<input type="text" readOnly value={value} tabIndex={-1} />
|
||||
</td>
|
||||
);
|
||||
|
||||
/**
|
||||
* 횡단도 수량 산출표 (CAD 화면 내부, 하단 중심 접이식 패널).
|
||||
* 헤더(제목·측점정보·확정상태·이전/다음)는 접어도 항상 보인다.
|
||||
*/
|
||||
export const QuantityPanel: FC = () => {
|
||||
const [meta, setMeta] = useState(getDesignMeta());
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [values, setValues] = useState<Record<string, string>>({});
|
||||
const identityRef = useRef<string>('');
|
||||
|
||||
const refresh = useCallback(() => setMeta(getDesignMeta()), []);
|
||||
useEffect(() => {
|
||||
window.addEventListener(HtmlEvent.UPDATE_STATE, refresh);
|
||||
return () => window.removeEventListener(HtmlEvent.UPDATE_STATE, refresh);
|
||||
}, [refresh]);
|
||||
|
||||
// 새 도면(측점)으로 바뀌면 입력값을 원본값으로 초기화한다.
|
||||
const identity = meta ? `${meta.kind}:${meta.title}` : '';
|
||||
useEffect(() => {
|
||||
if (!meta) return;
|
||||
if (identityRef.current === identity) return;
|
||||
identityRef.current = identity;
|
||||
const next: Record<string, string> = {};
|
||||
for (const key of EDITABLE_KEYS) next[key] = formatValue(meta.quantityTable?.[key]);
|
||||
setValues(next);
|
||||
}, [identity, meta]);
|
||||
|
||||
if (!meta) return null;
|
||||
|
||||
const handleEdit = (key: string, value: string) => {
|
||||
const nextValues = { ...values, [key]: value };
|
||||
setValues(nextValues);
|
||||
setDesignQuantityTable(toNumericTable(nextValues));
|
||||
notifyDrawingChangedByTable();
|
||||
};
|
||||
|
||||
const ground = parseValue(values.ground ?? '');
|
||||
const planned = parseValue(values.planned ?? '');
|
||||
const cutText =
|
||||
ground !== null && planned !== null ? formatValue(Math.max(ground - planned, 0)) : '';
|
||||
const fillText =
|
||||
ground !== null && planned !== null ? formatValue(Math.max(planned - ground, 0)) : '';
|
||||
|
||||
return (
|
||||
<section className="cad-qtable controls" data-collapsed={collapsed}>
|
||||
<header className="cad-qtable__header">
|
||||
<button
|
||||
type="button"
|
||||
className="cad-qtable__nav"
|
||||
disabled={!meta.hasPrev}
|
||||
title="이전 도면"
|
||||
onClick={() => requestDrawingNavigation('prev')}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<div className="cad-qtable__title">
|
||||
<strong>{meta.title}</strong>
|
||||
{meta.info && <span>{meta.info}</span>}
|
||||
<em
|
||||
className="cad-qtable__status"
|
||||
data-confirmed={meta.confirmed}
|
||||
title={meta.confirmed ? '설계 확정됨' : '미확정'}
|
||||
>
|
||||
{meta.confirmed ? '확정' : '미확정'}
|
||||
</em>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="cad-qtable__nav"
|
||||
disabled={!meta.hasNext}
|
||||
title="다음 도면"
|
||||
onClick={() => requestDrawingNavigation('next')}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
{meta.kind === 'cross' && (
|
||||
<button
|
||||
type="button"
|
||||
className="cad-qtable__collapse"
|
||||
title={collapsed ? '표 펼치기' : '표 접기'}
|
||||
onClick={() => setCollapsed((value) => !value)}
|
||||
>
|
||||
{collapsed ? '▲' : '▼'}
|
||||
</button>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{meta.kind === 'cross' && !collapsed && (
|
||||
<div className="cad-qtable__body">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
지반고
|
||||
</th>
|
||||
<ValueInput fieldKey="ground" colSpan={1} values={values} onEdit={handleEdit} />
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
계획고
|
||||
</th>
|
||||
<ValueInput fieldKey="planned" colSpan={1} values={values} onEdit={handleEdit} />
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
절토고
|
||||
</th>
|
||||
<DerivedCell value={cutText} />
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
성토고
|
||||
</th>
|
||||
<DerivedCell value={fillText} />
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="cad-qtable__label cad-qtable__label--vertical" rowSpan={3}>
|
||||
흙깎기
|
||||
</th>
|
||||
<th className="cad-qtable__label">토사</th>
|
||||
<ValueInput fieldKey="cut_soil" colSpan={2} values={values} onEdit={handleEdit} />
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
지장목제거
|
||||
</th>
|
||||
<ValueInput
|
||||
fieldKey="tree_removal"
|
||||
colSpan={2}
|
||||
values={values}
|
||||
onEdit={handleEdit}
|
||||
/>
|
||||
<th className="cad-qtable__label cad-qtable__label--vertical" rowSpan={3}>
|
||||
옆도랑파기
|
||||
</th>
|
||||
<th className="cad-qtable__label">토사</th>
|
||||
<ValueInput fieldKey="ditch_soil" colSpan={2} values={values} onEdit={handleEdit} />
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="cad-qtable__label">연암</th>
|
||||
<ValueInput
|
||||
fieldKey="cut_soft_rock"
|
||||
colSpan={2}
|
||||
values={values}
|
||||
onEdit={handleEdit}
|
||||
/>
|
||||
<th className="cad-qtable__label cad-qtable__label--vertical" rowSpan={2}>
|
||||
비탈보호공
|
||||
</th>
|
||||
<th className="cad-qtable__label">성토면</th>
|
||||
<ValueInput
|
||||
fieldKey="fill_slope_protection"
|
||||
colSpan={2}
|
||||
values={values}
|
||||
onEdit={handleEdit}
|
||||
/>
|
||||
<th className="cad-qtable__label">연암</th>
|
||||
<ValueInput
|
||||
fieldKey="ditch_soft_rock"
|
||||
colSpan={2}
|
||||
values={values}
|
||||
onEdit={handleEdit}
|
||||
/>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="cad-qtable__label">보통암</th>
|
||||
<ValueInput fieldKey="cut_rock" colSpan={2} values={values} onEdit={handleEdit} />
|
||||
<th className="cad-qtable__label">절토면</th>
|
||||
<ValueInput
|
||||
fieldKey="cut_slope_protection"
|
||||
colSpan={2}
|
||||
values={values}
|
||||
onEdit={handleEdit}
|
||||
/>
|
||||
<th className="cad-qtable__label">보통암</th>
|
||||
<ValueInput fieldKey="ditch_rock" colSpan={2} values={values} onEdit={handleEdit} />
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
흙쌓기
|
||||
</th>
|
||||
<ValueInput fieldKey="embankment" colSpan={2} values={values} onEdit={handleEdit} />
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
제근
|
||||
</th>
|
||||
<ValueInput fieldKey="grubbing" colSpan={2} values={values} onEdit={handleEdit} />
|
||||
<th className="cad-qtable__label" colSpan={2}>
|
||||
노면고르기
|
||||
</th>
|
||||
<ValueInput
|
||||
fieldKey="surface_grading"
|
||||
colSpan={2}
|
||||
values={values}
|
||||
onEdit={handleEdit}
|
||||
/>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -28,6 +28,7 @@ export enum StateVariable {
|
||||
activeLineWidth = 'activeLineWidth',
|
||||
activeLineDash = 'activeLineDash',
|
||||
activeTextStyle = 'activeTextStyle',
|
||||
designMeta = 'designMeta',
|
||||
layers = 'layers',
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { JsonDrawingFileSerialized } from '../helpers/import-export-handlers/export-entities-to-json.ts';
|
||||
import { exportEntitiesAndLayersToJsonString } from '../helpers/import-export-handlers/export-entities-to-json.ts';
|
||||
import { getEntitiesAndLayersFromJsonObject } from '../helpers/import-export-handlers/import-entities-from-json.ts';
|
||||
import { HtmlEvent } from '../App.types.ts';
|
||||
import { type DesignMeta, HtmlEvent } from '../App.types.ts';
|
||||
import {
|
||||
getDesignMeta,
|
||||
getScreenCanvasDrawController,
|
||||
setActiveLayerId,
|
||||
setDesignMeta,
|
||||
setEntities,
|
||||
setLayers,
|
||||
} from '../state.ts';
|
||||
@@ -16,10 +18,12 @@ export const AISLO_DRAWING_ERROR_MESSAGE = 'aislo:b07:drawing-error';
|
||||
export const AISLO_DRAWING_CHANGED_MESSAGE = 'aislo:b07:drawing-changed';
|
||||
export const AISLO_DRAWING_SAVE_REQUEST_MESSAGE = 'aislo:b07:save-request';
|
||||
export const AISLO_DRAWING_SAVE_RESPONSE_MESSAGE = 'aislo:b07:save-response';
|
||||
export const AISLO_DRAWING_NAVIGATE_MESSAGE = 'aislo:b07:navigate';
|
||||
|
||||
interface DrawingLoadMessage {
|
||||
type: typeof AISLO_DRAWING_LOAD_MESSAGE;
|
||||
drawing: JsonDrawingFileSerialized;
|
||||
meta?: DesignMeta | null;
|
||||
}
|
||||
|
||||
interface DrawingSaveRequestMessage {
|
||||
@@ -37,6 +41,16 @@ function notifyParent(type: string, payload: Record<string, unknown> = {}) {
|
||||
window.parent.postMessage({ type, ...payload }, window.location.origin);
|
||||
}
|
||||
|
||||
/** 수량 패널에서 이전/다음 도면으로 이동 요청 (부모가 처리). */
|
||||
export function requestDrawingNavigation(direction: 'prev' | 'next') {
|
||||
notifyParent(AISLO_DRAWING_NAVIGATE_MESSAGE, { direction });
|
||||
}
|
||||
|
||||
/** 수량표 값 편집을 부모에 알린다 (확정 상태 롤백 연동). */
|
||||
export function notifyDrawingChangedByTable() {
|
||||
notifyParent(AISLO_DRAWING_CHANGED_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* B07 parent page와 CAD 앱 사이의 same-origin JSON 경계다.
|
||||
* DXF/DWG 파일이나 파서 객체는 이 경계를 통과하지 않는다.
|
||||
@@ -50,7 +64,9 @@ export function registerAisloDrawingBridge() {
|
||||
const drawing = JSON.parse(
|
||||
await exportEntitiesAndLayersToJsonString()
|
||||
) as JsonDrawingFileSerialized;
|
||||
notifyParent(AISLO_DRAWING_SAVE_RESPONSE_MESSAGE, { drawing });
|
||||
// 편집된 수량표 값을 도면과 함께 부모로 돌려준다.
|
||||
const quantityTable = getDesignMeta()?.quantityTable ?? null;
|
||||
notifyParent(AISLO_DRAWING_SAVE_RESPONSE_MESSAGE, { drawing, quantityTable });
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : 'Unable to serialize drawing';
|
||||
notifyParent(AISLO_DRAWING_ERROR_MESSAGE, { detail });
|
||||
@@ -64,6 +80,8 @@ export function registerAisloDrawingBridge() {
|
||||
setEntities(drawing.entities, false);
|
||||
setLayers(drawing.layers);
|
||||
setActiveLayerId(drawing.layers[0].id);
|
||||
// 설계 컨텍스트(제목·측점정보·확정상태·수량표)를 수량 패널에 반영
|
||||
setDesignMeta(event.data.meta ?? null);
|
||||
getScreenCanvasDrawController().zoomToFitScreen();
|
||||
notifyParent(AISLO_DRAWING_LOADED_MESSAGE);
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { isEqual } from 'es-toolkit';
|
||||
import { toast } from 'react-toastify';
|
||||
import type { Actor, MachineSnapshot } from 'xstate';
|
||||
import {
|
||||
type DesignMeta,
|
||||
type HoverPoint,
|
||||
HtmlEvent,
|
||||
type Layer,
|
||||
@@ -164,6 +165,12 @@ let activeLayerId: string = layers[0].id;
|
||||
let snapEnabled = true;
|
||||
let gridEnabled = false;
|
||||
|
||||
/**
|
||||
* 부모(B07 페이지)에서 넘어온 설계 컨텍스트. 수량 산출 패널이 이 값을 읽어
|
||||
* 제목·측점정보·확정상태·수량표를 렌더한다. null이면 패널을 숨긴다.
|
||||
*/
|
||||
let designMeta: DesignMeta | null = null;
|
||||
|
||||
// getters
|
||||
export const getCanvas = () => canvas;
|
||||
export const getActiveToolActor = () => activeToolActor;
|
||||
@@ -215,6 +222,7 @@ export const getActiveLayerId = (): string => {
|
||||
};
|
||||
export const getSnapEnabled = () => snapEnabled;
|
||||
export const getGridEnabled = () => gridEnabled;
|
||||
export const getDesignMeta = (): DesignMeta | null => designMeta;
|
||||
|
||||
// setters
|
||||
export const setCanvas = (newCanvas: HTMLCanvasElement) => {
|
||||
@@ -392,6 +400,16 @@ export const setGridEnabled = (enabled: boolean) => {
|
||||
gridEnabled = enabled;
|
||||
window.dispatchEvent(new CustomEvent(HtmlEvent.UPDATE_STATE));
|
||||
};
|
||||
export const setDesignMeta = (newMeta: DesignMeta | null) => {
|
||||
designMeta = newMeta;
|
||||
triggerReactUpdate(StateVariable.designMeta);
|
||||
};
|
||||
/** 사용자가 편집한 수량표 전체를 반영하고 확정 상태를 롤백한다 (저장 대상). */
|
||||
export const setDesignQuantityTable = (table: Record<string, number | null>) => {
|
||||
if (!designMeta) return;
|
||||
designMeta = { ...designMeta, quantityTable: table, confirmed: false };
|
||||
triggerReactUpdate(StateVariable.designMeta);
|
||||
};
|
||||
|
||||
// Computed setters
|
||||
export const deleteEntities = (entitiesToDelete: Entity[], trackInUndoStack: boolean): Entity[] => {
|
||||
@@ -414,6 +432,7 @@ const reactStateVariables: StateVariable[] = [
|
||||
StateVariable.activeLineWidth,
|
||||
StateVariable.activeLineDash,
|
||||
StateVariable.activeTextStyle,
|
||||
StateVariable.designMeta,
|
||||
StateVariable.screenZoom,
|
||||
StateVariable.layers,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user