@cratis/components 4.26.3 → 4.27.0

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 (129) hide show
  1. package/compat-manifest.json +11 -11
  2. package/dist/esm/Common/CratisComponentsProvider.d.ts +4 -0
  3. package/dist/esm/Common/CratisComponentsProvider.d.ts.map +1 -1
  4. package/dist/esm/Common/CratisComponentsProvider.js +1 -0
  5. package/dist/esm/Common/CratisComponentsProvider.js.map +1 -1
  6. package/dist/esm/DataPage/DataPage.d.ts +2 -0
  7. package/dist/esm/DataPage/DataPage.d.ts.map +1 -1
  8. package/dist/esm/DataPage/DataPage.js.map +1 -1
  9. package/dist/esm/DataTables/DataTableForObservableQuery.d.ts +2 -0
  10. package/dist/esm/DataTables/DataTableForObservableQuery.d.ts.map +1 -1
  11. package/dist/esm/DataTables/DataTableForObservableQuery.js +1 -1
  12. package/dist/esm/DataTables/DataTableForObservableQuery.js.map +1 -1
  13. package/dist/esm/DataTables/DataTableForQuery.d.ts +2 -0
  14. package/dist/esm/DataTables/DataTableForQuery.d.ts.map +1 -1
  15. package/dist/esm/DataTables/DataTableForQuery.js +1 -1
  16. package/dist/esm/DataTables/DataTableForQuery.js.map +1 -1
  17. package/dist/esm/DataTables/PaginatorPageItem.d.ts +13 -0
  18. package/dist/esm/DataTables/PaginatorPageItem.d.ts.map +1 -0
  19. package/dist/esm/DataTables/PaginatorPageItem.js +4 -0
  20. package/dist/esm/DataTables/PaginatorPageItem.js.map +1 -0
  21. package/dist/esm/DataTables/TablePaginator.d.ts +22 -2
  22. package/dist/esm/DataTables/TablePaginator.d.ts.map +1 -1
  23. package/dist/esm/DataTables/TablePaginator.js.map +1 -1
  24. package/dist/esm/DataTables/TablePaginatorImplementation.d.ts +1 -1
  25. package/dist/esm/DataTables/TablePaginatorImplementation.d.ts.map +1 -1
  26. package/dist/esm/DataTables/TablePaginatorImplementation.js +8 -3
  27. package/dist/esm/DataTables/TablePaginatorImplementation.js.map +1 -1
  28. package/dist/esm/DataTables/TablePaginatorPages.d.ts +9 -0
  29. package/dist/esm/DataTables/TablePaginatorPages.d.ts.map +1 -0
  30. package/dist/esm/DataTables/TablePaginatorPages.js +20 -0
  31. package/dist/esm/DataTables/TablePaginatorPages.js.map +1 -0
  32. package/dist/esm/DataTables/index.d.ts +2 -0
  33. package/dist/esm/DataTables/index.d.ts.map +1 -1
  34. package/dist/esm/DataTables/index.js +1 -0
  35. package/dist/esm/DataTables/index.js.map +1 -1
  36. package/dist/esm/DataTables/paginatorPages.d.ts +15 -0
  37. package/dist/esm/DataTables/paginatorPages.d.ts.map +1 -0
  38. package/dist/esm/DataTables/paginatorPages.js +40 -0
  39. package/dist/esm/DataTables/paginatorPages.js.map +1 -0
  40. package/dist/esm/Filter/FilterGroup.d.ts +23 -0
  41. package/dist/esm/Filter/FilterGroup.d.ts.map +1 -0
  42. package/dist/esm/Filter/FilterGroup.js +51 -0
  43. package/dist/esm/Filter/FilterGroup.js.map +1 -0
  44. package/dist/esm/Filter/FilterPanel.d.ts.map +1 -1
  45. package/dist/esm/Filter/FilterPanel.js +24 -257
  46. package/dist/esm/Filter/FilterPanel.js.map +1 -1
  47. package/dist/esm/Filter/FilterSearchBox.d.ts +10 -0
  48. package/dist/esm/Filter/FilterSearchBox.d.ts.map +1 -0
  49. package/dist/esm/Filter/FilterSearchBox.js +10 -0
  50. package/dist/esm/Filter/FilterSearchBox.js.map +1 -0
  51. package/dist/esm/Filter/measurePanelPlacement.d.ts +31 -0
  52. package/dist/esm/Filter/measurePanelPlacement.d.ts.map +1 -0
  53. package/dist/esm/Filter/measurePanelPlacement.js +132 -0
  54. package/dist/esm/Filter/measurePanelPlacement.js.map +1 -0
  55. package/dist/esm/Filter/usePanelDismissal.d.ts +32 -0
  56. package/dist/esm/Filter/usePanelDismissal.d.ts.map +1 -0
  57. package/dist/esm/Filter/usePanelDismissal.js +63 -0
  58. package/dist/esm/Filter/usePanelDismissal.js.map +1 -0
  59. package/dist/esm/Filter/usePanelPosition.d.ts +24 -0
  60. package/dist/esm/Filter/usePanelPosition.d.ts.map +1 -0
  61. package/dist/esm/Filter/usePanelPosition.js +85 -0
  62. package/dist/esm/Filter/usePanelPosition.js.map +1 -0
  63. package/dist/esm/Filter/useResolvedAnchor.d.ts +17 -0
  64. package/dist/esm/Filter/useResolvedAnchor.d.ts.map +1 -0
  65. package/dist/esm/Filter/useResolvedAnchor.js +16 -0
  66. package/dist/esm/Filter/useResolvedAnchor.js.map +1 -0
  67. package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
  68. package/dist/esm/PivotViewer/PivotViewer.js +47 -207
  69. package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
  70. package/dist/esm/PivotViewer/components/pivot/sprites.js +1 -1
  71. package/dist/esm/PivotViewer/components/pivot/syncScrollSprites.d.ts +2 -2
  72. package/dist/esm/PivotViewer/components/pivot/syncScrollSprites.d.ts.map +1 -1
  73. package/dist/esm/PivotViewer/components/pivot/syncScrollSprites.js +1 -1
  74. package/dist/esm/PivotViewer/components/pivot/syncScrollSprites.js.map +1 -1
  75. package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.d.ts.map +1 -1
  76. package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.js +8 -2
  77. package/dist/esm/PivotViewer/components/pivot/usePivotScrollSync.js.map +1 -1
  78. package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.d.ts.map +1 -1
  79. package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.js +49 -28
  80. package/dist/esm/PivotViewer/components/pivot/usePivotSpriteSync.js.map +1 -1
  81. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts +12 -2
  82. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
  83. package/dist/esm/PivotViewer/components/pivot/visibility.js +34 -194
  84. package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
  85. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.d.ts +114 -0
  86. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.d.ts.map +1 -0
  87. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.js +306 -0
  88. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.js.map +1 -0
  89. package/dist/esm/PivotViewer/hooks/useColorCssVariables.d.ts +9 -0
  90. package/dist/esm/PivotViewer/hooks/useColorCssVariables.d.ts.map +1 -0
  91. package/dist/esm/PivotViewer/hooks/useColorCssVariables.js +32 -0
  92. package/dist/esm/PivotViewer/hooks/useColorCssVariables.js.map +1 -0
  93. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.d.ts +27 -0
  94. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.d.ts.map +1 -0
  95. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.js +40 -0
  96. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.js.map +1 -0
  97. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.d.ts +27 -0
  98. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.d.ts.map +1 -0
  99. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.js +46 -0
  100. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.js.map +1 -0
  101. package/dist/esm/PivotViewer/hooks/useRawGrouping.d.ts +22 -0
  102. package/dist/esm/PivotViewer/hooks/useRawGrouping.d.ts.map +1 -0
  103. package/dist/esm/PivotViewer/hooks/useRawGrouping.js +56 -0
  104. package/dist/esm/PivotViewer/hooks/useRawGrouping.js.map +1 -0
  105. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.d.ts +12 -0
  106. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.d.ts.map +1 -0
  107. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.js +20 -0
  108. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.js.map +1 -0
  109. package/dist/esm/PivotViewer/hooks/useViewerLayout.d.ts +16 -0
  110. package/dist/esm/PivotViewer/hooks/useViewerLayout.d.ts.map +1 -0
  111. package/dist/esm/PivotViewer/hooks/useViewerLayout.js +29 -0
  112. package/dist/esm/PivotViewer/hooks/useViewerLayout.js.map +1 -0
  113. package/dist/esm/PivotViewer/hooks/useVisibleIds.d.ts +21 -0
  114. package/dist/esm/PivotViewer/hooks/useVisibleIds.d.ts.map +1 -0
  115. package/dist/esm/PivotViewer/hooks/useVisibleIds.js +34 -0
  116. package/dist/esm/PivotViewer/hooks/useVisibleIds.js.map +1 -0
  117. package/dist/esm/configuration/CratisComponentsDefaults.d.ts.map +1 -1
  118. package/dist/esm/configuration/CratisComponentsDefaults.js +2 -0
  119. package/dist/esm/configuration/CratisComponentsDefaults.js.map +1 -1
  120. package/dist/esm/styles.DataPage.css +51 -6
  121. package/dist/esm/styles.DataTables.css +51 -6
  122. package/dist/esm/styles.SchemaEditor.css +51 -6
  123. package/dist/esm/styles.css +51 -6
  124. package/dist/esm/styles.renderer.builtin.css +51 -6
  125. package/dist/esm/types/parts.d.ts +4 -1
  126. package/dist/esm/types/parts.d.ts.map +1 -1
  127. package/dist/esm/types/parts.js +4 -1
  128. package/dist/esm/types/parts.js.map +1 -1
  129. package/package.json +1 -1
