@cratis/components 0.1.15 → 0.1.17

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.
Files changed (103) hide show
  1. package/dist/cjs/PivotViewer/PivotViewer.css +32 -19
  2. package/dist/cjs/PivotViewer/PivotViewer.js +44 -8
  3. package/dist/cjs/PivotViewer/PivotViewer.js.map +1 -1
  4. package/dist/cjs/PivotViewer/components/PivotCanvas.js +32 -51
  5. package/dist/cjs/PivotViewer/components/PivotCanvas.js.map +1 -1
  6. package/dist/cjs/PivotViewer/components/PivotViewerMain.js +4 -2
  7. package/dist/cjs/PivotViewer/components/PivotViewerMain.js.map +1 -1
  8. package/dist/cjs/PivotViewer/components/Toolbar.js +1 -1
  9. package/dist/cjs/PivotViewer/components/Toolbar.js.map +1 -1
  10. package/dist/cjs/PivotViewer/components/pivot/animation.js +4 -3
  11. package/dist/cjs/PivotViewer/components/pivot/animation.js.map +1 -1
  12. package/dist/cjs/PivotViewer/components/pivot/groups.js +151 -0
  13. package/dist/cjs/PivotViewer/components/pivot/groups.js.map +1 -0
  14. package/dist/cjs/PivotViewer/components/pivot/sprites.js +1 -15
  15. package/dist/cjs/PivotViewer/components/pivot/sprites.js.map +1 -1
  16. package/dist/cjs/PivotViewer/components/pivot/visibility.js +50 -27
  17. package/dist/cjs/PivotViewer/components/pivot/visibility.js.map +1 -1
  18. package/dist/cjs/PivotViewer/engine/layout.js +11 -7
  19. package/dist/cjs/PivotViewer/engine/layout.js.map +1 -1
  20. package/dist/cjs/PivotViewer/hooks/useCurrentFilters.js.map +1 -1
  21. package/dist/cjs/PivotViewer/hooks/usePanning.js +8 -4
  22. package/dist/cjs/PivotViewer/hooks/usePanning.js.map +1 -1
  23. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js +1 -31
  24. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  25. package/dist/cjs/PivotViewer/hooks/useViewModeScrollHandling.js +4 -3
  26. package/dist/cjs/PivotViewer/hooks/useViewModeScrollHandling.js.map +1 -1
  27. package/dist/cjs/PivotViewer/types.js.map +1 -1
  28. package/dist/cjs/PivotViewer/utils/animations.js +11 -19
  29. package/dist/cjs/PivotViewer/utils/animations.js.map +1 -1
  30. package/dist/cjs/PivotViewer/utils/cardPosition.js +0 -3
  31. package/dist/cjs/PivotViewer/utils/cardPosition.js.map +1 -1
  32. package/dist/cjs/PivotViewer/utils/constants.js +2 -2
  33. package/dist/cjs/PivotViewer/utils/constants.js.map +1 -1
  34. package/dist/esm/PivotViewer/PivotViewer.css +32 -19
  35. package/dist/esm/PivotViewer/PivotViewer.d.ts +1 -1
  36. package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
  37. package/dist/esm/PivotViewer/PivotViewer.js +45 -9
  38. package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
  39. package/dist/esm/PivotViewer/PivotViewer.stories.d.ts.map +1 -1
  40. package/dist/esm/PivotViewer/PivotViewer.stories.js +5 -2
  41. package/dist/esm/PivotViewer/PivotViewer.stories.js.map +1 -1
  42. package/dist/esm/PivotViewer/components/PivotCanvas.d.ts.map +1 -1
  43. package/dist/esm/PivotViewer/components/PivotCanvas.js +33 -52
  44. package/dist/esm/PivotViewer/components/PivotCanvas.js.map +1 -1
  45. package/dist/esm/PivotViewer/components/PivotViewerMain.d.ts +2 -1
  46. package/dist/esm/PivotViewer/components/PivotViewerMain.d.ts.map +1 -1
  47. package/dist/esm/PivotViewer/components/PivotViewerMain.js +4 -2
  48. package/dist/esm/PivotViewer/components/PivotViewerMain.js.map +1 -1
  49. package/dist/esm/PivotViewer/components/Toolbar.js +1 -1
  50. package/dist/esm/PivotViewer/components/Toolbar.js.map +1 -1
  51. package/dist/esm/PivotViewer/components/pivot/animation.d.ts.map +1 -1
  52. package/dist/esm/PivotViewer/components/pivot/animation.js +4 -3
  53. package/dist/esm/PivotViewer/components/pivot/animation.js.map +1 -1
  54. package/dist/esm/PivotViewer/components/pivot/groups.d.ts +6 -0
  55. package/dist/esm/PivotViewer/components/pivot/groups.d.ts.map +1 -0
  56. package/dist/esm/PivotViewer/components/pivot/groups.js +129 -0
  57. package/dist/esm/PivotViewer/components/pivot/groups.js.map +1 -0
  58. package/dist/esm/PivotViewer/components/pivot/sprites.d.ts.map +1 -1
  59. package/dist/esm/PivotViewer/components/pivot/sprites.js +2 -15
  60. package/dist/esm/PivotViewer/components/pivot/sprites.js.map +1 -1
  61. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts +4 -0
  62. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
  63. package/dist/esm/PivotViewer/components/pivot/visibility.js +50 -27
  64. package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
  65. package/dist/esm/PivotViewer/engine/layout.js +11 -7
  66. package/dist/esm/PivotViewer/engine/layout.js.map +1 -1
  67. package/dist/esm/PivotViewer/engine/pivot.worker.d.ts.map +1 -1
  68. package/dist/esm/PivotViewer/engine/pivot.worker.js +0 -8
  69. package/dist/esm/PivotViewer/engine/pivot.worker.js.map +1 -1
  70. package/dist/esm/PivotViewer/engine/types.d.ts +1 -0
  71. package/dist/esm/PivotViewer/engine/types.d.ts.map +1 -1
  72. package/dist/esm/PivotViewer/hooks/useCurrentFilters.js.map +1 -1
  73. package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts +1 -1
  74. package/dist/esm/PivotViewer/hooks/useFilteredData.js +4 -4
  75. package/dist/esm/PivotViewer/hooks/useFilteredData.js.map +1 -1
  76. package/dist/esm/PivotViewer/hooks/usePanning.d.ts.map +1 -1
  77. package/dist/esm/PivotViewer/hooks/usePanning.js +8 -4
  78. package/dist/esm/PivotViewer/hooks/usePanning.js.map +1 -1
  79. package/dist/esm/PivotViewer/hooks/usePivotEngine.d.ts.map +1 -1
  80. package/dist/esm/PivotViewer/hooks/usePivotEngine.js +1 -31
  81. package/dist/esm/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  82. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.d.ts.map +1 -1
  83. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.js +4 -3
  84. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.js.map +1 -1
  85. package/dist/esm/PivotViewer/types.d.ts +16 -0
  86. package/dist/esm/PivotViewer/types.d.ts.map +1 -1
  87. package/dist/esm/PivotViewer/types.js.map +1 -1
  88. package/dist/esm/PivotViewer/utils/animations.js +11 -19
  89. package/dist/esm/PivotViewer/utils/animations.js.map +1 -1
  90. package/dist/esm/PivotViewer/utils/cardPosition.js +1 -4
  91. package/dist/esm/PivotViewer/utils/cardPosition.js.map +1 -1
  92. package/dist/esm/PivotViewer/utils/constants.d.ts +4 -4
  93. package/dist/esm/PivotViewer/utils/constants.d.ts.map +1 -1
  94. package/dist/esm/PivotViewer/utils/constants.js +2 -2
  95. package/dist/esm/PivotViewer/utils/constants.js.map +1 -1
  96. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  97. package/package.json +1 -1
  98. package/dist/cjs/PivotViewer/components/pivot/buckets.js +0 -124
  99. package/dist/cjs/PivotViewer/components/pivot/buckets.js.map +0 -1
  100. package/dist/esm/PivotViewer/components/pivot/buckets.d.ts +0 -6
  101. package/dist/esm/PivotViewer/components/pivot/buckets.d.ts.map +0 -1
  102. package/dist/esm/PivotViewer/components/pivot/buckets.js +0 -102
  103. package/dist/esm/PivotViewer/components/pivot/buckets.js.map +0 -1
