@cratis/components 4.26.4 → 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 (118) 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/visibility.d.ts +1 -1
  71. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
  72. package/dist/esm/PivotViewer/components/pivot/visibility.js +24 -222
  73. package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
  74. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.d.ts +114 -0
  75. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.d.ts.map +1 -0
  76. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.js +306 -0
  77. package/dist/esm/PivotViewer/components/pivot/visibilityHelpers.js.map +1 -0
  78. package/dist/esm/PivotViewer/hooks/useColorCssVariables.d.ts +9 -0
  79. package/dist/esm/PivotViewer/hooks/useColorCssVariables.d.ts.map +1 -0
  80. package/dist/esm/PivotViewer/hooks/useColorCssVariables.js +32 -0
  81. package/dist/esm/PivotViewer/hooks/useColorCssVariables.js.map +1 -0
  82. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.d.ts +27 -0
  83. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.d.ts.map +1 -0
  84. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.js +40 -0
  85. package/dist/esm/PivotViewer/hooks/usePivotViewerGrouping.js.map +1 -0
  86. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.d.ts +27 -0
  87. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.d.ts.map +1 -0
  88. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.js +46 -0
  89. package/dist/esm/PivotViewer/hooks/usePivotViewerInteractions.js.map +1 -0
  90. package/dist/esm/PivotViewer/hooks/useRawGrouping.d.ts +22 -0
  91. package/dist/esm/PivotViewer/hooks/useRawGrouping.d.ts.map +1 -0
  92. package/dist/esm/PivotViewer/hooks/useRawGrouping.js +56 -0
  93. package/dist/esm/PivotViewer/hooks/useRawGrouping.js.map +1 -0
  94. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.d.ts +12 -0
  95. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.d.ts.map +1 -0
  96. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.js +20 -0
  97. package/dist/esm/PivotViewer/hooks/useScrollPositionTracking.js.map +1 -0
  98. package/dist/esm/PivotViewer/hooks/useViewerLayout.d.ts +16 -0
  99. package/dist/esm/PivotViewer/hooks/useViewerLayout.d.ts.map +1 -0
  100. package/dist/esm/PivotViewer/hooks/useViewerLayout.js +29 -0
  101. package/dist/esm/PivotViewer/hooks/useViewerLayout.js.map +1 -0
  102. package/dist/esm/PivotViewer/hooks/useVisibleIds.d.ts +21 -0
  103. package/dist/esm/PivotViewer/hooks/useVisibleIds.d.ts.map +1 -0
  104. package/dist/esm/PivotViewer/hooks/useVisibleIds.js +34 -0
  105. package/dist/esm/PivotViewer/hooks/useVisibleIds.js.map +1 -0
  106. package/dist/esm/configuration/CratisComponentsDefaults.d.ts.map +1 -1
  107. package/dist/esm/configuration/CratisComponentsDefaults.js +2 -0
  108. package/dist/esm/configuration/CratisComponentsDefaults.js.map +1 -1
  109. package/dist/esm/styles.DataPage.css +51 -6
  110. package/dist/esm/styles.DataTables.css +51 -6
  111. package/dist/esm/styles.SchemaEditor.css +51 -6
  112. package/dist/esm/styles.css +51 -6
  113. package/dist/esm/styles.renderer.builtin.css +51 -6
  114. package/dist/esm/types/parts.d.ts +4 -1
  115. package/dist/esm/types/parts.d.ts.map +1 -1
  116. package/dist/esm/types/parts.js +4 -1
  117. package/dist/esm/types/parts.js.map +1 -1
  118. package/package.json +1 -1
@@ -1,183 +1,37 @@
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
 
4
3
  function createTransitionCompleteSync(getParams) {
5
4
  return () => {
6
5
  syncSpritesToViewport({ ...getParams(), isViewTransition: false, sweepImmediately: true });
7
6
  };
8
7
  }
