@principal-ai/file-city-react 0.5.1 → 0.5.3

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.
@@ -22,6 +22,7 @@ export interface ArchitectureMapHighlightLayersProps {
22
22
  className?: string;
23
23
  selectiveRender?: SelectiveRenderOptions;
24
24
  canvasBackgroundColor?: string;
25
+ maxCanvasSize?: number;
25
26
  hoverBorderColor?: string;
26
27
  disableOpacityDimming?: boolean;
27
28
  defaultDirectoryColor?: string;
@@ -60,7 +61,7 @@ export interface ArchitectureMapHighlightLayersProps {
60
61
  buildingBorderRadius?: number;
61
62
  districtBorderRadius?: number;
62
63
  }
63
- declare function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers, onLayerToggle, focusDirectory, rootDirectoryName, onDirectorySelect, onFileClick, enableZoom, zoomToPath, onZoomComplete, zoomAnimationSpeed, allowZoomToPath, fullSize, showGrid, showFileNames, className, selectiveRender, canvasBackgroundColor, hoverBorderColor, disableOpacityDimming, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls, showFileTypeIcons, showDirectoryLabels, transform, // Default to no rotation
64
+ declare function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers, onLayerToggle, focusDirectory, rootDirectoryName, onDirectorySelect, onFileClick, enableZoom, zoomToPath, onZoomComplete, zoomAnimationSpeed, allowZoomToPath, fullSize, showGrid, showFileNames, className, selectiveRender, canvasBackgroundColor, maxCanvasSize, hoverBorderColor, disableOpacityDimming, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls, showFileTypeIcons, showDirectoryLabels, transform, // Default to no rotation
64
65
  onHover, buildingBorderRadius, districtBorderRadius, }: ArchitectureMapHighlightLayersProps): React.JSX.Element;
65
66
  export declare const ArchitectureMapHighlightLayers: typeof ArchitectureMapHighlightLayersInner;
66
67
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ArchitectureMapHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/components/ArchitectureMapHighlightLayers.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ArchitectureMapHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/components/ArchitectureMapHighlightLayers.tsx"],"names":[],"mappings":"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"}
@@ -179,7 +179,7 @@ class PathHierarchyLookup {
179
179
  return this.abstractedPaths.has(path);
180
180
  }
181
181
  }
182
- 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, hoverBorderColor, disableOpacityDimming = true, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls = false, showFileTypeIcons = true, showDirectoryLabels = true, transform = { rotation: 0 }, // Default to no rotation
182
+ 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
183
  onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
184
184
  const { theme } = (0, industry_theme_1.useTheme)();
185
185
  // Use theme colors as defaults, with prop overrides
@@ -309,7 +309,10 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
309
309
  const calculateCanvasResolution = (fileCount, _cityBounds) => {
310
310
  const minSize = 400;
311
311
  const scaleFactor = Math.sqrt(fileCount / 5);
312
- const resolution = Math.max(minSize, minSize + scaleFactor * 300);
312
+ let resolution = Math.max(minSize, minSize + scaleFactor * 300);
313
+ if (maxCanvasSize !== undefined) {
314
+ resolution = Math.min(resolution, maxCanvasSize);
315
+ }
313
316
  return { width: resolution, height: resolution };
314
317
  };
315
318
  const [canvasSize, setCanvasSize] = (0, react_1.useState)(() => calculateCanvasResolution(cityData?.buildings?.length || 10, cityData?.bounds));
@@ -815,8 +818,13 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
815
818
  return;
816
819
  }
817
820
  // Performance monitoring start available for debugging
818
- const displayWidth = canvas.clientWidth || canvasSize.width;
819
- const displayHeight = canvas.clientHeight || canvasSize.height;
821
+ let displayWidth = canvas.clientWidth || canvasSize.width;
822
+ let displayHeight = canvas.clientHeight || canvasSize.height;
823
+ // Cap canvas size if maxCanvasSize is set (prevents iOS Safari crashes)
824
+ if (maxCanvasSize !== undefined) {
825
+ displayWidth = Math.min(displayWidth, maxCanvasSize);
826
+ displayHeight = Math.min(displayHeight, maxCanvasSize);
827
+ }
820
828
  canvas.width = displayWidth;
821
829
  canvas.height = displayHeight;