@@ -4,12 +4,19 @@
4
4
 
5
5
  .pivot-viewer {
6
6
  position: relative;
7
- height: 100vh;
7
+ /* CRITICAL: Don't set explicit height - let parent control it via container constraints */
8
+ display: flex;
9
+ flex-direction: column;
10
+ min-height: 0; /* allow children to size without forcing overflow */
11
+ max-height: 100%; /* respect parent height if set */
8
12
  width: 100%;
9
- background: radial-gradient(circle at 25% 20%, var(--primary-500), var(--surface-ground));
13
+ /* Keep root neutral; move visual background to the viewport so it never exceeds the container */
14
+ background: var(--surface-ground);
10
15
  color: var(--text-color);
11
16
  overflow: hidden;
12
17
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
18
+ /* Ensure we shrink within flex parent */
19
+ flex: 1 1 0%;
13
20
  }
14
21
 
15
22
  .pv-filter-panel {
@@ -276,7 +283,8 @@
276
283
  display: flex;
277
284
  flex-direction: column;
278
285
  min-height: 0;
279
- height: 100%;
286
+ flex: 1 1 auto; /* grow within pivot-viewer */
287
+ height: auto;
280
288
  padding-left: 0;
281
289
  overflow: hidden;
282
290
  }
@@ -571,7 +579,7 @@
571
579
  cursor: grabbing;
572
580
  }
573
581
 
574
- /* Groups wrapper for bucket mode with axis labels */
582
+ /* Groups wrapper for grouped mode with axis labels */
575
583
  .pv-groups-wrapper {
576
584
  display: flex;
577
585
  flex-direction: column;
@@ -581,7 +589,12 @@
581
589
  background: transparent;
582
590
  }
583
591
 
584
- .pv-groups-buckets {
592
+ /* Visual background belongs to the scrollable viewport area so it respects container bounds */
593
+ .pv-viewport {
594
+ background: radial-gradient(circle at 25% 20%, var(--primary-500), var(--surface-ground));
595
+ }
596
+
597
+ .pv-groups-grouped {
585
598
  flex: 1;
586
599
  padding-bottom: 0;
587
600
  }
@@ -599,21 +612,21 @@
599
612
  transition: background 180ms ease;
600
613
  }
601
614
 
602
- /* Bucket mode groups need to align cards to bottom and extend full height */
603
- .pv-group-bucket {
615
+ /* Grouped mode groups need to align cards to bottom and extend full height */
616
+ .pv-group-grouped {
604
617
  justify-content: flex-end;
605
618
  position: relative;
606
619
  min-height: 100%; /* Ensure full height for hover */
607
620
  width: var(--column-width); /* Fixed width even when empty */
608
621
  }
609
622
 
610
- /* Zebra striping for bucket mode - every other bucket gets a lighter background */
611
- .pv-group-bucket:nth-child(even)::before {
623
+ /* Zebra striping for grouped mode - every other group gets a lighter background */
624
+ .pv-group-grouped:nth-child(even)::before {
612
625
  background: rgba(255, 255, 255, 0.03);
613
626
  }
614
627
 
615
628
  /* Full-height hover highlight that extends to the top */
616
- .pv-group-bucket::before {
629
+ .pv-group-grouped::before {
617
630
  content: '';
618
631
  position: absolute;
619
632
  inset: 0;
@@ -624,14 +637,14 @@
624
637
  z-index: 0;
625
638
  }
626
639
 
627
- .pv-group-bucket:hover::before,
628
- .pv-group-bucket.highlighted::before {
640
+ .pv-group-grouped:hover::before,
641
+ .pv-group-grouped.highlighted::before {
629
642
  background: var(--surface-hover);
630
643
  }
631
644
 
632
645
  /* Zebra striping hover state - combine with stripe background */
633
- .pv-group-bucket:nth-child(even):hover::before,
634
- .pv-group-bucket:nth-child(even).highlighted::before {
646
+ .pv-group-grouped:nth-child(even):hover::before,
647
+ .pv-group-grouped:nth-child(even).highlighted::before {
635
648
  background: var(--surface-hover);
636
649
  }
637
650
 
@@ -667,25 +680,25 @@
667
680
  width: var(--column-width, auto);
668
681
  }
669
682
 
670
- .pv-cards-bucket {
683
+ .pv-cards-grouped {
671
684
  /* Grid fills left-to-right with fixed columns */
672
685
  grid-template-columns: repeat(var(--cards-per-row, 5), 1fr);
673
686
  grid-auto-flow: row;
674
687
  width: var(--column-width);
675
688
  }
676
689
 
677
- .pv-cards-bucket .pv-card {
690
+ .pv-cards-grouped .pv-card {
678
691
  /* Cards maintain their direction */
679
692
  }
680
693
 
681
- /* Empty placeholder cells in bucket grid */
694
+ /* Empty placeholder cells in grouped grid */
682
695
  .pv-card-placeholder {
683
696
  visibility: hidden;
684
697
  width: var(--card-width);
685
698
  height: var(--card-height);
686
699
  }
687
700
 
688
- /* Axis labels at bottom of bucket view */
701
+ /* Axis labels at bottom of grouped view */
689
702
  .pv-axis-labels {
690
703
  display: flex;
691
704
  align-items: stretch;
@@ -715,7 +728,7 @@
715
728
  font: inherit;
716
729
  }
717
730
 
718
- /* Zebra striping for axis labels - match the bucket columns */
731
+ /* Zebra striping for axis labels - match the grouped columns */
719
732
  .pv-axis-label:nth-child(even) {
720
733
  background: rgba(255, 255, 255, 0.03);
721
734
  }
@@ -25,7 +25,7 @@ var useAnimationModeTracking = require('./hooks/useAnimationModeTracking.js');
25
25
  var useViewModeScrollHandling = require('./hooks/useViewModeScrollHandling.js');
26
26
  var useContainerDimensions = require('./hooks/useContainerDimensions.js');
27
27
 
28
- function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRenderer, getItemId, searchFields, className, emptyContent, isLoading = false, }) {
28
+ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRenderer, detailRenderer, getItemId, searchFields, className, emptyContent, isLoading = false, colors, }) {
29
29
  const containerRef = React.useRef(null);
30
30
  const filterButtonRef = React.useRef(null);
31
31
  const axisLabelsRef = React.useRef(null);
@@ -72,9 +72,17 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
72
72
  if (!ready)
73
73
  return;
74
74
  engineApplyFilters(currentFilters).then((result) => {
75
+ if (result.visibleIds.length === 0 && currentFilters.length === 0 && data.length > 0) {
76
+ const fallbackIds = new Uint32Array(data.length);
77
+ for (let i = 0; i < data.length; i++) {
78
+ fallbackIds[i] = i;
79
+ }
80
+ setVisibleIds(fallbackIds);
81
+ return;
82
+ }
75
83
  setVisibleIds(result.visibleIds);
76
84
  });
77
- }, [ready, currentFilters, engineApplyFilters]);
85
+ }, [ready, currentFilters, engineApplyFilters, data.length]);
78
86
  const lastGroupingRequest = React.useRef(null);
79
87
  React.useEffect(() => {
80
88
  if (!ready || visibleIds.length === 0) {
@@ -115,13 +123,10 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
115
123
  lastRequest.viewMode === viewMode &&
116
124
  lastRequest.groupBy?.field === currentGroupBy.field &&
117
125
  lastRequest.visibleIds === visibleIds) {
118
- console.log('[PivotViewer] Skipping duplicate grouping request');
119
126
  return;
120
127
  }
121
128
  lastGroupingRequest.current = { viewMode, groupBy: currentGroupBy, visibleIds };
122
- console.log('[PivotViewer] Computing grouping for', visibleIds.length, 'items');
123
129
  computeGrouping(visibleIds, currentGroupBy).then((result) => {
124
- console.log('[PivotViewer] Grouping result received:', result.groups.length, 'groups');
125
130
  setGrouping(result);
126
131
  });
127
132
  }, [ready, visibleIds, currentGroupBy, viewMode, computeGrouping, sortIds, activeDimensionKey]);
