@cratis/components 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/cjs/PivotViewer/PivotViewer.css +32 -19
  2. package/dist/cjs/PivotViewer/PivotViewer.js +44 -8
  3. package/dist/cjs/PivotViewer/PivotViewer.js.map +1 -1
  4. package/dist/cjs/PivotViewer/components/PivotCanvas.js +32 -51
  5. package/dist/cjs/PivotViewer/components/PivotCanvas.js.map +1 -1
  6. package/dist/cjs/PivotViewer/components/PivotViewerMain.js +4 -2
  7. package/dist/cjs/PivotViewer/components/PivotViewerMain.js.map +1 -1
  8. package/dist/cjs/PivotViewer/components/Toolbar.js +1 -1
  9. package/dist/cjs/PivotViewer/components/Toolbar.js.map +1 -1
  10. package/dist/cjs/PivotViewer/components/pivot/animation.js +4 -3
  11. package/dist/cjs/PivotViewer/components/pivot/animation.js.map +1 -1
  12. package/dist/cjs/PivotViewer/components/pivot/groups.js +151 -0
  13. package/dist/cjs/PivotViewer/components/pivot/groups.js.map +1 -0
  14. package/dist/cjs/PivotViewer/components/pivot/sprites.js +1 -15
  15. package/dist/cjs/PivotViewer/components/pivot/sprites.js.map +1 -1
  16. package/dist/cjs/PivotViewer/components/pivot/visibility.js +50 -27
  17. package/dist/cjs/PivotViewer/components/pivot/visibility.js.map +1 -1
  18. package/dist/cjs/PivotViewer/engine/layout.js +11 -7
  19. package/dist/cjs/PivotViewer/engine/layout.js.map +1 -1
  20. package/dist/cjs/PivotViewer/hooks/useCurrentFilters.js.map +1 -1
  21. package/dist/cjs/PivotViewer/hooks/usePanning.js +8 -4
  22. package/dist/cjs/PivotViewer/hooks/usePanning.js.map +1 -1
  23. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js +1 -31
  24. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  25. package/dist/cjs/PivotViewer/hooks/useViewModeScrollHandling.js +4 -3
  26. package/dist/cjs/PivotViewer/hooks/useViewModeScrollHandling.js.map +1 -1
  27. package/dist/cjs/PivotViewer/types.js.map +1 -1
  28. package/dist/cjs/PivotViewer/utils/animations.js +11 -19
  29. package/dist/cjs/PivotViewer/utils/animations.js.map +1 -1
  30. package/dist/cjs/PivotViewer/utils/cardPosition.js +0 -3
  31. package/dist/cjs/PivotViewer/utils/cardPosition.js.map +1 -1
  32. package/dist/cjs/PivotViewer/utils/constants.js +2 -2
  33. package/dist/cjs/PivotViewer/utils/constants.js.map +1 -1
  34. package/dist/esm/PivotViewer/PivotViewer.css +32 -19
  35. package/dist/esm/PivotViewer/PivotViewer.d.ts +1 -1
  36. package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
  37. package/dist/esm/PivotViewer/PivotViewer.js +45 -9
  38. package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
  39. package/dist/esm/PivotViewer/PivotViewer.stories.d.ts.map +1 -1
  40. package/dist/esm/PivotViewer/PivotViewer.stories.js +5 -2
  41. package/dist/esm/PivotViewer/PivotViewer.stories.js.map +1 -1
  42. package/dist/esm/PivotViewer/components/PivotCanvas.d.ts.map +1 -1
  43. package/dist/esm/PivotViewer/components/PivotCanvas.js +33 -52
  44. package/dist/esm/PivotViewer/components/PivotCanvas.js.map +1 -1
  45. package/dist/esm/PivotViewer/components/PivotViewerMain.d.ts +2 -1
  46. package/dist/esm/PivotViewer/components/PivotViewerMain.d.ts.map +1 -1
  47. package/dist/esm/PivotViewer/components/PivotViewerMain.js +4 -2
  48. package/dist/esm/PivotViewer/components/PivotViewerMain.js.map +1 -1
  49. package/dist/esm/PivotViewer/components/Toolbar.js +1 -1
  50. package/dist/esm/PivotViewer/components/Toolbar.js.map +1 -1
  51. package/dist/esm/PivotViewer/components/pivot/animation.d.ts.map +1 -1
  52. package/dist/esm/PivotViewer/components/pivot/animation.js +4 -3
  53. package/dist/esm/PivotViewer/components/pivot/animation.js.map +1 -1
  54. package/dist/esm/PivotViewer/components/pivot/groups.d.ts +6 -0
  55. package/dist/esm/PivotViewer/components/pivot/groups.d.ts.map +1 -0
  56. package/dist/esm/PivotViewer/components/pivot/groups.js +129 -0
  57. package/dist/esm/PivotViewer/components/pivot/groups.js.map +1 -0
  58. package/dist/esm/PivotViewer/components/pivot/sprites.d.ts.map +1 -1
  59. package/dist/esm/PivotViewer/components/pivot/sprites.js +2 -15
  60. package/dist/esm/PivotViewer/components/pivot/sprites.js.map +1 -1
  61. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts +4 -0
  62. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
  63. package/dist/esm/PivotViewer/components/pivot/visibility.js +50 -27
  64. package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
  65. package/dist/esm/PivotViewer/engine/layout.js +11 -7
  66. package/dist/esm/PivotViewer/engine/layout.js.map +1 -1
  67. package/dist/esm/PivotViewer/engine/pivot.worker.d.ts.map +1 -1
  68. package/dist/esm/PivotViewer/engine/pivot.worker.js +0 -8
  69. package/dist/esm/PivotViewer/engine/pivot.worker.js.map +1 -1
  70. package/dist/esm/PivotViewer/engine/types.d.ts +1 -0
  71. package/dist/esm/PivotViewer/engine/types.d.ts.map +1 -1
  72. package/dist/esm/PivotViewer/hooks/useCurrentFilters.js.map +1 -1
  73. package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts +1 -1
  74. package/dist/esm/PivotViewer/hooks/useFilteredData.js +4 -4
  75. package/dist/esm/PivotViewer/hooks/useFilteredData.js.map +1 -1
  76. package/dist/esm/PivotViewer/hooks/usePanning.d.ts.map +1 -1
  77. package/dist/esm/PivotViewer/hooks/usePanning.js +8 -4
  78. package/dist/esm/PivotViewer/hooks/usePanning.js.map +1 -1
  79. package/dist/esm/PivotViewer/hooks/usePivotEngine.d.ts.map +1 -1
  80. package/dist/esm/PivotViewer/hooks/usePivotEngine.js +1 -31
  81. package/dist/esm/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  82. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.d.ts.map +1 -1
  83. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.js +4 -3
  84. package/dist/esm/PivotViewer/hooks/useViewModeScrollHandling.js.map +1 -1
  85. package/dist/esm/PivotViewer/types.d.ts +16 -0
  86. package/dist/esm/PivotViewer/types.d.ts.map +1 -1
  87. package/dist/esm/PivotViewer/types.js.map +1 -1
  88. package/dist/esm/PivotViewer/utils/animations.js +11 -19
  89. package/dist/esm/PivotViewer/utils/animations.js.map +1 -1
  90. package/dist/esm/PivotViewer/utils/cardPosition.js +1 -4
  91. package/dist/esm/PivotViewer/utils/cardPosition.js.map +1 -1
  92. package/dist/esm/PivotViewer/utils/constants.d.ts +4 -4
  93. package/dist/esm/PivotViewer/utils/constants.d.ts.map +1 -1
  94. package/dist/esm/PivotViewer/utils/constants.js +2 -2
  95. package/dist/esm/PivotViewer/utils/constants.js.map +1 -1
  96. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  97. package/package.json +1 -1
  98. package/dist/cjs/PivotViewer/components/pivot/buckets.js +0 -124
  99. package/dist/cjs/PivotViewer/components/pivot/buckets.js.map +0 -1
  100. package/dist/esm/PivotViewer/components/pivot/buckets.d.ts +0 -6
  101. package/dist/esm/PivotViewer/components/pivot/buckets.d.ts.map +0 -1
  102. package/dist/esm/PivotViewer/components/pivot/buckets.js +0 -102
  103. package/dist/esm/PivotViewer/components/pivot/buckets.js.map +0 -1
@@ -100,6 +100,7 @@ function createCardSprite(id, x, y, items, onCardClick, onPanStart, cardWidth, c
100
100
  container.eventMode = 'static';
101
101
  container.cursor = 'pointer';
102
102
  container.position.set(x, y);
103
+ container.hitArea = new PIXI__namespace.Rectangle(constants.CARD_GAP / 2, constants.CARD_GAP / 2, cardWidth - constants.CARD_GAP, cardHeight - constants.CARD_GAP);
103
104
  container._eventContext = { items, onCardClick, id };
104
105
  const graphics = new PIXI__namespace.Graphics();
105
106
  const actualWidth = cardWidth - constants.CARD_GAP;
@@ -172,20 +173,6 @@ function destroySprite(sprite) {
172
173
  }
173
174
  spritePool.push(sprite);
174
175
  }
