@cratis/components 0.1.13 → 0.1.15

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 (78) hide show
  1. package/dist/cjs/PivotViewer/PivotViewer.js +57 -378
  2. package/dist/cjs/PivotViewer/PivotViewer.js.map +1 -1
  3. package/dist/cjs/PivotViewer/hooks/useAnimationModeTracking.js +37 -0
  4. package/dist/cjs/PivotViewer/hooks/useAnimationModeTracking.js.map +1 -0
  5. package/dist/cjs/PivotViewer/hooks/useCardSelection.js +64 -0
  6. package/dist/cjs/PivotViewer/hooks/useCardSelection.js.map +1 -0
  7. package/dist/cjs/PivotViewer/hooks/useCurrentFilters.js +52 -0
  8. package/dist/cjs/PivotViewer/hooks/useCurrentFilters.js.map +1 -0
  9. package/dist/cjs/PivotViewer/hooks/useDetailPanelClose.js +63 -0
  10. package/dist/cjs/PivotViewer/hooks/useDetailPanelClose.js.map +1 -0
  11. package/dist/cjs/PivotViewer/hooks/useFieldExtractors.js +50 -0
  12. package/dist/cjs/PivotViewer/hooks/useFieldExtractors.js.map +1 -0
  13. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js +99 -65
  14. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  15. package/dist/cjs/PivotViewer/hooks/useScrollSync.js +21 -0
  16. package/dist/cjs/PivotViewer/hooks/useScrollSync.js.map +1 -0
  17. package/dist/cjs/PivotViewer/hooks/useViewModeScrollHandling.js +42 -0
  18. package/dist/cjs/PivotViewer/hooks/useViewModeScrollHandling.js.map +1 -0
  19. package/dist/cjs/PivotViewer/utils/cardPosition.js +67 -0
  20. package/dist/cjs/PivotViewer/utils/cardPosition.js.map +1 -0
  21. package/dist/cjs/PivotViewer/utils/constants.js +4 -0
  22. package/dist/cjs/PivotViewer/utils/constants.js.map +1 -1
  23. package/dist/cjs/PivotViewer/utils/idResolution.js +34 -0
  24. package/dist/cjs/PivotViewer/utils/idResolution.js.map +1 -0
  25. package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
  26. package/dist/esm/PivotViewer/PivotViewer.js +57 -378
  27. package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
  28. package/dist/esm/PivotViewer/hooks/index.d.ts +7 -0
  29. package/dist/esm/PivotViewer/hooks/index.d.ts.map +1 -1
  30. package/dist/esm/PivotViewer/hooks/index.js +7 -0
  31. package/dist/esm/PivotViewer/hooks/index.js.map +1 -1
  32. package/dist/esm/PivotViewer/hooks/useAnimationModeTracking.d.ts +2 -0
  33. package/dist/esm/PivotViewer/hooks/useAnimationModeTracking.d.ts.map +1 -0
  34. package/dist/esm/PivotViewer/hooks/useAnimationModeTracking.js +35 -0
  35. package/dist/esm/PivotViewer/hooks/useAnimationModeTracking.js.map +1 -0
  36. package/dist/esm/PivotViewer/hooks/useCardSelection.d.ts +37 -0
  37. package/dist/esm/PivotViewer/hooks/useCardSelection.d.ts.map +1 -0
  38. package/dist/esm/PivotViewer/hooks/useCardSelection.js +62 -0
  39. package/dist/esm/PivotViewer/hooks/useCardSelection.js.map +1 -0
  40. package/dist/esm/PivotViewer/hooks/useCurrentFilters.d.ts +5 -0
  41. package/dist/esm/PivotViewer/hooks/useCurrentFilters.d.ts.map +1 -0
  42. package/dist/esm/PivotViewer/hooks/useCurrentFilters.js +49 -0
  43. package/dist/esm/PivotViewer/hooks/useCurrentFilters.js.map +1 -0
  44. package/dist/esm/PivotViewer/hooks/useDetailPanelClose.d.ts +27 -0
  45. package/dist/esm/PivotViewer/hooks/useDetailPanelClose.d.ts.map +1 -0
  46. package/dist/esm/PivotViewer/hooks/useDetailPanelClose.js +61 -0
  47. package/dist/esm/PivotViewer/hooks/useDetailPanelClose.js.map +1 -0
  48. package/dist/esm/PivotViewer/hooks/useFieldExtractors.d.ts +7 -0
  49. package/dist/esm/PivotViewer/hooks/useFieldExtractors.d.ts.map +1 -0
  50. package/dist/esm/PivotViewer/hooks/useFieldExtractors.js +48 -0
  51. package/dist/esm/PivotViewer/hooks/useFieldExtractors.js.map +1 -0
  52. package/dist/esm/PivotViewer/hooks/usePivotEngine.d.ts.map +1 -1
  53. package/dist/esm/PivotViewer/hooks/usePivotEngine.js +100 -66
  54. package/dist/esm/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  55. package/dist/esm/PivotViewer/hooks/useScrollSync.d.ts +3 -0
  56. package/dist/esm/PivotViewer/hooks/useScrollSync.d.ts.map +1 -0
  57. package/dist/esm/PivotViewer/hooks/useScrollSync.js +19 -0
  58. package/dist/esm/PivotViewer/hooks/useScrollSync.js.map +1 -0
  59. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.d.ts +16 -0
  60. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.d.ts.map +1 -0
  61. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.js +40 -0
  62. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.js.map +1 -0
  63. package/dist/esm/PivotViewer/utils/cardPosition.d.ts +18 -0
  64. package/dist/esm/PivotViewer/utils/cardPosition.d.ts.map +1 -0
  65. package/dist/esm/PivotViewer/utils/cardPosition.js +63 -0
  66. package/dist/esm/PivotViewer/utils/cardPosition.js.map +1 -0
  67. package/dist/esm/PivotViewer/utils/constants.js +3 -1
  68. package/dist/esm/PivotViewer/utils/constants.js.map +1 -1
  69. package/dist/esm/PivotViewer/utils/idResolution.d.ts +13 -0
  70. package/dist/esm/PivotViewer/utils/idResolution.d.ts.map +1 -0
  71. package/dist/esm/PivotViewer/utils/idResolution.js +31 -0
  72. package/dist/esm/PivotViewer/utils/idResolution.js.map +1 -0
  73. package/dist/esm/PivotViewer/utils/index.d.ts +2 -0
  74. package/dist/esm/PivotViewer/utils/index.d.ts.map +1 -1
  75. package/dist/esm/PivotViewer/utils/index.js +2 -0
  76. package/dist/esm/PivotViewer/utils/index.js.map +1 -1
  77. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +1 -1