@@ -137,7 +142,6 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
137
142
  cardWidth,
138
143
  cardHeight,
139
144
  cardsPerColumn: constants.CARDS_PER_COLUMN,
140
- groupSpacing: constants.GROUP_SPACING,
141
145
  containerWidth,
142
146
  containerHeight,
143
147
  });
@@ -207,11 +211,43 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
207
211
  'pivot-viewer',
208
212
  className,
209
213
  hasFilters ? (filtersOpen ? 'filters-open' : 'filters-closed') : 'no-filters',
210
- viewMode === 'grouped' ? 'bucket-mode' : 'collection-mode',
214
+ viewMode === 'grouped' ? 'grouped-mode' : 'collection-mode',
211
215
  ]
212
216
  .filter(Boolean)
213
217
  .join(' ');
214
- return (jsxRuntime.jsxs("div", { className: viewerClassName, children: [jsxRuntime.jsx(FilterPanelContainer.FilterPanelContainer, { isOpen: filtersOpen && hasFilters, search: search, filterState: filterState, rangeFilterState: rangeFilterState, expandedFilterKey: expandedFilterKey, filterOptions: filterOptions, anchorRef: filterButtonRef, onClose: () => setFiltersOpen(false), onSearchChange: setSearch, onFilterToggle: handleToggleFilter, onFilterClear: handleClearFilter, onRangeChange: handleRangeChange, onExpandedFilterChange: setExpandedFilterKey }), jsxRuntime.jsxs("main", { className: "pv-main", children: [jsxRuntime.jsx(ToolbarContainer.ToolbarContainer, { hasFilters: hasFilters, filtersOpen: filtersOpen, filteredCount: visibleIds.length, viewMode: viewMode, zoomLevel: zoomLevel, activeDimensionKey: activeDimensionKey, dimensions: dimensions, activeFilterCount: activeFilterCount, onFiltersToggle: () => setFiltersOpen((prev) => !prev), onViewModeChange: setViewMode, onZoomIn: handleZoomIn, onZoomOut: handleZoomOut, onZoomSlider: handleZoomSlider, onDimensionChange: setActiveDimensionKey, filterButtonRef: filterButtonRef }), jsxRuntime.jsx(PivotViewerMain.PivotViewerMain, { data: data, ready: ready, isLoading: isLoading, visibleIds: visibleIds, grouping: grouping, layout: layout$1, cardWidth: cardWidth, cardHeight: cardHeight, zoomLevel: zoomLevel, scrollPosition: scrollPosition, containerDimensions: containerDimensions, selectedItem: selectedItem, hoveredGroupIndex: hoveredGroupIndex, isZooming: isZooming, viewMode: viewMode, cardRenderer: cardRenderer, resolveId: resolveId, emptyContent: emptyContent, dimensionFilter: dimensionFilter, onCardClick: handleCardClick, onPanStart: handlePanStart, onPanMove: handlePanMove, onPanEnd: handlePanEnd, onGroupHover: setHoveredGroupIndex, onAxisLabelClick: handleAxisLabelClick, onCloseDetail: closeDetail, containerRef: containerRef, axisLabelsRef: axisLabelsRef, spacerRef: spacerRef })] })] }));
218
+ const cssVariables = React.useMemo(() => {
219
+ const vars = {};
220
+ if (!colors)
221
+ return vars;
222
+ if (colors.primaryColor)
223
+ vars['--primary-color'] = colors.primaryColor;
224
+ if (colors.primaryColorText)
225
+ vars['--primary-color-text'] = colors.primaryColorText;
226
+ if (colors.primary500)
227
+ vars['--primary-500'] = colors.primary500;
228
+ if (colors.surfaceGround)
229
+ vars['--surface-ground'] = colors.surfaceGround;
230
+ if (colors.surfaceCard)
231
+ vars['--surface-card'] = colors.surfaceCard;
232
+ if (colors.surfaceSection)
233
+ vars['--surface-section'] = colors.surfaceSection;
234
+ if (colors.surfaceOverlay)
235
+ vars['--surface-overlay'] = colors.surfaceOverlay;
236
+ if (colors.surfaceBorder)
237
+ vars['--surface-border'] = colors.surfaceBorder;
238
+ if (colors.textColor)
239
+ vars['--text-color'] = colors.textColor;
240
+ if (colors.textColorSecondary)
241
+ vars['--text-color-secondary'] = colors.textColorSecondary;
242
+ if (colors.highlightBg)
243
+ vars['--highlight-bg'] = colors.highlightBg;
244
+ if (colors.maskbg)
245
+ vars['--maskbg'] = colors.maskbg;
246
+ if (colors.focusRing)
247
+ vars['--focus-ring'] = colors.focusRing;
248
+ return vars;
249
+ }, [colors]);
250
+ return (jsxRuntime.jsxs("div", { className: viewerClassName, style: cssVariables, children: [jsxRuntime.jsx(FilterPanelContainer.FilterPanelContainer, { isOpen: filtersOpen && hasFilters, search: search, filterState: filterState, rangeFilterState: rangeFilterState, expandedFilterKey: expandedFilterKey, filterOptions: filterOptions, anchorRef: filterButtonRef, onClose: () => setFiltersOpen(false), onSearchChange: setSearch, onFilterToggle: handleToggleFilter, onFilterClear: handleClearFilter, onRangeChange: handleRangeChange, onExpandedFilterChange: setExpandedFilterKey }), jsxRuntime.jsxs("main", { className: "pv-main", children: [jsxRuntime.jsx(ToolbarContainer.ToolbarContainer, { hasFilters: hasFilters, filtersOpen: filtersOpen, filteredCount: visibleIds.length, viewMode: viewMode, zoomLevel: zoomLevel, activeDimensionKey: activeDimensionKey, dimensions: dimensions, activeFilterCount: activeFilterCount, onFiltersToggle: () => setFiltersOpen((prev) => !prev), onViewModeChange: setViewMode, onZoomIn: handleZoomIn, onZoomOut: handleZoomOut, onZoomSlider: handleZoomSlider, onDimensionChange: setActiveDimensionKey, filterButtonRef: filterButtonRef }), jsxRuntime.jsx(PivotViewerMain.PivotViewerMain, { data: data, ready: ready, isLoading: isLoading, visibleIds: visibleIds, grouping: grouping, layout: layout$1, cardWidth: cardWidth, cardHeight: cardHeight, zoomLevel: zoomLevel, scrollPosition: scrollPosition, containerDimensions: containerDimensions, selectedItem: selectedItem, hoveredGroupIndex: hoveredGroupIndex, isZooming: isZooming, viewMode: viewMode, cardRenderer: cardRenderer, detailRenderer: detailRenderer, resolveId: resolveId, emptyContent: emptyContent, dimensionFilter: dimensionFilter, onCardClick: handleCardClick, onPanStart: handlePanStart, onPanMove: handlePanMove, onPanEnd: handlePanEnd, onGroupHover: setHoveredGroupIndex, onAxisLabelClick: handleAxisLabelClick, onCloseDetail: closeDetail, containerRef: containerRef, axisLabelsRef: axisLabelsRef, spacerRef: spacerRef })] })] }));
215
251
  }
216
252
 
217
253
  exports.PivotViewer = PivotViewer;