9
- function createVisibleRect(source) {
10
- return {
11
- left: source.panWorldX,
12
- right: source.panWorldX + source.worldWidth,
13
- top: source.panWorldY,
14
- bottom: source.panWorldY + source.worldHeight,
15
- epsilon: Math.max(0.5, 0.5 * source.invScale),
16
- cardWidth: source.cardWidth,
17
- cardHeight: source.cardHeight,
18
- };
19
- }
20
- function rectIntersects(rect, x, y) {
21
- return x + rect.cardWidth >= rect.left - rect.epsilon &&
22
- x <= rect.right + rect.epsilon &&
23
- y + rect.cardHeight >= rect.top - rect.epsilon &&
24
- y <= rect.bottom + rect.epsilon;
25
- }
26
- function sweepableHiddenAt(sprite, position, rect) {
27
- if (position && rectIntersects(rect, position.x, position.y))
28
- return undefined;
29
- return sprite.__lastHiddenAt;
30
- }
31
- function tryConsumeCreationBudget(budget, isVisible) {
32
- if (isVisible)
33
- return true;
34
- if (budget.created >= budget.max) {
35
- budget.pending = true;
36
- return false;
37
- }
38
- budget.created++;
39
- return true;
40
- }
41
8
  function syncSpritesToViewport(params) {
42
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;
43
10
  if (!root || !container)
44
11
  return { pending: false };
45
- const viewportPxWidth = container.clientWidth || viewportWidth;
46
- const viewportPxHeight = container.clientHeight || viewportHeight;
47
- const contentHeightPx = (layout.totalHeight || 0) * zoomLevel;
48
- let offsetY = 0;
49
- if (viewMode === 'grouped' && contentHeightPx < viewportPxHeight) {
50
- offsetY = viewportPxHeight - contentHeightPx;
51
- }
52
- const actualScrollX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
53
- const actualScrollY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
54
- if (root) {
55
- root.scale.set(zoomLevel);
56
- root.position.set(-actualScrollX, offsetY - actualScrollY);
57
- }
58
- if (groupsContainer) {
59
- groupsContainer.scale.set(zoomLevel);
60
- groupsContainer.position.set(-actualScrollX, offsetY - actualScrollY);
61
- }
62
- const visibleSet = new Set();
63
- const baseBufferWorld = Math.max(cardWidth, cardHeight) * 4;
64
- const invScale = zoomLevel && zoomLevel !== 0 ? 1 / zoomLevel : 1;
65
- const effectivePanX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
66
- const effectivePanY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
67
- const effectivePanYWithOffset = effectivePanY - offsetY;
68
- const panWorldX = effectivePanX * invScale;
69
- const panWorldY = effectivePanYWithOffset * invScale;
70
- const viewportWorldWidth = viewportPxWidth * invScale;
71
- const viewportWorldHeight = viewportPxHeight * invScale;
72
- const bufferWorld = Math.max(baseBufferWorld * invScale, Math.max(viewportWorldWidth, viewportWorldHeight) * 2.0, baseBufferWorld);
73
- const viewportLeftWorld = panWorldX - bufferWorld;
74
- const viewportRightWorld = panWorldX + viewportWorldWidth + bufferWorld;
75
- const viewportTopWorld = panWorldY - bufferWorld;
76
- const viewportBottomWorld = panWorldY + viewportWorldHeight + bufferWorld;
77
- const visibleRect = createVisibleRect({ panWorldX, panWorldY, worldWidth: viewportWorldWidth, worldHeight: viewportWorldHeight, invScale, cardWidth, cardHeight });
78
- const inViewportIds = [];
79
- const worldEpsilon = Math.max(0.5, 0.5 * invScale);
80
- const intersectsViewport = (x, y) => x + cardWidth >= viewportLeftWorld - worldEpsilon &&
81
- x <= viewportRightWorld + worldEpsilon &&
82
- y + cardHeight >= viewportTopWorld - worldEpsilon &&
83
- y <= viewportBottomWorld + worldEpsilon;
84
- const addVisibleId = (id) => {
85
- if (!visibleSet.has(id)) {
86
- inViewportIds.push(id);
87
- visibleSet.add(id);
88
- }
12
+ const snapshot = {
13
+ root, groupsContainer, sprites, layout, cardWidth, cardHeight,
14
+ panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, zoomLevel,
15
+ isViewTransition, viewMode, prevLayout, transitionSeenIds,
89
16
  };
90
- for (const [id, position] of layout.positions) {
91
- if (position && intersectsViewport(position.x, position.y))
92
- addVisibleId(id);
93
- }
94
- if (isViewTransition) {
95
- for (const [id, position] of prevLayout?.positions ?? []) {
96
- if (!sprites.has(id) && !transitionSeenIds.has(id) && layout.positions.has(id) && position && intersectsViewport(position.x, position.y))
97
- addVisibleId(id);
98
- }
99
- for (const [id, sprite] of sprites) {
100
- transitionSeenIds.add(id);
101
- if (layout.positions.has(id) && intersectsViewport(sprite.currentX, sprite.currentY))
102
- addVisibleId(id);
103
- }
104
- }
105
- try {
106
- const slotHeight = cardHeight + (CARD_GAP || 8);
107
- const totalRows = Math.ceil((layout.totalHeight || 0) / slotHeight) || 0;
108
- const rowsVisible = Math.max(1, Math.ceil(viewportWorldHeight / slotHeight));
109
- const prefetchMultiplier = 0.75;
110
- const prefetchRows = Math.max(2, Math.ceil(rowsVisible * prefetchMultiplier * Math.max(1, invScale)));
111
- const lastRowThresholdY = Math.max(0, (totalRows - prefetchRows) * slotHeight);
112
- for (const [id, position] of layout.positions) {
113
- if (position.y >= lastRowThresholdY) {
114
- if (!visibleSet.has(id)) {
115
- inViewportIds.push(id);
116
- visibleSet.add(id);
117
- }
118
- }
119
- }
120
- }
121
- catch (e) {
122
- }
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;
123
32
  const aggressiveCull = !isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5));