@@ -5,10 +5,7 @@ import { computeLayout } from './engine/layout.js';
5
5
  import { useFilterState } from './hooks/useFilterState.js';
6
6
  import { useDimensionState } from './hooks/useDimensionState.js';
7
7
  import { useZoomState } from './hooks/useZoomState.js';
8
- import { handleCardSelection } from './utils/selection.js';
9
- import { calculateCenterScrollPosition, smoothScrollTo, animateZoomAndScroll } from './utils/animations.js';
10
8
  import { BASE_CARD_HEIGHT, BASE_CARD_WIDTH, GROUP_SPACING, CARDS_PER_COLUMN } from './constants.js';
11
- import { DETAIL_PANEL_WIDTH, ZOOM_MAX, MIN_ZOOM_ON_SELECT, ZOOM_MULTIPLIER } from './utils/constants.js';
12
9
  import './PivotViewer.css';
13
10
  import { PivotViewerMain } from './components/PivotViewerMain.js';
14
11
  import { FilterPanelContainer } from './components/FilterPanelContainer.js';
@@ -17,6 +14,13 @@ import { useFilterOptions } from './hooks/useFilterOptions.js';
17
14
  import { usePanning } from './hooks/usePanning.js';
18
15
  import { useWheelZoom } from './hooks/useWheelZoom.js';
19
16
  import 'react-dom';