@@ -1 +1 @@
1
- {"version":3,"file":"PivotViewer.js","sources":["../../../PivotViewer/PivotViewer.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { PivotViewerProps } from './types';\nimport type { GroupingResult } from './engine/types';\nimport { usePivotEngine } from './hooks/usePivotEngine';\nimport { computeLayout } from './engine/layout';\nimport { useFilterState } from './hooks/useFilterState';\nimport { useDimensionState } from './hooks/useDimensionState';\nimport { useZoomState } from './hooks/useZoomState';\nimport { BASE_CARD_WIDTH, BASE_CARD_HEIGHT, CARDS_PER_COLUMN, GROUP_SPACING } from './constants';\nimport './PivotViewer.css';\nimport { PivotViewerMain } from './components/PivotViewerMain';\nimport { FilterPanelContainer } from './components/FilterPanelContainer';\nimport { ToolbarContainer } from './components/ToolbarContainer';\nimport { usePanning, useWheelZoom, useFilterOptions } from './hooks';\nimport { useContainerDimensions } from './hooks/useContainerDimensions';\nimport type { ViewMode } from './components/Toolbar';\nimport { useFieldExtractors } from './hooks/useFieldExtractors';\nimport { useCurrentFilters, useCurrentGroupBy } from './hooks/useCurrentFilters';\nimport { useCardSelection } from './hooks/useCardSelection';\nimport { useDetailPanelClose } from './hooks/useDetailPanelClose';\nimport { useScrollSync } from './hooks/useScrollSync';\nimport { useAnimationModeTracking } from './hooks/useAnimationModeTracking';\nimport { useViewModeScrollHandling } from './hooks/useViewModeScrollHandling';\n\nexport function PivotViewer<TItem extends object>({\n data,\n dimensions,\n filters,\n defaultDimensionKey,\n cardRenderer,\n getItemId,\n searchFields,\n className,\n emptyContent,\n isLoading = false,\n}: PivotViewerProps<TItem>) {\n // Refs\n const containerRef = useRef<HTMLDivElement>(null!);\n const filterButtonRef = useRef<HTMLButtonElement>(null!);\n const axisLabelsRef = useRef<HTMLDivElement>(null!);\n const spacerRef = useRef<HTMLDivElement>(null!);\n\n // State\n const [search, setSearch] = useState('');\n const [viewMode, setViewMode] = useState<ViewMode>('collection');\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [selectedItem, setSelectedItem] = useState<TItem | null>(null);\n const [isZooming, setIsZooming] = useState(false);\n const [visibleIds, setVisibleIds] = useState<Uint32Array>(new Uint32Array(0));\n const [grouping, setGrouping] = useState<GroupingResult>({ groups: [] });\n const [hoveredGroupIndex, setHoveredGroupIndex] = useState<number | null>(null);\n const [preSelectionState, setPreSelectionState] = useState<{ zoom: number; scrollLeft: number; scrollTop: number } | null>(null);\n const [, setAnimationMode] = useState<'layout' | 'filter'>('layout');\n const [scrollPosition, setScrollPosition] = useState({ x: 0, y: 0 });\n\n // Filter hooks\n const {\n filterState,\n rangeFilterState,\n expandedFilterKey,\n setExpandedFilterKey,\n handleToggleFilter,\n handleClearFilter,\n handleRangeChange,\n } = useFilterState(filters);\n\n // Dimension hooks\n const {\n activeDimensionKey,\n setActiveDimensionKey,\n activeDimension,\n dimensionFilter,\n handleAxisLabelClick,\n } = useDimensionState(dimensions, defaultDimensionKey);\n\n // Zoom and pan hooks\n const {\n zoomLevel,\n setZoomLevel,\n handleZoomIn,\n handleZoomOut,\n handleZoomSlider,\n } = useZoomState(1);\n\n const {\n isPanning,\n handlePanStart,\n handlePanMove,\n handlePanEnd,\n } = usePanning(containerRef, undefined, setScrollPosition);\n\n useWheelZoom(containerRef, zoomLevel, setZoomLevel);\n\n // Track container dimensions for responsive layout\n const containerDimensions = useContainerDimensions(containerRef, isLoading);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleScroll = () => {\n setScrollPosition({\n x: container.scrollLeft,\n y: container.scrollTop,\n });\n };\n\n container.addEventListener('scroll', handleScroll);\n return () => container.removeEventListener('scroll', handleScroll);\n }, []);\n\n // Build field extractors for the columnar store\n const { fieldExtractors, indexFields } = useFieldExtractors(dimensions, filters);\n\n // Initialize the Web Worker engine\n const { ready, applyFilters: engineApplyFilters, computeGrouping, sortIds } = usePivotEngine({\n data,\n fieldExtractors,\n indexFields,\n });\n\n // Build filter specs from UI state\n const currentFilters = useCurrentFilters(\n filters,\n filterState,\n rangeFilterState,\n search,\n searchFields,\n dimensionFilter,\n activeDimension,\n );\n\n const currentGroupBy = useCurrentGroupBy(activeDimensionKey, dimensions);\n\n // Apply filters\n useEffect(() => {\n if (!ready) return;\n\n engineApplyFilters(currentFilters).then((result) => {\n setVisibleIds(result.visibleIds);\n });\n }, [ready, currentFilters, engineApplyFilters]);\n\n // Compute grouping\n const lastGroupingRequest = useRef<{ viewMode: ViewMode; groupBy: unknown; visibleIds: Uint32Array } | null>(null);\n \n useEffect(() => {\n if (!ready || visibleIds.length === 0) {\n setGrouping({ groups: [] });\n lastGroupingRequest.current = null;\n return;\n }\n\n if (viewMode === 'collection') {\n // In collection mode, create a single group with all items\n // Sort items if activeDimensionKey is set\n if (activeDimensionKey) {\n sortIds(visibleIds, activeDimensionKey).then((sortedIds) => {\n setGrouping({\n groups: [{\n key: 'all',\n label: 'All Items',\n value: 'all',\n ids: sortedIds,\n count: sortedIds.length\n }]\n });\n });\n } else {\n setGrouping({\n groups: [{\n key: 'all',\n label: 'All Items',\n value: 'all',\n ids: visibleIds,\n count: visibleIds.length\n }]\n });\n }\n lastGroupingRequest.current = null;\n return;\n }\n\n // Check if this is the same request as last time to prevent duplicate computations\n const lastRequest = lastGroupingRequest.current;\n if (\n lastRequest &&\n lastRequest.viewMode === viewMode &&\n (lastRequest.groupBy as unknown as typeof currentGroupBy)?.field === currentGroupBy.field &&\n lastRequest.visibleIds === visibleIds\n ) {\n console.log('[PivotViewer] Skipping duplicate grouping request');\n return;\n }\n\n lastGroupingRequest.current = { viewMode, groupBy: currentGroupBy, visibleIds };\n \n console.log('[PivotViewer] Computing grouping for', visibleIds.length, 'items');\n computeGrouping(visibleIds, currentGroupBy).then((result) => {\n console.log('[PivotViewer] Grouping result received:', result.groups.length, 'groups');\n setGrouping(result);\n });\n }, [ready, visibleIds, currentGroupBy, viewMode, computeGrouping, sortIds, activeDimensionKey]);\n\n // Compute layout\n const layout = useMemo(() => {\n // Calculate layout at base dimensions (zoom is applied as transform)\n const cardWidth = BASE_CARD_WIDTH;\n const cardHeight = BASE_CARD_HEIGHT;\n const containerWidth = containerDimensions.width / zoomLevel;\n // For grouped mode, use fixed container height to ensure stable layout during zoom\n const containerHeight = viewMode === 'collection'\n ? containerDimensions.height / zoomLevel\n : containerDimensions.height;\n\n const result = computeLayout(grouping, {\n viewMode,\n cardWidth,\n cardHeight,\n cardsPerColumn: CARDS_PER_COLUMN,\n groupSpacing: GROUP_SPACING,\n containerWidth,\n containerHeight,\n });\n\n return result;\n }, [grouping, viewMode, zoomLevel, containerDimensions.width, containerDimensions.height]);\n\n const resolveId = useCallback((item: TItem, index: number) => {\n if (getItemId) {\n const id = getItemId(item, index);\n return typeof id === 'number' ? id : index;\n }\n const id = (item as Record<string, unknown>)['id'];\n return typeof id === 'number' ? id : index;\n }, [getItemId]);\n\n // Track animation mode changes\n useAnimationModeTracking(filterState, rangeFilterState, search, activeDimensionKey, viewMode, setAnimationMode);\n\n // Sync axis labels scroll with container scroll\n useScrollSync(containerRef, axisLabelsRef, viewMode);\n\n // Handle scroll positioning when switching view modes or grouping changes\n useViewModeScrollHandling({\n containerRef,\n viewMode,\n grouping,\n layout,\n selectedItem,\n zoomLevel,\n resolveId,\n data,\n setPreSelectionState,\n });\n\n // Handle card selection (click)\n const handleCardClick = useCardSelection({\n data,\n isPanning,\n selectedItem,\n zoomLevel,\n viewMode,\n layout,\n containerDimensions,\n scrollPosition,\n preSelectionState,\n grouping,\n getItemId,\n resolveId,\n setZoomLevel,\n setIsZooming,\n setSelectedItem,\n setPreSelectionState,\n });\n\n // Handle detail panel close\n const closeDetail = useDetailPanelClose({\n selectedItem,\n preSelectionState,\n zoomLevel,\n viewMode,\n layout,\n containerDimensions,\n grouping,\n data,\n resolveId,\n setZoomLevel,\n setIsZooming,\n setSelectedItem,\n setPreSelectionState,\n });\n\n // Use base card dimensions - zoom is applied as transform in canvas\n const cardWidth = BASE_CARD_WIDTH;\n const cardHeight = BASE_CARD_HEIGHT;\n\n // Calculate filter options\n const filterOptions = useFilterOptions(data, filters, filterState, rangeFilterState);\n\n const hasFilters = Boolean(filters && filters.length > 0);\n const activeFilterCount = Object.values(filterState).reduce((sum: number, vals) => sum + (vals as Set<string>).size, 0) +\n Object.values(rangeFilterState).filter(r => r !== null).length;\n\n const viewerClassName = [\n 'pivot-viewer',\n className,\n hasFilters ? (filtersOpen ? 'filters-open' : 'filters-closed') : 'no-filters',\n viewMode === 'grouped' ? 'bucket-mode' : 'collection-mode',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div className={viewerClassName}>\n <FilterPanelContainer\n isOpen={filtersOpen && hasFilters}\n search={search}\n filterState={filterState}\n rangeFilterState={rangeFilterState}\n expandedFilterKey={expandedFilterKey}\n filterOptions={filterOptions}\n anchorRef={filterButtonRef}\n onClose={() => setFiltersOpen(false)}\n onSearchChange={setSearch}\n onFilterToggle={handleToggleFilter}\n onFilterClear={handleClearFilter}\n onRangeChange={handleRangeChange}\n onExpandedFilterChange={setExpandedFilterKey}\n />\n\n <main className=\"pv-main\">\n <ToolbarContainer\n hasFilters={hasFilters}\n filtersOpen={filtersOpen}\n filteredCount={visibleIds.length}\n viewMode={viewMode}\n zoomLevel={zoomLevel}\n activeDimensionKey={activeDimensionKey}\n dimensions={dimensions}\n activeFilterCount={activeFilterCount}\n onFiltersToggle={() => setFiltersOpen((prev) => !prev)}\n onViewModeChange={setViewMode}\n onZoomIn={handleZoomIn}\n onZoomOut={handleZoomOut}\n onZoomSlider={handleZoomSlider}\n onDimensionChange={setActiveDimensionKey}\n filterButtonRef={filterButtonRef}\n />\n\n <PivotViewerMain\n data={data}\n ready={ready}\n isLoading={isLoading}\n visibleIds={visibleIds}\n grouping={grouping}\n layout={layout}\n cardWidth={cardWidth}\n cardHeight={cardHeight}\n zoomLevel={zoomLevel}\n scrollPosition={scrollPosition}\n containerDimensions={containerDimensions}\n selectedItem={selectedItem}\n hoveredGroupIndex={hoveredGroupIndex}\n isZooming={isZooming}\n viewMode={viewMode}\n cardRenderer={cardRenderer}\n resolveId={resolveId}\n emptyContent={emptyContent}\n dimensionFilter={dimensionFilter}\n onCardClick={handleCardClick}\n onPanStart={handlePanStart as (e: React.MouseEvent) => void}\n onPanMove={handlePanMove as (e: React.MouseEvent) => void}\n onPanEnd={handlePanEnd}\n onGroupHover={setHoveredGroupIndex}\n onAxisLabelClick={handleAxisLabelClick}\n onCloseDetail={closeDetail}\n containerRef={containerRef}\n axisLabelsRef={axisLabelsRef}\n spacerRef={spacerRef}\n />\n </main>\n </div>\n );\n}\n"],"names":["useRef","useState","useFilterState","useDimensionState","useZoomState","usePanning","useWheelZoom","useContainerDimensions","useEffect","useFieldExtractors","usePivotEngine","useCurrentFilters","useCurrentGroupBy","layout","useMemo","BASE_CARD_WIDTH","BASE_CARD_HEIGHT","computeLayout","CARDS_PER_COLUMN","GROUP_SPACING","useCallback","useAnimationModeTracking","useScrollSync","useViewModeScrollHandling","useCardSelection","useDetailPanelClose","useFilterOptions","_jsxs","_jsx","FilterPanelContainer","ToolbarContainer","PivotViewerMain"],"mappings":"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"}
1
+ {"version":3,"file":"PivotViewer.js","sources":["../../../PivotViewer/PivotViewer.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { PivotViewerProps } from './types';\nimport type { GroupingResult } from './engine/types';\nimport { usePivotEngine } from './hooks/usePivotEngine';\nimport { computeLayout } from './engine/layout';\nimport { useFilterState } from './hooks/useFilterState';\nimport { useDimensionState } from './hooks/useDimensionState';\nimport { useZoomState } from './hooks/useZoomState';\nimport { BASE_CARD_WIDTH, BASE_CARD_HEIGHT, CARDS_PER_COLUMN, GROUP_SPACING } from './constants';\nimport './PivotViewer.css';\nimport { PivotViewerMain } from './components/PivotViewerMain';\nimport { FilterPanelContainer } from './components/FilterPanelContainer';\nimport { ToolbarContainer } from './components/ToolbarContainer';\nimport { usePanning, useWheelZoom, useFilterOptions } from './hooks';\nimport { useContainerDimensions } from './hooks/useContainerDimensions';\nimport type { ViewMode } from './components/Toolbar';\nimport { useFieldExtractors } from './hooks/useFieldExtractors';\nimport { useCurrentFilters, useCurrentGroupBy } from './hooks/useCurrentFilters';\nimport { useCardSelection } from './hooks/useCardSelection';\nimport { useDetailPanelClose } from './hooks/useDetailPanelClose';\nimport { useScrollSync } from './hooks/useScrollSync';\nimport { useAnimationModeTracking } from './hooks/useAnimationModeTracking';\nimport { useViewModeScrollHandling } from './hooks/useViewModeScrollHandling';\n\nexport function PivotViewer<TItem extends object>({\n data,\n dimensions,\n filters,\n defaultDimensionKey,\n cardRenderer,\n detailRenderer,\n getItemId,\n searchFields,\n className,\n emptyContent,\n isLoading = false,\n colors,\n}: PivotViewerProps<TItem>) {\n // Refs\n const containerRef = useRef<HTMLDivElement>(null!);\n const filterButtonRef = useRef<HTMLButtonElement>(null!);\n const axisLabelsRef = useRef<HTMLDivElement>(null!);\n const spacerRef = useRef<HTMLDivElement>(null!);\n\n // State\n const [search, setSearch] = useState('');\n const [viewMode, setViewMode] = useState<ViewMode>('collection');\n const [filtersOpen, setFiltersOpen] = useState(false);\n const [selectedItem, setSelectedItem] = useState<TItem | null>(null);\n const [isZooming, setIsZooming] = useState(false);\n const [visibleIds, setVisibleIds] = useState<Uint32Array>(new Uint32Array(0));\n const [grouping, setGrouping] = useState<GroupingResult>({ groups: [] });\n const [hoveredGroupIndex, setHoveredGroupIndex] = useState<number | null>(null);\n const [preSelectionState, setPreSelectionState] = useState<{ zoom: number; scrollLeft: number; scrollTop: number } | null>(null);\n const [, setAnimationMode] = useState<'layout' | 'filter'>('layout');\n const [scrollPosition, setScrollPosition] = useState({ x: 0, y: 0 });\n\n // Filter hooks\n const {\n filterState,\n rangeFilterState,\n expandedFilterKey,\n setExpandedFilterKey,\n handleToggleFilter,\n handleClearFilter,\n handleRangeChange,\n } = useFilterState(filters);\n\n // Dimension hooks\n const {\n activeDimensionKey,\n setActiveDimensionKey,\n activeDimension,\n dimensionFilter,\n handleAxisLabelClick,\n } = useDimensionState(dimensions, defaultDimensionKey);\n\n // Zoom and pan hooks\n const {\n zoomLevel,\n setZoomLevel,\n handleZoomIn,\n handleZoomOut,\n handleZoomSlider,\n } = useZoomState(1);\n\n const {\n isPanning,\n handlePanStart,\n handlePanMove,\n handlePanEnd,\n } = usePanning(containerRef, undefined, setScrollPosition);\n\n useWheelZoom(containerRef, zoomLevel, setZoomLevel);\n\n // Track container dimensions for responsive layout\n const containerDimensions = useContainerDimensions(containerRef, isLoading);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleScroll = () => {\n setScrollPosition({\n x: container.scrollLeft,\n y: container.scrollTop,\n });\n };\n\n container.addEventListener('scroll', handleScroll);\n return () => container.removeEventListener('scroll', handleScroll);\n }, []);\n\n // Build field extractors for the columnar store\n const { fieldExtractors, indexFields } = useFieldExtractors(dimensions, filters);\n\n // Initialize the Web Worker engine\n const { ready, applyFilters: engineApplyFilters, computeGrouping, sortIds } = usePivotEngine({\n data,\n fieldExtractors,\n indexFields,\n });\n\n // Build filter specs from UI state\n const currentFilters = useCurrentFilters(\n filters,\n filterState,\n rangeFilterState,\n search,\n searchFields,\n dimensionFilter,\n activeDimension,\n );\n\n const currentGroupBy = useCurrentGroupBy(activeDimensionKey, dimensions);\n\n // Apply filters\n useEffect(() => {\n if (!ready) return;\n\n engineApplyFilters(currentFilters).then((result) => {\n // If the engine failed to return any IDs while no filters are active,\n // fall back to showing the full dataset so the canvas never renders empty.\n if (result.visibleIds.length === 0 && currentFilters.length === 0 && data.length > 0) {\n const fallbackIds = new Uint32Array(data.length);\n for (let i = 0; i < data.length; i++) {\n fallbackIds[i] = i;\n }\n setVisibleIds(fallbackIds);\n return;\n }\n\n setVisibleIds(result.visibleIds);\n });\n }, [ready, currentFilters, engineApplyFilters, data.length]);\n\n // Compute grouping\n const lastGroupingRequest = useRef<{ viewMode: ViewMode; groupBy: unknown; visibleIds: Uint32Array } | null>(null);\n \n useEffect(() => {\n if (!ready || visibleIds.length === 0) {\n setGrouping({ groups: [] });\n lastGroupingRequest.current = null;\n return;\n }\n\n if (viewMode === 'collection') {\n // In collection mode, create a single group with all items\n // Sort items if activeDimensionKey is set\n if (activeDimensionKey) {\n sortIds(visibleIds, activeDimensionKey).then((sortedIds) => {\n setGrouping({\n groups: [{\n key: 'all',\n label: 'All Items',\n value: 'all',\n ids: sortedIds,\n count: sortedIds.length\n }]\n });\n });\n } else {\n setGrouping({\n groups: [{\n key: 'all',\n label: 'All Items',\n value: 'all',\n ids: visibleIds,\n count: visibleIds.length\n }]\n });\n }\n lastGroupingRequest.current = null;\n return;\n }\n\n // Check if this is the same request as last time to prevent duplicate computations\n const lastRequest = lastGroupingRequest.current;\n if (\n lastRequest &&\n lastRequest.viewMode === viewMode &&\n (lastRequest.groupBy as unknown as typeof currentGroupBy)?.field === currentGroupBy.field &&\n lastRequest.visibleIds === visibleIds\n ) {\n return;\n }\n\n lastGroupingRequest.current = { viewMode, groupBy: currentGroupBy, visibleIds };\n \n computeGrouping(visibleIds, currentGroupBy).then((result) => {\n setGrouping(result);\n });\n }, [ready, visibleIds, currentGroupBy, viewMode, computeGrouping, sortIds, activeDimensionKey]);\n\n // Compute layout\n const layout = useMemo(() => {\n // Calculate layout at base dimensions (zoom is applied as transform)\n const cardWidth = BASE_CARD_WIDTH;\n const cardHeight = BASE_CARD_HEIGHT;\n const containerWidth = containerDimensions.width / zoomLevel;\n // For grouped mode, use fixed container height to ensure stable layout during zoom\n const containerHeight = viewMode === 'collection'\n ? containerDimensions.height / zoomLevel\n : containerDimensions.height;\n\n const result = computeLayout(grouping, {\n viewMode,\n cardWidth,\n cardHeight,\n cardsPerColumn: CARDS_PER_COLUMN,\n groupSpacing: GROUP_SPACING,\n containerWidth,\n containerHeight,\n });\n\n return result;\n }, [grouping, viewMode, zoomLevel, containerDimensions.width, containerDimensions.height]);\n\n const resolveId = useCallback((item: TItem, index: number) => {\n if (getItemId) {\n const id = getItemId(item, index);\n return typeof id === 'number' ? id : index;\n }\n const id = (item as Record<string, unknown>)['id'];\n return typeof id === 'number' ? id : index;\n }, [getItemId]);\n\n // Track animation mode changes\n useAnimationModeTracking(filterState, rangeFilterState, search, activeDimensionKey, viewMode, setAnimationMode);\n\n // Sync axis labels scroll with container scroll\n useScrollSync(containerRef, axisLabelsRef, viewMode);\n\n // Handle scroll positioning when switching view modes or grouping changes\n useViewModeScrollHandling({\n containerRef,\n viewMode,\n grouping,\n layout,\n selectedItem,\n zoomLevel,\n resolveId,\n data,\n setPreSelectionState,\n });\n\n // Handle card selection (click)\n const handleCardClick = useCardSelection({\n data,\n isPanning,\n selectedItem,\n zoomLevel,\n viewMode,\n layout,\n containerDimensions,\n scrollPosition,\n preSelectionState,\n grouping,\n getItemId,\n resolveId,\n setZoomLevel,\n setIsZooming,\n setSelectedItem,\n setPreSelectionState,\n });\n\n // Handle detail panel close\n const closeDetail = useDetailPanelClose({\n selectedItem,\n preSelectionState,\n zoomLevel,\n viewMode,\n layout,\n containerDimensions,\n grouping,\n data,\n resolveId,\n setZoomLevel,\n setIsZooming,\n setSelectedItem,\n setPreSelectionState,\n });\n\n // Use base card dimensions - zoom is applied as transform in canvas\n const cardWidth = BASE_CARD_WIDTH;\n const cardHeight = BASE_CARD_HEIGHT;\n\n // Calculate filter options\n const filterOptions = useFilterOptions(data, filters, filterState, rangeFilterState);\n\n const hasFilters = Boolean(filters && filters.length > 0);\n const activeFilterCount = Object.values(filterState).reduce((sum: number, vals) => sum + (vals as Set<string>).size, 0) +\n Object.values(rangeFilterState).filter(r => r !== null).length;\n\n const viewerClassName = [\n 'pivot-viewer',\n className,\n hasFilters ? (filtersOpen ? 'filters-open' : 'filters-closed') : 'no-filters',\n viewMode === 'grouped' ? 'grouped-mode' : 'collection-mode',\n ]\n .filter(Boolean)\n .join(' ');\n\n // Map provided color overrides to CSS variables understood by the component.\n const cssVariables = useMemo(() => {\n const vars: Record<string, string> = {};\n if (!colors) return vars;\n if (colors.primaryColor) vars['--primary-color'] = colors.primaryColor;\n if (colors.primaryColorText) vars['--primary-color-text'] = colors.primaryColorText;\n if (colors.primary500) vars['--primary-500'] = colors.primary500;\n if (colors.surfaceGround) vars['--surface-ground'] = colors.surfaceGround;\n if (colors.surfaceCard) vars['--surface-card'] = colors.surfaceCard;\n if (colors.surfaceSection) vars['--surface-section'] = colors.surfaceSection;\n if (colors.surfaceOverlay) vars['--surface-overlay'] = colors.surfaceOverlay;\n if (colors.surfaceBorder) vars['--surface-border'] = colors.surfaceBorder;\n if (colors.textColor) vars['--text-color'] = colors.textColor;\n if (colors.textColorSecondary) vars['--text-color-secondary'] = colors.textColorSecondary;\n if (colors.highlightBg) vars['--highlight-bg'] = colors.highlightBg;\n if (colors.maskbg) vars['--maskbg'] = colors.maskbg;\n if (colors.focusRing) vars['--focus-ring'] = colors.focusRing;\n return vars;\n }, [colors]);\n\n return (\n <div className={viewerClassName} style={cssVariables as React.CSSProperties}>\n <FilterPanelContainer\n isOpen={filtersOpen && hasFilters}\n search={search}\n filterState={filterState}\n rangeFilterState={rangeFilterState}\n expandedFilterKey={expandedFilterKey}\n filterOptions={filterOptions}\n anchorRef={filterButtonRef}\n onClose={() => setFiltersOpen(false)}\n onSearchChange={setSearch}\n onFilterToggle={handleToggleFilter}\n onFilterClear={handleClearFilter}\n onRangeChange={handleRangeChange}\n onExpandedFilterChange={setExpandedFilterKey}\n />\n\n <main className=\"pv-main\">\n <ToolbarContainer\n hasFilters={hasFilters}\n filtersOpen={filtersOpen}\n filteredCount={visibleIds.length}\n viewMode={viewMode}\n zoomLevel={zoomLevel}\n activeDimensionKey={activeDimensionKey}\n dimensions={dimensions}\n activeFilterCount={activeFilterCount}\n onFiltersToggle={() => setFiltersOpen((prev) => !prev)}\n onViewModeChange={setViewMode}\n onZoomIn={handleZoomIn}\n onZoomOut={handleZoomOut}\n onZoomSlider={handleZoomSlider}\n onDimensionChange={setActiveDimensionKey}\n filterButtonRef={filterButtonRef}\n />\n\n <PivotViewerMain\n data={data}\n ready={ready}\n isLoading={isLoading}\n visibleIds={visibleIds}\n grouping={grouping}\n layout={layout}\n cardWidth={cardWidth}\n cardHeight={cardHeight}\n zoomLevel={zoomLevel}\n scrollPosition={scrollPosition}\n containerDimensions={containerDimensions}\n selectedItem={selectedItem}\n hoveredGroupIndex={hoveredGroupIndex}\n isZooming={isZooming}\n viewMode={viewMode}\n cardRenderer={cardRenderer}\n detailRenderer={detailRenderer}\n resolveId={resolveId}\n emptyContent={emptyContent}\n dimensionFilter={dimensionFilter}\n onCardClick={handleCardClick}\n onPanStart={handlePanStart as (e: React.MouseEvent) => void}\n onPanMove={handlePanMove as (e: React.MouseEvent) => void}\n onPanEnd={handlePanEnd}\n onGroupHover={setHoveredGroupIndex}\n onAxisLabelClick={handleAxisLabelClick}\n onCloseDetail={closeDetail}\n containerRef={containerRef}\n axisLabelsRef={axisLabelsRef}\n spacerRef={spacerRef}\n />\n </main>\n </div>\n );\n}\n"],"names":["useRef","useState","useFilterState","useDimensionState","useZoomState","usePanning","useWheelZoom","useContainerDimensions","useEffect","useFieldExtractors","usePivotEngine","useCurrentFilters","useCurrentGroupBy","layout","useMemo","BASE_CARD_WIDTH","BASE_CARD_HEIGHT","computeLayout","CARDS_PER_COLUMN","useCallback","useAnimationModeTracking","useScrollSync","useViewModeScrollHandling","useCardSelection","useDetailPanelClose","useFilterOptions","_jsxs","_jsx","FilterPanelContainer","ToolbarContainer","PivotViewerMain"],"mappings":"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"}
@@ -5,7 +5,7 @@ var PIXI = require('pixi.js');
5
5
  var colorResolver = require('./pivot/colorResolver.js');
