@cratis/components 0.1.10 → 0.1.12

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 (158) hide show
  1. package/dist/cjs/CommandForm/CommandFormFields.js +9 -3
  2. package/dist/cjs/CommandForm/CommandFormFields.js.map +1 -1
  3. package/dist/cjs/CommandForm/ValidationMessage.js +24 -0
  4. package/dist/cjs/CommandForm/ValidationMessage.js.map +1 -0
  5. package/dist/cjs/CommandForm/asCommandFormField.js +47 -0
  6. package/dist/cjs/CommandForm/asCommandFormField.js.map +1 -0
  7. package/dist/cjs/CommandForm/fields/CheckboxField.js +13 -0
  8. package/dist/cjs/CommandForm/fields/CheckboxField.js.map +1 -0
  9. package/dist/cjs/CommandForm/fields/DropdownField.js +13 -0
  10. package/dist/cjs/CommandForm/fields/DropdownField.js.map +1 -0
  11. package/dist/cjs/CommandForm/fields/InputTextField.js +13 -0
  12. package/dist/cjs/CommandForm/fields/InputTextField.js.map +1 -0
  13. package/dist/cjs/CommandForm/fields/NumberField.js +13 -0
  14. package/dist/cjs/CommandForm/fields/NumberField.js.map +1 -0
  15. package/dist/cjs/CommandForm/fields/SliderField.js +17 -0
  16. package/dist/cjs/CommandForm/fields/SliderField.js.map +1 -0
  17. package/dist/cjs/CommandForm/fields/TextAreaField.js +13 -0
  18. package/dist/cjs/CommandForm/fields/TextAreaField.js.map +1 -0
  19. package/dist/cjs/CommandForm/index.js +15 -7
  20. package/dist/cjs/CommandForm/index.js.map +1 -1
  21. package/dist/cjs/PivotViewer/PivotViewer.js +14 -0
  22. package/dist/cjs/PivotViewer/PivotViewer.js.map +1 -1
  23. package/dist/cjs/PivotViewer/components/PivotCanvas.js +33 -10
  24. package/dist/cjs/PivotViewer/components/PivotCanvas.js.map +1 -1
  25. package/dist/cjs/PivotViewer/components/PivotViewerMain.js +1 -1
  26. package/dist/cjs/PivotViewer/components/PivotViewerMain.js.map +1 -1
  27. package/dist/cjs/PivotViewer/components/pivot/sprites.js +79 -15
  28. package/dist/cjs/PivotViewer/components/pivot/sprites.js.map +1 -1
  29. package/dist/cjs/PivotViewer/components/pivot/visibility.js +36 -10
  30. package/dist/cjs/PivotViewer/components/pivot/visibility.js.map +1 -1
  31. package/dist/cjs/PivotViewer/engine/layout.js +2 -1
  32. package/dist/cjs/PivotViewer/engine/layout.js.map +1 -1
  33. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js +37 -2
  34. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  35. package/dist/cjs/PivotViewer/index.js +3 -0
  36. package/dist/cjs/PivotViewer/index.js.map +1 -1
  37. package/dist/cjs/PivotViewer/types.js +22 -0
  38. package/dist/cjs/PivotViewer/types.js.map +1 -0
  39. package/dist/cjs/TimeMachine/TimeMachine.js +8 -3
  40. package/dist/cjs/TimeMachine/TimeMachine.js.map +1 -1
  41. package/dist/esm/CommandForm/CommandForm.stories.d.ts +1 -0
  42. package/dist/esm/CommandForm/CommandForm.stories.d.ts.map +1 -1
  43. package/dist/esm/CommandForm/CommandForm.stories.js +34 -1
  44. package/dist/esm/CommandForm/CommandForm.stories.js.map +1 -1
  45. package/dist/esm/CommandForm/CommandFormFields.d.ts.map +1 -1
  46. package/dist/esm/CommandForm/CommandFormFields.js +9 -3
  47. package/dist/esm/CommandForm/CommandFormFields.js.map +1 -1
  48. package/dist/esm/CommandForm/UserRegistrationCommand.d.ts +63 -0
  49. package/dist/esm/CommandForm/UserRegistrationCommand.d.ts.map +1 -0
  50. package/dist/esm/CommandForm/UserRegistrationCommand.js +143 -0
  51. package/dist/esm/CommandForm/UserRegistrationCommand.js.map +1 -0
  52. package/dist/esm/CommandForm/ValidationMessage.d.ts +8 -0
  53. package/dist/esm/CommandForm/ValidationMessage.d.ts.map +1 -0
  54. package/dist/esm/CommandForm/ValidationMessage.js +22 -0
  55. package/dist/esm/CommandForm/ValidationMessage.js.map +1 -0
  56. package/dist/esm/CommandForm/asCommandFormField.d.ts +32 -0
  57. package/dist/esm/CommandForm/asCommandFormField.d.ts.map +1 -0
  58. package/dist/esm/CommandForm/asCommandFormField.js +45 -0
  59. package/dist/esm/CommandForm/asCommandFormField.js.map +1 -0
  60. package/dist/esm/CommandForm/fields/CheckboxField.d.ts +10 -0
  61. package/dist/esm/CommandForm/fields/CheckboxField.d.ts.map +1 -0
  62. package/dist/esm/CommandForm/fields/CheckboxField.js +11 -0
  63. package/dist/esm/CommandForm/fields/CheckboxField.js.map +1 -0
  64. package/dist/esm/CommandForm/fields/DropdownField.d.ts +15 -0
  65. package/dist/esm/CommandForm/fields/DropdownField.d.ts.map +1 -0
  66. package/dist/esm/CommandForm/fields/DropdownField.js +11 -0
  67. package/dist/esm/CommandForm/fields/DropdownField.js.map +1 -0
  68. package/dist/esm/CommandForm/fields/InputTextField.d.ts +11 -0
  69. package/dist/esm/CommandForm/fields/InputTextField.d.ts.map +1 -0
  70. package/dist/esm/CommandForm/fields/InputTextField.js +11 -0
  71. package/dist/esm/CommandForm/fields/InputTextField.js.map +1 -0
  72. package/dist/esm/CommandForm/fields/NumberField.d.ts +13 -0
  73. package/dist/esm/CommandForm/fields/NumberField.d.ts.map +1 -0
  74. package/dist/esm/CommandForm/fields/NumberField.js +11 -0
  75. package/dist/esm/CommandForm/fields/NumberField.js.map +1 -0
  76. package/dist/esm/CommandForm/fields/SliderField.d.ts +12 -0
  77. package/dist/esm/CommandForm/fields/SliderField.d.ts.map +1 -0
  78. package/dist/esm/CommandForm/fields/SliderField.js +15 -0
  79. package/dist/esm/CommandForm/fields/SliderField.js.map +1 -0
  80. package/dist/esm/CommandForm/fields/TextAreaField.d.ts +12 -0
  81. package/dist/esm/CommandForm/fields/TextAreaField.d.ts.map +1 -0
  82. package/dist/esm/CommandForm/fields/TextAreaField.js +11 -0
  83. package/dist/esm/CommandForm/fields/TextAreaField.js.map +1 -0
  84. package/dist/esm/CommandForm/fields/index.d.ts +7 -0
  85. package/dist/esm/CommandForm/fields/index.d.ts.map +1 -0
  86. package/dist/esm/CommandForm/fields/index.js +7 -0
  87. package/dist/esm/CommandForm/fields/index.js.map +1 -0
  88. package/dist/esm/CommandForm/index.d.ts +3 -4
  89. package/dist/esm/CommandForm/index.d.ts.map +1 -1
  90. package/dist/esm/CommandForm/index.js +8 -4
  91. package/dist/esm/CommandForm/index.js.map +1 -1
  92. package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
  93. package/dist/esm/PivotViewer/PivotViewer.js +14 -0
  94. package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
  95. package/dist/esm/PivotViewer/PivotViewer.stories.d.ts +1 -0
  96. package/dist/esm/PivotViewer/PivotViewer.stories.d.ts.map +1 -1
  97. package/dist/esm/PivotViewer/PivotViewer.stories.js +43 -3
  98. package/dist/esm/PivotViewer/PivotViewer.stories.js.map +1 -1
  99. package/dist/esm/PivotViewer/components/PivotCanvas.d.ts.map +1 -1
  100. package/dist/esm/PivotViewer/components/PivotCanvas.js +33 -10
  101. package/dist/esm/PivotViewer/components/PivotCanvas.js.map +1 -1
  102. package/dist/esm/PivotViewer/components/PivotViewerMain.js +1 -1
  103. package/dist/esm/PivotViewer/components/PivotViewerMain.js.map +1 -1
  104. package/dist/esm/PivotViewer/components/pivot/sprites.d.ts.map +1 -1
  105. package/dist/esm/PivotViewer/components/pivot/sprites.js +79 -15
  106. package/dist/esm/PivotViewer/components/pivot/sprites.js.map +1 -1
  107. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
  108. package/dist/esm/PivotViewer/components/pivot/visibility.js +36 -10
  109. package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
  110. package/dist/esm/PivotViewer/engine/layout.js +2 -1
  111. package/dist/esm/PivotViewer/engine/layout.js.map +1 -1
  112. package/dist/esm/PivotViewer/engine/pivot.worker.d.ts.map +1 -1
  113. package/dist/esm/PivotViewer/engine/pivot.worker.js +22 -7
  114. package/dist/esm/PivotViewer/engine/pivot.worker.js.map +1 -1
  115. package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts +2 -2
  116. package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts.map +1 -1
  117. package/dist/esm/PivotViewer/hooks/useFilteredData.js +4 -2
  118. package/dist/esm/PivotViewer/hooks/useFilteredData.js.map +1 -1
  119. package/dist/esm/PivotViewer/hooks/usePivotEngine.d.ts.map +1 -1
  120. package/dist/esm/PivotViewer/hooks/usePivotEngine.js +37 -2
  121. package/dist/esm/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  122. package/dist/esm/PivotViewer/index.d.ts +2 -1
  123. package/dist/esm/PivotViewer/index.d.ts.map +1 -1
  124. package/dist/esm/PivotViewer/index.js +1 -0
  125. package/dist/esm/PivotViewer/index.js.map +1 -1
  126. package/dist/esm/PivotViewer/types.d.ts +4 -1
  127. package/dist/esm/PivotViewer/types.d.ts.map +1 -1
  128. package/dist/esm/PivotViewer/types.js +19 -2
  129. package/dist/esm/PivotViewer/types.js.map +1 -1
  130. package/dist/esm/TimeMachine/TimeMachine.d.ts.map +1 -1
  131. package/dist/esm/TimeMachine/TimeMachine.js +8 -3
  132. package/dist/esm/TimeMachine/TimeMachine.js.map +1 -1
  133. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  134. package/package.json +29 -29
  135. package/dist/cjs/CommandForm/DatePickerField.js +0 -31
  136. package/dist/cjs/CommandForm/DatePickerField.js.map +0 -1
  137. package/dist/cjs/CommandForm/DropdownField.js +0 -31
  138. package/dist/cjs/CommandForm/DropdownField.js.map +0 -1
  139. package/dist/cjs/CommandForm/InputTextField.js +0 -32
  140. package/dist/cjs/CommandForm/InputTextField.js.map +0 -1
  141. package/dist/cjs/CommandForm/SliderField.js +0 -34
  142. package/dist/cjs/CommandForm/SliderField.js.map +0 -1
  143. package/dist/esm/CommandForm/DatePickerField.d.ts +0 -20
  144. package/dist/esm/CommandForm/DatePickerField.d.ts.map +0 -1
  145. package/dist/esm/CommandForm/DatePickerField.js +0 -29
  146. package/dist/esm/CommandForm/DatePickerField.js.map +0 -1
  147. package/dist/esm/CommandForm/DropdownField.d.ts +0 -24
  148. package/dist/esm/CommandForm/DropdownField.d.ts.map +0 -1
  149. package/dist/esm/CommandForm/DropdownField.js +0 -29
  150. package/dist/esm/CommandForm/DropdownField.js.map +0 -1
  151. package/dist/esm/CommandForm/InputTextField.d.ts +0 -20
  152. package/dist/esm/CommandForm/InputTextField.d.ts.map +0 -1
  153. package/dist/esm/CommandForm/InputTextField.js +0 -30
  154. package/dist/esm/CommandForm/InputTextField.js.map +0 -1
  155. package/dist/esm/CommandForm/SliderField.d.ts +0 -23
  156. package/dist/esm/CommandForm/SliderField.d.ts.map +0 -1
  157. package/dist/esm/CommandForm/SliderField.js +0 -32
  158. package/dist/esm/CommandForm/SliderField.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PivotViewerMain.js","sources":["../../../../PivotViewer/components/PivotViewerMain.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type { ReactNode } from 'react';\nimport type { ItemId, LayoutResult, GroupingResult } from '../engine/types';\nimport type { ViewMode } from './Toolbar';\nimport type { PivotDimensionFilter } from '../hooks/useDimensionState';\nimport { Spinner } from './Spinner';\nimport { PivotCanvas } from './PivotCanvas';\nimport { AxisLabels } from './AxisLabels';\nimport { DetailPanel } from './DetailPanel';\n\nexport interface PivotViewerMainProps<TItem extends object> {\n data: TItem[];\n ready: boolean;\n isLoading: boolean;\n visibleIds: Uint32Array;\n grouping: GroupingResult;\n layout: LayoutResult;\n cardWidth: number;\n cardHeight: number;\n zoomLevel: number;\n scrollPosition: { x: number; y: number };\n containerDimensions: { width: number; height: number };\n selectedItem: TItem | null;\n hoveredGroupIndex: number | null;\n isZooming: boolean;\n viewMode: ViewMode;\n cardRenderer?: (item: TItem) => ReactNode;\n resolveId: (item: TItem, index: number) => ItemId;\n emptyContent?: ReactNode;\n dimensionFilter: PivotDimensionFilter;\n onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void;\n onPanStart: (e: React.MouseEvent) => void;\n onPanMove: (e: React.MouseEvent) => void;\n onPanEnd: () => void;\n onGroupHover: (index: number | null) => void;\n onAxisLabelClick: (value: string) => void;\n onCloseDetail: () => void;\n containerRef: React.RefObject<HTMLDivElement | null>;\n axisLabelsRef: React.RefObject<HTMLDivElement | null>;\n spacerRef: React.RefObject<HTMLDivElement | null>;\n}\n\nexport function PivotViewerMain<TItem extends object>({\n data,\n ready,\n isLoading,\n visibleIds,\n grouping,\n layout,\n cardWidth,\n cardHeight,\n zoomLevel,\n scrollPosition,\n containerDimensions,\n selectedItem,\n hoveredGroupIndex,\n isZooming,\n viewMode,\n cardRenderer,\n resolveId,\n emptyContent,\n dimensionFilter,\n onCardClick,\n onPanStart,\n onPanMove,\n onPanEnd,\n onGroupHover,\n onAxisLabelClick,\n onCloseDetail,\n containerRef,\n axisLabelsRef,\n spacerRef,\n}: PivotViewerMainProps<TItem>) {\n const handleViewportClick = (e: React.MouseEvent) => {\n if (isZooming || !containerRef.current) return;\n\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n // Use live DOM scroll position for accurate hit testing\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const clickX = e.clientX - rect.left + scrollLeft;\n const clickY = e.clientY - rect.top + scrollTop;\n\n const worldX = clickX / zoomLevel;\n const worldY = clickY / zoomLevel;\n\n // Check visible items\n for (let i = 0; i < visibleIds.length; i++) {\n const id = visibleIds[i];\n const pos = layout.positions.get(id);\n if (pos) {\n if (worldX >= pos.x && worldX <= pos.x + cardWidth &&\n worldY >= pos.y && worldY <= pos.y + cardHeight) {\n const item = data[id];\n if (item) {\n onCardClick(item, e.nativeEvent as unknown as MouseEvent, id);\n }\n return;\n }\n }\n }\n };\n\n const handleViewportMouseMove = (e: React.MouseEvent) => {\n if (isZooming || !containerRef.current) return;\n\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const mouseX = e.clientX - rect.left + scrollLeft;\n const mouseY = e.clientY - rect.top + scrollTop;\n\n const worldX = mouseX / zoomLevel;\n const worldY = mouseY / zoomLevel;\n\n let isOverCard = false;\n for (let i = 0; i < visibleIds.length; i++) {\n const id = visibleIds[i];\n const pos = layout.positions.get(id);\n if (pos) {\n if (worldX >= pos.x && worldX <= pos.x + cardWidth &&\n worldY >= pos.y && worldY <= pos.y + cardHeight) {\n isOverCard = true;\n break;\n }\n }\n }\n\n container.style.cursor = isOverCard ? 'pointer' : 'default';\n };\n\n return isLoading ? (\n <Spinner />\n ) : (\n <div className=\"pv-groups-wrapper\">\n <div style={{ position: 'relative', flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>\n <div\n className={`pv-viewport ${isZooming ? 'pv-zooming' : ''}`}\n ref={containerRef}\n style={{ overflow: 'auto', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 1 }}\n onClick={handleViewportClick}\n onMouseMove={handleViewportMouseMove}\n >\n {/* Spacer for scrolling - explicitly rendered to allow synchronous updates during animation */}\n <div\n ref={spacerRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: layout.totalWidth * zoomLevel,\n height: layout.totalHeight * zoomLevel,\n pointerEvents: 'none'\n }}\n />\n\n {!ready && (\n <div className=\"pv-loading\">Building indexes...</div>\n )}\n\n {ready && visibleIds.length === 0 && (\n <div className=\"pv-empty\">\n {emptyContent ?? 'No items to display.'}\n </div>\n )}\n\n {ready && visibleIds.length > 0 && (\n <PivotCanvas\n items={data}\n layout={layout}\n grouping={grouping}\n visibleIds={visibleIds}\n cardWidth={cardWidth}\n cardHeight={cardHeight}\n zoomLevel={zoomLevel}\n panX={scrollPosition.x}\n panY={scrollPosition.y}\n viewportWidth={containerDimensions.width}\n viewportHeight={containerDimensions.height}\n selectedId={selectedItem ? resolveId(selectedItem, 0) : null}\n hoveredGroupIndex={hoveredGroupIndex}\n isZooming={isZooming}\n cardRenderer={cardRenderer}\n resolveId={resolveId}\n onCardClick={onCardClick}\n onPanStart={onPanStart as any}\n onPanMove={onPanMove as any}\n onPanEnd={onPanEnd}\n containerRef={containerRef}\n viewMode={viewMode}\n />\n )}\n </div>\n <DetailPanel\n selectedItem={selectedItem}\n onClose={onCloseDetail}\n />\n </div>\n\n {viewMode === 'grouped' && grouping.groups.length > 0 && (\n <AxisLabels\n groups={grouping.groups.map((g) => ({\n key: g.key,\n value: g.value,\n label: String(g.value),\n items: [],\n count: g.ids.length,\n }))}\n bucketWidths={layout.bucketWidths || []}\n zoomLevel={zoomLevel}\n dimensionFilter={dimensionFilter}\n hoveredGroup={hoveredGroupIndex !== null ? String(grouping.groups[hoveredGroupIndex]?.value) : null}\n onHover={(label) => {\n const index = grouping.groups.findIndex(g => String(g.value) === label);\n onGroupHover(index >= 0 ? index : null);\n }}\n onClick={onAxisLabelClick}\n containerRef={axisLabelsRef}\n />\n )}\n </div>\n );\n}\n"],"names":["_jsx","Spinner","_jsxs","PivotCanvas","DetailPanel","AxisLabels"],"mappings":"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"}
1
+ {"version":3,"file":"PivotViewerMain.js","sources":["../../../../PivotViewer/components/PivotViewerMain.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type { ReactNode } from 'react';\nimport type { ItemId, LayoutResult, GroupingResult } from '../engine/types';\nimport type { ViewMode } from './Toolbar';\nimport type { PivotDimensionFilter } from '../hooks/useDimensionState';\nimport { Spinner } from './Spinner';\nimport { PivotCanvas } from './PivotCanvas';\nimport { AxisLabels } from './AxisLabels';\nimport { DetailPanel } from './DetailPanel';\n\nexport interface PivotViewerMainProps<TItem extends object> {\n data: TItem[];\n ready: boolean;\n isLoading: boolean;\n visibleIds: Uint32Array;\n grouping: GroupingResult;\n layout: LayoutResult;\n cardWidth: number;\n cardHeight: number;\n zoomLevel: number;\n scrollPosition: { x: number; y: number };\n containerDimensions: { width: number; height: number };\n selectedItem: TItem | null;\n hoveredGroupIndex: number | null;\n isZooming: boolean;\n viewMode: ViewMode;\n cardRenderer?: (item: TItem) => ReactNode;\n resolveId: (item: TItem, index: number) => ItemId;\n emptyContent?: ReactNode;\n dimensionFilter: PivotDimensionFilter;\n onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void;\n onPanStart: (e: React.MouseEvent) => void;\n onPanMove: (e: React.MouseEvent) => void;\n onPanEnd: () => void;\n onGroupHover: (index: number | null) => void;\n onAxisLabelClick: (value: string) => void;\n onCloseDetail: () => void;\n containerRef: React.RefObject<HTMLDivElement | null>;\n axisLabelsRef: React.RefObject<HTMLDivElement | null>;\n spacerRef: React.RefObject<HTMLDivElement | null>;\n}\n\nexport function PivotViewerMain<TItem extends object>({\n data,\n ready,\n isLoading,\n visibleIds,\n grouping,\n layout,\n cardWidth,\n cardHeight,\n zoomLevel,\n scrollPosition,\n containerDimensions,\n selectedItem,\n hoveredGroupIndex,\n isZooming,\n viewMode,\n cardRenderer,\n resolveId,\n emptyContent,\n dimensionFilter,\n onCardClick,\n onPanStart,\n onPanMove,\n onPanEnd,\n onGroupHover,\n onAxisLabelClick,\n onCloseDetail,\n containerRef,\n axisLabelsRef,\n spacerRef,\n}: PivotViewerMainProps<TItem>) {\n const handleViewportClick = (e: React.MouseEvent) => {\n if (isZooming || !containerRef.current) return;\n\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n // Use live DOM scroll position for accurate hit testing\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const clickX = e.clientX - rect.left + scrollLeft;\n const clickY = e.clientY - rect.top + scrollTop;\n\n const worldX = clickX / zoomLevel;\n const worldY = clickY / zoomLevel;\n\n // Check visible items\n for (let i = 0; i < visibleIds.length; i++) {\n const id = visibleIds[i];\n const pos = layout.positions.get(id);\n if (pos) {\n if (worldX >= pos.x && worldX <= pos.x + cardWidth &&\n worldY >= pos.y && worldY <= pos.y + cardHeight) {\n const item = data[id];\n if (item) {\n onCardClick(item, e.nativeEvent as unknown as MouseEvent, id);\n }\n return;\n }\n }\n }\n };\n\n const handleViewportMouseMove = (e: React.MouseEvent) => {\n if (isZooming || !containerRef.current) return;\n\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const mouseX = e.clientX - rect.left + scrollLeft;\n const mouseY = e.clientY - rect.top + scrollTop;\n\n const worldX = mouseX / zoomLevel;\n const worldY = mouseY / zoomLevel;\n\n let isOverCard = false;\n for (let i = 0; i < visibleIds.length; i++) {\n const id = visibleIds[i];\n const pos = layout.positions.get(id);\n if (pos) {\n if (worldX >= pos.x && worldX <= pos.x + cardWidth &&\n worldY >= pos.y && worldY <= pos.y + cardHeight) {\n isOverCard = true;\n break;\n }\n }\n }\n\n container.style.cursor = isOverCard ? 'pointer' : 'default';\n };\n\n return isLoading ? (\n <Spinner />\n ) : (\n <div className=\"pv-groups-wrapper\">\n <div style={{ position: 'relative', flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>\n <div\n className={`pv-viewport ${isZooming ? 'pv-zooming' : ''}`}\n ref={containerRef}\n style={{ overflow: 'auto', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}\n onClick={handleViewportClick}\n onMouseMove={handleViewportMouseMove}\n >\n {/* Spacer for scrolling - explicitly rendered to allow synchronous updates during animation */}\n <div\n ref={spacerRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n width: layout.totalWidth * zoomLevel,\n height: layout.totalHeight * zoomLevel,\n pointerEvents: 'none'\n }}\n />\n\n {!ready && (\n <div className=\"pv-loading\">Building indexes...</div>\n )}\n\n {ready && visibleIds.length === 0 && (\n <div className=\"pv-empty\">\n {emptyContent ?? 'No items to display.'}\n </div>\n )}\n\n {ready && visibleIds.length > 0 && (\n <PivotCanvas\n items={data}\n layout={layout}\n grouping={grouping}\n visibleIds={visibleIds}\n cardWidth={cardWidth}\n cardHeight={cardHeight}\n zoomLevel={zoomLevel}\n panX={scrollPosition.x}\n panY={scrollPosition.y}\n viewportWidth={containerDimensions.width}\n viewportHeight={containerDimensions.height}\n selectedId={selectedItem ? resolveId(selectedItem, 0) : null}\n hoveredGroupIndex={hoveredGroupIndex}\n isZooming={isZooming}\n cardRenderer={cardRenderer}\n resolveId={resolveId}\n onCardClick={onCardClick}\n onPanStart={onPanStart as any}\n onPanMove={onPanMove as any}\n onPanEnd={onPanEnd}\n containerRef={containerRef}\n viewMode={viewMode}\n />\n )}\n </div>\n <DetailPanel\n selectedItem={selectedItem}\n onClose={onCloseDetail}\n />\n </div>\n\n {viewMode === 'grouped' && grouping.groups.length > 0 && (\n <AxisLabels\n groups={grouping.groups.map((g) => ({\n key: g.key,\n value: g.value,\n label: String(g.value),\n items: [],\n count: g.ids.length,\n }))}\n bucketWidths={layout.bucketWidths || []}\n zoomLevel={zoomLevel}\n dimensionFilter={dimensionFilter}\n hoveredGroup={hoveredGroupIndex !== null ? String(grouping.groups[hoveredGroupIndex]?.value) : null}\n onHover={(label) => {\n const index = grouping.groups.findIndex(g => String(g.value) === label);\n onGroupHover(index >= 0 ? index : null);\n }}\n onClick={onAxisLabelClick}\n containerRef={axisLabelsRef}\n />\n )}\n </div>\n );\n}\n"],"names":["_jsx","Spinner","_jsxs","PivotCanvas","DetailPanel","AxisLabels"],"mappings":"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"}
@@ -26,9 +26,11 @@ const spritePool = [];
26
26
  function createCardSprite(id, x, y, items, onCardClick, onPanStart, cardWidth, cardHeight, cardColors) {
27
27
  if (spritePool.length > 0) {
28
28
  const sprite = spritePool.pop();
29
- sprite.container.visible = true;
30
- sprite.container.alpha = 1;
31
- sprite.container.position.set(x, y);
29
+ if (sprite.container) {
30
+ sprite.container.visible = true;
31
+ sprite.container.alpha = 1;
32
+ sprite.container.position.set(x, y);
33
+ }
32
34
  sprite.itemId = id;
33
35
  sprite.targetX = x;
34
36
  sprite.targetY = y;
@@ -43,7 +45,55 @@ function createCardSprite(id, x, y, items, onCardClick, onPanStart, cardWidth, c
43
45
  sprite.lastTitle = undefined;
44
46
  sprite.lastLabels = undefined;
45
47
  sprite.lastValues = undefined;
46
- sprite.container._eventContext = { items, onCardClick, id };
48
+ if (!sprite.graphics || sprite.graphics.destroyed) {
49
+ sprite.graphics = new PIXI__namespace.Graphics();
50
+ if (sprite.container) {
51
+ sprite.container.addChildAt(sprite.graphics, 0);
52
+ }
53
+ }
54
+ const offsetX = constants.CARD_GAP / 2;
55
+ const offsetY = constants.CARD_GAP / 2;
56
+ if (!sprite.titleText || sprite.titleText.destroyed) {
57
+ sprite.titleText = new PIXI__namespace.Text('', {
58
+ fontSize: 13,
59
+ fill: cardColors.text,
60
+ fontWeight: '600',
61
+ lineHeight: 18,
62
+ wordWrap: false,
63
+ });
64
+ sprite.titleText.position.set(offsetX + constants.CARD_PADDING, offsetY + constants.CARD_PADDING);
65
+ if (sprite.container) {
66
+ sprite.container.addChild(sprite.titleText);
67
+ }
68
+ }
69
+ if (!sprite.labelsText || sprite.labelsText.destroyed) {
70
+ sprite.labelsText = new PIXI__namespace.Text('', {
71
+ fontSize: 11,
72
+ fill: cardColors.textSecondary,
73
+ fontWeight: '400',
74
+ lineHeight: 18,
75
+ });
76
+ sprite.labelsText.position.set(offsetX + constants.CARD_PADDING, offsetY + constants.CARD_PADDING + 40);
77
+ if (sprite.container) {
78
+ sprite.container.addChild(sprite.labelsText);
79
+ }
80
+ }
81
+ if (!sprite.valuesText || sprite.valuesText.destroyed) {
82
+ sprite.valuesText = new PIXI__namespace.Text('', {
83
+ fontSize: 11,
84
+ fill: cardColors.text,
85
+ fontWeight: '500',
86
+ lineHeight: 18,
87
+ wordWrap: false,
88
+ });
89
+ sprite.valuesText.position.set(offsetX + constants.CARD_PADDING + 65, offsetY + constants.CARD_PADDING + 40);
90
+ if (sprite.container) {
91
+ sprite.container.addChild(sprite.valuesText);
92
+ }
93
+ }
94
+ if (sprite.container) {
95
+ sprite.container._eventContext = { items, onCardClick, id };
96
+ }
47
97
  return sprite;
48
98
  }
49
99
  const container = new PIXI__namespace.Container();
@@ -91,8 +141,8 @@ function createCardSprite(id, x, y, items, onCardClick, onPanStart, cardWidth, c
91
141
  container.on('click', (e) => {
92
142
  e.stopPropagation();
93
143
  const ctx = container._eventContext;
94
- const itemsMap = ctx.items;
95
- const item = itemsMap[String(ctx.id)];
144
+ const itemsArray = ctx.items;
145
+ const item = itemsArray[Number(ctx.id)];
96
146
  if (item) {
97
147
  ctx.onCardClick(item, e.nativeEvent, ctx.id);
98
148
  }
@@ -163,6 +213,12 @@ function updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, card
163
213
  const labelsText = 'Type\nOccurred\nCorrelation';
164
214
  const valuesText = `${typeDisplay}\n${timeStr}\n${correlationShort}`;
165
215
  const colorsChanged = sprite.lastCardColors !== colors;
216
+ if (!sprite.titleText || sprite.titleText.destroyed)
217
+ return;
218
+ if (!sprite.labelsText || sprite.labelsText.destroyed)
219
+ return;
220
+ if (!sprite.valuesText || sprite.valuesText.destroyed)
221
+ return;
166
222
  if (sprite.lastTitle !== titleDisplay) {
167
223
  sprite.titleText.text = titleDisplay;
168
224
  sprite.lastTitle = titleDisplay;
@@ -171,14 +227,14 @@ function updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, card
171
227
  sprite.labelsText.text = labelsText;
172
228
  sprite.lastLabels = labelsText;
173
229
  }
174
- if (colorsChanged) {
230
+ if (colorsChanged && sprite.labelsText.style) {
175
231
  sprite.labelsText.style.fill = colors.textSecondary;
176
232
  }
177
233
  if (sprite.lastValues !== valuesText) {
178
234
  sprite.valuesText.text = valuesText;
179
235
  sprite.lastValues = valuesText;
180
236
  }
181
- if (colorsChanged) {
237
+ if (colorsChanged && sprite.valuesText.style) {
182
238
  sprite.valuesText.style.fill = colors.text;
183
239
  }
184
240
  sprite.titleText.visible = true;
@@ -190,12 +246,14 @@ function updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, card
190
246
  }
191
247
  sprite.lastSelectedId = selectedId;
192
248
  sprite.lastCardColors = cardColors;
193
- if (sprite.graphics) {
194
- sprite.graphics.clear();
249
+ if (!sprite.graphics || sprite.graphics.destroyed) {
250
+ sprite.graphics = new PIXI__namespace.Graphics();
251
+ if (sprite.container) {
252
+ sprite.container.addChildAt(sprite.graphics, 0);
253
+ }
195
254
  }
196
255
  else {
197
- sprite.graphics = new PIXI__namespace.Graphics();
198
- sprite.container.addChildAt(sprite.graphics, 0);
256
+ sprite.graphics.clear();
199
257
  }
200
258
  const actualWidth = cardWidth - constants.CARD_GAP;
201
259
  const actualHeight = cardHeight - constants.CARD_GAP;
@@ -211,12 +269,18 @@ function updateCardContent(sprite, item, selectedId, cardWidth, cardHeight, card
211
269
  gradient.addColorStop(1, colors.base);
212
270
  }
213
271
  sprite.graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, constants.CARD_RADIUS);
214
- sprite.graphics.fill(gradient);
272
+ if (sprite.graphics && !sprite.graphics.destroyed) {
273
+ sprite.graphics.fill(gradient);
274
+ }
215
275
  if (isSelected) {
216
- sprite.graphics.stroke({ width: 2, color: colors.border });
276
+ if (sprite.graphics && !sprite.graphics.destroyed) {
277
+ sprite.graphics.stroke({ width: 2, color: colors.border });
278
+ }
217
279
  }
218
280
  else {
219
- sprite.graphics.stroke({ width: 1, color: colors.border, alpha: 0.35 });
281
+ if (sprite.graphics && !sprite.graphics.destroyed) {
282
+ sprite.graphics.stroke({ width: 1, color: colors.border, alpha: 0.35 });
283
+ }
220
284
  }
221
285
  }
222
286
 
@@ -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 sprite.container.visible = true;\n sprite.container.alpha = 1;\n sprite.container.position.set(x, y);\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 // Update event context\n (sprite.container as any)._eventContext = { items, onCardClick, id };\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 any)._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 any,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as any);\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 any,\n fontWeight: '400',\n lineHeight: 18,\n } as any);\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 any,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as any);\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 any)._eventContext as { items: any; onCardClick: (item: any, e: MouseEvent, id: number | string) => void; id: number | string };\n const itemsMap = ctx.items as Record<string, any>;\n const item = itemsMap[String(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\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 any;\n const eventType = event.type || event.name || event.title || 'Event';\n\n const timeStr = event.occurred ? new Date(event.occurred).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 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) {\n (sprite.labelsText.style as any).fill = colors.textSecondary as any;\n }\n\n if (sprite.lastValues !== valuesText) {\n sprite.valuesText.text = valuesText;\n sprite.lastValues = valuesText;\n }\n\n if (colorsChanged) {\n (sprite.valuesText.style as any).fill = colors.text as any;\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 if (sprite.graphics) {\n sprite.graphics.clear();\n } else {\n sprite.graphics = new PIXI.Graphics();\n sprite.container.addChildAt(sprite.graphics, 0);\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 sprite.graphics.fill(gradient);\n\n if (isSelected) {\n sprite.graphics.stroke({ width: 2, color: colors.border });\n } else {\n sprite.graphics.stroke({ width: 1, color: colors.border, alpha: 0.35 });\n }\n}\n"],"names":["PIXI","CARD_GAP","CARD_RADIUS","CARD_PADDING"],"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 any,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as any);\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 any,\n fontWeight: '400',\n lineHeight: 18,\n } as any);\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 any,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as any);\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 any)._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 any)._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 any,\n fontWeight: '600',\n lineHeight: 18,\n wordWrap: false,\n } as any);\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 any,\n fontWeight: '400',\n lineHeight: 18,\n } as any);\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 any,\n fontWeight: '500',\n lineHeight: 18,\n wordWrap: false,\n } as any);\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 any)._eventContext as { items: any; onCardClick: (item: any, e: MouseEvent, id: number | string) => void; id: number | string };\n const itemsArray = ctx.items as any[];\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 any;\n const eventType = event.type || event.name || event.title || 'Event';\n\n const timeStr = event.occurred ? new Date(event.occurred).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 any).fill = colors.textSecondary as any;\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 any).fill = colors.text as any;\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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"}
@@ -7,6 +7,14 @@ function syncSpritesToViewport(params) {
7
7
  const { root, container, sprites: sprites$1, layout, visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, prevLayout } = 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
18
  if (isViewTransition && (panDeltaX || panDeltaY)) {
11
19
  const dx = (panDeltaX || 0) / (zoomLevel || 1);
12
20
  const dy = (panDeltaY || 0) / (zoomLevel || 1);
@@ -18,7 +26,7 @@ function syncSpritesToViewport(params) {
18
26
  sprite.startY += dy;
19
27
  sprite.currentX += dx;
20
28
  sprite.currentY += dy;
21
- sprite.container.position.set(sprite.currentX, sprite.currentY);
29
+ sprite.container?.position?.set(sprite.currentX, sprite.currentY);
22
30
  }
23
31
  }
24
32
  }
@@ -73,7 +81,9 @@ function syncSpritesToViewport(params) {
73
81
  }
74
82
  catch (e) {
75
83
  }
76
- const aggressiveCull = !isViewTransition && sprites$1.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5));
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);
77
87
  for (const [id, sprite] of sprites$1) {
78
88
  if (!visibleSet.has(id)) {
79
89
  if (isViewTransition && layout.positions.has(id)) {
@@ -129,7 +139,7 @@ function syncSpritesToViewport(params) {
129
139
  }
130
140
  }
131
141
  try {
132
- const SWEEP_MS = 500;
142
+ const SWEEP_MS = 100;
133
143
  const now = Date.now();
134
144
  for (const [id, sprite] of sprites$1) {
135
145
  const lastHidden = sprite.__lastHiddenAt;
@@ -155,15 +165,22 @@ function syncSpritesToViewport(params) {
155
165
  }
156
166
  const MAX_SPRITES_PER_FRAME = 50;
157
167
  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()));
158
170
  for (const id of inViewportIds) {
159
171
  const position = layout.positions.get(id);
160
- if (!position)
172
+ if (!position) {
173
+ console.log('[syncSpritesToViewport] No position for id:', id, 'in layout.positions');
161
174
  continue;
175
+ }
162
176
  let sprite = sprites$1.get(id);
163
177
  if (!sprite) {
164
- if (createdCount >= MAX_SPRITES_PER_FRAME)
178
+ if (createdCount >= MAX_SPRITES_PER_FRAME) {
179
+ console.log('[syncSpritesToViewport] Max sprites per frame reached');
165
180
  continue;
181
+ }
166
182
  createdCount++;
183
+ console.log('[syncSpritesToViewport] Creating sprite for id:', id, 'at position:', position);
167
184
  let startX = position.x;
168
185
  let startY = position.y;
169
186
  let shouldAnimate = false;
@@ -183,10 +200,12 @@ function syncSpritesToViewport(params) {
183
200
  }
184
201
  sprite = createCardSprite(id, startX, startY);
185
202
  sprites$1.set(id, sprite);
186
- root.addChild(sprite.container);
187
- sprite.currentX = startX;
188
- sprite.currentY = startY;
189
- sprite.container.position.set(startX, startY);
203
+ if (sprite.container) {
204
+ root.addChild(sprite.container);
205
+ sprite.currentX = startX;
206
+ sprite.currentY = startY;
207
+ sprite.container.position.set(startX, startY);
208
+ }
190
209
  if (shouldAnimate) {
191
210
  sprite.targetX = position.x;
192
211
  sprite.targetY = position.y;
@@ -197,6 +216,10 @@ function syncSpritesToViewport(params) {
197
216
  }
198
217
  }
199
218
  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);
200
223
  if (isViewTransition) {
201
224
  sprite.startX = sprite.currentX;
202
225
  sprite.startY = sprite.currentY;
@@ -212,7 +235,10 @@ function syncSpritesToViewport(params) {
212
235
  delete sprite.animationDelay;
213
236
  }
214
237
  }
215
- const item = items[String(id)];
238
+ else {
239
+ console.log('[syncSpritesToViewport] Sprite position unchanged for id:', id, 'at', position);
240
+ }
241
+ const item = items[Number(id)];
216
242
  if (item) {
217
243
  updateCardContent(sprite, item);
218
244
  }
@@ -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 // `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 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 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 any).__lastHiddenAt) delete (sprite as any).__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 any).__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 any).__lastHiddenAt) delete (sprite as any).__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 = 500; // keep hidden sprites for 500ms before destruction\n const now = Date.now();\n for (const [id, sprite] of sprites) {\n const lastHidden = (sprite as any).__lastHiddenAt as number | undefined;\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 for (const id of inViewportIds) {\n const position = layout.positions.get(id);\n if (!position) continue;\n\n let sprite = sprites.get(id);\n if (!sprite) {\n if (createdCount >= MAX_SPRITES_PER_FRAME) continue;\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 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 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 as any)[String(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 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 any).__lastHiddenAt) delete (sprite as any).__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 any).__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 any).__lastHiddenAt) delete (sprite as any).__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 any).__lastHiddenAt as number | undefined;\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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"}
@@ -70,7 +70,8 @@ function computeGroupedLayout(grouping, spec, positions) {
70
70
  const col = i % COLUMNS_PER_BUCKET;
71
71
  const row = Math.floor(i / COLUMNS_PER_BUCKET);
72
72
  const x = groupX + col * slotWidth;
73
- const y = contentHeight - (row + 1) * slotHeight + constants.CANVAS_PADDING;
73
+ const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);
74
+ const y = constants.CANVAS_PADDING + contentHeight - (rowsInGroup - row) * slotHeight;
74
75
  positions.set(id, {
75
76
  x,
76
77
  y,
@@ -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 // Stack from bottom of total content upward:\n // row 0 (first card) at bottom: contentHeight - slotHeight\n // row 1 above it: contentHeight - 2*slotHeight, etc.\n const y = contentHeight - (row + 1) * slotHeight + CANVAS_PADDING;\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, 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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"}