17
+ import { useFieldExtractors } from './hooks/useFieldExtractors.js';
18
+ import { useCurrentFilters, useCurrentGroupBy } from './hooks/useCurrentFilters.js';
19
+ import { useCardSelection } from './hooks/useCardSelection.js';
20
+ import { useDetailPanelClose } from './hooks/useDetailPanelClose.js';
21
+ import { useScrollSync } from './hooks/useScrollSync.js';
22
+ import { useAnimationModeTracking } from './hooks/useAnimationModeTracking.js';
23
+ import { useViewModeScrollHandling } from './hooks/useViewModeScrollHandling.js';
20
24
  import { useContainerDimensions } from './hooks/useContainerDimensions.js';
21
25
 
22
26
  function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRenderer, getItemId, searchFields, className, emptyContent, isLoading = false, }) {
@@ -37,12 +41,6 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
37
41
  const [scrollPosition, setScrollPosition] = useState({ x: 0, y: 0 });
38
42
  const { filterState, rangeFilterState, expandedFilterKey, setExpandedFilterKey, handleToggleFilter, handleClearFilter, handleRangeChange, } = useFilterState(filters);
39
43
  const { activeDimensionKey, setActiveDimensionKey, activeDimension, dimensionFilter, handleAxisLabelClick, } = useDimensionState(dimensions, defaultDimensionKey);
40
- const prevFilterStateRef = useRef(filterState);
41
- const prevRangeFilterStateRef = useRef(rangeFilterState);
42
- const prevSearchRef = useRef(search);
43
- const prevDimensionRef = useRef(activeDimensionKey);
44
- const prevViewModeRef = useRef(viewMode);
45
- const isFirstRenderRef = useRef(true);
46
44
  const { zoomLevel, setZoomLevel, handleZoomIn, handleZoomOut, handleZoomSlider, } = useZoomState(1);
47
45
  const { isPanning, handlePanStart, handlePanMove, handlePanEnd, } = usePanning(containerRef, undefined, setScrollPosition);
48
46
  useWheelZoom(containerRef, zoomLevel, setZoomLevel);
@@ -60,125 +58,14 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
60
58
  container.addEventListener('scroll', handleScroll);
61
59
  return () => container.removeEventListener('scroll', handleScroll);
62
60
  }, []);
63
- useEffect(() => {
64
- if (isFirstRenderRef.current) {
65
- isFirstRenderRef.current = false;
66
- return;
67
- }
68
- const filterChanged = prevFilterStateRef.current !== filterState;
69
- const rangeChanged = prevRangeFilterStateRef.current !== rangeFilterState;
70
- const searchChanged = prevSearchRef.current !== search;
71
- const dimensionChanged = prevDimensionRef.current !== activeDimensionKey;
72
- const viewModeChanged = prevViewModeRef.current !== viewMode;
73
- if (filterChanged || rangeChanged || searchChanged) {
74
- setAnimationMode('filter');
75
- }
76
- else if (dimensionChanged || viewModeChanged) {
77
- setAnimationMode('layout');
78
- }
79
- prevFilterStateRef.current = filterState;
80
- prevRangeFilterStateRef.current = rangeFilterState;
81
- prevSearchRef.current = search;
82
- prevDimensionRef.current = activeDimensionKey;
83
- prevViewModeRef.current = viewMode;
84
- }, [filterState, rangeFilterState, search, activeDimensionKey, viewMode]);
85
- useEffect(() => {
86
- const container = containerRef.current;
87
- const axisLabels = axisLabelsRef.current;
88
- if (!container || !axisLabels || viewMode !== 'grouped')
89
- return;
90
- const handleScroll = () => {
91
- axisLabels.scrollLeft = container.scrollLeft;
92
- };
93
- handleScroll();
94
- container.addEventListener('scroll', handleScroll);
95
- return () => container.removeEventListener('scroll', handleScroll);
96
- }, [viewMode]);
97
- const fieldExtractors = useMemo(() => {
98
- const extractors = new Map();
99
- for (const dim of dimensions) {
100
- extractors.set(dim.key, (item) => {
101
- const val = dim.getValue(item);
102
- if (val instanceof Date)
103
- return val.getTime();
104
- if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' || val === null) {
105
- return val;
106
- }
107
- return String(val);
108
- });
109
- }
110
- if (filters) {
111
- for (const filter of filters) {
112
- extractors.set(filter.key, (item) => {
113
- const val = filter.getValue(item);
114
- if (val instanceof Date)
115
- return val.getTime();
116
- if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' || val === null) {
117
- return val;
118
- }
119
- return String(val);
120
- });
121
- }
122
- }
123
- return extractors;
124
- }, [dimensions, filters]);
125
- const indexFields = useMemo(() => {
126
- const fields = new Set();
127
- for (const dim of dimensions) {
128
- fields.add(dim.key);
129
- }
130
- if (filters) {
131
- for (const filter of filters) {
132
- fields.add(filter.key);
133
- }
134
- }
135
- return Array.from(fields);
136
- }, [dimensions, filters]);
61
+ const { fieldExtractors, indexFields } = useFieldExtractors(dimensions, filters);
137
62
  const { ready, applyFilters: engineApplyFilters, computeGrouping, sortIds } = usePivotEngine({
138
63
  data,
139
64
  fieldExtractors,
140
65
  indexFields,
141
66
  });