175
- function clearSpritePool() {
176
- for (const sprite of spritePool) {
177
- try {
178
- sprite.graphics?.destroy();
179
- sprite.titleText?.destroy();
180
- sprite.labelsText?.destroy();
181
- sprite.valuesText?.destroy();
182
- sprite.container?.destroy();
183
- }
184
- catch (e) {
185
- }
186
- }
187
- spritePool.length = 0;
188
- }
189
176
  function updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, cardColors) {
190
177
  if (!item)
191
178
  return;
@@ -284,7 +271,6 @@ function updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, card
284
271
  }
285
272
  }
286
273
 
287
- exports.clearSpritePool = clearSpritePool;
288
274
  exports.createCardSprite = createCardSprite;
289
275
  exports.destroySprite = destroySprite;
290
276
  exports.updateCardContent = updateCardContent;
@@ -1 +1 @@
1
- {"version":3,"file":"sprites.js","sources":["../../../../../PivotViewer/components/pivot/sprites.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 { CARD_GAP, CARD_PADDING, CARD_RADIUS } from './constants';\nimport type { CardSprite, CardColors } from './constants';\n\nconst spritePool: CardSprite[] = [];\n\nexport function createCardSprite<TItem extends object>(\n id: number | string,\n x: number,\n y: number,\n items: TItem[],\n onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void,\n onPanStart: (e: MouseEvent) => void,\n cardWidth: number,\n cardHeight: number,\n cardColors: CardColors,\n): CardSprite {\n if (spritePool.length > 0) {\n const sprite = spritePool.pop()!;\n if (sprite.container) {\n sprite.container.visible = true;\n sprite.container.alpha = 1;\n sprite.container.position.set(x, y);\n }\n sprite.itemId = id;\n sprite.targetX = x;\n sprite.targetY = y;\n sprite.currentX = x;\n sprite.currentY = y;\n\n // Reset animation state\n delete sprite.animationStartTime;\n delete sprite.animationDelay;\n delete sprite.startX;\n delete sprite.startY;\n\n // Reset cache\n sprite.lastSelectedId = null;\n sprite.lastCardColors = undefined;\n sprite.lastTitle = undefined;\n sprite.lastLabels = undefined;\n sprite.lastValues = undefined;\n\n // Recreate graphics if it was destroyed\n if (!sprite.graphics || sprite.graphics.destroyed) {\n sprite.graphics = new PIXI.Graphics();\n if (sprite.container) {\n sprite.container.addChildAt(sprite.graphics, 0);\n }\n }\n\n // Recreate text objects if they were destroyed\n const offsetX = CARD_GAP / 2;\n const offsetY = CARD_GAP / 2;\n \n if (!sprite.titleText || sprite.titleText.destroyed) {\n sprite.titleText = new PIXI.Text('', {\n fontSize: 13,\n fill: cardColors.text as string | number,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n sprite.titleText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING);\n if (sprite.container) {\n sprite.container.addChild(sprite.titleText);\n }\n }\n\n if (!sprite.labelsText || sprite.labelsText.destroyed) {\n sprite.labelsText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.textSecondary as string | number,\n fontWeight: '400',\n lineHeight: 18,\n } as PIXI.TextStyle);\n sprite.labelsText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING + 40);\n if (sprite.container) {\n sprite.container.addChild(sprite.labelsText);\n }\n }\n\n if (!sprite.valuesText || sprite.valuesText.destroyed) {\n sprite.valuesText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.text as string | number,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n sprite.valuesText.position.set(offsetX + CARD_PADDING + 65, offsetY + CARD_PADDING + 40);\n if (sprite.container) {\n sprite.container.addChild(sprite.valuesText);\n }\n }\n\n // Update event context\n if (sprite.container) {\n (sprite.container as unknown as { _eventContext: { items: TItem[]; onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void; id: number | string } })._eventContext = { items, onCardClick, id };\n }\n\n return sprite;\n }\n\n const container = new PIXI.Container();\n container.eventMode = 'static';\n container.cursor = 'pointer';\n container.position.set(x, y);\n\n // Store context for event handlers\n (container as unknown as { _eventContext: { items: TItem[]; onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void; id: number | string } })._eventContext = { items, onCardClick, id };\n\n const graphics = new PIXI.Graphics();\n\n const actualWidth = cardWidth - CARD_GAP;\n const actualHeight = cardHeight - CARD_GAP;\n const offsetX = CARD_GAP / 2;\n const offsetY = CARD_GAP / 2;\n\n const gradient = new PIXI.FillGradient(0, offsetY, 0, offsetY + actualHeight);\n gradient.addColorStop(0, cardColors.mid);\n gradient.addColorStop(1, cardColors.base);\n\n graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, CARD_RADIUS);\n graphics.fill(gradient);\n\n container.addChild(graphics);\n\n const titleText = new PIXI.Text('', {\n fontSize: 13,\n fill: cardColors.text as string | number,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n titleText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING);\n container.addChild(titleText);\n\n const labelsText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.textSecondary as string | number,\n fontWeight: '400',\n lineHeight: 18,\n } as PIXI.TextStyle);\n labelsText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING + 40);\n container.addChild(labelsText);\n\n const valuesText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.text as string | number,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n valuesText.position.set(offsetX + CARD_PADDING + 65, offsetY + CARD_PADDING + 40);\n container.addChild(valuesText);\n\n container.on('click', (e: PIXI.FederatedPointerEvent) => {\n e.stopPropagation();\n const ctx = (container as unknown as { _eventContext: { items: TItem[]; onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void; id: number | string } })._eventContext;\n const itemsArray = ctx.items;\n const item = itemsArray[Number(ctx.id)];\n if (item) {\n ctx.onCardClick(item, e.nativeEvent as MouseEvent, ctx.id);\n }\n });\n\n container.on('pointerdown', (e: PIXI.FederatedPointerEvent) => {\n e.stopPropagation();\n // onPanStart(e.nativeEvent as MouseEvent);\n });\n\n return {\n container,\n graphics,\n titleText,\n labelsText,\n valuesText,\n itemId: id,\n targetX: x,\n targetY: y,\n currentX: x,\n currentY: y,\n };\n}\n\nexport function destroySprite(sprite: CardSprite) {\n if (sprite.container && sprite.container.parent) {\n sprite.container.parent.removeChild(sprite.container);\n }\n // Reset visibility to ensure it doesn't ghost if something goes wrong\n if (sprite.container) {\n sprite.container.visible = false;\n }\n spritePool.push(sprite);\n}\n\nexport function clearSpritePool() {\n for (const sprite of spritePool) {\n try {\n sprite.graphics?.destroy();\n sprite.titleText?.destroy();\n sprite.labelsText?.destroy();\n sprite.valuesText?.destroy();\n sprite.container?.destroy();\n } catch (e) {\n void e;\n }\n }\n spritePool.length = 0;\n}\n\n// Updated: Text objects now recreated when recycling pooled sprites\nexport function updateCardContent<TItem extends object>(\n sprite: CardSprite,\n item: TItem,\n selectedId: string | number | null,\n cardWidth: number,\n cardHeight: number,\n cardColors: CardColors,\n) {\n if (!item) return;\n\n const event = item as unknown as Record<string, unknown>;\n const eventType = String(event.type || event.name || event.title || 'Event');\n\n const timeStr = event.occurred ? new Date(event.occurred as string | number | Date).toLocaleString('en-US', {\n month: '2-digit',\n day: '2-digit',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false\n }).replace(',', '') : '';\n\n const correlation = event.correlationId || event.correlation || '';\n const correlationShort = correlation ? String(correlation).substring(0, 12) + '...' : '';\n\n const maxTitleLength = 20;\n const titleDisplay = eventType.length > maxTitleLength\n ? eventType.substring(0, maxTitleLength) + '...'\n : eventType;\n\n const maxTypeLength = 16;\n const typeDisplay = eventType.length > maxTypeLength\n ? eventType.substring(0, maxTypeLength) + '...'\n : eventType;\n\n const colors = cardColors;\n const labelsText = 'Type\\nOccurred\\nCorrelation';\n const valuesText = `${typeDisplay}\\n${timeStr}\\n${correlationShort}`;\n const colorsChanged = sprite.lastCardColors !== colors;\n\n // Ensure text objects exist before using them\n if (!sprite.titleText || sprite.titleText.destroyed) return;\n if (!sprite.labelsText || sprite.labelsText.destroyed) return;\n if (!sprite.valuesText || sprite.valuesText.destroyed) return;\n\n if (sprite.lastTitle !== titleDisplay) {\n sprite.titleText.text = titleDisplay;\n sprite.lastTitle = titleDisplay;\n }\n\n if (sprite.lastLabels !== labelsText) {\n sprite.labelsText.text = labelsText;\n sprite.lastLabels = labelsText;\n }\n\n if (colorsChanged && sprite.labelsText.style) {\n (sprite.labelsText.style as unknown as { fill: string | number }).fill = colors.textSecondary;\n }\n\n if (sprite.lastValues !== valuesText) {\n sprite.valuesText.text = valuesText;\n sprite.lastValues = valuesText;\n }\n\n if (colorsChanged && sprite.valuesText.style) {\n (sprite.valuesText.style as unknown as { fill: string | number }).fill = colors.text;\n }\n\n sprite.titleText.visible = true;\n sprite.labelsText.visible = true;\n sprite.valuesText.visible = true;\n\n const isSelected = sprite.itemId === selectedId;\n\n // Only redraw graphics if selection state or colors changed\n if (sprite.lastSelectedId === selectedId && !colorsChanged && sprite.graphics) {\n return;\n }\n\n sprite.lastSelectedId = selectedId;\n sprite.lastCardColors = cardColors;\n\n // Ensure graphics exists before attempting to use it\n if (!sprite.graphics || sprite.graphics.destroyed) {\n sprite.graphics = new PIXI.Graphics();\n if (sprite.container) {\n sprite.container.addChildAt(sprite.graphics, 0);\n }\n } else {\n sprite.graphics.clear();\n }\n\n const actualWidth = cardWidth - CARD_GAP;\n const actualHeight = cardHeight - CARD_GAP;\n const offsetX = CARD_GAP / 2;\n const offsetY = CARD_GAP / 2;\n\n const gradient = new PIXI.FillGradient(0, offsetY, 0, offsetY + actualHeight);\n if (isSelected) {\n gradient.addColorStop(0, colors.gradient);\n gradient.addColorStop(1, colors.mid);\n } else {\n gradient.addColorStop(0, colors.mid);\n gradient.addColorStop(1, colors.base);\n }\n\n sprite.graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, CARD_RADIUS);\n \n // Ensure graphics is still valid before filling\n if (sprite.graphics && !sprite.graphics.destroyed) {\n sprite.graphics.fill(gradient);\n }\n\n if (isSelected) {\n if (sprite.graphics && !sprite.graphics.destroyed) {\n sprite.graphics.stroke({ width: 2, color: colors.border });\n }\n } else {\n if (sprite.graphics && !sprite.graphics.destroyed) {\n sprite.graphics.stroke({ width: 1, color: colors.border, alpha: 0.35 });\n }\n }\n}\n"],"names":["PIXI","CARD_GAP","CARD_PADDING","CARD_RADIUS"],"mappings":"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"}
1
+ {"version":3,"file":"sprites.js","sources":["../../../../../PivotViewer/components/pivot/sprites.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 { CARD_GAP, CARD_PADDING, CARD_RADIUS } from './constants';\nimport type { CardSprite, CardColors } from './constants';\n\nconst spritePool: CardSprite[] = [];\n\nexport function createCardSprite<TItem extends object>(\n id: number | string,\n x: number,\n y: number,\n items: TItem[],\n onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void,\n onPanStart: (e: MouseEvent) => void,\n cardWidth: number,\n cardHeight: number,\n cardColors: CardColors,\n): CardSprite {\n if (spritePool.length > 0) {\n const sprite = spritePool.pop()!;\n if (sprite.container) {\n sprite.container.visible = true;\n sprite.container.alpha = 1;\n sprite.container.position.set(x, y);\n }\n sprite.itemId = id;\n sprite.targetX = x;\n sprite.targetY = y;\n sprite.currentX = x;\n sprite.currentY = y;\n\n // Reset animation state\n delete sprite.animationStartTime;\n delete sprite.animationDelay;\n delete sprite.startX;\n delete sprite.startY;\n\n // Reset cache\n sprite.lastSelectedId = null;\n sprite.lastCardColors = undefined;\n sprite.lastTitle = undefined;\n sprite.lastLabels = undefined;\n sprite.lastValues = undefined;\n\n // Recreate graphics if it was destroyed\n if (!sprite.graphics || sprite.graphics.destroyed) {\n sprite.graphics = new PIXI.Graphics();\n if (sprite.container) {\n sprite.container.addChildAt(sprite.graphics, 0);\n }\n }\n\n // Recreate text objects if they were destroyed\n const offsetX = CARD_GAP / 2;\n const offsetY = CARD_GAP / 2;\n \n if (!sprite.titleText || sprite.titleText.destroyed) {\n sprite.titleText = new PIXI.Text('', {\n fontSize: 13,\n fill: cardColors.text as string | number,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n sprite.titleText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING);\n if (sprite.container) {\n sprite.container.addChild(sprite.titleText);\n }\n }\n\n if (!sprite.labelsText || sprite.labelsText.destroyed) {\n sprite.labelsText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.textSecondary as string | number,\n fontWeight: '400',\n lineHeight: 18,\n } as PIXI.TextStyle);\n sprite.labelsText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING + 40);\n if (sprite.container) {\n sprite.container.addChild(sprite.labelsText);\n }\n }\n\n if (!sprite.valuesText || sprite.valuesText.destroyed) {\n sprite.valuesText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.text as string | number,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n sprite.valuesText.position.set(offsetX + CARD_PADDING + 65, offsetY + CARD_PADDING + 40);\n if (sprite.container) {\n sprite.container.addChild(sprite.valuesText);\n }\n }\n\n // Update event context\n if (sprite.container) {\n (sprite.container as unknown as { _eventContext: { items: TItem[]; onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void; id: number | string } })._eventContext = { items, onCardClick, id };\n }\n\n return sprite;\n }\n\n const container = new PIXI.Container();\n container.eventMode = 'static';\n container.cursor = 'pointer';\n container.position.set(x, y);\n\n // Define hit area to match the visible card size (excluding gaps)\n // This ensures the entire card is clickable and avoids issues with text blocking hits\n container.hitArea = new PIXI.Rectangle(\n CARD_GAP / 2,\n CARD_GAP / 2,\n cardWidth - CARD_GAP,\n cardHeight - CARD_GAP\n );\n\n // Store context for event handlers\n (container as unknown as { _eventContext: { items: TItem[]; onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void; id: number | string } })._eventContext = { items, onCardClick, id };\n\n const graphics = new PIXI.Graphics();\n\n const actualWidth = cardWidth - CARD_GAP;\n const actualHeight = cardHeight - CARD_GAP;\n const offsetX = CARD_GAP / 2;\n const offsetY = CARD_GAP / 2;\n\n const gradient = new PIXI.FillGradient(0, offsetY, 0, offsetY + actualHeight);\n gradient.addColorStop(0, cardColors.mid);\n gradient.addColorStop(1, cardColors.base);\n\n graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, CARD_RADIUS);\n graphics.fill(gradient);\n\n container.addChild(graphics);\n\n const titleText = new PIXI.Text('', {\n fontSize: 13,\n fill: cardColors.text as string | number,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n titleText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING);\n container.addChild(titleText);\n\n const labelsText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.textSecondary as string | number,\n fontWeight: '400',\n lineHeight: 18,\n } as PIXI.TextStyle);\n labelsText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING + 40);\n container.addChild(labelsText);\n\n const valuesText = new PIXI.Text('', {\n fontSize: 11,\n fill: cardColors.text as string | number,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as PIXI.TextStyle);\n valuesText.position.set(offsetX + CARD_PADDING + 65, offsetY + CARD_PADDING + 40);\n container.addChild(valuesText);\n\n container.on('click', (e: PIXI.FederatedPointerEvent) => {\n e.stopPropagation();\n const ctx = (container as unknown as { _eventContext: { items: TItem[]; onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void; id: number | string } })._eventContext;\n const itemsArray = ctx.items;\n const item = itemsArray[Number(ctx.id)];\n if (item) {\n ctx.onCardClick(item, e.nativeEvent as MouseEvent, ctx.id);\n }\n });\n\n container.on('pointerdown', (e: PIXI.FederatedPointerEvent) => {\n e.stopPropagation();\n // onPanStart(e.nativeEvent as MouseEvent);\n });\n\n return {\n container,\n graphics,\n titleText,\n labelsText,\n valuesText,\n itemId: id,\n targetX: x,\n targetY: y,\n currentX: x,\n currentY: y,\n };\n}\n\nexport function destroySprite(sprite: CardSprite) {\n if (sprite.container && sprite.container.parent) {\n sprite.container.parent.removeChild(sprite.container);\n }\n // Reset visibility to ensure it doesn't ghost if something goes wrong\n if (sprite.container) {\n sprite.container.visible = false;\n }\n spritePool.push(sprite);\n}\n\nexport function clearSpritePool() {\n for (const sprite of spritePool) {\n try {\n sprite.graphics?.destroy();\n sprite.titleText?.destroy();\n sprite.labelsText?.destroy();\n sprite.valuesText?.destroy();\n sprite.container?.destroy();\n } catch (e) {\n void e;\n }\n }\n spritePool.length = 0;\n}\n\n// Updated: Text objects now recreated when recycling pooled sprites\nexport function updateCardContent<TItem extends object>(\n sprite: CardSprite,\n item: TItem,\n selectedId: string | number | null,\n cardWidth: number,\n cardHeight: number,\n cardColors: CardColors,\n) {\n if (!item) return;\n\n const event = item as unknown as Record<string, unknown>;\n const eventType = String(event.type || event.name || event.title || 'Event');\n\n const timeStr = event.occurred ? new Date(event.occurred as string | number | Date).toLocaleString('en-US', {\n month: '2-digit',\n day: '2-digit',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false\n }).replace(',', '') : '';\n\n const correlation = event.correlationId || event.correlation || '';\n const correlationShort = correlation ? String(correlation).substring(0, 12) + '...' : '';\n\n const maxTitleLength = 20;\n const titleDisplay = eventType.length > maxTitleLength\n ? eventType.substring(0, maxTitleLength) + '...'\n : eventType;\n\n const maxTypeLength = 16;\n const typeDisplay = eventType.length > maxTypeLength\n ? eventType.substring(0, maxTypeLength) + '...'\n : eventType;\n\n const colors = cardColors;\n const labelsText = 'Type\\nOccurred\\nCorrelation';\n const valuesText = `${typeDisplay}\\n${timeStr}\\n${correlationShort}`;\n const colorsChanged = sprite.lastCardColors !== colors;\n\n // Ensure text objects exist before using them\n if (!sprite.titleText || sprite.titleText.destroyed) return;\n if (!sprite.labelsText || sprite.labelsText.destroyed) return;\n if (!sprite.valuesText || sprite.valuesText.destroyed) return;\n\n if (sprite.lastTitle !== titleDisplay) {\n sprite.titleText.text = titleDisplay;\n sprite.lastTitle = titleDisplay;\n }\n\n if (sprite.lastLabels !== labelsText) {\n sprite.labelsText.text = labelsText;\n sprite.lastLabels = labelsText;\n }\n\n if (colorsChanged && sprite.labelsText.style) {\n (sprite.labelsText.style as unknown as { fill: string | number }).fill = colors.textSecondary;\n }\n\n if (sprite.lastValues !== valuesText) {\n sprite.valuesText.text = valuesText;\n sprite.lastValues = valuesText;\n }\n\n if (colorsChanged && sprite.valuesText.style) {\n (sprite.valuesText.style as unknown as { fill: string | number }).fill = colors.text;\n }\n\n sprite.titleText.visible = true;\n sprite.labelsText.visible = true;\n sprite.valuesText.visible = true;\n\n const isSelected = sprite.itemId === selectedId;\n\n // Only redraw graphics if selection state or colors changed\n if (sprite.lastSelectedId === selectedId && !colorsChanged && sprite.graphics) {\n return;\n }\n\n sprite.lastSelectedId = selectedId;\n sprite.lastCardColors = cardColors;\n\n // Ensure graphics exists before attempting to use it\n if (!sprite.graphics || sprite.graphics.destroyed) {\n sprite.graphics = new PIXI.Graphics();\n if (sprite.container) {\n sprite.container.addChildAt(sprite.graphics, 0);\n }\n } else {\n sprite.graphics.clear();\n }\n\n const actualWidth = cardWidth - CARD_GAP;\n const actualHeight = cardHeight - CARD_GAP;\n const offsetX = CARD_GAP / 2;\n const offsetY = CARD_GAP / 2;\n\n const gradient = new PIXI.FillGradient(0, offsetY, 0, offsetY + actualHeight);\n if (isSelected) {\n gradient.addColorStop(0, colors.gradient);\n gradient.addColorStop(1, colors.mid);\n } else {\n gradient.addColorStop(0, colors.mid);\n gradient.addColorStop(1, colors.base);\n }\n\n sprite.graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, CARD_RADIUS);\n \n // Ensure graphics is still valid before filling\n if (sprite.graphics && !sprite.graphics.destroyed) {\n sprite.graphics.fill(gradient);\n }\n\n if (isSelected) {\n if (sprite.graphics && !sprite.graphics.destroyed) {\n sprite.graphics.stroke({ width: 2, color: colors.border });\n }\n } else {\n if (sprite.graphics && !sprite.graphics.destroyed) {\n sprite.graphics.stroke({ width: 1, color: colors.border, alpha: 0.35 });\n }\n }\n}\n"],"names":["PIXI","CARD_GAP","CARD_PADDING","CARD_RADIUS"],"mappings":"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"}
@@ -4,17 +4,26 @@ var constants = require('./constants.js');
4
4
  var sprites = require('./sprites.js');
