@principal-ai/file-city-react 0.5.6 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder/cityDataUtils.js +3 -9
- package/dist/components/ArchitectureMapHighlightLayers.d.ts +1 -2
- package/dist/components/ArchitectureMapHighlightLayers.d.ts.map +1 -1
- package/dist/components/ArchitectureMapHighlightLayers.js +144 -187
- package/dist/components/CityViewWithReactFlow.js +72 -125
- package/dist/components/FileCity3D/FileCity3D.d.ts +1 -1
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +64 -129
- package/dist/components/FileCity3D/index.js +1 -6
- package/dist/hooks/useCodeCityData.js +12 -15
- package/dist/index.js +11 -34
- package/dist/render/client/drawLayeredBuildings.js +9 -16
- package/dist/stories/sample-data.js +7 -11
- package/dist/stories/stress-test-data.js +6 -11
- package/dist/types/react-types.js +1 -2
- package/dist/utils/fileColorHighlightLayers.js +8 -13
- package/dist/utils/fileColorOverrides.js +2 -6
- package/dist/utils/fileTypeIcons.js +5 -10
- package/dist/utils/lucideIconConverter.js +5 -12
- package/package.json +3 -1
- package/dist/config/files.json +0 -1012
|
@@ -1,45 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ArchitectureMapHighlightLayers = void 0;
|
|
37
|
-
const react_1 = __importStar(require("react"));
|
|
38
|
-
const industry_theme_1 = require("@principal-ade/industry-theme");
|
|
39
|
-
const cityDataUtils_1 = require("../builder/cityDataUtils");
|
|
40
|
-
const drawLayeredBuildings_1 = require("../render/client/drawLayeredBuildings");
|
|
41
|
-
const fileColorHighlightLayers_1 = require("../utils/fileColorHighlightLayers");
|
|
42
|
-
const fileTypeIcons_1 = require("../utils/fileTypeIcons");
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState, useMemo, useCallback } from 'react';
|
|
3
|
+
import { useTheme } from '@principal-ade/industry-theme';
|
|
4
|
+
import { filterCityDataForSelectiveRender, filterCityDataForSubdirectory, filterCityDataForMultipleDirectories, } from '../builder/cityDataUtils';
|
|
5
|
+
import { drawLayeredBuildings, drawLayeredDistricts, drawGrid, LayerIndex, } from '../render/client/drawLayeredBuildings';
|
|
6
|
+
import { getDefaultFileColorConfig } from '../utils/fileColorHighlightLayers';
|
|
7
|
+
import { extractIconConfig } from '../utils/fileTypeIcons';
|
|
43
8
|
const DEFAULT_DISPLAY_OPTIONS = {
|
|
44
9
|
showGrid: false,
|
|
45
10
|
showConnections: true,
|
|
@@ -181,25 +146,25 @@ class PathHierarchyLookup {
|
|
|
181
146
|
}
|
|
182
147
|
function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers = [], onLayerToggle, focusDirectory = null, rootDirectoryName, onDirectorySelect, onFileClick, enableZoom = false, zoomToPath = null, onZoomComplete, zoomAnimationSpeed = 0.12, allowZoomToPath = true, fullSize = false, showGrid = false, showFileNames = false, className = '', selectiveRender, canvasBackgroundColor, maxCanvasSize, hoverBorderColor, disableOpacityDimming = true, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls = false, showFileTypeIcons = true, showDirectoryLabels = true, transform = { rotation: 0 }, // Default to no rotation
|
|
183
148
|
onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
184
|
-
const { theme } =
|
|
149
|
+
const { theme } = useTheme();
|
|
185
150
|
// Use theme colors as defaults, with prop overrides
|
|
186
151
|
const resolvedCanvasBackgroundColor = canvasBackgroundColor ?? theme.colors.background;
|
|
187
152
|
const resolvedHoverBorderColor = hoverBorderColor ?? theme.colors.text;
|
|
188
153
|
const resolvedDefaultDirectoryColor = defaultDirectoryColor ?? theme.colors.backgroundSecondary;
|
|
189
154
|
const resolvedDefaultBuildingColor = defaultBuildingColor ?? theme.colors.muted;
|
|
190
|
-
const canvasRef =
|
|
191
|
-
const containerRef =
|
|
155
|
+
const canvasRef = useRef(null);
|
|
156
|
+
const containerRef = useRef(null);
|
|
192
157
|
// Extract icon configuration from file color config
|
|
193
|
-
const iconMap =
|
|
194
|
-
const colorConfig =
|
|
195
|
-
return
|
|
158
|
+
const iconMap = useMemo(() => {
|
|
159
|
+
const colorConfig = getDefaultFileColorConfig();
|
|
160
|
+
return extractIconConfig(colorConfig);
|
|
196
161
|
}, []);
|
|
197
|
-
const [interactionState, setInteractionState] =
|
|
162
|
+
const [interactionState, setInteractionState] = useState({
|
|
198
163
|
hoveredDistrict: null,
|
|
199
164
|
hoveredBuilding: null,
|
|
200
165
|
mousePos: { x: 0, y: 0 },
|
|
201
166
|
});
|
|
202
|
-
const [zoomState, setZoomState] =
|
|
167
|
+
const [zoomState, setZoomState] = useState({
|
|
203
168
|
scale: 1,
|
|
204
169
|
offsetX: 0,
|
|
205
170
|
offsetY: 0,
|
|
@@ -208,19 +173,19 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
208
173
|
hasMouseMoved: false,
|
|
209
174
|
});
|
|
210
175
|
// Target zoom state for animated transitions
|
|
211
|
-
const [targetZoom, setTargetZoom] =
|
|
176
|
+
const [targetZoom, setTargetZoom] = useState(null);
|
|
212
177
|
// Stable zoom scale - only updates when animation completes or user stops zooming
|
|
213
178
|
// Used for expensive calculations that shouldn't run every frame
|
|
214
|
-
const [stableZoomScale, setStableZoomScale] =
|
|
215
|
-
const stableZoomTimeoutRef =
|
|
179
|
+
const [stableZoomScale, setStableZoomScale] = useState(1);
|
|
180
|
+
const stableZoomTimeoutRef = useRef(null);
|
|
216
181
|
// Track if we're currently animating
|
|
217
182
|
const isAnimating = targetZoom !== null;
|
|
218
183
|
// Track the last zoomToPath to detect changes
|
|
219
|
-
const lastZoomToPathRef =
|
|
184
|
+
const lastZoomToPathRef = useRef(null);
|
|
220
185
|
// Throttle ref for hover updates (improves performance with large datasets)
|
|
221
|
-
const lastHoverUpdateRef =
|
|
186
|
+
const lastHoverUpdateRef = useRef(0);
|
|
222
187
|
const HOVER_THROTTLE_MS = 16; // ~60fps max for hover updates
|
|
223
|
-
|
|
188
|
+
useEffect(() => {
|
|
224
189
|
// Reset user interaction state when enableZoom is disabled
|
|
225
190
|
if (!enableZoom) {
|
|
226
191
|
setZoomState(prev => ({
|
|
@@ -241,7 +206,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
241
206
|
}
|
|
242
207
|
}, [enableZoom, allowZoomToPath]);
|
|
243
208
|
// Animation loop for smooth zoom transitions
|
|
244
|
-
|
|
209
|
+
useEffect(() => {
|
|
245
210
|
if (!targetZoom)
|
|
246
211
|
return;
|
|
247
212
|
const animate = () => {
|
|
@@ -279,7 +244,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
279
244
|
}, [targetZoom, zoomState, zoomAnimationSpeed, onZoomComplete]);
|
|
280
245
|
// Update stable zoom scale with debouncing
|
|
281
246
|
// This ensures expensive calculations only run when zoom stabilizes
|
|
282
|
-
|
|
247
|
+
useEffect(() => {
|
|
283
248
|
// Clear any pending timeout
|
|
284
249
|
if (stableZoomTimeoutRef.current) {
|
|
285
250
|
clearTimeout(stableZoomTimeoutRef.current);
|
|
@@ -299,13 +264,13 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
299
264
|
};
|
|
300
265
|
}, [zoomState.scale, isAnimating]);
|
|
301
266
|
// Immediately update stable zoom when animation completes
|
|
302
|
-
|
|
267
|
+
useEffect(() => {
|
|
303
268
|
if (!isAnimating && targetZoom === null) {
|
|
304
269
|
// Animation just completed, update stable zoom immediately
|
|
305
270
|
setStableZoomScale(zoomState.scale);
|
|
306
271
|
}
|
|
307
272
|
}, [isAnimating, targetZoom, zoomState.scale]);
|
|
308
|
-
const [hitTestCache, setHitTestCache] =
|
|
273
|
+
const [hitTestCache, setHitTestCache] = useState(null);
|
|
309
274
|
const calculateCanvasResolution = (fileCount, _cityBounds) => {
|
|
310
275
|
const minSize = 400;
|
|
311
276
|
const scaleFactor = Math.sqrt(fileCount / 5);
|
|
@@ -315,12 +280,12 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
315
280
|
}
|
|
316
281
|
return { width: resolution, height: resolution };
|
|
317
282
|
};
|
|
318
|
-
const [canvasSize, setCanvasSize] =
|
|
319
|
-
const [displayOptions] =
|
|
283
|
+
const [canvasSize, setCanvasSize] = useState(() => calculateCanvasResolution(cityData?.buildings?.length || 10, cityData?.bounds));
|
|
284
|
+
const [displayOptions] = useState({
|
|
320
285
|
...DEFAULT_DISPLAY_OPTIONS,
|
|
321
286
|
showGrid,
|
|
322
287
|
});
|
|
323
|
-
const filteredCityData =
|
|
288
|
+
const filteredCityData = useMemo(() => {
|
|
324
289
|
if (!cityData) {
|
|
325
290
|
return undefined;
|
|
326
291
|
}
|
|
@@ -329,23 +294,23 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
329
294
|
const autoCenter = subdirectoryMode.autoCenter === true;
|
|
330
295
|
// Use new multi-filter function if filters are provided
|
|
331
296
|
if (subdirectoryMode.filters && subdirectoryMode.filters.length > 0) {
|
|
332
|
-
processedData =
|
|
297
|
+
processedData = filterCityDataForMultipleDirectories(cityData, subdirectoryMode.filters, autoCenter, subdirectoryMode.combineMode || 'union');
|
|
333
298
|
}
|
|
334
299
|
else if (subdirectoryMode.rootPath) {
|
|
335
300
|
// Fallback to single path for backward compatibility
|
|
336
|
-
processedData =
|
|
301
|
+
processedData = filterCityDataForSubdirectory(cityData, subdirectoryMode.rootPath, autoCenter);
|
|
337
302
|
}
|
|
338
303
|
}
|
|
339
|
-
return
|
|
304
|
+
return filterCityDataForSelectiveRender(processedData, selectiveRender);
|
|
340
305
|
}, [cityData, selectiveRender, subdirectoryMode]);
|
|
341
|
-
const canvasSizingData =
|
|
306
|
+
const canvasSizingData = useMemo(() => {
|
|
342
307
|
if (subdirectoryMode?.enabled && subdirectoryMode.autoCenter !== true && cityData) {
|
|
343
308
|
return cityData;
|
|
344
309
|
}
|
|
345
310
|
return filteredCityData;
|
|
346
311
|
}, [subdirectoryMode?.enabled, subdirectoryMode?.autoCenter, cityData, filteredCityData]);
|
|
347
312
|
// Handle zoomToPath changes - calculate target zoom to frame the specified path
|
|
348
|
-
|
|
313
|
+
useEffect(() => {
|
|
349
314
|
// Skip if programmatic zoom is not allowed or path hasn't changed
|
|
350
315
|
if (!allowZoomToPath || zoomToPath === lastZoomToPathRef.current) {
|
|
351
316
|
return;
|
|
@@ -435,7 +400,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
435
400
|
displayOptions.padding,
|
|
436
401
|
]);
|
|
437
402
|
// Build hit test cache with spatial indexing
|
|
438
|
-
const buildHitTestCache =
|
|
403
|
+
const buildHitTestCache = useCallback((cityData, scale, offsetX, offsetZ, zoomState, abstractedPaths) => {
|
|
439
404
|
const spatialGrid = new SpatialGrid(cityData.bounds);
|
|
440
405
|
const pathLookup = new PathHierarchyLookup(abstractedPaths);
|
|
441
406
|
// Only add visible buildings to spatial grid
|
|
@@ -474,19 +439,19 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
474
439
|
};
|
|
475
440
|
}, []);
|
|
476
441
|
// Update canvas size when city data changes
|
|
477
|
-
|
|
442
|
+
useEffect(() => {
|
|
478
443
|
if (canvasSizingData) {
|
|
479
444
|
const newSize = calculateCanvasResolution(canvasSizingData.buildings.length, canvasSizingData.bounds);
|
|
480
445
|
setCanvasSize(newSize);
|
|
481
446
|
}
|
|
482
447
|
}, [canvasSizingData, subdirectoryMode]);
|
|
483
448
|
// Separate stable and dynamic layers for performance optimization
|
|
484
|
-
const stableLayers =
|
|
485
|
-
const dynamicLayers =
|
|
449
|
+
const stableLayers = useMemo(() => highlightLayers.filter(layer => layer.dynamic !== true), [highlightLayers]);
|
|
450
|
+
const dynamicLayers = useMemo(() => highlightLayers.filter(layer => layer.dynamic === true), [highlightLayers]);
|
|
486
451
|
// Combine all layers for rendering (moved up so abstractionLayer can use it)
|
|
487
|
-
const allLayersWithoutAbstraction =
|
|
452
|
+
const allLayersWithoutAbstraction = useMemo(() => [...stableLayers, ...dynamicLayers], [stableLayers, dynamicLayers]);
|
|
488
453
|
// Calculate abstracted directories based on current zoom using tree structure
|
|
489
|
-
const abstractionLayer =
|
|
454
|
+
const abstractionLayer = useMemo(() => {
|
|
490
455
|
// Find the abstraction layer in our highlight layers
|
|
491
456
|
const abstractionLayerDef = highlightLayers.find(layer => layer.id === 'directory-abstraction' && layer.abstractionLayer);
|
|
492
457
|
if (!abstractionLayerDef || !abstractionLayerDef.enabled || !filteredCityData) {
|
|
@@ -695,7 +660,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
695
660
|
allLayersWithoutAbstraction,
|
|
696
661
|
]);
|
|
697
662
|
// Combine all layers for rendering, including calculated abstraction layer
|
|
698
|
-
const allLayers =
|
|
663
|
+
const allLayers = useMemo(() => {
|
|
699
664
|
const layers = [...stableLayers, ...dynamicLayers];
|
|
700
665
|
// Replace abstraction layer with calculated one if it exists
|
|
701
666
|
if (abstractionLayer) {
|
|
@@ -707,9 +672,9 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
707
672
|
return layers;
|
|
708
673
|
}, [stableLayers, dynamicLayers, abstractionLayer]);
|
|
709
674
|
// Memoize layer index for O(1) path lookups - only rebuilds when layers change
|
|
710
|
-
const layerIndex =
|
|
675
|
+
const layerIndex = useMemo(() => new LayerIndex(allLayers), [allLayers]);
|
|
711
676
|
// Memoize abstracted paths lookup - only recalculates when abstraction layer changes
|
|
712
|
-
const { abstractedPathsSet, abstractedPathLookup } =
|
|
677
|
+
const { abstractedPathsSet, abstractedPathLookup } = useMemo(() => {
|
|
713
678
|
const pathsSet = new Set();
|
|
714
679
|
const abstractionLayerDef = allLayers.find(l => l.id === 'directory-abstraction');
|
|
715
680
|
if (abstractionLayerDef && abstractionLayerDef.enabled) {
|
|
@@ -725,7 +690,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
725
690
|
};
|
|
726
691
|
}, [allLayers]);
|
|
727
692
|
// Memoize visible districts - only recalculates when data or abstraction changes, NOT on hover
|
|
728
|
-
const visibleDistrictsMemo =
|
|
693
|
+
const visibleDistrictsMemo = useMemo(() => {
|
|
729
694
|
if (!filteredCityData)
|
|
730
695
|
return [];
|
|
731
696
|
let districts = abstractedPathLookup.size > 0
|
|
@@ -759,7 +724,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
759
724
|
return districts;
|
|
760
725
|
}, [filteredCityData, abstractedPathLookup, abstractedPathsSet]);
|
|
761
726
|
// Memoize visible buildings - only recalculates when data or abstraction changes, NOT on hover
|
|
762
|
-
const visibleBuildingsMemo =
|
|
727
|
+
const visibleBuildingsMemo = useMemo(() => {
|
|
763
728
|
if (!filteredCityData)
|
|
764
729
|
return [];
|
|
765
730
|
return abstractedPathLookup.size > 0
|
|
@@ -773,7 +738,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
773
738
|
// 1. The spatial grid only depends on buildings/districts and abstraction
|
|
774
739
|
// 2. performHitTest calculates coordinates using the current zoomState directly
|
|
775
740
|
// 3. This avoids expensive cache rebuilds during zoom animation
|
|
776
|
-
|
|
741
|
+
useEffect(() => {
|
|
777
742
|
if (!filteredCityData)
|
|
778
743
|
return;
|
|
779
744
|
const width = canvasRef.current?.clientWidth || canvasSize.width;
|
|
@@ -808,7 +773,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
808
773
|
abstractionLayer,
|
|
809
774
|
]);
|
|
810
775
|
// Main canvas drawing with layer-based highlighting
|
|
811
|
-
|
|
776
|
+
useEffect(() => {
|
|
812
777
|
if (!canvasRef.current || !filteredCityData) {
|
|
813
778
|
return;
|
|
814
779
|
}
|
|
@@ -830,7 +795,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
830
795
|
ctx.fillStyle = resolvedCanvasBackgroundColor;
|
|
831
796
|
ctx.fillRect(0, 0, displayWidth, displayHeight);
|
|
832
797
|
if (displayOptions.showGrid) {
|
|
833
|
-
|
|
798
|
+
drawGrid(ctx, displayWidth, displayHeight, displayOptions.gridSize);
|
|
834
799
|
}
|
|
835
800
|
const coordinateSystemData = subdirectoryMode?.enabled && subdirectoryMode.autoCenter !== true && cityData
|
|
836
801
|
? cityData
|
|
@@ -844,10 +809,10 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
844
809
|
});
|
|
845
810
|
// Use memoized visible districts and buildings (pre-filtered, doesn't recalculate on hover)
|
|
846
811
|
// Draw districts with layer support
|
|
847
|
-
|
|
812
|
+
drawLayeredDistricts(ctx, visibleDistrictsMemo, worldToCanvas, scale * zoomState.scale, allLayers, interactionState.hoveredDistrict, fullSize, resolvedDefaultDirectoryColor, filteredCityData.metadata.layoutConfig, abstractedPathsSet, // Pass abstracted paths to skip labels
|
|
848
813
|
showDirectoryLabels, districtBorderRadius, layerIndex);
|
|
849
814
|
// Draw buildings with layer support
|
|
850
|
-
|
|
815
|
+
drawLayeredBuildings(ctx, visibleBuildingsMemo, worldToCanvas, scale * zoomState.scale, allLayers, interactionState.hoveredBuilding, resolvedDefaultBuildingColor, showFileNames, resolvedHoverBorderColor, disableOpacityDimming, showFileTypeIcons, buildingBorderRadius, layerIndex, // Pre-built index for O(1) lookups
|
|
851
816
|
iconMap);
|
|
852
817
|
// Performance monitoring end available for debugging
|
|
853
818
|
// Performance stats available but not logged to reduce console noise
|
|
@@ -885,7 +850,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
885
850
|
iconMap,
|
|
886
851
|
]);
|
|
887
852
|
// Optimized hit testing
|
|
888
|
-
const performHitTest =
|
|
853
|
+
const performHitTest = useCallback((canvasX, canvasY) => {
|
|
889
854
|
if (!hitTestCache || !filteredCityData) {
|
|
890
855
|
return { hoveredBuilding: null, hoveredDistrict: null };
|
|
891
856
|
}
|
|
@@ -953,7 +918,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
953
918
|
]);
|
|
954
919
|
// Mouse event handlers
|
|
955
920
|
// Call onHover callback when interaction state changes
|
|
956
|
-
|
|
921
|
+
useEffect(() => {
|
|
957
922
|
if (onHover && interactionState) {
|
|
958
923
|
const fileTooltip = interactionState.hoveredBuilding
|
|
959
924
|
? {
|
|
@@ -977,7 +942,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
977
942
|
}
|
|
978
943
|
}, [interactionState, onHover]);
|
|
979
944
|
// Helper function to transform mouse coordinates based on rotation/flip
|
|
980
|
-
const transformMouseCoordinates =
|
|
945
|
+
const transformMouseCoordinates = useCallback((x, y, canvasWidth, canvasHeight) => {
|
|
981
946
|
const centerX = canvasWidth / 2;
|
|
982
947
|
const centerY = canvasHeight / 2;
|
|
983
948
|
// Translate to center
|
|
@@ -1006,7 +971,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1006
971
|
transformedY += centerY;
|
|
1007
972
|
return { x: transformedX, y: transformedY };
|
|
1008
973
|
}, [transform]);
|
|
1009
|
-
const handleMouseMoveInternal =
|
|
974
|
+
const handleMouseMoveInternal = useCallback((e) => {
|
|
1010
975
|
if (!canvasRef.current || !containerRef.current || !filteredCityData || zoomState.isDragging)
|
|
1011
976
|
return;
|
|
1012
977
|
// Throttle hover updates to improve performance with large datasets
|
|
@@ -1040,7 +1005,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1040
1005
|
};
|
|
1041
1006
|
});
|
|
1042
1007
|
}, [filteredCityData, zoomState.isDragging, performHitTest, transformMouseCoordinates]);
|
|
1043
|
-
const handleMouseMove =
|
|
1008
|
+
const handleMouseMove = useCallback((e) => {
|
|
1044
1009
|
if (enableZoom && zoomState.isDragging) {
|
|
1045
1010
|
const rawDeltaX = e.clientX - zoomState.lastMousePos.x;
|
|
1046
1011
|
const rawDeltaY = e.clientY - zoomState.lastMousePos.y;
|
|
@@ -1099,7 +1064,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1099
1064
|
}
|
|
1100
1065
|
}
|
|
1101
1066
|
};
|
|
1102
|
-
const handleMouseLeave =
|
|
1067
|
+
const handleMouseLeave = useCallback(() => {
|
|
1103
1068
|
setInteractionState(prev => ({
|
|
1104
1069
|
...prev,
|
|
1105
1070
|
hoveredDistrict: null,
|
|
@@ -1133,10 +1098,10 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1133
1098
|
hasMouseMoved: false,
|
|
1134
1099
|
}));
|
|
1135
1100
|
};
|
|
1136
|
-
const handleMouseUp =
|
|
1101
|
+
const handleMouseUp = useCallback(() => {
|
|
1137
1102
|
setZoomState(prev => ({ ...prev, isDragging: false }));
|
|
1138
1103
|
}, []);
|
|
1139
|
-
const interactionCursor =
|
|
1104
|
+
const interactionCursor = useMemo(() => {
|
|
1140
1105
|
if (enableZoom) {
|
|
1141
1106
|
if (zoomState.isDragging) {
|
|
1142
1107
|
return 'grabbing';
|
|
@@ -1157,7 +1122,7 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1157
1122
|
interactionState.hoveredDistrict,
|
|
1158
1123
|
]);
|
|
1159
1124
|
if (!filteredCityData) {
|
|
1160
|
-
return (
|
|
1125
|
+
return (_jsxs("div", { className: className, style: {
|
|
1161
1126
|
width: '100%',
|
|
1162
1127
|
height: '100%',
|
|
1163
1128
|
minHeight: '250px',
|
|
@@ -1168,41 +1133,39 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1168
1133
|
justifyContent: 'center',
|
|
1169
1134
|
position: 'relative',
|
|
1170
1135
|
overflow: 'hidden',
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
minWidth: '300px',
|
|
1203
|
-
} }, "No city data available")));
|
|
1136
|
+
}, children: [_jsx("div", { style: {
|
|
1137
|
+
position: 'absolute',
|
|
1138
|
+
inset: 0,
|
|
1139
|
+
display: 'grid',
|
|
1140
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(80px, 1fr))',
|
|
1141
|
+
gridAutoRows: 'minmax(80px, 1fr)',
|
|
1142
|
+
gap: '20px',
|
|
1143
|
+
padding: '20px',
|
|
1144
|
+
}, children: Array.from({ length: 200 }).map((_, i) => (_jsx("div", { style: {
|
|
1145
|
+
backgroundColor: resolvedDefaultBuildingColor,
|
|
1146
|
+
borderRadius: buildingBorderRadius,
|
|
1147
|
+
opacity: 0.6 + (i % 3) * 0.2,
|
|
1148
|
+
} }, i))) }), _jsx("div", { style: {
|
|
1149
|
+
position: 'absolute',
|
|
1150
|
+
top: '35%',
|
|
1151
|
+
left: '50%',
|
|
1152
|
+
transform: 'translate(-50%, -50%)',
|
|
1153
|
+
zIndex: 1,
|
|
1154
|
+
display: 'flex',
|
|
1155
|
+
alignItems: 'center',
|
|
1156
|
+
justifyContent: 'center',
|
|
1157
|
+
color: theme.colors.textMuted,
|
|
1158
|
+
fontFamily: theme.fonts.body,
|
|
1159
|
+
fontSize: `${theme.fontSizes[3]}px`,
|
|
1160
|
+
fontWeight: theme.fontWeights.medium,
|
|
1161
|
+
backgroundColor: resolvedCanvasBackgroundColor,
|
|
1162
|
+
borderRadius: `${theme.radii[2]}px`,
|
|
1163
|
+
padding: `${theme.space[4]}px ${theme.space[5]}px`,
|
|
1164
|
+
textAlign: 'center',
|
|
1165
|
+
minWidth: '300px',
|
|
1166
|
+
}, children: "No city data available" })] }));
|
|
1204
1167
|
}
|
|
1205
|
-
return (
|
|
1168
|
+
return (_jsxs("div", { ref: containerRef, className: className, style: {
|
|
1206
1169
|
position: 'relative',
|
|
1207
1170
|
width: '100%',
|
|
1208
1171
|
height: '100%',
|
|
@@ -1224,65 +1187,59 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
1224
1187
|
}
|
|
1225
1188
|
return transforms.length > 0 ? transforms.join(' ') : undefined;
|
|
1226
1189
|
})(),
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
left: 0,
|
|
1281
|
-
width: '100%',
|
|
1282
|
-
height: '100%',
|
|
1283
|
-
zIndex: 2,
|
|
1284
|
-
cursor: interactionCursor,
|
|
1285
|
-
}, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onMouseLeave: handleMouseLeave, onWheel: handleWheel })));
|
|
1190
|
+
}, children: [showLayerControls && highlightLayers.length > 0 && (_jsx("div", { style: {
|
|
1191
|
+
position: 'absolute',
|
|
1192
|
+
bottom: `${theme.space[4]}px`,
|
|
1193
|
+
left: `${theme.space[4]}px`,
|
|
1194
|
+
zIndex: 10,
|
|
1195
|
+
display: 'flex',
|
|
1196
|
+
flexDirection: 'column',
|
|
1197
|
+
gap: `${theme.space[2]}px`,
|
|
1198
|
+
}, children: highlightLayers.map(layer => (_jsxs("button", { onClick: () => onLayerToggle?.(layer.id, !layer.enabled), style: {
|
|
1199
|
+
padding: `${theme.space[2]}px ${theme.space[3]}px`,
|
|
1200
|
+
borderRadius: `${theme.radii[2]}px`,
|
|
1201
|
+
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
|
1202
|
+
transition: 'all 0.2s ease',
|
|
1203
|
+
display: 'flex',
|
|
1204
|
+
alignItems: 'center',
|
|
1205
|
+
gap: `${theme.space[2]}px`,
|
|
1206
|
+
fontSize: `${theme.fontSizes[0]}px`,
|
|
1207
|
+
fontWeight: theme.fontWeights.medium,
|
|
1208
|
+
fontFamily: theme.fonts.body,
|
|
1209
|
+
backgroundColor: layer.enabled
|
|
1210
|
+
? theme.colors.backgroundSecondary
|
|
1211
|
+
: theme.colors.background,
|
|
1212
|
+
color: layer.enabled ? theme.colors.text : theme.colors.textSecondary,
|
|
1213
|
+
border: `2px solid ${layer.enabled ? layer.color : theme.colors.border}`,
|
|
1214
|
+
minWidth: '120px',
|
|
1215
|
+
cursor: 'pointer',
|
|
1216
|
+
}, title: `Toggle ${layer.name}`, children: [_jsx("div", { style: {
|
|
1217
|
+
width: '12px',
|
|
1218
|
+
height: '12px',
|
|
1219
|
+
borderRadius: '50%',
|
|
1220
|
+
backgroundColor: layer.color,
|
|
1221
|
+
opacity: layer.enabled ? 1 : 0.4,
|
|
1222
|
+
transition: 'opacity 0.2s ease',
|
|
1223
|
+
} }), _jsx("span", { style: { textAlign: 'left', flex: 1 }, children: layer.name }), layer.items && layer.items.length > 0 && (_jsx("span", { style: {
|
|
1224
|
+
fontSize: `${theme.fontSizes[0]}px`,
|
|
1225
|
+
color: layer.enabled ? theme.colors.textSecondary : theme.colors.textMuted,
|
|
1226
|
+
}, children: layer.items.length }))] }, layer.id))) })), _jsx("canvas", { ref: canvasRef, style: {
|
|
1227
|
+
position: 'absolute',
|
|
1228
|
+
top: 0,
|
|
1229
|
+
left: 0,
|
|
1230
|
+
width: '100%',
|
|
1231
|
+
height: '100%',
|
|
1232
|
+
objectFit: 'contain',
|
|
1233
|
+
zIndex: 1,
|
|
1234
|
+
} }), _jsx("div", { style: {
|
|
1235
|
+
position: 'absolute',
|
|
1236
|
+
top: 0,
|
|
1237
|
+
left: 0,
|
|
1238
|
+
width: '100%',
|
|
1239
|
+
height: '100%',
|
|
1240
|
+
zIndex: 2,
|
|
1241
|
+
cursor: interactionCursor,
|
|
1242
|
+
}, onMouseMove: handleMouseMove, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onClick: handleClick, onMouseLeave: handleMouseLeave, onWheel: handleWheel })] }));
|
|
1286
1243
|
}
|
|
1287
1244
|
function calculateScaleAndOffset(cityData, width, height, padding) {
|
|
1288
1245
|
const cityWidth = cityData.bounds.maxX - cityData.bounds.minX;
|
|
@@ -1298,4 +1255,4 @@ function calculateScaleAndOffset(cityData, width, height, padding) {
|
|
|
1298
1255
|
const offsetZ = (height - scaledCityHeight) / 2;
|
|
1299
1256
|
return { scale, offsetX, offsetZ };
|
|
1300
1257
|
}
|
|
1301
|
-
|
|
1258
|
+
export const ArchitectureMapHighlightLayers = ArchitectureMapHighlightLayersInner;
|