142
- const currentFilters = useMemo(() => {
143
- const specs = [];
144
- const searchTerm = search.trim().toLowerCase();
145
- if (searchTerm && searchFields && searchFields.length > 0) ;
146
- for (const [key, values] of Object.entries(filterState)) {
147
- const valueSet = values;
148
- if (valueSet.size > 0) {
149
- specs.push({
150
- field: key,
151
- type: 'categorical',
152
- values: valueSet,
153
- });
154
- }
155
- }
156
- for (const [key, range] of Object.entries(rangeFilterState)) {
157
- if (range && (range[0] !== null || range[1] !== null)) {
158
- const min = range[0] ?? -Infinity;
159
- const max = range[1] ?? Infinity;
160
- specs.push({
161
- field: key,
162
- type: 'numeric',
163
- range: { min, max },
164
- });
165
- }
166
- }
167
- if (dimensionFilter && activeDimension) {
168
- specs.push({
169
- field: activeDimension.key,
170
- type: 'categorical',
171
- values: new Set([dimensionFilter]),
172
- });
173
- }
174
- return specs;
175
- }, [filterState, rangeFilterState, search, searchFields, dimensionFilter, activeDimension]);
176
- const currentGroupBy = useMemo(() => {
177
- return {
178
- field: activeDimensionKey || dimensions[0]?.key || '',
179
- buckets: 10,
180
- };
181
- }, [activeDimensionKey, dimensions]);
67
+ const currentFilters = useCurrentFilters(filters, filterState, rangeFilterState, search, searchFields, dimensionFilter, activeDimension);
68
+ const currentGroupBy = useCurrentGroupBy(activeDimensionKey, dimensions);
182
69
  useEffect(() => {
183
70
  if (!ready)
184
71
  return;
@@ -224,7 +111,7 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
224
111
  const lastRequest = lastGroupingRequest.current;
225
112
  if (lastRequest &&
226
113
  lastRequest.viewMode === viewMode &&
227
- lastRequest.groupBy.field === currentGroupBy.field &&
114
+ lastRequest.groupBy?.field === currentGroupBy.field &&
228
115
  lastRequest.visibleIds === visibleIds) {
229
116
  console.log('[PivotViewer] Skipping duplicate grouping request');
230
117
  return;
@@ -262,260 +149,52 @@ function PivotViewer({ data, dimensions, filters, defaultDimensionKey, cardRende
262
149
  const id = item['id'];
263
150
  return typeof id === 'number' ? id : index;
264
151
  }, [getItemId]);
265
- const lastProcessedViewMode = useRef(viewMode);
266
- const lastProcessedGrouping = useRef(grouping);
267
- useEffect(() => {
268
- const viewModeChanged = lastProcessedViewMode.current !== viewMode;
269
- const groupingChanged = lastProcessedGrouping.current !== grouping;
270
- if (!viewModeChanged && !groupingChanged)
271
- return;
272
- lastProcessedViewMode.current = viewMode;
273
- lastProcessedGrouping.current = grouping;
274
- const container = containerRef.current;
275
- if (!container)
276
- return;
277
- if (selectedItem) {
278
- let itemId = resolveId(selectedItem, 0);
279
- if (typeof itemId === 'string' && !layout.positions.has(itemId)) {
280
- const numId = Number(itemId);
281
- if (!isNaN(numId) && layout.positions.has(numId))
282
- itemId = numId;
283
- }
284
- else if (typeof itemId === 'number' && !layout.positions.has(itemId)) {
285
- const strId = String(itemId);
286
- if (layout.positions.has(strId))
287
- itemId = strId;
288
- }
289
- const position = layout.positions.get(itemId);
290
- if (position) {
291
- const cardPosition = {
292
- x: position.x,
293
- y: position.y,
294
- width: BASE_CARD_WIDTH,
295
- height: BASE_CARD_HEIGHT
296
- };
297
- const detailWidth = viewMode === 'collection' ? 0 : DETAIL_PANEL_WIDTH;
298
- const { scrollLeft, scrollTop } = calculateCenterScrollPosition(container, cardPosition, zoomLevel, detailWidth, layout.totalHeight);
299
- container.scrollTo({ left: scrollLeft, top: scrollTop });
300
- setPreSelectionState(null);
301
- }
302
- }
303
- else if (viewMode === 'grouped') {
304
- setTimeout(() => {
305
- container.scrollTop = container.scrollHeight;
306
- setScrollPosition({ x: container.scrollLeft, y: container.scrollTop });
307
- }, 0);
308
- }
309
- }, [viewMode, grouping, layout, selectedItem, resolveId, zoomLevel]);
310
- const handleCardClick = useCallback((item, e, id) => {
311
- if (isPanning)
312
- return;
313
- const container = containerRef.current;
314
- if (!container)
315
- return;
316
- let itemId = (id !== undefined && id !== null) ? id : resolveId(item, 0);
317
- if (typeof itemId === 'string' && !layout.positions.has(itemId)) {
318
- const numId = Number(itemId);
319
- if (!isNaN(numId) && layout.positions.has(numId)) {
320
- itemId = numId;
321
- }
322
- }
323
- else if (typeof itemId === 'number' && !layout.positions.has(itemId)) {
324
- const strId = String(itemId);
325
- if (layout.positions.has(strId)) {
326
- itemId = strId;
327
- }
328
- }
329
- const selectedId = selectedItem ? (data.indexOf(selectedItem) !== -1 ? data.indexOf(selectedItem) : resolveId(selectedItem, 0)) : null;
330
- const position = layout.positions.get(itemId);
331
- const cardPosition = position ? {
332
- x: position.x,
333
- y: position.y,
334
- width: BASE_CARD_WIDTH,
335
- height: BASE_CARD_HEIGHT
336
- } : null;
337
- let targetCardPosition = null;
338
- let getCardPositionAtZoom = undefined;
339
- let targetTotalHeight = layout.totalHeight;
340
- if (viewMode === 'grouped' && cardPosition) {
341
- const targetZoom = Math.min(ZOOM_MAX, Math.max(MIN_ZOOM_ON_SELECT, zoomLevel * ZOOM_MULTIPLIER));
342
- const targetContainerWidth = containerDimensions.width / targetZoom;
343
- const targetContainerHeight = containerDimensions.height;
344
- const targetLayout = computeLayout(grouping, {
345
- viewMode,
346
- cardWidth: BASE_CARD_WIDTH,
347
- cardHeight: BASE_CARD_HEIGHT,
348
- cardsPerColumn: CARDS_PER_COLUMN,
349
- groupSpacing: GROUP_SPACING,
350
- containerWidth: targetContainerWidth,
351
- containerHeight: targetContainerHeight,
352
- });
353
- targetTotalHeight = targetLayout.totalHeight;
354
- const targetPos = targetLayout.positions.get(itemId);
355
- if (targetPos) {
356
- targetCardPosition = {
357
- x: targetPos.x,
358
- y: targetPos.y,
359
- width: BASE_CARD_WIDTH,
360
- height: BASE_CARD_HEIGHT
361
- };
362
- }
363
- getCardPositionAtZoom = (zoom) => {
364
- const currentContainerWidth = containerDimensions.width / zoom;
365
- const currentContainerHeight = containerDimensions.height;
366
- const currentLayout = computeLayout(grouping, {
367
- viewMode,
368
- cardWidth: BASE_CARD_WIDTH,
369
- cardHeight: BASE_CARD_HEIGHT,
370
- cardsPerColumn: CARDS_PER_COLUMN,
371
- groupSpacing: GROUP_SPACING,
372
- containerWidth: currentContainerWidth,
373
- containerHeight: currentContainerHeight,
374
- });
375
- const pos = currentLayout.positions.get(itemId);
376
- return pos ? { x: pos.x, y: pos.y, width: BASE_CARD_WIDTH, height: BASE_CARD_HEIGHT } : null;
377
- };
378
- }
379
- const getLayoutSizeAtZoom = (zoom) => {
380
- if (viewMode === 'collection') {
381
- return { width: layout.totalWidth, height: layout.totalHeight };
382
- }
383
- const currentContainerWidth = containerDimensions.width / zoom;
384
- const currentContainerHeight = containerDimensions.height;
385
- const currentLayout = computeLayout(grouping, {
386
- viewMode,
387
- cardWidth: BASE_CARD_WIDTH,
388
- cardHeight: BASE_CARD_HEIGHT,
389
- cardsPerColumn: CARDS_PER_COLUMN,
390
- groupSpacing: GROUP_SPACING,
391
- containerWidth: currentContainerWidth,
392
- containerHeight: currentContainerHeight,
393
- });
394
- return { width: currentLayout.totalWidth, height: currentLayout.totalHeight };
395
- };
396
- handleCardSelection({
397
- item,
398
- itemId,
399
- selectedItemId: selectedId,
400
- container,
401
- cardPosition,
402
- targetCardPosition,
403
- getCardPositionAtZoom,
404
- getLayoutSizeAtZoom,
405
- spacer: spacerRef.current,
406
- preSelectionState,
407
- startScrollPosition: { x: scrollPosition.x, y: scrollPosition.y },
408
- setZoomLevel,
409
- setIsZooming,
410
- setSelectedItem,
411
- setPreSelectionState,
412
- viewMode,
413
- zoomLevel,
414
- totalHeight: targetTotalHeight,
415
- });
416
- }, [isPanning, selectedItem, zoomLevel, preSelectionState, viewMode, resolveId, setZoomLevel, layout, grouping, containerDimensions, scrollPosition]);
417
- const closeDetail = useCallback(() => {
418
- const container = containerRef.current;
419
- if (!container || !selectedItem) {
420
- setSelectedItem(null);
421
- return;
422
- }
423
- const index = data.indexOf(selectedItem);
424
- let itemId = index !== -1 ? index : resolveId(selectedItem, 0);
425
- if (typeof itemId === 'string' && !layout.positions.has(itemId)) {
426
- const numId = Number(itemId);
427
- if (!isNaN(numId) && layout.positions.has(numId)) {
428
- itemId = numId;
429
- }
430
- }
431
- else if (typeof itemId === 'number' && !layout.positions.has(itemId)) {
432
- const strId = String(itemId);
433
- if (layout.positions.has(strId)) {
434
- itemId = strId;
435
- }
436
- }
437
- const position = layout.positions.get(itemId);
438
- const cardPosition = position ? {
439
- x: position.x,
440
- y: position.y,
441
- width: BASE_CARD_WIDTH,
442
- height: BASE_CARD_HEIGHT
443
- } : null;
444
- if (!preSelectionState) {
445
- setSelectedItem(null);
446
- return;
447
- }
448
- if (viewMode === 'collection') {
449
- setSelectedItem(null);
450
- smoothScrollTo(container, preSelectionState.scrollLeft, preSelectionState.scrollTop, true);
451
- setPreSelectionState(null);
452
- return;
453
- }
454
- const zoomChanged = Math.abs(preSelectionState.zoom - zoomLevel) > 0.001;
455
- if (!zoomChanged || !cardPosition) {
456
- setSelectedItem(null);
457
- smoothScrollTo(container, preSelectionState.scrollLeft, preSelectionState.scrollTop, true);
458
- setPreSelectionState(null);
459
- return;
460
- }
461
- let targetCardPosition = null;
462
- let getCardPositionAtZoom = undefined;
463
- if (viewMode === 'grouped') {
464
- const targetZoom = preSelectionState.zoom;
465
- const targetContainerWidth = containerDimensions.width / targetZoom;
466
- const targetContainerHeight = containerDimensions.height;
467
- const targetLayout = computeLayout(grouping, {
468
- viewMode,
469
- cardWidth: BASE_CARD_WIDTH,
470
- cardHeight: BASE_CARD_HEIGHT,
471
- cardsPerColumn: CARDS_PER_COLUMN,
472
- groupSpacing: GROUP_SPACING,
473
- containerWidth: targetContainerWidth,
474
- containerHeight: targetContainerHeight,
475
- });
476
- const targetPos = targetLayout.positions.get(itemId);
477
- if (targetPos) {
478
- targetCardPosition = {
479
- x: targetPos.x,
480
- y: targetPos.y,
481
- width: BASE_CARD_WIDTH,
482
- height: BASE_CARD_HEIGHT
483
- };
484
- }
485
- getCardPositionAtZoom = (zoom) => {
486
- const currentContainerWidth = containerDimensions.width / zoom;
487
- const currentContainerHeight = containerDimensions.height;
488
- const currentLayout = computeLayout(grouping, {
489
- viewMode,
490
- cardWidth: BASE_CARD_WIDTH,
491
- cardHeight: BASE_CARD_HEIGHT,
492
- cardsPerColumn: CARDS_PER_COLUMN,
493
- groupSpacing: GROUP_SPACING,
494
- containerWidth: currentContainerWidth,
495
- containerHeight: currentContainerHeight,
496
- });
497
- const pos = currentLayout.positions.get(itemId);
498
- return pos ? { x: pos.x, y: pos.y, width: BASE_CARD_WIDTH, height: BASE_CARD_HEIGHT } : null;
499
- };
500
- }
501
- setIsZooming(true);
502
- animateZoomAndScroll({
503
- container,
504
- cardPosition,
505
- targetCardPosition,
506
- getCardPositionAtZoom,
507
- startZoom: zoomLevel,
508
- targetZoom: preSelectionState.zoom,
509
- targetScrollLeft: preSelectionState.scrollLeft,
510
- targetScrollTop: preSelectionState.scrollTop,
511
- onUpdate: setZoomLevel,
512
- onComplete: () => {
513
- setIsZooming(false);
514
- setSelectedItem(null);
515
- setPreSelectionState(null);
516
- },
517
- });
518
- }, [preSelectionState, selectedItem, zoomLevel, viewMode, resolveId, setZoomLevel, layout, grouping, containerDimensions]);
152
+ useAnimationModeTracking(filterState, rangeFilterState, search, activeDimensionKey, viewMode, setAnimationMode);
153
+ useScrollSync(containerRef, axisLabelsRef, viewMode);
154
+ useViewModeScrollHandling({
155
+ containerRef,
156
+ viewMode,
157
+ grouping,
158
+ layout,
159
+ selectedItem,
160
+ zoomLevel,
161
+ resolveId,
162
+ data,
163
+ setPreSelectionState,
164
+ });
165
+ const handleCardClick = useCardSelection({
166
+ data,
167
+ isPanning,
168
+ selectedItem,
169
+ zoomLevel,
170
+ viewMode,
171
+ layout,
172
+ containerDimensions,
173
+ scrollPosition,
174
+ preSelectionState,
175
+ grouping,
176
+ getItemId,
177
+ resolveId,
178
+ setZoomLevel,
179
+ setIsZooming,
180
+ setSelectedItem,
181
+ setPreSelectionState,
182
+ });
183
+ const closeDetail = useDetailPanelClose({
184
+ selectedItem,
185
+ preSelectionState,
186
+ zoomLevel,
187
+ viewMode,
188
+ layout,
189
+ containerDimensions,
190
+ grouping,
191
+ data,
192
+ resolveId,
193
+ setZoomLevel,
194
+ setIsZooming,
195
+ setSelectedItem,
196
+ setPreSelectionState,
197
+ });
519
198
  const cardWidth = BASE_CARD_WIDTH;
520
199
  const cardHeight = BASE_CARD_HEIGHT;
521
200
  const filterOptions = useFilterOptions(data, filters, filterState, rangeFilterState);