SPipe 데모 v2 (2026-10-30) 서버 배포본
- SPipe_server_2026-10-30.zip 내용을 풀어서 등록 (압축 파일은 올리지 않음) - README에 데모 v2 서버 업데이트 순서 추가, 저장소에 없는 문서 목차 정리 - 패치내역을 docs/로 이동, _worklog.md 추가 - 임시 파일(backend/temp.*)과 API 키가 든 파일은 제외
This commit is contained in:
commit
21ebb16fb7
181 files changed
+57207
No files matched your search
@@ -0,0 +1,111 @@
|
||||
import React from 'react';
|
||||
import { ContextMenu } from './ContextMenu';
|
||||
import { type MapContextMenuEvent, type EditMode, type SimulationResult, type SelectionTarget } from './types';
|
||||
import { type NetworkModel, type InpNode } from './inpModel';
|
||||
import { type CRS } from './crs';
|
||||
import { type MapFocusRequest } from './Map';
|
||||
|
||||
export interface MapContextMenuProps {
|
||||
contextMenu: MapContextMenuEvent;
|
||||
setContextMenu: React.Dispatch<React.SetStateAction<MapContextMenuEvent | null>>;
|
||||
appMode: 'explore' | 'edit' | 'result';
|
||||
setAppMode: React.Dispatch<React.SetStateAction<'explore' | 'edit' | 'result'>>;
|
||||
setEditMode: React.Dispatch<React.SetStateAction<EditMode>>;
|
||||
networkModel: NetworkModel | null;
|
||||
wasmResult: SimulationResult | null;
|
||||
crs: CRS;
|
||||
setSelectedTarget: React.Dispatch<React.SetStateAction<SelectionTarget>>;
|
||||
setActiveTab: React.Dispatch<React.SetStateAction<import('./types').RightTab | null>>;
|
||||
requestMapFocus: (request: Omit<MapFocusRequest, 'id'>) => void;
|
||||
showToast: (msg: string) => void;
|
||||
handleAddNodeAtCoords: (x: number, y: number, nodeType?: InpNode['type']) => void;
|
||||
handleCopyText: (text: string, label: string) => void;
|
||||
handleZoomToLayer: (layerId: string) => void;
|
||||
handleDeleteSelected: () => void;
|
||||
handleRecalculateLength: (pipeId: string) => void;
|
||||
handleReversePipe: (pipeId: string) => void;
|
||||
handleSplitPipe: (pipeId: string) => void;
|
||||
}
|
||||
|
||||
/** Right-click menu on the map (node/link/empty-space actions). */
|
||||
export const MapContextMenu: React.FC<MapContextMenuProps> = ({
|
||||
contextMenu,
|
||||
setContextMenu,
|
||||
appMode,
|
||||
setAppMode,
|
||||
setEditMode,
|
||||
networkModel,
|
||||
wasmResult,
|
||||
crs,
|
||||
setSelectedTarget,
|
||||
setActiveTab,
|
||||
requestMapFocus,
|
||||
showToast,
|
||||
handleAddNodeAtCoords,
|
||||
handleCopyText,
|
||||
handleZoomToLayer,
|
||||
handleDeleteSelected,
|
||||
handleRecalculateLength,
|
||||
handleReversePipe,
|
||||
handleSplitPipe,
|
||||
}) => (
|
||||
<ContextMenu
|
||||
crs={crs}
|
||||
event={contextMenu}
|
||||
onClose={() => setContextMenu(null)}
|
||||
onCopyText={handleCopyText}
|
||||
onAddNodeAtCoords={(x, y) => {
|
||||
handleAddNodeAtCoords(x, y, 'JUNCTION');
|
||||
setContextMenu(null);
|
||||
}}
|
||||
onFitBounds={() => {
|
||||
showToast('전체 화면을 표시합니다.');
|
||||
}}
|
||||
onPanTo={(lngLat) => {
|
||||
requestMapFocus({ lngLat, zoom: 17 });
|
||||
showToast(`좌표 (${lngLat[0].toFixed(4)}, ${lngLat[1].toFixed(4)})로 이동했습니다.`);
|
||||
}}
|
||||
onInspectTarget={(target) => {
|
||||
if (target.type === 'node' || target.type === 'link') {
|
||||
setSelectedTarget(target);
|
||||
}
|
||||
setActiveTab('inspector');
|
||||
}}
|
||||
onViewResults={(target) => {
|
||||
setSelectedTarget(target);
|
||||
setActiveTab('results');
|
||||
}}
|
||||
onStartPipeFromNode={(nodeId) => {
|
||||
if (appMode !== 'edit') setAppMode('edit');
|
||||
setEditMode('add-pipe');
|
||||
setSelectedTarget({ type: 'node', id: nodeId });
|
||||
}}
|
||||
onSplitPipe={handleSplitPipe}
|
||||
onSelectConnectedLinks={(nodeId) => {
|
||||
if (!networkModel) return;
|
||||
const connectedPipes = Object.values(networkModel.pipes).filter(
|
||||
(p) => p.node1 === nodeId || p.node2 === nodeId
|
||||
);
|
||||
if (connectedPipes.length > 0) {
|
||||
setSelectedTarget({ type: 'link', id: connectedPipes[0].id });
|
||||
setActiveTab('inspector');
|
||||
showToast(`연결 관로 [${connectedPipes[0].id}] 선택됨 (총 ${connectedPipes.length}개 관로)`);
|
||||
}
|
||||
}}
|
||||
onSelectConnectedNodes={(pipeId) => {
|
||||
if (!networkModel) return;
|
||||
const pipe = networkModel.pipes[pipeId];
|
||||
if (pipe) {
|
||||
setSelectedTarget({ type: 'node', id: pipe.node1 });
|
||||
setActiveTab('inspector');
|
||||
showToast(`시작 절점 [${pipe.node1}] 선택됨 (끝 절점: ${pipe.node2})`);
|
||||
}
|
||||
}}
|
||||
onCalculatePipeLength={handleRecalculateLength}
|
||||
onReversePipe={handleReversePipe}
|
||||
onDeleteNode={handleDeleteSelected}
|
||||
onDeletePipe={handleDeleteSelected}
|
||||
onZoomToLayer={handleZoomToLayer}
|
||||
canRunResults={!!wasmResult}
|
||||
/>
|
||||
);
|
||||
Reference in new issue
Block a user