@@ -1,13 +1,40 @@
1
- import { useEffect } from 'react';
1
+ import { useRef, useEffect } from 'react';
2
2
  import { startAnimationLoop } from './animation.js';
3
3
  import { asReactMouseEvent } from './asReactMouseEvent.js';
4
4
  import { updateCardContent, createCardSprite } from './sprites.js';
5
- import { syncSpritesToViewport } from './visibility.js';
5
+ import { syncSpritesToViewport, createTransitionCompleteSync } from './visibility.js';
6
6
 
7
7
  const usePivotSpriteSync = ({ props, stage, transition, cardColorsRef, colorRevision, onCardClickRef, }) => {
8
8
  const { items, layout, grouping, visibleIds, cardWidth, cardHeight, zoomLevel, panX, panY, viewportWidth, viewportHeight, selectedId, viewMode, cardRenderer, colorOverrides, resolveId, onPanStart, containerRef: parentContainerRef, } = props;
9
9
  const { rootRef, groupsContainerRef, spritesRef, appRef, animationFrameRef, mountedRef, needsRenderRef, pixiReady, } = stage;
10
10
  const { isAnimatingRef, isViewTransitionRef, lastViewChangeTimeRef, previousViewModeRef, prevLayoutRef, transitionLayoutRef, transitionSeenIdsRef, prevGroupingRef, prevScrollTopRef, prevScrollLeftRef, prevPanRef, } = transition;
11
+ const buildSyncParamsRef = useRef(null);
12
+ useEffect(() => {
13
+ buildSyncParamsRef.current = () => {
14
+ return {
15
+ root: rootRef.current,
16
+ groupsContainer: groupsContainerRef.current,
17
+ container: parentContainerRef.current,
18
+ sprites: spritesRef.current,
19
+ layout,
20
+ visibleIds,
21
+ items,
22
+ cardWidth,
23
+ cardHeight,
24
+ panX,
25
+ panY,
26
+ zoomLevel,
27
+ viewportWidth,
28
+ viewportHeight,
29
+ viewMode,
30
+ createCardSprite: (id, x, y) => createCardSprite(id, x, y, items, (item, e, id) => onCardClickRef.current(item, e, id), (e) => onPanStart(asReactMouseEvent(e)), cardWidth, cardHeight, cardColorsRef.current, cardRenderer, resolveId),
31
+ updateCardContent: (sprite, item) => updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, cardColorsRef.current, cardRenderer),
32
+ transitionSeenIds: transitionSeenIdsRef.current,
33
+ prevScrollTop: prevScrollTopRef.current,
34
+ prevScrollLeft: prevScrollLeftRef.current,
35
+ };
36
+ };
37
+ });
11
38
  useEffect(() => {
12
39
  if (!rootRef.current || !parentContainerRef.current || !pixiReady) {
13
40
  return;
@@ -30,32 +57,13 @@ const usePivotSpriteSync = ({ props, stage, transition, cardColorsRef, colorRevi
30
57
  const currentScrollTop = parentContainerRef.current?.scrollTop || 0;
31
58
  const currentScrollLeft = parentContainerRef.current?.scrollLeft || 0;
32
59
  const syncParams = {
33
- root: rootRef.current,
34
- groupsContainer: groupsContainerRef.current,
35
- container: parentContainerRef.current,
36
- sprites: spritesRef.current,
37
- layout,
38
- visibleIds,
39
- items,
40
- cardWidth,
41
- cardHeight,
42
- panX,
43
- panY,
60
+ ...buildSyncParamsRef.current(),
44
61
  panDeltaX,
45
62
  panDeltaY,
46
- zoomLevel,
47
- viewportWidth,
48
- viewportHeight,
49
- viewMode,
50
- createCardSprite: (id, x, y) => createCardSprite(id, x, y, items, (item, e, id) => onCardClickRef.current(item, e, id), (e) => onPanStart(asReactMouseEvent(e)), cardWidth, cardHeight, cardColorsRef.current, cardRenderer, resolveId),
51
- updateCardContent: (sprite, item) => updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, cardColorsRef.current, cardRenderer),
52
63
  isViewTransition: isViewTransitionRef.current,
53
64
  prevLayout: prevLayoutRef.current,
54
- transitionSeenIds: transitionSeenIdsRef.current,
55
- prevScrollTop: prevScrollTopRef.current,
56
- prevScrollLeft: prevScrollLeftRef.current,
57
65
  };
58
- syncSpritesToViewport(syncParams);
66
+ const { pending } = syncSpritesToViewport(syncParams);
59
67
  prevScrollTopRef.current = currentScrollTop;
60
68
  prevScrollLeftRef.current = currentScrollLeft;
61
69
  needsRenderRef.current = true;
@@ -71,13 +79,26 @@ const usePivotSpriteSync = ({ props, stage, transition, cardColorsRef, colorRevi
71
79
  needsRenderRef,
72
80
  spritesRef,
73
81
  isViewTransitionRef,
74
- syncVisibility: () => syncSpritesToViewport({
75
- ...syncParams,
76
- isViewTransition: false,
77
- sweepImmediately: true,
78
- }),
82
+ syncVisibility: createTransitionCompleteSync(() => buildSyncParamsRef.current()),
79
83
  onTransitionComplete: () => transitionSeenIdsRef.current.clear(),
80
84
  });
85
+ let frameHandle = 0;
86
+ const replenish = () => {
87
+ const result = syncSpritesToViewport({
88
+ ...syncParams,
89
+ panDeltaX: 0,
90
+ panDeltaY: 0,
91
+ isViewTransition: isViewTransitionRef.current,
92
+ });
93
+ appRef.current?.renderer?.render(appRef.current.stage);
94
+ frameHandle = result.pending ? requestAnimationFrame(replenish) : 0;
95
+ };
96
+ if (pending)
97
+ frameHandle = requestAnimationFrame(replenish);
98
+ return () => {
99
+ if (frameHandle)
100
+ cancelAnimationFrame(frameHandle);
101
+ };
81
102
  }, [
82
103
  layout,
83
104
  visibleIds,
@@ -1 +1 @@
1
- {"version":3,"file":"usePivotSpriteSync.js","sources":["../../../../../PivotViewer/components/pivot/usePivotSpriteSync.ts"],"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 { useEffect } from 'react';\nimport { startAnimationLoop as startAnimationLoopExternal } from './animation';\nimport { asReactMouseEvent } from './asReactMouseEvent';\nimport type { CardSprite } from './constants';\nimport {\n createCardSprite as createCardSpriteExternal,\n updateCardContent as updateCardContentExternal,\n} from './sprites';\nimport { syncSpritesToViewport } from './visibility';\nimport type { PivotRenderContext } from './PivotRenderContext';\n\n/**\n * Keeps the card sprites in step with the layout, filter, zoom and pan, starting transitions when the\n * view, grouping or layout changes and running the animation loop.\n * @param context The props, stage, transition state and colors.\n */\nexport const usePivotSpriteSync = <TItem extends object>({\n props,\n stage,\n transition,\n cardColorsRef,\n colorRevision,\n onCardClickRef,\n}: PivotRenderContext<TItem>) => {\n const {\n items,\n layout,\n grouping,\n visibleIds,\n cardWidth,\n cardHeight,\n zoomLevel,\n panX,\n panY,\n viewportWidth,\n viewportHeight,\n selectedId,\n viewMode,\n cardRenderer,\n colorOverrides,\n resolveId,\n onPanStart,\n containerRef: parentContainerRef,\n } = props;\n const {\n rootRef,\n groupsContainerRef,\n spritesRef,\n appRef,\n animationFrameRef,\n mountedRef,\n needsRenderRef,\n pixiReady,\n } = stage;\n const {\n isAnimatingRef,\n isViewTransitionRef,\n lastViewChangeTimeRef,\n previousViewModeRef,\n prevLayoutRef,\n transitionLayoutRef,\n transitionSeenIdsRef,\n prevGroupingRef,\n prevScrollTopRef,\n prevScrollLeftRef,\n prevPanRef,\n } = transition;\n\n useEffect(() => {\n if (!rootRef.current || !parentContainerRef.current || !pixiReady) {\n return;\n }\n\n // Check if this is a view mode change (not just pan/scroll)\n const viewModeChanged = previousViewModeRef.current !== viewMode;\n const groupingChanged = prevGroupingRef.current !== grouping;\n const layoutChanged = prevLayoutRef.current !== layout;\n\n if (viewModeChanged || groupingChanged || layoutChanged) {\n transitionSeenIdsRef.current.clear();\n if (layoutChanged) transitionLayoutRef.current = prevLayoutRef.current;\n isViewTransitionRef.current = true;\n lastViewChangeTimeRef.current = Date.now();\n previousViewModeRef.current = viewMode;\n prevGroupingRef.current = grouping;\n\n // Don't hide sprites here - let visibility.ts handle the transition\n // The syncSpritesToViewport function will properly animate sprites to new positions\n // during view transitions (isViewTransitionRef.current = true), and visibility.ts\n // will handle cleanup of sprites that no longer have positions in the layout.\n // Previously, hiding sprites here caused sorting/transitions to not work because\n // sprites were destroyed before they could animate.\n }\n\n const panDeltaX = panX - prevPanRef.current.x;\n const panDeltaY = panY - prevPanRef.current.y;\n prevPanRef.current = { x: panX, y: panY };\n\n // Sync sprites into viewport and create/remove as needed\n // Provide wrappers for sprite creation and content update so helpers have required context\n const currentScrollTop = parentContainerRef.current?.scrollTop || 0;\n const currentScrollLeft = parentContainerRef.current?.scrollLeft || 0;\n\n const syncParams = {\n root: rootRef.current,\n groupsContainer: groupsContainerRef.current,\n container: parentContainerRef.current,\n sprites: spritesRef.current,\n layout,\n visibleIds,\n items,\n cardWidth,\n cardHeight,\n panX,\n panY,\n panDeltaX,\n panDeltaY,\n zoomLevel,\n viewportWidth,\n viewportHeight,\n viewMode,\n createCardSprite: (id: string | number, x: number, y: number) =>\n createCardSpriteExternal(\n id,\n x,\n y,\n items as TItem[],\n (item: TItem, e: MouseEvent, id: string | number) =>\n onCardClickRef.current(item, e, id),\n (e: MouseEvent) => onPanStart(asReactMouseEvent(e)),\n cardWidth,\n cardHeight,\n cardColorsRef.current,\n cardRenderer,\n resolveId,\n ),\n updateCardContent: (sprite: CardSprite, item: TItem) =>\n updateCardContentExternal(\n sprite,\n item,\n selectedId,\n cardWidth,\n cardHeight,\n cardColorsRef.current,\n cardRenderer,\n ),\n isViewTransition: isViewTransitionRef.current,\n prevLayout: prevLayoutRef.current,\n transitionSeenIds: transitionSeenIdsRef.current,\n prevScrollTop: prevScrollTopRef.current,\n prevScrollLeft: prevScrollLeftRef.current,\n };\n syncSpritesToViewport(syncParams);\n\n // Update previous scroll position for next frame\n prevScrollTopRef.current = currentScrollTop;\n prevScrollLeftRef.current = currentScrollLeft;\n needsRenderRef.current = true;\n\n // Force an immediate render after syncing sprites to ensure cards appear\n if (appRef.current?.renderer && rootRef.current) {\n appRef.current.renderer.render(appRef.current.stage);\n needsRenderRef.current = false;\n }\n\n startAnimationLoopExternal({\n mountedRef,\n appRef,\n animationFrameRef,\n isAnimatingRef,\n needsRenderRef,\n spritesRef,\n isViewTransitionRef,\n syncVisibility: () =>\n syncSpritesToViewport({\n ...syncParams,\n isViewTransition: false,\n sweepImmediately: true,\n }),\n onTransitionComplete: () => transitionSeenIdsRef.current.clear(),\n });\n }, [\n layout,\n visibleIds,\n items,\n cardWidth,\n cardHeight,\n pixiReady,\n zoomLevel,\n panX,\n panY,\n grouping,\n viewMode,\n colorOverrides,\n colorRevision,\n animationFrameRef,\n appRef,\n cardColorsRef,\n groupsContainerRef,\n isAnimatingRef,\n isViewTransitionRef,\n lastViewChangeTimeRef,\n mountedRef,\n needsRenderRef,\n onCardClickRef,\n parentContainerRef,\n prevGroupingRef,\n prevLayoutRef,\n prevPanRef,\n prevScrollLeftRef,\n prevScrollTopRef,\n previousViewModeRef,\n rootRef,\n spritesRef,\n transitionLayoutRef,\n transitionSeenIdsRef,\n ]);\n\n // Update prevLayoutRef after processing layout changes\n useEffect(() => {\n prevLayoutRef.current = layout;\n }, [layout, prevLayoutRef]);\n};\n"],"names":["createCardSpriteExternal","updateCardContentExternal","startAnimationLoopExternal"],"mappings":";;;;;;AAmBO,MAAM,kBAAkB,GAAG,CAAuB,EACrD,KAAK,EACL,KAAK,EACL,UAAU,EACV,aAAa,EACb,aAAa,EACb,cAAc,GACU,KAAI;AAC5B,IAAA,MAAM,EACF,KAAK,EACL,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACV,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,aAAa,EACb,cAAc,EACd,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,SAAS,EACT,UAAU,EACV,YAAY,EAAE,kBAAkB,GACnC,GAAG,KAAK;AACT,IAAA,MAAM,EACF,OAAO,EACP,kBAAkB,EAClB,UAAU,EACV,MAAM,EACN,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,SAAS,GACZ,GAAG,KAAK;IACT,MAAM,EACF,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,GACb,GAAG,UAAU;IAEd,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE;YAC/D;QACJ;AAGA,QAAA,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,KAAK,QAAQ;AAChE,QAAA,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,KAAK,QAAQ;AAC5D,QAAA,MAAM,aAAa,GAAG,aAAa,CAAC,OAAO,KAAK,MAAM;AAEtD,QAAA,IAAI,eAAe,IAAI,eAAe,IAAI,aAAa,EAAE;AACrD,YAAA,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE;AACpC,YAAA,IAAI,aAAa;AAAE,gBAAA,mBAAmB,CAAC,OAAO,GAAG,aAAa,CAAC,OAAO;AACtE,YAAA,mBAAmB,CAAC,OAAO,GAAG,IAAI;AAClC,YAAA,qBAAqB,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE;AAC1C,YAAA,mBAAmB,CAAC,OAAO,GAAG,QAAQ;AACtC,YAAA,eAAe,CAAC,OAAO,GAAG,QAAQ;QAQtC;QAEA,MAAM,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAC7C,QAAA,UAAU,CAAC,OAAO,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE;QAIzC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,OAAO,EAAE,SAAS,IAAI,CAAC;QACnE,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC;AAErE,QAAA,MAAM,UAAU,GAAG;YACf,IAAI,EAAE,OAAO,CAAC,OAAO;YACrB,eAAe,EAAE,kBAAkB,CAAC,OAAO;YAC3C,SAAS,EAAE,kBAAkB,CAAC,OAAO;YACrC,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,MAAM;YACN,UAAU;YACV,KAAK;YACL,SAAS;YACT,UAAU;YACV,IAAI;YACJ,IAAI;YACJ,SAAS;YACT,SAAS;YACT,SAAS;YACT,aAAa;YACb,cAAc;YACd,QAAQ;AACR,YAAA,gBAAgB,EAAE,CAAC,EAAmB,EAAE,CAAS,EAAE,CAAS,KACxDA,gBAAwB,CACpB,EAAE,EACF,CAAC,EACD,CAAC,EACD,KAAgB,EAChB,CAAC,IAAW,EAAE,CAAa,EAAE,EAAmB,KAC5C,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EACvC,CAAC,CAAa,KAAK,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,EACnD,SAAS,EACT,UAAU,EACV,aAAa,CAAC,OAAO,EACrB,YAAY,EACZ,SAAS,CACZ;YACL,iBAAiB,EAAE,CAAC,MAAkB,EAAE,IAAW,KAC/CC,iBAAyB,CACrB,MAAM,EACN,IAAI,EACJ,UAAU,EACV,SAAS,EACT,UAAU,EACV,aAAa,CAAC,OAAO,EACrB,YAAY,CACf;YACL,gBAAgB,EAAE,mBAAmB,CAAC,OAAO;YAC7C,UAAU,EAAE,aAAa,CAAC,OAAO;YACjC,iBAAiB,EAAE,oBAAoB,CAAC,OAAO;YAC/C,aAAa,EAAE,gBAAgB,CAAC,OAAO;YACvC,cAAc,EAAE,iBAAiB,CAAC,OAAO;SAC5C;QACD,qBAAqB,CAAC,UAAU,CAAC;AAGjC,QAAA,gBAAgB,CAAC,OAAO,GAAG,gBAAgB;AAC3C,QAAA,iBAAiB,CAAC,OAAO,GAAG,iBAAiB;AAC7C,QAAA,cAAc,CAAC,OAAO,GAAG,IAAI;QAG7B,IAAI,MAAM,CAAC,OAAO,EAAE,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE;AAC7C,YAAA,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AACpD,YAAA,cAAc,CAAC,OAAO,GAAG,KAAK;QAClC;AAEA,QAAAC,kBAA0B,CAAC;YACvB,UAAU;YACV,MAAM;YACN,iBAAiB;YACjB,cAAc;YACd,cAAc;YACd,UAAU;YACV,mBAAmB;AACnB,YAAA,cAAc,EAAE,MACZ,qBAAqB,CAAC;AAClB,gBAAA,GAAG,UAAU;AACb,gBAAA,gBAAgB,EAAE,KAAK;AACvB,gBAAA,gBAAgB,EAAE,IAAI;aACzB,CAAC;YACN,oBAAoB,EAAE,MAAM,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE;AACnE,SAAA,CAAC;AACN,IAAA,CAAC,EAAE;QACC,MAAM;QACN,UAAU;QACV,KAAK;QACL,SAAS;QACT,UAAU;QACV,SAAS;QACT,SAAS;QACT,IAAI;QACJ,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,cAAc;QACd,aAAa;QACb,iBAAiB;QACjB,MAAM;QACN,aAAa;QACb,kBAAkB;QAClB,cAAc;QACd,mBAAmB;QACnB,qBAAqB;QACrB,UAAU;QACV,cAAc;QACd,cAAc;QACd,kBAAkB;QAClB,eAAe;QACf,aAAa;QACb,UAAU;QACV,iBAAiB;QACjB,gBAAgB;QAChB,mBAAmB;QACnB,OAAO;QACP,UAAU;QACV,mBAAmB;QACnB,oBAAoB;AACvB,KAAA,CAAC;IAGF,SAAS,CAAC,MAAK;AACX,QAAA,aAAa,CAAC,OAAO,GAAG,MAAM;AAClC,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC/B;;"}
1
+ {"version":3,"file":"usePivotSpriteSync.js","sources":["../../../../../PivotViewer/components/pivot/usePivotSpriteSync.ts"],"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 { useEffect, useRef } from 'react';\nimport { startAnimationLoop as startAnimationLoopExternal } from './animation';\nimport { asReactMouseEvent } from './asReactMouseEvent';\nimport type { CardSprite } from './constants';\nimport {\n createCardSprite as createCardSpriteExternal,\n updateCardContent as updateCardContentExternal,\n} from './sprites';\nimport { createTransitionCompleteSync, syncSpritesToViewport, type SyncParams } from './visibility';\nimport type { PivotRenderContext } from './PivotRenderContext';\n\n/**\n * Keeps the card sprites in step with the layout, filter, zoom and pan, starting transitions when the\n * view, grouping or layout changes and running the animation loop.\n * @param context The props, stage, transition state and colors.\n */\nexport const usePivotSpriteSync = <TItem extends object>({\n props,\n stage,\n transition,\n cardColorsRef,\n colorRevision,\n onCardClickRef,\n}: PivotRenderContext<TItem>) => {\n const {\n items,\n layout,\n grouping,\n visibleIds,\n cardWidth,\n cardHeight,\n zoomLevel,\n panX,\n panY,\n viewportWidth,\n viewportHeight,\n selectedId,\n viewMode,\n cardRenderer,\n colorOverrides,\n resolveId,\n onPanStart,\n containerRef: parentContainerRef,\n } = props;\n const {\n rootRef,\n groupsContainerRef,\n spritesRef,\n appRef,\n animationFrameRef,\n mountedRef,\n needsRenderRef,\n pixiReady,\n } = stage;\n const {\n isAnimatingRef,\n isViewTransitionRef,\n lastViewChangeTimeRef,\n previousViewModeRef,\n prevLayoutRef,\n transitionLayoutRef,\n transitionSeenIdsRef,\n prevGroupingRef,\n prevScrollTopRef,\n prevScrollLeftRef,\n prevPanRef,\n } = transition;\n\n // Always holds a builder for the sync parameters of the latest render. It is refreshed before\n // the sync effect below runs, so deferred callbacks never see stale layout, zoom or items.\n const buildSyncParamsRef = useRef<(() => SyncParams<TItem>) | null>(null);\n useEffect(() => {\n buildSyncParamsRef.current = (): SyncParams<TItem> => {\n return {\n root: rootRef.current,\n groupsContainer: groupsContainerRef.current,\n container: parentContainerRef.current,\n sprites: spritesRef.current,\n layout,\n visibleIds,\n items,\n cardWidth,\n cardHeight,\n panX,\n panY,\n zoomLevel,\n viewportWidth,\n viewportHeight,\n viewMode,\n createCardSprite: (id: string | number, x: number, y: number) =>\n createCardSpriteExternal(\n id,\n x,\n y,\n items as TItem[],\n (item: TItem, e: MouseEvent, id: string | number) =>\n onCardClickRef.current(item, e, id),\n (e: MouseEvent) => onPanStart(asReactMouseEvent(e)),\n cardWidth,\n cardHeight,\n cardColorsRef.current,\n cardRenderer,\n resolveId,\n ),\n updateCardContent: (sprite: CardSprite, item: TItem) =>\n updateCardContentExternal(\n sprite,\n item,\n selectedId,\n cardWidth,\n cardHeight,\n cardColorsRef.current,\n cardRenderer,\n ),\n transitionSeenIds: transitionSeenIdsRef.current,\n prevScrollTop: prevScrollTopRef.current,\n prevScrollLeft: prevScrollLeftRef.current,\n };\n };\n });\n\n useEffect(() => {\n if (!rootRef.current || !parentContainerRef.current || !pixiReady) {\n return;\n }\n\n // Check if this is a view mode change (not just pan/scroll)\n const viewModeChanged = previousViewModeRef.current !== viewMode;\n const groupingChanged = prevGroupingRef.current !== grouping;\n const layoutChanged = prevLayoutRef.current !== layout;\n\n if (viewModeChanged || groupingChanged || layoutChanged) {\n transitionSeenIdsRef.current.clear();\n if (layoutChanged) transitionLayoutRef.current = prevLayoutRef.current;\n isViewTransitionRef.current = true;\n lastViewChangeTimeRef.current = Date.now();\n previousViewModeRef.current = viewMode;\n prevGroupingRef.current = grouping;\n\n // Don't hide sprites here - let visibility.ts handle the transition\n // The syncSpritesToViewport function will properly animate sprites to new positions\n // during view transitions (isViewTransitionRef.current = true), and visibility.ts\n // will handle cleanup of sprites that no longer have positions in the layout.\n // Previously, hiding sprites here caused sorting/transitions to not work because\n // sprites were destroyed before they could animate.\n }\n\n const panDeltaX = panX - prevPanRef.current.x;\n const panDeltaY = panY - prevPanRef.current.y;\n prevPanRef.current = { x: panX, y: panY };\n\n // Sync sprites into viewport and create/remove as needed\n // Provide wrappers for sprite creation and content update so helpers have required context\n const currentScrollTop = parentContainerRef.current?.scrollTop || 0;\n const currentScrollLeft = parentContainerRef.current?.scrollLeft || 0;\n\n const syncParams: SyncParams<TItem> = {\n ...buildSyncParamsRef.current!(),\n panDeltaX,\n panDeltaY,\n isViewTransition: isViewTransitionRef.current,\n prevLayout: prevLayoutRef.current,\n };\n const { pending } = syncSpritesToViewport(syncParams);\n\n // Update previous scroll position for next frame\n prevScrollTopRef.current = currentScrollTop;\n prevScrollLeftRef.current = currentScrollLeft;\n needsRenderRef.current = true;\n\n // Force an immediate render after syncing sprites to ensure cards appear\n if (appRef.current?.renderer && rootRef.current) {\n appRef.current.renderer.render(appRef.current.stage);\n needsRenderRef.current = false;\n }\n\n startAnimationLoopExternal({\n mountedRef,\n appRef,\n animationFrameRef,\n isAnimatingRef,\n needsRenderRef,\n spritesRef,\n isViewTransitionRef,\n // Read the current layout, zoom and items when the transition completes, not the\n // values captured when it started.\n syncVisibility: createTransitionCompleteSync(() => buildSyncParamsRef.current!()),\n onTransitionComplete: () => transitionSeenIdsRef.current.clear(),\n });\n\n // Creation was deferred by the per-frame budget: continue on the next frame until\n // the buffer is filled.\n let frameHandle = 0;\n const replenish = () => {\n const result = syncSpritesToViewport({\n ...syncParams,\n panDeltaX: 0,\n panDeltaY: 0,\n isViewTransition: isViewTransitionRef.current,\n });\n appRef.current?.renderer?.render(appRef.current.stage);\n frameHandle = result.pending ? requestAnimationFrame(replenish) : 0;\n };\n if (pending) frameHandle = requestAnimationFrame(replenish);\n\n return () => {\n if (frameHandle) cancelAnimationFrame(frameHandle);\n };\n }, [\n layout,\n visibleIds,\n items,\n cardWidth,\n cardHeight,\n pixiReady,\n zoomLevel,\n panX,\n panY,\n grouping,\n viewMode,\n colorOverrides,\n colorRevision,\n animationFrameRef,\n appRef,\n cardColorsRef,\n groupsContainerRef,\n isAnimatingRef,\n isViewTransitionRef,\n lastViewChangeTimeRef,\n mountedRef,\n needsRenderRef,\n onCardClickRef,\n parentContainerRef,\n prevGroupingRef,\n prevLayoutRef,\n prevPanRef,\n prevScrollLeftRef,\n prevScrollTopRef,\n previousViewModeRef,\n rootRef,\n spritesRef,\n transitionLayoutRef,\n transitionSeenIdsRef,\n ]);\n\n // Update prevLayoutRef after processing layout changes\n useEffect(() => {\n prevLayoutRef.current = layout;\n }, [layout, prevLayoutRef]);\n};\n"],"names":["createCardSpriteExternal","updateCardContentExternal","startAnimationLoopExternal"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import * as PIXI from 'pixi.js';
1
+ import type * as PIXI from 'pixi.js';
2
2
  import type { CardSprite } from './constants.js';
3
3
  import type { LayoutResult } from '../../engine/types.js';
4
4
  export interface SyncParams<TItem> {
@@ -28,5 +28,15 @@ export interface SyncParams<TItem> {
28
28
  prevScrollTop?: number;
29
29
  prevScrollLeft?: number;
30
30
  }
31
- export declare function syncSpritesToViewport<TItem>(params: SyncParams<TItem>): void;
31
+ export interface SyncResult {
32
+ /** True when sprite creation was deferred to respect the per-frame budget; sync again on the next frame. */
33
+ pending: boolean;
34
+ }
35
+ /**
36
+ * Creates the sync used when a transition completes. Parameters are read when the sync runs, so a
37
+ * layout, zoom or item change since the transition started is honored and on-screen cards survive the sweep.
38
+ * @param getParams Returns the current sync parameters.
39
+ */
40
+ export declare function createTransitionCompleteSync<TItem>(getParams: () => SyncParams<TItem>): () => void;
41
+ export declare function syncSpritesToViewport<TItem>(params: SyncParams<TItem>): SyncResult;
32
42
  //# sourceMappingURL=visibility.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../../../../PivotViewer/components/pivot/visibility.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAoB,CAAC;AAGvD,MAAM,WAAW,UAAU,CAAC,KAAK;IAC7B,IAAI,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC;IACxB,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;IAC5E,iBAAiB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;IAC7D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,QAiTrE"}
1
+ {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../../../../PivotViewer/components/pivot/visibility.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,IAAI,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAoB,CAAC;AAgBvD,MAAM,WAAW,UAAU,CAAC,KAAK;IAC7B,IAAI,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC;IACxB,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;IAC5E,iBAAiB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;IAC7D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACvB,4GAA4G;IAC5G,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC,KAAK,CAAC,cAIrF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,UAAU,CA0ElF"}
@@ -1,147 +1,39 @@
1
- import { CARD_GAP } from './constants.js';
2
- import { destroySprite } from './sprites.js';
1
+ import { measureViewport, applyContainerTransforms, collectInViewportIds, addLastRowIds, applySpriteVisibility, sweepHiddenSprites, tryConsumeCreationBudget, rectIntersects, spawnSprite, retargetSprite, createVisibleRect, computeWorldViewport } from './visibilityHelpers.js';
3
2
 
3
+ function createTransitionCompleteSync(getParams) {
4
+ return () => {
5
+ syncSpritesToViewport({ ...getParams(), isViewTransition: false, sweepImmediately: true });
6
+ };
7
+ }
4
8
  function syncSpritesToViewport(params) {
5
9
  const { root, groupsContainer, container, sprites, layout, visibleIds: _visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, viewMode, prevLayout, transitionSeenIds } = params;
6
10
  if (!root || !container)
7
- return;
8
- const viewportPxWidth = container.clientWidth || viewportWidth;
9
- const viewportPxHeight = container.clientHeight || viewportHeight;
10
- const contentHeightPx = (layout.totalHeight || 0) * zoomLevel;
11
- let offsetY = 0;
12
- if (viewMode === 'grouped' && contentHeightPx < viewportPxHeight) {
13
- offsetY = viewportPxHeight - contentHeightPx;
14
- }
15
- const actualScrollX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
16
- const actualScrollY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
17
- if (root) {
18
- root.scale.set(zoomLevel);
19
- root.position.set(-actualScrollX, offsetY - actualScrollY);
20
- }
21
- if (groupsContainer) {
22
- groupsContainer.scale.set(zoomLevel);
23
- groupsContainer.position.set(-actualScrollX, offsetY - actualScrollY);
24
- }
25
- const visibleSet = new Set();
26
- const baseBufferWorld = Math.max(cardWidth, cardHeight) * 4;
27
- const invScale = zoomLevel && zoomLevel !== 0 ? 1 / zoomLevel : 1;
28
- const effectivePanX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
29
- const effectivePanY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
30
- const effectivePanYWithOffset = effectivePanY - offsetY;
31
- const panWorldX = effectivePanX * invScale;
32
- const panWorldY = effectivePanYWithOffset * invScale;
33
- const viewportWorldWidth = viewportPxWidth * invScale;
34
- const viewportWorldHeight = viewportPxHeight * invScale;
35
- const bufferWorld = Math.max(baseBufferWorld * invScale, Math.max(viewportWorldWidth, viewportWorldHeight) * 2.0, baseBufferWorld);
36
- const viewportLeftWorld = panWorldX - bufferWorld;
37
- const viewportRightWorld = panWorldX + viewportWorldWidth + bufferWorld;
38
- const viewportTopWorld = panWorldY - bufferWorld;
39
- const viewportBottomWorld = panWorldY + viewportWorldHeight + bufferWorld;
40
- const inViewportIds = [];
41
- const worldEpsilon = Math.max(0.5, 0.5 * invScale);
42
- const intersectsViewport = (x, y) => x + cardWidth >= viewportLeftWorld - worldEpsilon &&
43
- x <= viewportRightWorld + worldEpsilon &&
44
- y + cardHeight >= viewportTopWorld - worldEpsilon &&
45
- y <= viewportBottomWorld + worldEpsilon;
46
- const addVisibleId = (id) => {
47
- if (!visibleSet.has(id)) {
48
- inViewportIds.push(id);
49
- visibleSet.add(id);
50
- }
11
+ return { pending: false };
12
+ const snapshot = {
13
+ root, groupsContainer, sprites, layout, cardWidth, cardHeight,
14
+ panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, zoomLevel,
15
+ isViewTransition, viewMode, prevLayout, transitionSeenIds,
51
16
  };
52
- for (const [id, position] of layout.positions) {
53
- if (position && intersectsViewport(position.x, position.y))
54
- addVisibleId(id);
55
- }
56
- if (isViewTransition) {
57
- for (const [id, position] of prevLayout?.positions ?? []) {
58
- if (!sprites.has(id) && !transitionSeenIds.has(id) && layout.positions.has(id) && position && intersectsViewport(position.x, position.y))
59
- addVisibleId(id);
60
- }
61
- for (const [id, sprite] of sprites) {
62
- transitionSeenIds.add(id);
63
- if (layout.positions.has(id) && intersectsViewport(sprite.currentX, sprite.currentY))
64
- addVisibleId(id);
65
- }
66
- }
67
- try {
68
- const slotHeight = cardHeight + (CARD_GAP || 8);
69
- const totalRows = Math.ceil((layout.totalHeight || 0) / slotHeight) || 0;
70
- const rowsVisible = Math.max(1, Math.ceil(viewportWorldHeight / slotHeight));
71
- const prefetchMultiplier = 0.75;
72
- const prefetchRows = Math.max(2, Math.ceil(rowsVisible * prefetchMultiplier * Math.max(1, invScale)));
73
- const lastRowThresholdY = Math.max(0, (totalRows - prefetchRows) * slotHeight);
74
- for (const [id, position] of layout.positions) {
75
- if (position.y >= lastRowThresholdY) {
76
- if (!visibleSet.has(id)) {
77
- inViewportIds.push(id);
78
- visibleSet.add(id);
79
- }
80
- }
81
- }
82
- }
83
- catch (e) {
84
- }
17
+ const metrics = measureViewport(snapshot, container);
18
+ applyContainerTransforms(snapshot, metrics);
19
+ const viewport = computeWorldViewport(snapshot, metrics);
20
+ const visibleRect = createVisibleRect({
21
+ panWorldX: viewport.panWorldX,
22
+ panWorldY: viewport.panWorldY,
23
+ worldWidth: viewport.worldWidth,
24
+ worldHeight: viewport.worldHeight,
25
+ invScale: viewport.invScale,
26
+ cardWidth,
27
+ cardHeight,
28
+ });
29
+ const collector = collectInViewportIds(snapshot, viewport);
30
+ addLastRowIds(snapshot, viewport, collector);
31
+ const { ids: inViewportIds, set: visibleSet } = collector;
85
32
  const aggressiveCull = !isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5));
86
- for (const [id, sprite] of sprites) {
87
- if (!visibleSet.has(id)) {
88
- if (aggressiveCull) {
89
- try {
90
- if (sprite.container)
91
- sprite.container.visible = true;
92
- }
93
- catch (e) {
94
- }
95
- continue;
96
- }
97
- try {
98
- if (sprite.container) {
99
- sprite.container.visible = false;
100
- }
101
- sprite.__lastHiddenAt ??= Date.now();
102
- }
103
- catch (e) {
104
- }
105
- }
106
- else {
107
- try {
108
- if (sprite.container) {
109
- sprite.container.visible = true;
110
- }
111
- if (sprite.__lastHiddenAt)
112
- delete sprite.__lastHiddenAt;
113
- }
114
- catch (e) {
115
- }
116
- }
117
- }
118
- try {
119
- const SWEEP_MS = 100;
120
- const now = Date.now();
121
- for (const [id, sprite] of sprites) {
122
- const lastHidden = sprite.__lastHiddenAt;
123
- if (lastHidden && (params.sweepImmediately || now - lastHidden > SWEEP_MS)) {
124
- try {
125
- if (sprite.container && sprite.container.parent)
126
- sprite.container.parent.removeChild(sprite.container);
127
- }
128
- catch (e) {
129
- void e;
130
- }
131
- try {
132
- destroySprite(sprite);
133
- }
134
- catch (e) {
135
- void e;
136
- }
137
- sprites.delete(id);
138
- }
139
- }
140
- }
141
- catch (e) {
142
- }
33
+ applySpriteVisibility(sprites, visibleSet, aggressiveCull);
34
+ sweepHiddenSprites(sprites, layout, visibleRect, () => params.sweepImmediately);
143
35
  const MAX_SPRITES_PER_FRAME = 50;
144
- let createdCount = 0;
36
+ const budget = { created: 0, pending: false, max: MAX_SPRITES_PER_FRAME };
145
37
  for (const id of inViewportIds) {
146
38
  const position = layout.positions.get(id);
147
39
  if (!position) {
@@ -149,71 +41,19 @@ function syncSpritesToViewport(params) {
149
41
  }
150
42
  let sprite = sprites.get(id);
151
43
  if (!sprite) {
152
- if (createdCount >= MAX_SPRITES_PER_FRAME) {
44
+ if (!tryConsumeCreationBudget(budget, rectIntersects(visibleRect, position.x, position.y))) {
153
45
  continue;
154
46
  }
155
- createdCount++;
156
- let startX = position.x;
157
- let startY = position.y;
158
- let shouldAnimate = false;
159
- if (isViewTransition && prevLayout && prevLayout.positions.has(id)) {
160
- const oldPos = prevLayout.positions.get(id);
161
- if (oldPos && intersectsViewport(oldPos.x, oldPos.y)) {
162
- startX = oldPos.x;
163
- startY = oldPos.y;
164
- if (panDeltaX || panDeltaY) {
165
- const dx = (panDeltaX || 0) / (zoomLevel || 1);
166
- const dy = (panDeltaY || 0) / (zoomLevel || 1);
167
- startX += dx;
168
- startY += dy;
169
- }
170
- shouldAnimate = true;
171
- }
172
- }
173
- sprite = createCardSprite(id, startX, startY);
174
- sprites.set(id, sprite);
175
- if (isViewTransition)
176
- transitionSeenIds.add(id);
177
- if (sprite.container) {
178
- root.addChild(sprite.container);
179
- sprite.currentX = startX;
180
- sprite.currentY = startY;
181
- sprite.container.position.set(startX, startY);
182
- }
183
- if (shouldAnimate) {
184
- sprite.targetX = position.x;
185
- sprite.targetY = position.y;
186
- sprite.startX = startX;
187
- sprite.startY = startY;
188
- sprite.animationStartTime = Date.now();
189
- sprite.animationDelay = Math.random() * 300;
190
- }
191
- }
192
- const targetChanged = sprite.targetX !== position.x || sprite.targetY !== position.y;
193
- if (targetChanged) {
194
- if (isViewTransition) {
195
- if (sprite.animationStartTime === undefined) {
196
- sprite.startX = sprite.currentX;
197
- sprite.startY = sprite.currentY;
198
- sprite.animationStartTime = Date.now();
199
- sprite.animationDelay = Math.random() * 300;
200
- }
201
- sprite.targetX = position.x;
202
- sprite.targetY = position.y;
203
- }
204
- else {
205
- sprite.targetX = position.x;
206
- sprite.targetY = position.y;
207
- delete sprite.animationStartTime;
208
- delete sprite.animationDelay;
209
- }
47
+ sprite = spawnSprite(snapshot, viewport, root, id, position, createCardSprite);
210
48
  }
49
+ retargetSprite(sprite, position, isViewTransition);
211
50
  const item = items[Number(id)];
212
51
  if (item) {
213
52
  updateCardContent(sprite, item);
214
53
  }
215
54
  }
55
+ return { pending: budget.pending };
216
56
  }
217
57
 
218
- export { syncSpritesToViewport };
58
+ export { createTransitionCompleteSync, syncSpritesToViewport };
219
59
  //# sourceMappingURL=visibility.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"visibility.js","sources":["../../../../../PivotViewer/components/pivot/visibility.ts"],"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 * as PIXI from 'pixi.js';\nimport type { CardSprite } from './constants';\nimport { CARD_GAP } from './constants';\nimport type { LayoutResult } from '../../engine/types';\nimport { destroySprite } from './sprites';\n\nexport interface SyncParams<TItem> {\n root: PIXI.Container | null;\n groupsContainer?: PIXI.Container | null;\n container: HTMLDivElement | null;\n sprites: Map<string | number, CardSprite>;\n layout: LayoutResult;\n visibleIds: Uint32Array;\n items: TItem[];\n cardWidth: number;\n cardHeight: number;\n panX: number;\n panY: number;\n panDeltaX?: number;\n panDeltaY?: number;\n viewportWidth: number;\n viewportHeight: number;\n zoomLevel: number;\n createCardSprite: (id: string | number, x: number, y: number) => CardSprite;\n updateCardContent: (sprite: CardSprite, item: TItem) => void;\n isViewTransition?: boolean;\n viewMode: string;\n prevLayout?: LayoutResult | null;\n transitionSeenIds: Set<string | number>;\n sweepImmediately?: boolean;\n prevScrollTop?: number;\n prevScrollLeft?: number;\n}\n\nexport function syncSpritesToViewport<TItem>(params: SyncParams<TItem>) {\n const { root, groupsContainer, container, sprites, layout, visibleIds: _visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, viewMode, prevLayout, transitionSeenIds } = params;\n if (!root || !container) return;\n\n void _visibleIds;\n\n // Use the container's measured client size for the viewport dimensions\n // (in pixels). The passed `viewportWidth`/`viewportHeight` can be stale\n // when the browser/device zoom changes; `clientWidth/clientHeight` are\n // authoritative for the actual visible pixel area.\n const viewportPxWidth = container.clientWidth || viewportWidth;\n const viewportPxHeight = container.clientHeight || viewportHeight;\n\n const contentHeightPx = (layout.totalHeight || 0) * zoomLevel;\n let offsetY = 0;\n\n // If content fits vertically within the viewport, align relative to bottom.\n // The spacer ensures container isn't scrollable in this case (scrollTop=0).\n // The items are at large Y coordinates relative to layout.totalHeight.\n // We want the bottom of the layout (totalHeight) to align with viewport bottom.\n if (viewMode === 'grouped' && contentHeightPx < viewportPxHeight) {\n // Calculate offset in pixels to shift the content down\n offsetY = viewportPxHeight - contentHeightPx;\n }\n\n // Use the container's actual scroll position for positioning, not the passed panX/panY\n // which may be stale (from React state) compared to live DOM scroll values.\n const actualScrollX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);\n const actualScrollY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);\n\n // Apply scaling and position to root container\n if (root) {\n root.scale.set(zoomLevel);\n // Standard position: -scrollX, -scrollY\n // Plus vertical alignment offset if zoomed out\n root.position.set(-actualScrollX, offsetY - actualScrollY);\n }\n \n // Apply synchronization to groups container if present\n if (groupsContainer) {\n groupsContainer.scale.set(zoomLevel);\n groupsContainer.position.set(-actualScrollX, offsetY - actualScrollY);\n }\n\n // `visibleIds` comes from callers but this module iterates `layout.positions`\n\n // Note: Pan delta compensation is NOT applied during view transitions because:\n // 1. The time-based animation system handles position changes smoothly\n // 2. Applying pan delta during animation interferes with the interpolation\n // 3. The scroll position changes are a natural part of the view mode switch\n // This compensation is only useful for rapid scrolling/panning where sprites\n // need to maintain visual stability, which isn't the case during view transitions.\n\n const visibleSet = new Set<string | number>();\n\n // Increase buffer (in world units) to reduce edge cases where rapid\n // scrolling skips sprite creation. Keep buffer in world units and convert\n // DOM pixel measurements into world coordinates below to avoid mixing\n // coordinate spaces which can cause precision drift at browser zooms.\n const baseBufferWorld = Math.max(cardWidth, cardHeight) * 4;\n // Ensure buffer scales with viewport size (in world units) so that when\n // zoomed out we still pre-create enough sprites ahead of the viewport.\n const invScale = zoomLevel && zoomLevel !== 0 ? 1 / zoomLevel : 1;\n // The layout positions are in world units; when the root container is scaled\n // (zoomed) the rendered pixel position = position * zoomLevel. The DOM\n // scroll positions (`container.scrollLeft/Top`) are the authoritative pixel\n // camera offsets; prefer them over the passed `panX/panY` to avoid stale\n // values or race conditions between React state and direct DOM updates.\n const effectivePanX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);\n const effectivePanY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);\n\n // Convert pixel-based DOM measurements into world units so we compare like\n // with like. root.position is set using -pixels, so the mapping\n // from DOM scroll (pixels) to world units is: world = pixels / zoomLevel.\n // OffsetY is already in pixels and doesn't need scaling in panWorldY calc\n // because panWorldY is used for viewport buffering/culling, which is relative\n // to the \"camera\" position in world space.\n // The camera world Y = (scrollTop - offsetY) / zoomLevel.\n const effectivePanYWithOffset = effectivePanY - offsetY;\n const panWorldX = effectivePanX * invScale;\n const panWorldY = effectivePanYWithOffset * invScale;\n\n const viewportWorldWidth = viewportPxWidth * invScale;\n const viewportWorldHeight = viewportPxHeight * invScale;\n\n // Ensure bufferWorld is calculated from the actual measured viewport\n // in world units (after converting client pixel dims using invScale).\n // Make buffer adaptive to zoom: when zoomed out (invScale > 1) a small\n // pixel scroll maps to a larger world delta, so increase the buffer.\n // Use the larger of width/height to ensure we buffer enough in both directions.\n const bufferWorld = Math.max(baseBufferWorld * invScale, Math.max(viewportWorldWidth, viewportWorldHeight) * 2.0, baseBufferWorld);\n\n // Do not clamp viewport edges to 0 — allow negative top/left values so the\n // visible window correctly follows the scroll even when the buffer is\n // larger than the current scroll offset.\n const viewportLeftWorld = panWorldX - bufferWorld;\n const viewportRightWorld = panWorldX + viewportWorldWidth + bufferWorld;\n const viewportTopWorld = panWorldY - bufferWorld;\n const viewportBottomWorld = panWorldY + viewportWorldHeight + bufferWorld;\n\n const inViewportIds: (string | number)[] = [];\n // Small tolerance in world units to avoid floating-point edge cases when\n // browser/device zoom or high scroll values produce tiny rounding errors.\n const worldEpsilon = Math.max(0.5, 0.5 * invScale);\n const intersectsViewport = (x: number, y: number) =>\n x + cardWidth >= viewportLeftWorld - worldEpsilon &&\n x <= viewportRightWorld + worldEpsilon &&\n y + cardHeight >= viewportTopWorld - worldEpsilon &&\n y <= viewportBottomWorld + worldEpsilon;\n const addVisibleId = (id: string | number) => {\n if (!visibleSet.has(id)) {\n inViewportIds.push(id);\n visibleSet.add(id);\n }\n };\n\n // Target positions are authoritative, but during a transition cards may\n // still be at their old positions while the camera catches up.\n for (const [id, position] of layout.positions) {\n if (position && intersectsViewport(position.x, position.y)) addVisibleId(id);\n }\n if (isViewTransition) {\n for (const [id, position] of prevLayout?.positions ?? []) {\n if (!sprites.has(id) && !transitionSeenIds.has(id) && layout.positions.has(id) && position && intersectsViewport(position.x, position.y)) addVisibleId(id);\n }\n for (const [id, sprite] of sprites) {\n transitionSeenIds.add(id);\n if (layout.positions.has(id) && intersectsViewport(sprite.currentX, sprite.currentY)) addVisibleId(id);\n }\n }\n\n // Ensure last rows are present when the user scrolls near the bottom.\n // Compute slot/row information and force-insert IDs from the last few\n // rows to avoid missing tiles due to rounding/precision at zoom levels.\n try {\n const slotHeight = cardHeight + (CARD_GAP || 8);\n const totalRows = Math.ceil((layout.totalHeight || 0) / slotHeight) || 0;\n // Determine how many rows are visible in the viewport (world units),\n // then prefetch a fraction of that adjusted by zoom (invScale).\n const rowsVisible = Math.max(1, Math.ceil(viewportWorldHeight / slotHeight));\n const prefetchMultiplier = 0.75; // fraction of viewport to prefetch\n const prefetchRows = Math.max(2, Math.ceil(rowsVisible * prefetchMultiplier * Math.max(1, invScale)));\n const lastRowThresholdY = Math.max(0, (totalRows - prefetchRows) * slotHeight);\n for (const [id, position] of layout.positions) {\n if (position.y >= lastRowThresholdY) {\n if (!visibleSet.has(id)) {\n inViewportIds.push(id);\n visibleSet.add(id);\n }\n }\n }\n } catch (e) {\n void e;\n }\n\n // Outside transitions, avoid mass-hiding sprites on a potentially stale viewport calculation.\n const aggressiveCull = !isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5));\n\n for (const [id, sprite] of sprites) {\n if (!visibleSet.has(id)) {\n if (aggressiveCull) {\n // Keep sprite visible this frame to avoid visual holes\n try { if (sprite.container) sprite.container.visible = true; } catch (e) { void e; }\n continue;\n }\n\n try {\n if (sprite.container) {\n sprite.container.visible = false;\n }\n (sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt ??= Date.now();\n } catch (e) {\n void e;\n }\n } else {\n try {\n if (sprite.container) {\n sprite.container.visible = true;\n }\n if ((sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt) delete (sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt;\n } catch (e) { void e; }\n }\n }\n\n // Sweep: actually destroy sprites that have been hidden longer than threshold\n try {\n const SWEEP_MS = 100; // keep hidden sprites for 100ms before destruction (reduced from 500ms for faster mode transitions)\n const now = Date.now();\n for (const [id, sprite] of sprites) {\n const lastHidden = (sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt;\n if (lastHidden && (params.sweepImmediately || now - lastHidden > SWEEP_MS)) {\n try {\n // remove from parent if present\n if (sprite.container && sprite.container.parent) sprite.container.parent.removeChild(sprite.container);\n } catch (e) {\n void e;\n }\n try {\n destroySprite(sprite);\n } catch (e) {\n void e;\n }\n sprites.delete(id);\n }\n }\n } catch (e) {\n void e;\n }\n\n // Limit the number of sprites created per frame to avoid choking the GPU/CPU\n // when scrolling rapidly or zooming out significantly.\n const MAX_SPRITES_PER_FRAME = 50;\n let createdCount = 0;\n\n\n for (const id of inViewportIds) {\n const position = layout.positions.get(id);\n if (!position) {\n continue;\n }\n\n let sprite = sprites.get(id);\n if (!sprite) {\n if (createdCount >= MAX_SPRITES_PER_FRAME) {\n continue;\n }\n createdCount++;\n\n let startX = position.x;\n let startY = position.y;\n let shouldAnimate = false;\n\n // If view transition, try to find old position to fly in from\n if (isViewTransition && prevLayout && prevLayout.positions.has(id)) {\n const oldPos = prevLayout.positions.get(id);\n if (oldPos && intersectsViewport(oldPos.x, oldPos.y)) {\n startX = oldPos.x;\n startY = oldPos.y;\n\n // If we have a pan delta (camera jump), we need to adjust the start position\n // so that the sprite appears at the same visual location relative to the NEW camera.\n // StartWorld = OldWorld + PanDelta\n if (panDeltaX || panDeltaY) {\n const dx = (panDeltaX || 0) / (zoomLevel || 1);\n const dy = (panDeltaY || 0) / (zoomLevel || 1);\n startX += dx;\n startY += dy;\n }\n\n shouldAnimate = true;\n }\n }\n\n sprite = createCardSprite(id, startX, startY);\n sprites.set(id, sprite);\n if (isViewTransition) transitionSeenIds.add(id);\n if (sprite.container) {\n root.addChild(sprite.container);\n sprite.currentX = startX;\n sprite.currentY = startY;\n // Keep sprite.container positioned in world units; animation/update\n // loop will apply root.scale/position to convert to pixels.\n sprite.container.position.set(startX, startY);\n }\n\n if (shouldAnimate) {\n sprite.targetX = position.x;\n sprite.targetY = position.y;\n sprite.startX = startX;\n sprite.startY = startY;\n sprite.animationStartTime = Date.now();\n sprite.animationDelay = Math.random() * 300;\n }\n }\n\n // Check if target changed to trigger animation\n const targetChanged = sprite.targetX !== position.x || sprite.targetY !== position.y;\n if (targetChanged) {\n if (isViewTransition) {\n // Only set up animation if not already animating - don't reset animation\n // when targets change during layout recalculations\n if (sprite.animationStartTime === undefined) {\n sprite.startX = sprite.currentX;\n sprite.startY = sprite.currentY;\n sprite.animationStartTime = Date.now();\n // Add random delay for \"organic\" fly effect\n sprite.animationDelay = Math.random() * 300;\n }\n // Always update target to the new position (animation will smoothly\n // adjust to the new target)\n sprite.targetX = position.x;\n sprite.targetY = position.y;\n } else {\n sprite.targetX = position.x;\n sprite.targetY = position.y;\n delete sprite.animationStartTime;\n delete sprite.animationDelay;\n }\n }\n\n const item = items[Number(id)];\n if (item) {\n updateCardContent(sprite, item);\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"visibility.js","sources":["../../../../../PivotViewer/components/pivot/visibility.ts"],"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 type * as PIXI from 'pixi.js';\nimport type { CardSprite } from './constants';\nimport type { LayoutResult } from '../../engine/types';\nimport {\n createVisibleRect,\n rectIntersects,\n tryConsumeCreationBudget,\n measureViewport,\n applyContainerTransforms,\n computeWorldViewport,\n collectInViewportIds,\n addLastRowIds,\n applySpriteVisibility,\n sweepHiddenSprites,\n spawnSprite,\n retargetSprite,\n} from './visibilityHelpers';\n\nexport interface SyncParams<TItem> {\n root: PIXI.Container | null;\n groupsContainer?: PIXI.Container | null;\n container: HTMLDivElement | null;\n sprites: Map<string | number, CardSprite>;\n layout: LayoutResult;\n visibleIds: Uint32Array;\n items: TItem[];\n cardWidth: number;\n cardHeight: number;\n panX: number;\n panY: number;\n panDeltaX?: number;\n panDeltaY?: number;\n viewportWidth: number;\n viewportHeight: number;\n zoomLevel: number;\n createCardSprite: (id: string | number, x: number, y: number) => CardSprite;\n updateCardContent: (sprite: CardSprite, item: TItem) => void;\n isViewTransition?: boolean;\n viewMode: string;\n prevLayout?: LayoutResult | null;\n transitionSeenIds: Set<string | number>;\n sweepImmediately?: boolean;\n prevScrollTop?: number;\n prevScrollLeft?: number;\n}\n\nexport interface SyncResult {\n /** True when sprite creation was deferred to respect the per-frame budget; sync again on the next frame. */\n pending: boolean;\n}\n\n/**\n * Creates the sync used when a transition completes. Parameters are read when the sync runs, so a\n * layout, zoom or item change since the transition started is honored and on-screen cards survive the sweep.\n * @param getParams Returns the current sync parameters.\n */\nexport function createTransitionCompleteSync<TItem>(getParams: () => SyncParams<TItem>) {\n return () => {\n syncSpritesToViewport({ ...getParams(), isViewTransition: false, sweepImmediately: true });\n };\n}\n\nexport function syncSpritesToViewport<TItem>(params: SyncParams<TItem>): SyncResult {\n // Read every parameter exactly once, in this order, as a snapshot. Helpers use the snapshot\n // so that later changes made by callbacks (e.g. createCardSprite) are not observed mid-sync.\n const { root, groupsContainer, container, sprites, layout, visibleIds: _visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, viewMode, prevLayout, transitionSeenIds } = params;\n if (!root || !container) return { pending: false };\n\n void _visibleIds;\n\n const snapshot: SyncParams<TItem> = {\n root, groupsContainer, container, sprites, layout, visibleIds: _visibleIds, items, cardWidth, cardHeight,\n panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, zoomLevel,\n createCardSprite, updateCardContent, isViewTransition, viewMode, prevLayout, transitionSeenIds,\n };\n\n const metrics = measureViewport(snapshot, container);\n applyContainerTransforms(snapshot, metrics);\n\n // `visibleIds` comes from callers but this module iterates `layout.positions`\n\n // Note: Pan delta compensation is NOT applied during view transitions because:\n // 1. The time-based animation system handles position changes smoothly\n // 2. Applying pan delta during animation interferes with the interpolation\n // 3. The scroll position changes are a natural part of the view mode switch\n // This compensation is only useful for rapid scrolling/panning where sprites\n // need to maintain visual stability, which isn't the case during view transitions.\n\n const viewport = computeWorldViewport(snapshot, metrics);\n const visibleRect = createVisibleRect({\n panWorldX: viewport.panWorldX,\n panWorldY: viewport.panWorldY,\n worldWidth: viewport.worldWidth,\n worldHeight: viewport.worldHeight,\n invScale: viewport.invScale,\n cardWidth,\n cardHeight,\n });\n\n const collector = collectInViewportIds(snapshot, viewport);\n addLastRowIds(snapshot, viewport, collector);\n const { ids: inViewportIds, set: visibleSet } = collector;\n\n // Outside transitions, avoid mass-hiding sprites on a potentially stale viewport calculation.\n const aggressiveCull = !isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5));\n\n applySpriteVisibility(sprites, visibleSet, aggressiveCull);\n sweepHiddenSprites(sprites, layout, visibleRect, () => params.sweepImmediately);\n\n // Limit the number of buffer-only sprites created per frame to avoid choking the GPU/CPU\n // when scrolling rapidly or zooming out significantly.\n const MAX_SPRITES_PER_FRAME = 50;\n const budget = { created: 0, pending: false, max: MAX_SPRITES_PER_FRAME };\n\n for (const id of inViewportIds) {\n const position = layout.positions.get(id);\n if (!position) {\n continue;\n }\n\n let sprite = sprites.get(id);\n if (!sprite) {\n if (!tryConsumeCreationBudget(budget, rectIntersects(visibleRect, position.x, position.y))) {\n continue;\n }\n sprite = spawnSprite(snapshot, viewport, root, id, position, createCardSprite);\n }\n\n retargetSprite(sprite, position, isViewTransition);\n\n const item = items[Number(id)];\n if (item) {\n updateCardContent(sprite, item);\n }\n }\n return { pending: budget.pending };\n}\n"],"names":[],"mappings":"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"}