6
6
  var sprites = require('./pivot/sprites.js');
7
7
  var visibility = require('./pivot/visibility.js');
8
- var buckets = require('./pivot/buckets.js');
8
+ var groups = require('./pivot/groups.js');
9
9
  var animation = require('./pivot/animation.js');
10
10
  var constants = require('./pivot/constants.js');
11
11
 
@@ -29,13 +29,12 @@ function _interopNamespaceDefault(e) {
29
29
  var PIXI__namespace = /*#__PURE__*/_interopNamespaceDefault(PIXI);
30
30
 
31
31
  function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeight, zoomLevel, panX, panY, viewportWidth, viewportHeight, selectedId, hoveredGroupIndex, isZooming: _isZooming = false, resolveId: _resolveId, onCardClick, onPanStart, onPanMove, onPanEnd, viewMode, cardRenderer, containerRef, }) {
32
- console.log('[PivotCanvas] Render', { viewMode });
33
32
  const parentContainerRef = containerRef;
34
33
  const canvasRef = React.useRef(null);
35
34
  const spacerRef = React.useRef(null);
36
35
  const appRef = React.useRef(null);
37
36
  const rootRef = React.useRef(null);
38
- const bucketsContainerRef = React.useRef(null);
37
+ const groupsContainerRef = React.useRef(null);
39
38
  const spritesRef = React.useRef(new Map());
40
39
  const animationFrameRef = React.useRef(0);
41
40
  const mountedRef = React.useRef(true);
@@ -48,6 +47,8 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
48
47
  const previousViewModeRef = React.useRef(viewMode);
49
48
  const prevLayoutRef = React.useRef(null);
50
49
  const prevGroupingRef = React.useRef(null);
50
+ const prevScrollTopRef = React.useRef(0);
51
+ const prevScrollLeftRef = React.useRef(0);
51
52
  const cardColorsRef = React.useRef(constants.DEFAULT_COLORS);
52
53
  const cssColorResolver = React.useMemo(() => colorResolver.createCssColorResolver(), []);
53
54
  const onPanStartRef = React.useRef(onPanStart);
@@ -99,9 +100,9 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
99
100
  return;
100
101
  }