124
- for (const [id, sprite] of sprites) {
125
- if (!visibleSet.has(id)) {
126
- if (aggressiveCull) {
127
- try {
128
- if (sprite.container)
129
- sprite.container.visible = true;
130
- }
131
- catch (e) {
132
- }
133
- continue;
134
- }
135
- try {
136
- if (sprite.container) {
137
- sprite.container.visible = false;
138
- }
139
- sprite.__lastHiddenAt ??= Date.now();
140
- }
141
- catch (e) {
142
- }
143
- }
144
- else {
145
- try {
146
- if (sprite.container) {
147
- sprite.container.visible = true;
148
- }
149
- if (sprite.__lastHiddenAt)
150
- delete sprite.__lastHiddenAt;
151
- }
152
- catch (e) {
153
- }
154
- }
155
- }
156
- try {
157
- const SWEEP_MS = 100;
158
- const now = Date.now();
159
- for (const [id, sprite] of sprites) {
160
- const lastHidden = sweepableHiddenAt(sprite, layout.positions.get(id), visibleRect);
161
- if (lastHidden && (params.sweepImmediately || now - lastHidden > SWEEP_MS)) {
162
- try {
163
- if (sprite.container && sprite.container.parent)
164
- sprite.container.parent.removeChild(sprite.container);
165
- }
166
- catch (e) {
167
- void e;
168
- }
169
- try {
170
- destroySprite(sprite);
171
- }
172
- catch (e) {
173
- void e;
174
- }
175
- sprites.delete(id);
176
- }
177
- }
178
- }
179
- catch (e) {
180
- }
33
+ applySpriteVisibility(sprites, visibleSet, aggressiveCull);
34
+ sweepHiddenSprites(sprites, layout, visibleRect, () => params.sweepImmediately);
181
35
  const MAX_SPRITES_PER_FRAME = 50;
182
36
  const budget = { created: 0, pending: false, max: MAX_SPRITES_PER_FRAME };