5
5
 
6
6
  function syncSpritesToViewport(params) {
7
- const { root, container, sprites: sprites$1, layout, visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, prevLayout } = params;
7
+ const { root, groupsContainer, container, sprites: sprites$1, layout, visibleIds: _visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, viewMode, prevLayout, prevScrollTop, prevScrollLeft } = params;
8
8
  if (!root || !container)
9
9
  return;
10
- console.log('[syncSpritesToViewport] Called with', {
11
- layoutPositionsSize: layout.positions.size,
12
- visibleIdsSize: visibleIds.length,
13
- spritesSize: sprites$1.size,
14
- itemsLength: items.length,
15
- isViewTransition,
16
- zoomLevel
17
- });
10
+ const viewportPxWidth = container.clientWidth || viewportWidth;
11
+ const viewportPxHeight = container.clientHeight || viewportHeight;
12
+ const contentHeightPx = (layout.totalHeight || 0) * zoomLevel;
13
+ let offsetY = 0;
14
+ if (viewMode === 'grouped' && contentHeightPx < viewportPxHeight) {
15
+ offsetY = viewportPxHeight - contentHeightPx;
16
+ }
17
+ const actualScrollX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
18
+ const actualScrollY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
19
+ if (root) {
20
+ root.scale.set(zoomLevel);
21
+ root.position.set(-actualScrollX, offsetY - actualScrollY);
22
+ }
23
+ if (groupsContainer) {
24
+ groupsContainer.scale.set(zoomLevel);
25
+ groupsContainer.position.set(-actualScrollX, offsetY - actualScrollY);
26
+ }
18
27
  if (isViewTransition && (panDeltaX || panDeltaY)) {
19
28
  const dx = (panDeltaX || 0) / (zoomLevel || 1);
20
29
  const dy = (panDeltaY || 0) / (zoomLevel || 1);
@@ -35,10 +44,9 @@ function syncSpritesToViewport(params) {
35
44
  const invScale = zoomLevel && zoomLevel !== 0 ? 1 / zoomLevel : 1;
36
45
  const effectivePanX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
37
46
  const effectivePanY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
47
+ const effectivePanYWithOffset = effectivePanY - offsetY;
38
48
  const panWorldX = effectivePanX * invScale;
39
- const panWorldY = effectivePanY * invScale;
40
- const viewportPxWidth = container.clientWidth || viewportWidth;
41
- const viewportPxHeight = container.clientHeight || viewportHeight;
49
+ const panWorldY = effectivePanYWithOffset * invScale;
42
50
  const viewportWorldWidth = viewportPxWidth * invScale;
43
51
  const viewportWorldHeight = viewportPxHeight * invScale;
44
52
  const bufferWorld = Math.max(baseBufferWorld * invScale, Math.max(viewportWorldWidth, viewportWorldHeight) * 2.0, baseBufferWorld);
@@ -63,6 +71,16 @@ function syncSpritesToViewport(params) {
63
71
  visibleSet.add(id);
64
72
  }
65
73
  }
74
+ if (isViewTransition && inViewportIds.length === 0 && layout.positions.size > 0) {
75
+ let count = 0;
76
+ for (const [id, position] of layout.positions) {
77
+ if (count < 5 && position) {
78
+ inViewportIds.push(id);
79
+ visibleSet.add(id);
80
+ count++;
81
+ }
82
+ }
83
+ }
66
84
  try {
67
85
  const slotHeight = cardHeight + (constants.CARD_GAP || 8);
68
86
  const totalRows = Math.ceil((layout.totalHeight || 0) / slotHeight) || 0;
@@ -81,9 +99,26 @@ function syncSpritesToViewport(params) {
81
99
  }
82
100
  catch (e) {
83
101
  }
84
- const scrollStabilized = Math.abs(panWorldY - (container.scrollTop * invScale)) < 10;
85
- const aggressiveCull = (!isViewTransition && sprites$1.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5))) ||
86
- (isViewTransition && !scrollStabilized);
102
+ let injectedFallback = false;
103
+ const fallbackCount = isViewTransition ? 30 : 12;
104
+ if (inViewportIds.length === 0 && layout.positions.size > 0) {
105
+ injectedFallback = true;
106
+ let count = 0;
107
+ for (const [id] of layout.positions) {
108
+ inViewportIds.push(id);
109
+ visibleSet.add(id);
110
+ count++;
111
+ if (count >= fallbackCount)
112
+ break;
113
+ }
114
+ }
115
+ const currentScrollTop = container.scrollTop || 0;
116
+ const currentScrollLeft = container.scrollLeft || 0;
117
+ const scrollTopDelta = Math.abs(currentScrollTop - (prevScrollTop || currentScrollTop));
118
+ const scrollLeftDelta = Math.abs(currentScrollLeft - (prevScrollLeft || currentScrollLeft));
119
+ const scrollStabilized = scrollTopDelta < 10 && scrollLeftDelta < 10;
120
+ const aggressiveCull = !injectedFallback && ((!isViewTransition && sprites$1.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5))) ||
121
+ (isViewTransition && !scrollStabilized));
87
122
  for (const [id, sprite] of sprites$1) {
88
123
  if (!visibleSet.has(id)) {
89
124
  if (isViewTransition && layout.positions.has(id)) {
@@ -165,22 +200,17 @@ function syncSpritesToViewport(params) {
165
200
  }
166
201
  const MAX_SPRITES_PER_FRAME = 50;
167
202
  let createdCount = 0;
168
- console.log('[syncSpritesToViewport] About to create sprites for inViewportIds:', inViewportIds.length);
169
- console.log('[syncSpritesToViewport] layout.positions IDs:', Array.from(layout.positions.keys()));
170
203
  for (const id of inViewportIds) {
171
204
  const position = layout.positions.get(id);
172
205
  if (!position) {
173
- console.log('[syncSpritesToViewport] No position for id:', id, 'in layout.positions');
174
206
  continue;
175
207
  }
176
208
  let sprite = sprites$1.get(id);
177
209
  if (!sprite) {
178
210
  if (createdCount >= MAX_SPRITES_PER_FRAME) {
179
- console.log('[syncSpritesToViewport] Max sprites per frame reached');
180
211
  continue;
181
212
  }
182
213
  createdCount++;
183
- console.log('[syncSpritesToViewport] Creating sprite for id:', id, 'at position:', position);
184
214
  let startX = position.x;
185
215
  let startY = position.y;
186
216
  let shouldAnimate = false;
@@ -216,10 +246,6 @@ function syncSpritesToViewport(params) {
216
246
  }
217
247
  }
218
248
  if (sprite.targetX !== position.x || sprite.targetY !== position.y) {
219
- console.log('[syncSpritesToViewport] Updating sprite target position for id:', id, 'from', {
220
- oldX: sprite.targetX,
221
- oldY: sprite.targetY
222
- }, 'to', position);
223
249
  if (isViewTransition) {
224
250
  sprite.startX = sprite.currentX;
225
251
  sprite.startY = sprite.currentY;
@@ -235,9 +261,6 @@ function syncSpritesToViewport(params) {
235
261
  delete sprite.animationDelay;
236
262
  }
237
263
  }
238
- else {
239
- console.log('[syncSpritesToViewport] Sprite position unchanged for id:', id, 'at', position);
240
- }
241
264
  const item = items[Number(id)];
242
265
  if (item) {
243
266
  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 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 prevLayout?: LayoutResult | null;\n}\n\nexport function syncSpritesToViewport<TItem>(params: SyncParams<TItem>) {\n const { root, container, sprites, layout, visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, prevLayout } = params;\n if (!root || !container) return;\n\n console.log('[syncSpritesToViewport] Called with', {\n layoutPositionsSize: layout.positions.size,\n visibleIdsSize: visibleIds.length,\n spritesSize: sprites.size,\n itemsLength: items.length,\n isViewTransition,\n zoomLevel\n });\n\n // `visibleIds` comes from callers but this module iterates `layout.positions`.\n // Keep a reference to avoid unused variable lint errors when callers include it.\n void visibleIds;\n\n // Apply pan delta to animating sprites to keep them visually stable during camera jumps\n if (isViewTransition && (panDeltaX || panDeltaY)) {\n const dx = (panDeltaX || 0) / (zoomLevel || 1);\n const dy = (panDeltaY || 0) / (zoomLevel || 1);\n\n for (const sprite of sprites.values()) {\n if (sprite.animationStartTime !== undefined) {\n if (sprite.startX !== undefined) sprite.startX += dx;\n if (sprite.startY !== undefined) sprite.startY += dy;\n sprite.currentX += dx;\n sprite.currentY += dy;\n sprite.container?.position?.set(sprite.currentX, sprite.currentY);\n }\n }\n }\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 const panWorldX = effectivePanX * invScale;\n const panWorldY = effectivePanY * invScale;\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 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 // Scale epsilon with invScale so tolerance grows when zoomed out.\n const worldEpsilon = Math.max(0.5, 0.5 * invScale);\n\n // Iterate layout positions directly to avoid depending on `visibleIds`\n // which may be calculated in a different coordinate space or with\n // different assumptions about zoom. Looping the positions map is\n // deterministic and uses world coordinates directly.\n for (const [id, position] of layout.positions) {\n if (!position) continue;\n const worldX = position.x;\n const worldY = position.y;\n const worldCardW = cardWidth;\n const worldCardH = cardHeight;\n\n if (\n worldX + worldCardW >= viewportLeftWorld - worldEpsilon &&\n worldX <= viewportRightWorld + worldEpsilon &&\n worldY + worldCardH >= viewportTopWorld - worldEpsilon &&\n worldY <= viewportBottomWorld + worldEpsilon\n ) {\n inViewportIds.push(id);\n visibleSet.add(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 // If we detect a very large discrepancy between created sprites and the\n // computed in-viewport count, that's a signal our culling math may be\n // unstable (especially at non-100% zoom). In that case, skip hiding this\n // frame as a conservative safeguard to avoid mass disappearing tiles.\n // However, disable this safeguard during view transitions to ensure old sprites are cleaned up.\n // EXCEPT: During view transitions, if scroll position hasn't stabilized yet (e.g., switching to grouped\n // mode triggers a scroll-to-bottom), keep all sprites visible to prevent flickering\n const scrollStabilized = Math.abs(panWorldY - (container.scrollTop * invScale)) < 10;\n const aggressiveCull = (!isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5))) || \n (isViewTransition && !scrollStabilized);\n\n for (const [id, sprite] of sprites) {\n if (!visibleSet.has(id)) {\n // If view transition is active, check if this sprite has a valid target in the new layout\n // If so, keep it visible and animate it to the new position (even if off-screen)\n if (isViewTransition && layout.positions.has(id)) {\n const newPos = layout.positions.get(id);\n if (newPos) {\n sprite.targetX = newPos.x;\n sprite.targetY = newPos.y;\n\n // Trigger animation if not already animating\n if (sprite.animationStartTime === undefined) {\n sprite.startX = sprite.currentX;\n sprite.startY = sprite.currentY;\n sprite.animationStartTime = Date.now();\n sprite.animationDelay = Math.random() * 300;\n }\n\n try { if (sprite.container) sprite.container.visible = true; } catch (e) { void e; }\n // Don't mark as hidden, so it won't be swept\n if ((sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt) delete (sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt;\n continue;\n }\n }\n\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 && 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 console.log('[syncSpritesToViewport] About to create sprites for inViewportIds:', inViewportIds.length);\n console.log('[syncSpritesToViewport] layout.positions IDs:', Array.from(layout.positions.keys()));\n\n for (const id of inViewportIds) {\n const position = layout.positions.get(id);\n if (!position) {\n console.log('[syncSpritesToViewport] No position for id:', id, 'in layout.positions');\n continue;\n }\n\n let sprite = sprites.get(id);\n if (!sprite) {\n if (createdCount >= MAX_SPRITES_PER_FRAME) {\n console.log('[syncSpritesToViewport] Max sprites per frame reached');\n continue;\n }\n createdCount++;\n\n console.log('[syncSpritesToViewport] Creating sprite for id:', id, 'at position:', position);\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) {\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 (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 if (sprite.targetX !== position.x || sprite.targetY !== position.y) {\n console.log('[syncSpritesToViewport] Updating sprite target position for id:', id, 'from', {\n oldX: sprite.targetX,\n oldY: sprite.targetY\n }, 'to', position);\n if (isViewTransition) {\n sprite.startX = sprite.currentX;\n sprite.startY = sprite.currentY;\n sprite.targetX = position.x;\n sprite.targetY = position.y;\n sprite.animationStartTime = Date.now();\n // Add random delay for \"organic\" fly effect\n sprite.animationDelay = Math.random() * 300;\n } else {\n sprite.targetX = position.x;\n sprite.targetY = position.y;\n delete sprite.animationStartTime;\n delete sprite.animationDelay;\n }\n } else {\n console.log('[syncSpritesToViewport] Sprite position unchanged for id:', id, 'at', position);\n }\n\n const item = items[Number(id)];\n if (item) {\n updateCardContent(sprite, item);\n }\n }\n}\n"],"names":["sprites","CARD_GAP","destroySprite"],"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 * 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 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, prevScrollTop, prevScrollLeft } = 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 // Apply pan delta to animating sprites to keep them visually stable during camera jumps\n if (isViewTransition && (panDeltaX || panDeltaY)) {\n const dx = (panDeltaX || 0) / (zoomLevel || 1);\n const dy = (panDeltaY || 0) / (zoomLevel || 1);\n\n for (const sprite of sprites.values()) {\n if (sprite.animationStartTime !== undefined) {\n if (sprite.startX !== undefined) sprite.startX += dx;\n if (sprite.startY !== undefined) sprite.startY += dy;\n sprite.currentX += dx;\n sprite.currentY += dy;\n sprite.container?.position?.set(sprite.currentX, sprite.currentY);\n }\n }\n }\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 // Scale epsilon with invScale so tolerance grows when zoomed out.\n const worldEpsilon = Math.max(0.5, 0.5 * invScale);\n\n // Iterate layout positions directly to avoid depending on `visibleIds`\n // which may be calculated in a different coordinate space or with\n // different assumptions about zoom. Looping the positions map is\n // deterministic and uses world coordinates directly.\n for (const [id, position] of layout.positions) {\n if (!position) continue;\n const worldX = position.x;\n const worldY = position.y;\n const worldCardW = cardWidth;\n const worldCardH = cardHeight;\n\n if (\n worldX + worldCardW >= viewportLeftWorld - worldEpsilon &&\n worldX <= viewportRightWorld + worldEpsilon &&\n worldY + worldCardH >= viewportTopWorld - worldEpsilon &&\n worldY <= viewportBottomWorld + worldEpsilon\n ) {\n inViewportIds.push(id);\n visibleSet.add(id);\n }\n }\n\n // During view transitions, if no cards are visible and we're not animating yet,\n // force-add the first few cards from the layout to ensure content appears.\n // This prevents a blank screen when switching modes, especially in packaged builds\n // where scroll stabilization might be delayed.\n if (isViewTransition && inViewportIds.length === 0 && layout.positions.size > 0) {\n let count = 0;\n for (const [id, position] of layout.positions) {\n if (count < 5 && position) { // Add up to 5 cards\n inViewportIds.push(id);\n visibleSet.add(id);\n count++;\n }\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 // Fallback: if no sprites are calculated as visible (e.g., due to rounding\n // or scroll/zoom race conditions), force a handful of cards into view so\n // the canvas never renders empty at certain zoom levels.\n // When transitioning views, be more aggressive to ensure content appears during the transition\n let injectedFallback = false;\n const fallbackCount = isViewTransition ? 30 : 12;\n if (inViewportIds.length === 0 && layout.positions.size > 0) {\n injectedFallback = true;\n let count = 0;\n for (const [id] of layout.positions) {\n inViewportIds.push(id);\n visibleSet.add(id);\n count++;\n if (count >= fallbackCount) break;\n }\n }\n\n // If we detect a very large discrepancy between created sprites and the\n // computed in-viewport count, that's a signal our culling math may be\n // unstable (especially at non-100% zoom). In that case, skip hiding this\n // frame as a conservative safeguard to avoid mass disappearing tiles.\n // However, disable this safeguard during view transitions to ensure old sprites are cleaned up.\n // EXCEPT: During view transitions, if scroll position hasn't stabilized yet (e.g., switching to grouped\n // mode triggers a scroll-to-bottom), keep all sprites visible to prevent flickering.\n // Check scroll stabilization by comparing current scroll to previous scroll position.\n const currentScrollTop = container.scrollTop || 0;\n const currentScrollLeft = container.scrollLeft || 0;\n const scrollTopDelta = Math.abs(currentScrollTop - (prevScrollTop || currentScrollTop));\n const scrollLeftDelta = Math.abs(currentScrollLeft - (prevScrollLeft || currentScrollLeft));\n const scrollStabilized = scrollTopDelta < 10 && scrollLeftDelta < 10;\n const aggressiveCull = !injectedFallback && (\n (!isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5))) ||\n (isViewTransition && !scrollStabilized)\n );\n\n for (const [id, sprite] of sprites) {\n if (!visibleSet.has(id)) {\n // If view transition is active, check if this sprite has a valid target in the new layout\n // If so, keep it visible and animate it to the new position (even if off-screen)\n if (isViewTransition && layout.positions.has(id)) {\n const newPos = layout.positions.get(id);\n if (newPos) {\n sprite.targetX = newPos.x;\n sprite.targetY = newPos.y;\n\n // Trigger animation if not already animating\n if (sprite.animationStartTime === undefined) {\n sprite.startX = sprite.currentX;\n sprite.startY = sprite.currentY;\n sprite.animationStartTime = Date.now();\n sprite.animationDelay = Math.random() * 300;\n }\n\n try { if (sprite.container) sprite.container.visible = true; } catch (e) { void e; }\n // Don't mark as hidden, so it won't be swept\n if ((sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt) delete (sprite as unknown as { __lastHiddenAt?: number }).__lastHiddenAt;\n continue;\n }\n }\n\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 && 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) {\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 (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 if (sprite.targetX !== position.x || sprite.targetY !== position.y) {\n if (isViewTransition) {\n sprite.startX = sprite.currentX;\n sprite.startY = sprite.currentY;\n sprite.targetX = position.x;\n sprite.targetY = position.y;\n sprite.animationStartTime = Date.now();\n // Add random delay for \"organic\" fly effect\n sprite.animationDelay = Math.random() * 300;\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":["sprites","CARD_GAP","destroySprite"],"mappings":"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"}
@@ -47,31 +47,34 @@ function computeCollectionLayout(grouping, spec, positions) {
47
47
  };
48
48
  }
49
49
  function computeGroupedLayout(grouping, spec, positions) {
50
- const { cardWidth, cardHeight, cardsPerColumn, groupSpacing } = spec;
50
+ const { cardWidth, cardHeight, cardsPerColumn } = spec;
51
+ const effectiveGroupSpacing = 0;
51
52
  const slotWidth = cardWidth + constants.CARD_GAP;
52
53
  const slotHeight = cardHeight + constants.CARD_GAP;
54
+ const BOTTOM_MARGIN = 0;
53
55
  const COLUMNS_PER_BUCKET = 2;
54
56
  const bucketWidth = COLUMNS_PER_BUCKET * slotWidth;
55
- let groupX = constants.CANVAS_PADDING;
57
+ let groupX = 0;
56
58
  const layoutHeight = spec.containerHeight || (cardsPerColumn * slotHeight);
57
59
  const bucketWidths = [];
60
+ const groupXs = [];
58
61
  let maxRows = 0;
59
62
  for (const group of grouping.groups) {
60
63
  const itemsInGroup = group.ids.length;
61
64
  const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);
62
65
  maxRows = Math.max(maxRows, rowsInGroup);
63
66
  }
64
- const contentHeight = Math.max(layoutHeight, maxRows * slotHeight);
67
+ const contentHeight = Math.max(layoutHeight, (maxRows * slotHeight) + BOTTOM_MARGIN);
65
68
  for (let groupIndex = 0; groupIndex < grouping.groups.length; groupIndex++) {
66
69
  const group = grouping.groups[groupIndex];
70
+ groupXs.push(groupX);
67
71
  const itemsInGroup = group.ids.length;
68
72
  for (let i = 0; i < itemsInGroup; i++) {
69
73
  const id = group.ids[i];
70
74
  const col = i % COLUMNS_PER_BUCKET;
71
75
  const row = Math.floor(i / COLUMNS_PER_BUCKET);
72
- const x = groupX + col * slotWidth;
73
- const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);
74
- const y = constants.CANVAS_PADDING + contentHeight - (rowsInGroup - row) * slotHeight;
76
+ const x = groupX + (col * slotWidth) + (constants.CARD_GAP / 2);
77
+ const y = constants.CANVAS_PADDING + contentHeight - BOTTOM_MARGIN - ((row + 1) * slotHeight);
75
78
  positions.set(id, {
76
79
  x,
77
80
  y,
@@ -81,7 +84,7 @@ function computeGroupedLayout(grouping, spec, positions) {
81
84
  bucketWidths.push(bucketWidth);
82
85
  groupX += bucketWidth;
83
86
  if (groupIndex < grouping.groups.length - 1) {
84
- groupX += groupSpacing;
87
+ groupX += effectiveGroupSpacing;
85
88
  }
86
89
  }
87
90
  return {
@@ -89,6 +92,7 @@ function computeGroupedLayout(grouping, spec, positions) {
89
92
  totalWidth: groupX + constants.CANVAS_PADDING,
90
93
  totalHeight: contentHeight + (constants.CANVAS_PADDING * 2),
91
94
  bucketWidths,
95
+ groupXs,
92
96
  };
93
97
  }
94
98
 
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","sources":["../../../../PivotViewer/engine/layout.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 {\n LayoutSpec,\n LayoutResult,\n ItemPosition,\n GroupingResult,\n ItemId,\n} from './types';\nimport { CARD_GAP, CANVAS_PADDING } from '../constants';\n\nexport function computeLayout(\n grouping: GroupingResult,\n spec: LayoutSpec\n): LayoutResult {\n const positions = new Map<ItemId, ItemPosition>();\n\n if (spec.viewMode === 'collection') {\n return computeCollectionLayout(grouping, spec, positions);\n } else {\n return computeGroupedLayout(grouping, spec, positions);\n }\n}\n\nfunction computeCollectionLayout(\n grouping: GroupingResult,\n spec: LayoutSpec,\n positions: Map<ItemId, ItemPosition>\n): LayoutResult {\n const { cardWidth, cardHeight, containerWidth } = spec;\n const slotWidth = cardWidth + CARD_GAP;\n const slotHeight = cardHeight + CARD_GAP;\n\n // Calculate how many cards fit per row based on container width (include gap)\n const cardsPerRow = Math.max(1, Math.floor((containerWidth + CARD_GAP - (CANVAS_PADDING * 2)) / slotWidth));\n\n let x = CANVAS_PADDING;\n let y = CANVAS_PADDING;\n let column = 0;\n let itemCount = 0;\n\n for (const group of grouping.groups) {\n for (let i = 0; i < group.ids.length; i++) {\n const id = group.ids[i];\n\n positions.set(id, {\n x,\n y,\n groupIndex: 0,\n });\n\n // Move to next position horizontally (left to right)\n column++;\n x += slotWidth;\n\n // Wrap to next row when we've filled the width\n if (column >= cardsPerRow) {\n column = 0;\n x = CANVAS_PADDING;\n y += slotHeight;\n }\n\n itemCount++;\n }\n }\n\n const rows = Math.ceil(itemCount / cardsPerRow);\n const contentWidth = Math.min(itemCount, cardsPerRow) * slotWidth;\n\n return {\n positions,\n totalWidth: Math.max(containerWidth, contentWidth + (CANVAS_PADDING * 2)),\n totalHeight: (rows * slotHeight) + (CANVAS_PADDING * 2),\n };\n}\n\nfunction computeGroupedLayout(\n grouping: GroupingResult,\n spec: LayoutSpec,\n positions: Map<ItemId, ItemPosition>\n): LayoutResult {\n const { cardWidth, cardHeight, cardsPerColumn, groupSpacing } = spec;\n const slotWidth = cardWidth + CARD_GAP;\n const slotHeight = cardHeight + CARD_GAP;\n\n // Fixed bucket width: 2 columns of cards per bucket (always)\n const COLUMNS_PER_BUCKET = 2;\n const bucketWidth = COLUMNS_PER_BUCKET * slotWidth;\n\n let groupX = CANVAS_PADDING;\n // Use container height for layout, or fallback to cardsPerColumn height\n const layoutHeight = spec.containerHeight || (cardsPerColumn * slotHeight);\n const bucketWidths: number[] = [];\n let maxRows = 0;\n\n // First pass: calculate max rows to determine total height\n for (const group of grouping.groups) {\n const itemsInGroup = group.ids.length;\n const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);\n maxRows = Math.max(maxRows, rowsInGroup);\n }\n\n // Calculate actual content height needed (ensure it's at least as tall as the container)\n const contentHeight = Math.max(layoutHeight, maxRows * slotHeight);\n\n for (let groupIndex = 0; groupIndex < grouping.groups.length; groupIndex++) {\n const group = grouping.groups[groupIndex];\n\n const itemsInGroup = group.ids.length;\n\n for (let i = 0; i < itemsInGroup; i++) {\n const id = group.ids[i];\n\n // Cards fill from left to right, bottom to top\n // For a 2-column bucket: i=0,1 in row 0; i=2,3 in row 1; etc.\n const col = i % COLUMNS_PER_BUCKET;\n const row = Math.floor(i / COLUMNS_PER_BUCKET);\n\n const x = groupX + col * slotWidth;\n // Position cards from bottom of container, stacking upwards\n // Start from contentHeight and subtract row positions\n const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);\n const y = CANVAS_PADDING + contentHeight - (rowsInGroup - row) * slotHeight;\n\n positions.set(id, {\n x,\n y,\n groupIndex,\n });\n }\n\n // Always use fixed bucket width\n bucketWidths.push(bucketWidth);\n\n // Advance position by fixed bucket width + spacing\n groupX += bucketWidth;\n if (groupIndex < grouping.groups.length - 1) {\n groupX += groupSpacing;\n }\n }\n\n return {\n positions,\n totalWidth: groupX + CANVAS_PADDING,\n totalHeight: contentHeight + (CANVAS_PADDING * 2),\n bucketWidths,\n };\n}\n"],"names":["CARD_GAP","CANVAS_PADDING"],"mappings":"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"}
1
+ {"version":3,"file":"layout.js","sources":["../../../../PivotViewer/engine/layout.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 {\n LayoutSpec,\n LayoutResult,\n ItemPosition,\n GroupingResult,\n ItemId,\n} from './types';\nimport { CARD_GAP, CANVAS_PADDING } from '../constants';\n\nexport function computeLayout(\n grouping: GroupingResult,\n spec: LayoutSpec\n): LayoutResult {\n const positions = new Map<ItemId, ItemPosition>();\n\n if (spec.viewMode === 'collection') {\n return computeCollectionLayout(grouping, spec, positions);\n } else {\n return computeGroupedLayout(grouping, spec, positions);\n }\n}\n\nfunction computeCollectionLayout(\n grouping: GroupingResult,\n spec: LayoutSpec,\n positions: Map<ItemId, ItemPosition>\n): LayoutResult {\n const { cardWidth, cardHeight, containerWidth } = spec;\n const slotWidth = cardWidth + CARD_GAP;\n const slotHeight = cardHeight + CARD_GAP;\n\n // Calculate how many cards fit per row based on container width (include gap)\n const cardsPerRow = Math.max(1, Math.floor((containerWidth + CARD_GAP - (CANVAS_PADDING * 2)) / slotWidth));\n\n let x = CANVAS_PADDING;\n let y = CANVAS_PADDING;\n let column = 0;\n let itemCount = 0;\n\n for (const group of grouping.groups) {\n for (let i = 0; i < group.ids.length; i++) {\n const id = group.ids[i];\n\n positions.set(id, {\n x,\n y,\n groupIndex: 0,\n });\n\n // Move to next position horizontally (left to right)\n column++;\n x += slotWidth;\n\n // Wrap to next row when we've filled the width\n if (column >= cardsPerRow) {\n column = 0;\n x = CANVAS_PADDING;\n y += slotHeight;\n }\n\n itemCount++;\n }\n }\n\n const rows = Math.ceil(itemCount / cardsPerRow);\n const contentWidth = Math.min(itemCount, cardsPerRow) * slotWidth;\n\n return {\n positions,\n totalWidth: Math.max(containerWidth, contentWidth + (CANVAS_PADDING * 2)),\n totalHeight: (rows * slotHeight) + (CANVAS_PADDING * 2),\n };\n}\n\nfunction computeGroupedLayout(\n grouping: GroupingResult,\n spec: LayoutSpec,\n positions: Map<ItemId, ItemPosition>\n): LayoutResult {\n const { cardWidth, cardHeight, cardsPerColumn } = spec;\n // Override group spacing to ensure consistent card spacing across groups\n // We want visual gap between groups to match gap between cards (CARD_GAP)\n const effectiveGroupSpacing = 0;\n\n const slotWidth = cardWidth + CARD_GAP;\n const slotHeight = cardHeight + CARD_GAP;\n // Bottom margin of 0 - the CARD_GAP in slotHeight provides sufficient spacing\n // between the bottom card and the footer label\n const BOTTOM_MARGIN = 0;\n\n // Fixed bucket width: 2 columns of cards per bucket (always)\n const COLUMNS_PER_BUCKET = 2;\n const bucketWidth = COLUMNS_PER_BUCKET * slotWidth;\n\n let groupX = 0;\n // Use container height for layout, or fallback to cardsPerColumn height\n const layoutHeight = spec.containerHeight || (cardsPerColumn * slotHeight);\n const bucketWidths: number[] = [];\n const groupXs: number[] = [];\n let maxRows = 0;\n\n // First pass: calculate max rows to determine total height\n for (const group of grouping.groups) {\n const itemsInGroup = group.ids.length;\n const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);\n maxRows = Math.max(maxRows, rowsInGroup);\n }\n\n // Calculate actual content height needed (ensure it's at least as tall as the container)\n // We need to fit the tallest column plus the bottom margin\n const contentHeight = Math.max(layoutHeight, (maxRows * slotHeight) + BOTTOM_MARGIN);\n\n for (let groupIndex = 0; groupIndex < grouping.groups.length; groupIndex++) {\n const group = grouping.groups[groupIndex];\n groupXs.push(groupX);\n\n const itemsInGroup = group.ids.length;\n\n for (let i = 0; i < itemsInGroup; i++) {\n const id = group.ids[i];\n\n // Cards fill from left to right, bottom to top\n // For a 2-column bucket: i=0,1 in row 0; i=2,3 in row 1; etc.\n const col = i % COLUMNS_PER_BUCKET;\n const row = Math.floor(i / COLUMNS_PER_BUCKET);\n\n // Center the cards within the bucket\n // The bucket width fits 2 slots (2W + 2G). Cards take 2W + G.\n // So we have G/2 padding on each side to center them.\n const x = groupX + (col * slotWidth) + (CARD_GAP / 2);\n\n // Position cards from bottom of container, stacking upwards, starting at row 0 (bottom)\n const y = CANVAS_PADDING + contentHeight - BOTTOM_MARGIN - ((row + 1) * slotHeight);\n\n positions.set(id, {\n x,\n y,\n groupIndex,\n });\n }\n\n // Always use fixed bucket width\n bucketWidths.push(bucketWidth);\n\n // Advance position by fixed bucket width + spacing\n groupX += bucketWidth;\n if (groupIndex < grouping.groups.length - 1) {\n groupX += effectiveGroupSpacing;\n }\n }\n\n return {\n positions,\n totalWidth: groupX + CANVAS_PADDING,\n totalHeight: contentHeight + (CANVAS_PADDING * 2),\n bucketWidths,\n groupXs,\n };\n}\n"],"names":["CARD_GAP","CANVAS_PADDING"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCurrentFilters.js","sources":["../../../../PivotViewer/hooks/useCurrentFilters.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 { useMemo } from 'react';\nimport type { FilterSpec, GroupSpec } from '../engine/types';\nimport type { PivotDimension, PivotFilter } from '../types';\n\nexport function useCurrentFilters<TItem extends object>(\n filters: PivotFilter<TItem>[] | undefined,\n filterState: Record<string, Set<string>>,\n rangeFilterState: Record<string, [number | null, number | null] | null>,\n search: string,\n searchFields: unknown[] | undefined,\n dimensionFilter: string | null,\n activeDimension: PivotDimension<TItem> | undefined,\n): FilterSpec[] {\n return useMemo((): FilterSpec[] => {\n const specs: FilterSpec[] = [];\n\n // Search filter\n const searchTerm = search.trim().toLowerCase();\n if (searchTerm && searchFields && searchFields.length > 0) {\n // TODO: Implement search in worker\n // For now, search will be handled client-side after worker filtering\n }\n\n // Categorical filters\n for (const [key, values] of Object.entries(filterState)) {\n const valueSet = values as Set<string>;\n if (valueSet.size > 0) {\n specs.push({\n field: key,\n type: 'categorical',\n values: valueSet,\n });\n }\n }\n\n // Range filters\n for (const [key, range] of Object.entries(rangeFilterState)) {\n if (range && (range[0] !== null || range[1] !== null)) {\n const min = range[0] ?? -Infinity;\n const max = range[1] ?? Infinity;\n specs.push({\n field: key,\n type: 'numeric',\n range: { min, max },\n });\n }\n }\n\n // Dimension filter (bucket filter)\n if (dimensionFilter && activeDimension) {\n specs.push({\n field: activeDimension.key,\n type: 'categorical',\n values: new Set([dimensionFilter]),\n });\n }\n\n return specs;\n }, [filterState, rangeFilterState, search, searchFields, dimensionFilter, activeDimension]);\n}\n\nexport function useCurrentGroupBy<TItem extends object>(\n activeDimensionKey: string | null,\n dimensions: PivotDimension<TItem>[],\n): GroupSpec {\n return useMemo((): GroupSpec => {\n return {\n field: activeDimensionKey || dimensions[0]?.key || '',\n buckets: 10,\n };\n }, [activeDimensionKey, dimensions]);\n}\n"],"names":["useMemo"],"mappings":";;;;AAOM,SAAU,iBAAiB,CAC7B,OAAyC,EACzC,WAAwC,EACxC,gBAAuE,EACvE,MAAc,EACd,YAAmC,EACnC,eAA8B,EAC9B,eAAkD,EAAA;IAElD,OAAOA,aAAO,CAAC,MAAmB;QAC9B,MAAM,KAAK,GAAiB,EAAE;QAG9B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;QAC9C,IAAI,UAAU,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE;AAM3D,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;YACrD,MAAM,QAAQ,GAAG,MAAqB;AACtC,YAAA,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE;gBACnB,KAAK,CAAC,IAAI,CAAC;AACP,oBAAA,KAAK,EAAE,GAAG;AACV,oBAAA,IAAI,EAAE,aAAa;AACnB,oBAAA,MAAM,EAAE,QAAQ;AACnB,iBAAA,CAAC;YACN;QACJ;AAGA,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE;AACzD,YAAA,IAAI,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE;gBACnD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;gBACjC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,QAAQ;gBAChC,KAAK,CAAC,IAAI,CAAC;AACP,oBAAA,KAAK,EAAE,GAAG;AACV,oBAAA,IAAI,EAAE,SAAS;AACf,oBAAA,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;AACtB,iBAAA,CAAC;YACN;QACJ;AAGA,QAAA,IAAI,eAAe,IAAI,eAAe,EAAE;YACpC,KAAK,CAAC,IAAI,CAAC;gBACP,KAAK,EAAE,eAAe,CAAC,GAAG;AAC1B,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC;AACrC,aAAA,CAAC;QACN;AAEA,QAAA,OAAO,KAAK;AAChB,IAAA,CAAC,EAAE,CAAC,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;AAC/F;AAEM,SAAU,iBAAiB,CAC7B,kBAAiC,EACjC,UAAmC,EAAA;IAEnC,OAAOA,aAAO,CAAC,MAAgB;QAC3B,OAAO;YACH,KAAK,EAAE,kBAAkB,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE;AACrD,YAAA,OAAO,EAAE,EAAE;SACd;AACL,IAAA,CAAC,EAAE,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;AACxC;;;;;"}
1
+ {"version":3,"file":"useCurrentFilters.js","sources":["../../../../PivotViewer/hooks/useCurrentFilters.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 { useMemo } from 'react';\nimport type { FilterSpec, GroupSpec } from '../engine/types';\nimport type { PivotDimension, PivotFilter } from '../types';\n\nexport function useCurrentFilters<TItem extends object>(\n filters: PivotFilter<TItem>[] | undefined,\n filterState: Record<string, Set<string>>,\n rangeFilterState: Record<string, [number | null, number | null] | null>,\n search: string,\n searchFields: unknown[] | undefined,\n dimensionFilter: string | null,\n activeDimension: PivotDimension<TItem> | undefined,\n): FilterSpec[] {\n return useMemo((): FilterSpec[] => {\n const specs: FilterSpec[] = [];\n\n // Search filter\n const searchTerm = search.trim().toLowerCase();\n if (searchTerm && searchFields && searchFields.length > 0) {\n // TODO: Implement search in worker\n // For now, search will be handled client-side after worker filtering\n }\n\n // Categorical filters\n for (const [key, values] of Object.entries(filterState)) {\n const valueSet = values as Set<string>;\n if (valueSet.size > 0) {\n specs.push({\n field: key,\n type: 'categorical',\n values: valueSet,\n });\n }\n }\n\n // Range filters\n for (const [key, range] of Object.entries(rangeFilterState)) {\n if (range && (range[0] !== null || range[1] !== null)) {\n const min = range[0] ?? -Infinity;\n const max = range[1] ?? Infinity;\n specs.push({\n field: key,\n type: 'numeric',\n range: { min, max },\n });\n }\n }\n\n // Dimension filter (grouped filter)\n if (dimensionFilter && activeDimension) {\n specs.push({\n field: activeDimension.key,\n type: 'categorical',\n values: new Set([dimensionFilter]),\n });\n }\n\n return specs;\n }, [filterState, rangeFilterState, search, searchFields, dimensionFilter, activeDimension]);\n}\n\nexport function useCurrentGroupBy<TItem extends object>(\n activeDimensionKey: string | null,\n dimensions: PivotDimension<TItem>[],\n): GroupSpec {\n return useMemo((): GroupSpec => {\n return {\n field: activeDimensionKey || dimensions[0]?.key || '',\n buckets: 10,\n };\n }, [activeDimensionKey, dimensions]);\n}\n"],"names":["useMemo"],"mappings":";;;;AAOM,SAAU,iBAAiB,CAC7B,OAAyC,EACzC,WAAwC,EACxC,gBAAuE,EACvE,MAAc,EACd,YAAmC,EACnC,eAA8B,EAC9B,eAAkD,EAAA;IAElD,OAAOA,aAAO,CAAC,MAAmB;QAC9B,MAAM,KAAK,GAAiB,EAAE;QAG9B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;QAC9C,IAAI,UAAU,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE;AAM3D,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;YACrD,MAAM,QAAQ,GAAG,MAAqB;AACtC,YAAA,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE;gBACnB,KAAK,CAAC,IAAI,CAAC;AACP,oBAAA,KAAK,EAAE,GAAG;AACV,oBAAA,IAAI,EAAE,aAAa;AACnB,oBAAA,MAAM,EAAE,QAAQ;AACnB,iBAAA,CAAC;YACN;QACJ;AAGA,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE;AACzD,YAAA,IAAI,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE;gBACnD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;gBACjC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,QAAQ;gBAChC,KAAK,CAAC,IAAI,CAAC;AACP,oBAAA,KAAK,EAAE,GAAG;AACV,oBAAA,IAAI,EAAE,SAAS;AACf,oBAAA,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;AACtB,iBAAA,CAAC;YACN;QACJ;AAGA,QAAA,IAAI,eAAe,IAAI,eAAe,EAAE;YACpC,KAAK,CAAC,IAAI,CAAC;gBACP,KAAK,EAAE,eAAe,CAAC,GAAG;AAC1B,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC;AACrC,aAAA,CAAC;QACN;AAEA,QAAA,OAAO,KAAK;AAChB,IAAA,CAAC,EAAE,CAAC,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;AAC/F;AAEM,SAAU,iBAAiB,CAC7B,kBAAiC,EACjC,UAAmC,EAAA;IAEnC,OAAOA,aAAO,CAAC,MAAgB;QAC3B,OAAO;YACH,KAAK,EAAE,kBAAkB,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE;AACrD,YAAA,OAAO,EAAE,EAAE;SACd;AACL,IAAA,CAAC,EAAE,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;AACxC;;;;;"}
@@ -53,8 +53,10 @@ function usePanning(containerRef, onBackgroundClick, onScrollChange) {
53
53
  const newCameraY = panStart.scrollTop - dy;
54
54
  const container = containerRef.current;
55
55
  if (container) {
56
- container.scrollLeft = Math.max(0, Math.round(newCameraX));
57
- container.scrollTop = Math.max(0, Math.round(newCameraY));
56
+ const maxScrollLeft = Math.max(0, container.scrollWidth - container.clientWidth);
57
+ const maxScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
58
+ container.scrollLeft = Math.max(0, Math.min(maxScrollLeft, Math.round(newCameraX)));
59
+ container.scrollTop = Math.max(0, Math.min(maxScrollTop, Math.round(newCameraY)));
58
60
  }
59
61
  if (onScrollChange) {
60
62
  onScrollChange({ x: container ? container.scrollLeft : newCameraX, y: container ? container.scrollTop : newCameraY });
@@ -98,8 +100,10 @@ function usePanning(containerRef, onBackgroundClick, onScrollChange) {
98
100
  inertiaAnimationRef.current = null;
99
101
  return;
100
102
  }
101
- container.scrollLeft += vx;
102
- container.scrollTop += vy;
103
+ const maxScrollLeft = Math.max(0, container.scrollWidth - container.clientWidth);
104
+ const maxScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
105
+ container.scrollLeft = Math.max(0, Math.min(maxScrollLeft, container.scrollLeft + vx));
106
+ container.scrollTop = Math.max(0, Math.min(maxScrollTop, container.scrollTop + vy));
103
107
  vx *= 0.95;
104
108
  vy *= 0.95;
105
109
  inertiaAnimationRef.current = requestAnimationFrame(animate);