101
102
  appRef.current = app;
102
- const bucketsContainer = new PIXI__namespace.Container();
103
- bucketsContainerRef.current = bucketsContainer;
104
- app.stage.addChild(bucketsContainer);
103
+ const groupsContainer = new PIXI__namespace.Container();
104
+ groupsContainerRef.current = groupsContainer;
105
+ app.stage.addChild(groupsContainer);
105
106
  const root = new PIXI__namespace.Container();
106
107
  rootRef.current = root;
107
108
  app.stage.addChild(root);
@@ -122,6 +123,9 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
122
123
  overlayParent.appendChild(c);
123
124
  canvasRef.current = c;
124
125
  }
126
+ else {
127
+ console.error('PivotCanvas: Could not find canvas element from Pixi application');
128
+ }
125
129
  if (canvasRef.current && parentContainerRef.current) {
126
130
  const parentBounds = parentContainerRef.current.getBoundingClientRect();
127
131
  void parentBounds;
@@ -175,7 +179,7 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
175
179
  appRef.current = null;
176
180
  rootRef.current = null;
177
181
  }
178
- sprites.clearSpritePool();
182
+ spritesRef.current.clear();
179
183
  try {
180
184
  const parentEl = parentContainerRef.current;
181
185
  if (parentEl) {
@@ -223,9 +227,9 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
223
227
  };
224
228
  }, [pixiReady, viewportWidth, viewportHeight]);