183
37
  for (const id of inViewportIds) {
@@ -190,61 +44,9 @@ function syncSpritesToViewport(params) {
190
44
  if (!tryConsumeCreationBudget(budget, rectIntersects(visibleRect, position.x, position.y))) {
191
45
  continue;
192
46
  }
193
- let startX = position.x;
194
- let startY = position.y;
195
- let shouldAnimate = false;
196
- if (isViewTransition && prevLayout && prevLayout.positions.has(id)) {
197
- const oldPos = prevLayout.positions.get(id);
198
- if (oldPos && intersectsViewport(oldPos.x, oldPos.y)) {
199
- startX = oldPos.x;
200
- startY = oldPos.y;
201
- if (panDeltaX || panDeltaY) {
202
- const dx = (panDeltaX || 0) / (zoomLevel || 1);
203
- const dy = (panDeltaY || 0) / (zoomLevel || 1);
204
- startX += dx;
205
- startY += dy;
206
- }
207
- shouldAnimate = true;
208
- }
209
- }
210
- sprite = createCardSprite(id, startX, startY);
211
- sprites.set(id, sprite);
212
- if (isViewTransition)
213
- transitionSeenIds.add(id);
214
- if (sprite.container) {
215
- root.addChild(sprite.container);
216
- sprite.currentX = startX;
217
- sprite.currentY = startY;
218
- sprite.container.position.set(startX, startY);
219
- }
220
- if (shouldAnimate) {
221
- sprite.targetX = position.x;
222
- sprite.targetY = position.y;
223
- sprite.startX = startX;
224
- sprite.startY = startY;
225
- sprite.animationStartTime = Date.now();
226
- sprite.animationDelay = Math.random() * 300;
227
- }
228
- }
229
- const targetChanged = sprite.targetX !== position.x || sprite.targetY !== position.y;
230
- if (targetChanged) {
231
- if (isViewTransition) {
232
- if (sprite.animationStartTime === undefined) {
233
- sprite.startX = sprite.currentX;
234
- sprite.startY = sprite.currentY;
235
- sprite.animationStartTime = Date.now();
236
- sprite.animationDelay = Math.random() * 300;
237
- }
238
- sprite.targetX = position.x;
239
- sprite.targetY = position.y;
240
- }
241
- else {
242
- sprite.targetX = position.x;
243
- sprite.targetY = position.y;
244
- delete sprite.animationStartTime;
245
- delete sprite.animationDelay;
246
- }
47
+ sprite = spawnSprite(snapshot, viewport, root, id, position, createCardSprite);
247
48
  }
49
+ retargetSprite(sprite, position, isViewTransition);
248
50
  const item = items[Number(id)];
249
51
  if (item) {
250
52
  updateCardContent(sprite, item);
@@ -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 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\ninterface VisibleRect {\n left: number;\n right: number;\n top: number;\n bottom: number;\n epsilon: number;\n cardWidth: number;\n cardHeight: number;\n}\n\ninterface VisibleRectSource {\n panWorldX: number;\n panWorldY: number;\n worldWidth: number;\n worldHeight: number;\n invScale: number;\n cardWidth: number;\n cardHeight: number;\n}\n\n/**\n * Builds the unbuffered visible rect in world units, with a zoom-aware rounding tolerance. Cards\n * intersecting it are created first and are never subject to the per-frame budget or the sweep.\n */\nfunction createVisibleRect(source: VisibleRectSource): VisibleRect {\n return {\n left: source.panWorldX,\n right: source.panWorldX + source.worldWidth,\n top: source.panWorldY,\n bottom: source.panWorldY + source.worldHeight,\n epsilon: Math.max(0.5, 0.5 * source.invScale),\n cardWidth: source.cardWidth,\n cardHeight: source.cardHeight,\n };\n}\n\nfunction rectIntersects(rect: VisibleRect, x: number, y: number) {\n return x + rect.cardWidth >= rect.left - rect.epsilon &&\n x <= rect.right + rect.epsilon &&\n y + rect.cardHeight >= rect.top - rect.epsilon &&\n y <= rect.bottom + rect.epsilon;\n}\n\n/** When the sprite was hidden, or undefined when it is not hidden or its card is on screen (never swept). */\nfunction sweepableHiddenAt(sprite: CardSprite, position: { x: number; y: number } | undefined, rect: VisibleRect) {\n if (position && rectIntersects(rect, position.x, position.y)) return undefined;\n return (sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt;\n}\n\n/**\n * Decides whether a sprite may be created this frame. Visible cards are always allowed;\n * buffer-only cards are limited by the budget and mark the sync as pending when deferred.\n */\nfunction tryConsumeCreationBudget(budget: { created: number; pending: boolean; max: number }, isVisible: boolean) {\n if (isVisible) return true;\n if (budget.created >= budget.max) {\n budget.pending = true;\n return false;\n }\n budget.created++;\n return true;\n}\n\nexport function syncSpritesToViewport<TItem>(params: SyncParams<TItem>): SyncResult {\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 // 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 visibleRect = createVisibleRect({ panWorldX, panWorldY, worldWidth: viewportWorldWidth, worldHeight: viewportWorldHeight, invScale, cardWidth, cardHeight });\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 = sweepableHiddenAt(sprite, layout.positions.get(id), visibleRect);\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 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\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 return { pending: budget.pending };\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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"}
@@ -0,0 +1,114 @@
1
+ import type * as PIXI from 'pixi.js';
2
+ import type { CardSprite } from './constants.js';
3
+ import type { LayoutResult } from '../../engine/types.js';
4
+ import type { SyncParams } from './visibility.js';
5
+ export interface VisibleRect {
6
+ left: number;
7
+ right: number;
8
+ top: number;
9
+ bottom: number;
10
+ epsilon: number;
11
+ cardWidth: number;
12
+ cardHeight: number;
13
+ }
14
+ export interface VisibleRectSource {
15
+ panWorldX: number;
16
+ panWorldY: number;
17
+ worldWidth: number;
18
+ worldHeight: number;
19
+ invScale: number;
20
+ cardWidth: number;
21
+ cardHeight: number;
22
+ }
23
+ /**
24
+ * Builds the unbuffered visible rect in world units, with a zoom-aware rounding tolerance. Cards
25
+ * intersecting it are created first and are never subject to the per-frame budget or the sweep.
26
+ */
27
+ export declare function createVisibleRect(source: VisibleRectSource): VisibleRect;
28
+ export declare function rectIntersects(rect: VisibleRect, x: number, y: number): boolean;
29
+ /** When the sprite was hidden, or undefined when it is not hidden or its card is on screen (never swept). */
30
+ export declare function sweepableHiddenAt(sprite: CardSprite, position: {
31
+ x: number;
32
+ y: number;
33
+ } | undefined, rect: VisibleRect): number | undefined;
34
+ /**
35
+ * Decides whether a sprite may be created this frame. Visible cards are always allowed;
36
+ * buffer-only cards are limited by the budget and mark the sync as pending when deferred.
37
+ */
38
+ export declare function tryConsumeCreationBudget(budget: {
39
+ created: number;
40
+ pending: boolean;
41
+ max: number;
42
+ }, isVisible: boolean): boolean;
43
+ export type SpriteId = string | number;
44
+ export type SpriteMap = Map<SpriteId, CardSprite>;
45
+ export type Point = {
46
+ x: number;
47
+ y: number;
48
+ };
49
+ export type HiddenAwareSprite = {
50
+ __lastHiddenAt?: number;
51
+ };
52
+ /** The measured viewport in pixels, the vertical alignment offset and the live scroll position. */
53
+ export interface ViewportMetrics {
54
+ viewportPxWidth: number;
55
+ viewportPxHeight: number;
56
+ offsetY: number;
57
+ scrollX: number;
58
+ scrollY: number;
59
+ }
60
+ /** The viewport in world units, with the pre-creation buffer applied to its edges. */
61
+ export interface WorldViewport {
62
+ invScale: number;
63
+ panWorldX: number;
64
+ panWorldY: number;
65
+ worldWidth: number;
66
+ worldHeight: number;
67
+ bufferedLeft: number;
68
+ bufferedRight: number;
69
+ bufferedTop: number;
70
+ bufferedBottom: number;
71
+ epsilon: number;
72
+ }
73
+ /** Collects ids to keep, preserving first-seen order. */
74
+ export interface IdCollector {
75
+ ids: SpriteId[];
76
+ set: Set<SpriteId>;
77
+ add: (id: SpriteId) => void;
78
+ }
79
+ export declare function createIdCollector(): IdCollector;
80
+ export declare function scrollOrPan(scroll: number, pan: number | undefined): number;
81
+ export declare function measureViewport<TItem>(params: SyncParams<TItem>, container: HTMLDivElement): ViewportMetrics;
82
+ /** Applies scaling and position to the root (and groups) container. */
83
+ export declare function applyContainerTransforms<TItem>(params: SyncParams<TItem>, metrics: ViewportMetrics): void;
84
+ export declare function computeWorldViewport<TItem>(params: SyncParams<TItem>, metrics: ViewportMetrics): WorldViewport;
85
+ export interface CardSize {
86
+ cardWidth: number;
87
+ cardHeight: number;
88
+ }
89
+ export declare function intersectsBufferedViewport(viewport: WorldViewport, card: CardSize, x: number, y: number): boolean;
90
+ /** Collects the ids whose target positions, or (during a transition) current positions, are in the buffered viewport. */
91
+ export declare function collectInViewportIds<TItem>(params: SyncParams<TItem>, viewport: WorldViewport): IdCollector;
92
+ /**
93
+ * Ensures last rows are present when the user scrolls near the bottom.
94
+ * Computes slot/row information and force-inserts IDs from the last few
95
+ * rows to avoid missing tiles due to rounding/precision at zoom levels.
96
+ */
97
+ export declare function addLastRowIds<TItem>(params: SyncParams<TItem>, viewport: WorldViewport, collector: IdCollector): void;
98
+ export declare function showSprite(sprite: CardSprite): void;
99
+ export declare function hideSprite(sprite: CardSprite): void;
100
+ /** Shows sprites that are wanted and hides the rest (unless culling would risk visual holes). */
101
+ export declare function applySpriteVisibility(sprites: SpriteMap, visibleSet: Set<SpriteId>, aggressiveCull: boolean): void;
102
+ export declare function removeSprite(sprite: CardSprite): void;
103
+ /** Sweep: actually destroy sprites that have been hidden longer than threshold. */
104
+ export declare function sweepHiddenSprites(sprites: SpriteMap, layout: LayoutResult, visibleRect: VisibleRect, readSweepImmediately: () => boolean | undefined): void;
105
+ /** Where a new sprite starts, and whether it flies in from a previous position. */
106
+ export declare function resolveSpawnStart<TItem>(params: SyncParams<TItem>, viewport: WorldViewport, id: SpriteId, position: Point): {
107
+ startX: number;
108
+ startY: number;
109
+ shouldAnimate: boolean;
110
+ };
111
+ export declare function spawnSprite<TItem>(params: SyncParams<TItem>, viewport: WorldViewport, root: PIXI.Container, id: SpriteId, position: Point, createCardSprite: SyncParams<TItem>['createCardSprite']): CardSprite;
112
+ /** Check if target changed to trigger animation. */
113
+ export declare function retargetSprite(sprite: CardSprite, position: Point, isViewTransition: boolean | undefined): void;
114
+ //# sourceMappingURL=visibilityHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibilityHelpers.d.ts","sourceRoot":"","sources":["../../../../../PivotViewer/components/pivot/visibilityHelpers.ts"],"names":[],"mappings":"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"}