822
830
  ctx.fillStyle = resolvedCanvasBackgroundColor;
@@ -1153,14 +1161,46 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
1153
1161
  width: '100%',
1154
1162
  height: '100%',
1155
1163
  minHeight: '250px',
1156
- backgroundColor: theme.colors.backgroundSecondary,
1157
- borderRadius: `${theme.radii[2]}px`,
1158
- padding: `${theme.space[4]}px`,
1164
+ backgroundColor: resolvedCanvasBackgroundColor,
1165
+ borderRadius: fullSize ? 0 : `${theme.radii[2]}px`,
1159
1166
  display: 'flex',
1160
1167
  alignItems: 'center',
1161
1168
  justifyContent: 'center',
1169
+ position: 'relative',
1170
+ overflow: 'hidden',
1162
1171
  } },
1163
- react_1.default.createElement("div", { style: { color: theme.colors.textMuted, fontFamily: theme.fonts.body } }, "No city data available")));
1172
+ react_1.default.createElement("div", { style: {
1173
+ position: 'absolute',
1174
+ inset: 0,
1175
+ display: 'grid',
1176
+ gridTemplateColumns: 'repeat(auto-fit, minmax(80px, 1fr))',
1177
+ gridAutoRows: 'minmax(80px, 1fr)',
1178
+ gap: '20px',
1179
+ padding: '20px',
1180
+ } }, Array.from({ length: 200 }).map((_, i) => (react_1.default.createElement("div", { key: i, style: {
1181
+ backgroundColor: resolvedDefaultBuildingColor,
1182
+ borderRadius: buildingBorderRadius,
1183
+ opacity: 0.6 + (i % 3) * 0.2,
1184
+ } })))),
1185
+ react_1.default.createElement("div", { style: {
1186
+ position: 'absolute',
1187
+ top: '35%',
1188
+ left: '50%',
1189
+ transform: 'translate(-50%, -50%)',
1190
+ zIndex: 1,
1191
+ display: 'flex',
1192
+ alignItems: 'center',
1193
+ justifyContent: 'center',
1194
+ color: theme.colors.textMuted,
1195
+ fontFamily: theme.fonts.body,
1196
+ fontSize: `${theme.fontSizes[3]}px`,
1197
+ fontWeight: theme.fontWeights.medium,
1198
+ backgroundColor: resolvedCanvasBackgroundColor,
1199
+ borderRadius: `${theme.radii[2]}px`,
1200
+ padding: `${theme.space[4]}px ${theme.space[5]}px`,
1201
+ textAlign: 'center',
1202
+ minWidth: '300px',
1203
+ } }, "No city data available")));
1164
1204
  }