225
229
  React.useEffect(() => {
226
- if (!bucketsContainerRef.current || !parentContainerRef.current || !pixiReady)
230
+ if (!groupsContainerRef.current || !parentContainerRef.current || !pixiReady)
227
231
  return;
228
- buckets.updateBucketBackgrounds(bucketsContainerRef.current, parentContainerRef.current, grouping, layout, zoomLevel, cardColorsRef.current, viewMode);
232
+ groups.updateGroupBackgrounds(groupsContainerRef.current, parentContainerRef.current, grouping, layout, zoomLevel, cardColorsRef.current, viewMode);
229
233
  needsRenderRef.current = true;
230
234
  appRef.current?.renderer?.render(appRef.current.stage);
231
235
  }, [grouping, layout, zoomLevel, viewMode, pixiReady]);
@@ -235,22 +239,12 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
235
239
  }
236
240
  const viewModeChanged = previousViewModeRef.current !== viewMode;
237
241
  const groupingChanged = prevGroupingRef.current !== grouping;
238
- if (viewModeChanged || groupingChanged) {
242
+ const layoutChanged = prevLayoutRef.current !== layout;
243
+ if (viewModeChanged || groupingChanged || layoutChanged) {
239
244
  isViewTransitionRef.current = true;
240
245
  lastViewChangeTimeRef.current = Date.now();
241
246
  previousViewModeRef.current = viewMode;
242
247
  prevGroupingRef.current = grouping;
243
- console.log('[PivotCanvas] View mode or grouping changed - marking sprites for cleanup');
244
- for (const sprite of spritesRef.current.values()) {
245
- try {
246
- if (sprite.container) {
247
- sprite.container.visible = false;
248
- }
249
- sprite.__lastHiddenAt = Date.now();
250
- }
251
- catch (e) {
252
- }
253
- }
254
248
  }
255
249
  if (spacerRef.current) {
256
250
  const spacer = spacerRef.current;
@@ -265,8 +259,11 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
265
259
  const panDeltaX = panX - prevPanRef.current.x;
266
260
  const panDeltaY = panY - prevPanRef.current.y;
267
261
  prevPanRef.current = { x: panX, y: panY };
262
+ const currentScrollTop = parentContainerRef.current?.scrollTop || 0;
263
+ const currentScrollLeft = parentContainerRef.current?.scrollLeft || 0;
268
264
  visibility.syncSpritesToViewport({
269
265
  root: rootRef.current,
266
+ groupsContainer: groupsContainerRef.current,
270
267
  container: parentContainerRef.current,
271
268
  sprites: spritesRef.current,
272
269
  layout,
@@ -281,11 +278,16 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
281
278
  zoomLevel,
282
279
  viewportWidth,
283
280
  viewportHeight,
281
+ viewMode,
284
282
  createCardSprite: (id, x, y) => sprites.createCardSprite(id, x, y, items, (item, e, id) => (onCardClickRef.current)(item, e, id), (e) => (onPanStart)(e), cardWidth, cardHeight, cardColorsRef.current),
285
283
  updateCardContent: (sprite, item) => sprites.updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, cardColorsRef.current),
286
- isViewTransition: isViewTransitionRef.current || (Date.now() - lastViewChangeTimeRef.current < 1000),
284
+ isViewTransition: isViewTransitionRef.current,
287
285
  prevLayout: prevLayoutRef.current,
286
+ prevScrollTop: prevScrollTopRef.current,
287
+ prevScrollLeft: prevScrollLeftRef.current,
288
288
  });
289
+ prevScrollTopRef.current = currentScrollTop;
290
+ prevScrollLeftRef.current = currentScrollLeft;
289
291
  needsRenderRef.current = true;
290
292
  if (appRef.current?.renderer && rootRef.current) {
291
293
  appRef.current.renderer.render(appRef.current.stage);
@@ -304,21 +306,6 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
304
306
  React.useEffect(() => {
305
307
  prevLayoutRef.current = layout;
306
308
  }, [layout]);
307
- React.useEffect(() => {
308
- if (!rootRef.current || !bucketsContainerRef.current)
309
- return;
310
- const effectivePanX = parentContainerRef.current ? parentContainerRef.current.scrollLeft : panX;
311
- const effectivePanY = parentContainerRef.current ? parentContainerRef.current.scrollTop : panY;
312
- if (rootRef.current.scale && bucketsContainerRef.current.scale) {
313
- rootRef.current.scale.set(zoomLevel);
314
- bucketsContainerRef.current.scale.set(zoomLevel);
315
- }
316
- if (rootRef.current.position && bucketsContainerRef.current.position) {
317
- rootRef.current.position.set(-effectivePanX, -effectivePanY);
318
- bucketsContainerRef.current.position.set(-effectivePanX, -effectivePanY);
319
- }
320
- appRef.current?.renderer?.render(appRef.current.stage);
321
- }, [zoomLevel, panX, panY]);
322
309
  React.useEffect(() => {
323
310
  if (!rootRef.current)
324
311
  return;
@@ -347,20 +334,9 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
347
334
  const effectivePanY = container.scrollTop;
348
335
  lastScroll.x = effectivePanX;
349
336
  lastScroll.y = effectivePanY;
350
- if (rootRef.current && bucketsContainerRef.current) {
351
- if (rootRef.current.scale && bucketsContainerRef.current.scale) {
352
- rootRef.current.scale.set(zoomLevel);
353
- bucketsContainerRef.current.scale.set(zoomLevel);
354
- }
355
- const invScale = zoomLevel && zoomLevel !== 0 ? 1 / zoomLevel : 1;
356
- void invScale;
357
- if (rootRef.current.position && bucketsContainerRef.current.position) {
358
- rootRef.current.position.set(-effectivePanX, -effectivePanY);
359
- bucketsContainerRef.current.position.set(-effectivePanX, -effectivePanY);
360
- }
361
- }
362
337
  visibility.syncSpritesToViewport({
363
338
  root: rootRef.current,
339
+ groupsContainer: groupsContainerRef.current,
364
340
  container: parentContainerRef.current,
365
341
  sprites: spritesRef.current,
366
342
  layout,
@@ -375,8 +351,13 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
375
351
  viewportHeight,
376
352
  createCardSprite: (id, x, y) => sprites.createCardSprite(id, x, y, items, (item, e, id) => (onCardClickRef.current)(item, e, id), (e) => (onPanStartRef.current)(e), cardWidth, cardHeight, cardColorsRef.current),
377
353
  updateCardContent: (sprite, item) => sprites.updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, cardColorsRef.current),
378
- isViewTransition: isViewTransitionRef.current || (Date.now() - lastViewChangeTimeRef.current < 1000),
354
+ isViewTransition: isViewTransitionRef.current,
355
+ viewMode,
356
+ prevScrollTop: prevScrollTopRef.current,
357
+ prevScrollLeft: prevScrollLeftRef.current,
379
358
  });
359
+ prevScrollTopRef.current = container.scrollTop || 0;
360
+ prevScrollLeftRef.current = container.scrollLeft || 0;
380
361
  needsRenderRef.current = true;
381
362
  app.renderer?.render(app.stage);
382
363
  }
@@ -408,7 +389,7 @@ function PivotCanvas({ items, layout, grouping, visibleIds, cardWidth, cardHeigh
408
389
  }
409
390
  }
410
391
  function updateHighlight() {
411
- buckets.updateHighlight(bucketsContainerRef.current, parentContainerRef.current, grouping, layout, hoveredGroupIndex, cardWidth);
392
+ groups.updateHighlight(groupsContainerRef.current, parentContainerRef.current, grouping, layout, hoveredGroupIndex, cardWidth, zoomLevel);
412
393
  }
413
394
  return null;
414
395
  }