1165
1205
  return (react_1.default.createElement("div", { ref: containerRef, className: className, style: {
1166
1206
  position: 'relative',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "React components for File City visualization",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -66,6 +66,7 @@ export interface ArchitectureMapHighlightLayersProps {
66
66
 
67
67
  // Canvas appearance
68
68
  canvasBackgroundColor?: string;
69
+ maxCanvasSize?: number; // Maximum canvas dimension in pixels (default: 6000, use ~2048 for mobile)
69
70
 
70
71
  // Additional styling options
71
72
  hoverBorderColor?: string;
@@ -305,6 +306,7 @@ function ArchitectureMapHighlightLayersInner({
305
306
  className = '',
306
307
  selectiveRender,
307
308
  canvasBackgroundColor,
309
+ maxCanvasSize,
308
310
  hoverBorderColor,
309
311
  disableOpacityDimming = true,
310
312
  defaultDirectoryColor,
@@ -476,7 +478,11 @@ function ArchitectureMapHighlightLayersInner({
476
478
  ) => {
477
479
  const minSize = 400;
478
480
  const scaleFactor = Math.sqrt(fileCount / 5);
479
- const resolution = Math.max(minSize, minSize + scaleFactor * 300);
481
+ let resolution = Math.max(minSize, minSize + scaleFactor * 300);
482
+
483
+ if (maxCanvasSize !== undefined) {
484
+ resolution = Math.min(resolution, maxCanvasSize);
485
+ }
480
486
 
481
487
  return { width: resolution, height: resolution };
482
488
  };
@@ -1152,8 +1158,14 @@ function ArchitectureMapHighlightLayersInner({
1152
1158
 
1153
1159
  // Performance monitoring start available for debugging
1154
1160
 
1155
- const displayWidth = canvas.clientWidth || canvasSize.width;
1156
- const displayHeight = canvas.clientHeight || canvasSize.height;
1161
+ let displayWidth = canvas.clientWidth || canvasSize.width;
1162
+ let displayHeight = canvas.clientHeight || canvasSize.height;
1163
+
1164
+ // Cap canvas size if maxCanvasSize is set (prevents iOS Safari crashes)
1165
+ if (maxCanvasSize !== undefined) {
1166
+ displayWidth = Math.min(displayWidth, maxCanvasSize);
1167
+ displayHeight = Math.min(displayHeight, maxCanvasSize);
1168
+ }
1157
1169
 
1158
1170
  canvas.width = displayWidth;
1159
1171
  canvas.height = displayHeight;
@@ -1624,15 +1636,61 @@ function ArchitectureMapHighlightLayersInner({
1624
1636
  width: '100%',
1625
1637
  height: '100%',
1626
1638
  minHeight: '250px',
1627
- backgroundColor: theme.colors.backgroundSecondary,
1628
- borderRadius: `${theme.radii[2]}px`,
1629
- padding: `${theme.space[4]}px`,
1639
+ backgroundColor: resolvedCanvasBackgroundColor,
1640
+ borderRadius: fullSize ? 0 : `${theme.radii[2]}px`,
1630
1641
  display: 'flex',
1631
1642
  alignItems: 'center',
1632
1643
  justifyContent: 'center',
1644
+ position: 'relative',
1645
+ overflow: 'hidden',
1633
1646
  }}
1634
1647
  >
1635
- <div style={{ color: theme.colors.textMuted, fontFamily: theme.fonts.body }}>
1648
+ {/* Decorative grid background with integrated text */}
1649
+ <div
1650
+ style={{
1651
+ position: 'absolute',
1652
+ inset: 0,
1653
+ display: 'grid',
1654
+ gridTemplateColumns: 'repeat(auto-fit, minmax(80px, 1fr))',
1655
+ gridAutoRows: 'minmax(80px, 1fr)',
1656
+ gap: '20px',
1657
+ padding: '20px',
1658
+ }}
1659
+ >
1660
+ {/* Generate grid squares to fill space */}
1661
+ {Array.from({ length: 200 }).map((_, i) => (
1662
+ <div
1663
+ key={i}
1664
+ style={{
1665
+ backgroundColor: resolvedDefaultBuildingColor,
1666
+ borderRadius: buildingBorderRadius,
1667
+ opacity: 0.6 + (i % 3) * 0.2,
1668
+ }}
1669
+ />
1670
+ ))}
1671
+ </div>
1672
+ {/* Text overlay centered */}
1673
+ <div
1674
+ style={{
1675
+ position: 'absolute',
1676
+ top: '35%',
1677
+ left: '50%',
1678
+ transform: 'translate(-50%, -50%)',
1679
+ zIndex: 1,
1680
+ display: 'flex',
1681
+ alignItems: 'center',
1682
+ justifyContent: 'center',
1683
+ color: theme.colors.textMuted,
1684
+ fontFamily: theme.fonts.body,
1685
+ fontSize: `${theme.fontSizes[3]}px`,
1686
+ fontWeight: theme.fontWeights.medium,
1687
+ backgroundColor: resolvedCanvasBackgroundColor,
1688
+ borderRadius: `${theme.radii[2]}px`,
1689
+ padding: `${theme.space[4]}px ${theme.space[5]}px`,
1690
+ textAlign: 'center',
1691
+ minWidth: '300px',
1692
+ }}
1693
+ >
1636
1694
  No city data available
1637
1695
  </div>
1638
1696
  </div>
@@ -310,6 +310,14 @@ export const WithBorderRadius: Story = {
310
310
  },
311
311
  };
312
312
 
313
+ // Story showing the loading/empty state when cityData is not available
314
+ export const LoadingState: Story = {
315
+ args: {
316
+ cityData: undefined,
317
+ fullSize: true,
318
+ },
319
+ };
320
+
313
321
  // Story with programmatic zoom only (no user interaction)
314
322
  // Demonstrates allowZoomToPath={true} with enableZoom={false}
315
323
  export const ProgrammaticZoomOnly: Story = {