@cratis/components 0.1.9 → 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 (258) 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.css +1258 -0
  22. package/dist/cjs/PivotViewer/PivotViewer.js +14 -0
  23. package/dist/cjs/PivotViewer/PivotViewer.js.map +1 -1
  24. package/dist/cjs/PivotViewer/components/PivotCanvas.js +33 -10
  25. package/dist/cjs/PivotViewer/components/PivotCanvas.js.map +1 -1
  26. package/dist/cjs/PivotViewer/components/PivotViewerMain.js +1 -1
  27. package/dist/cjs/PivotViewer/components/PivotViewerMain.js.map +1 -1
  28. package/dist/cjs/PivotViewer/components/Spinner.css +77 -0
  29. package/dist/cjs/PivotViewer/components/pivot/sprites.js +79 -15
  30. package/dist/cjs/PivotViewer/components/pivot/sprites.js.map +1 -1
  31. package/dist/cjs/PivotViewer/components/pivot/visibility.js +36 -10
  32. package/dist/cjs/PivotViewer/components/pivot/visibility.js.map +1 -1
  33. package/dist/cjs/PivotViewer/engine/layout.js +2 -1
  34. package/dist/cjs/PivotViewer/engine/layout.js.map +1 -1
  35. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js +37 -2
  36. package/dist/cjs/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  37. package/dist/cjs/PivotViewer/index.js +3 -0
  38. package/dist/cjs/PivotViewer/index.js.map +1 -1
  39. package/dist/cjs/PivotViewer/types.js +22 -0
  40. package/dist/cjs/PivotViewer/types.js.map +1 -0
  41. package/dist/cjs/TimeMachine/EventsView.css +213 -0
  42. package/dist/cjs/TimeMachine/TimeMachine.css +567 -0
  43. package/dist/cjs/TimeMachine/TimeMachine.js +8 -3
  44. package/dist/cjs/TimeMachine/TimeMachine.js.map +1 -1
  45. package/dist/esm/CommandForm/CommandForm.stories.d.ts +1 -0
  46. package/dist/esm/CommandForm/CommandForm.stories.d.ts.map +1 -1
  47. package/dist/esm/CommandForm/CommandForm.stories.js +34 -1
  48. package/dist/esm/CommandForm/CommandForm.stories.js.map +1 -1
  49. package/dist/esm/CommandForm/CommandFormFields.d.ts.map +1 -1
  50. package/dist/esm/CommandForm/CommandFormFields.js +9 -3
  51. package/dist/esm/CommandForm/CommandFormFields.js.map +1 -1
  52. package/dist/esm/CommandForm/UserRegistrationCommand.d.ts +63 -0
  53. package/dist/esm/CommandForm/UserRegistrationCommand.d.ts.map +1 -0
  54. package/dist/esm/CommandForm/UserRegistrationCommand.js +143 -0
  55. package/dist/esm/CommandForm/UserRegistrationCommand.js.map +1 -0
  56. package/dist/esm/CommandForm/ValidationMessage.d.ts +8 -0
  57. package/dist/esm/CommandForm/ValidationMessage.d.ts.map +1 -0
  58. package/dist/esm/CommandForm/ValidationMessage.js +22 -0
  59. package/dist/esm/CommandForm/ValidationMessage.js.map +1 -0
  60. package/dist/esm/CommandForm/asCommandFormField.d.ts +32 -0
  61. package/dist/esm/CommandForm/asCommandFormField.d.ts.map +1 -0
  62. package/dist/esm/CommandForm/asCommandFormField.js +45 -0
  63. package/dist/esm/CommandForm/asCommandFormField.js.map +1 -0
  64. package/dist/esm/CommandForm/fields/CheckboxField.d.ts +10 -0
  65. package/dist/esm/CommandForm/fields/CheckboxField.d.ts.map +1 -0
  66. package/dist/esm/CommandForm/fields/CheckboxField.js +11 -0
  67. package/dist/esm/CommandForm/fields/CheckboxField.js.map +1 -0
  68. package/dist/esm/CommandForm/fields/DropdownField.d.ts +15 -0
  69. package/dist/esm/CommandForm/fields/DropdownField.d.ts.map +1 -0
  70. package/dist/esm/CommandForm/fields/DropdownField.js +11 -0
  71. package/dist/esm/CommandForm/fields/DropdownField.js.map +1 -0
  72. package/dist/esm/CommandForm/fields/InputTextField.d.ts +11 -0
  73. package/dist/esm/CommandForm/fields/InputTextField.d.ts.map +1 -0
  74. package/dist/esm/CommandForm/fields/InputTextField.js +11 -0
  75. package/dist/esm/CommandForm/fields/InputTextField.js.map +1 -0
  76. package/dist/esm/CommandForm/fields/NumberField.d.ts +13 -0
  77. package/dist/esm/CommandForm/fields/NumberField.d.ts.map +1 -0
  78. package/dist/esm/CommandForm/fields/NumberField.js +11 -0
  79. package/dist/esm/CommandForm/fields/NumberField.js.map +1 -0
  80. package/dist/esm/CommandForm/fields/SliderField.d.ts +12 -0
  81. package/dist/esm/CommandForm/fields/SliderField.d.ts.map +1 -0
  82. package/dist/esm/CommandForm/fields/SliderField.js +15 -0
  83. package/dist/esm/CommandForm/fields/SliderField.js.map +1 -0
  84. package/dist/esm/CommandForm/fields/TextAreaField.d.ts +12 -0
  85. package/dist/esm/CommandForm/fields/TextAreaField.d.ts.map +1 -0
  86. package/dist/esm/CommandForm/fields/TextAreaField.js +11 -0
  87. package/dist/esm/CommandForm/fields/TextAreaField.js.map +1 -0
  88. package/dist/esm/CommandForm/fields/index.d.ts +7 -0
  89. package/dist/esm/CommandForm/fields/index.d.ts.map +1 -0
  90. package/dist/esm/CommandForm/fields/index.js +7 -0
  91. package/dist/esm/CommandForm/fields/index.js.map +1 -0
  92. package/dist/esm/CommandForm/index.d.ts +3 -4
  93. package/dist/esm/CommandForm/index.d.ts.map +1 -1
  94. package/dist/esm/CommandForm/index.js +8 -4
  95. package/dist/esm/CommandForm/index.js.map +1 -1
  96. package/dist/esm/PivotViewer/PivotViewer.css +1258 -0
  97. package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
  98. package/dist/esm/PivotViewer/PivotViewer.js +14 -0
  99. package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
  100. package/dist/esm/PivotViewer/PivotViewer.stories.d.ts +1 -0
  101. package/dist/esm/PivotViewer/PivotViewer.stories.d.ts.map +1 -1
  102. package/dist/esm/PivotViewer/PivotViewer.stories.js +43 -3
  103. package/dist/esm/PivotViewer/PivotViewer.stories.js.map +1 -1
  104. package/dist/esm/PivotViewer/components/PivotCanvas.d.ts.map +1 -1
  105. package/dist/esm/PivotViewer/components/PivotCanvas.js +33 -10
  106. package/dist/esm/PivotViewer/components/PivotCanvas.js.map +1 -1
  107. package/dist/esm/PivotViewer/components/PivotViewerMain.js +1 -1
  108. package/dist/esm/PivotViewer/components/PivotViewerMain.js.map +1 -1
  109. package/dist/esm/PivotViewer/components/Spinner.css +77 -0
  110. package/dist/esm/PivotViewer/components/pivot/sprites.d.ts.map +1 -1
  111. package/dist/esm/PivotViewer/components/pivot/sprites.js +79 -15
  112. package/dist/esm/PivotViewer/components/pivot/sprites.js.map +1 -1
  113. package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
  114. package/dist/esm/PivotViewer/components/pivot/visibility.js +36 -10
  115. package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
  116. package/dist/esm/PivotViewer/engine/layout.js +2 -1
  117. package/dist/esm/PivotViewer/engine/layout.js.map +1 -1
  118. package/dist/esm/PivotViewer/engine/pivot.worker.d.ts.map +1 -1
  119. package/dist/esm/PivotViewer/engine/pivot.worker.js +22 -7
  120. package/dist/esm/PivotViewer/engine/pivot.worker.js.map +1 -1
  121. package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts +2 -2
  122. package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts.map +1 -1
  123. package/dist/esm/PivotViewer/hooks/useFilteredData.js +4 -2
  124. package/dist/esm/PivotViewer/hooks/useFilteredData.js.map +1 -1
  125. package/dist/esm/PivotViewer/hooks/usePivotEngine.d.ts.map +1 -1
  126. package/dist/esm/PivotViewer/hooks/usePivotEngine.js +37 -2
  127. package/dist/esm/PivotViewer/hooks/usePivotEngine.js.map +1 -1
  128. package/dist/esm/PivotViewer/index.d.ts +2 -1
  129. package/dist/esm/PivotViewer/index.d.ts.map +1 -1
  130. package/dist/esm/PivotViewer/index.js +1 -0
  131. package/dist/esm/PivotViewer/index.js.map +1 -1
  132. package/dist/esm/PivotViewer/types.d.ts +4 -1
  133. package/dist/esm/PivotViewer/types.d.ts.map +1 -1
  134. package/dist/esm/PivotViewer/types.js +19 -2
  135. package/dist/esm/PivotViewer/types.js.map +1 -1
  136. package/dist/esm/TimeMachine/EventsView.css +213 -0
  137. package/dist/esm/TimeMachine/TimeMachine.css +567 -0
  138. package/dist/esm/TimeMachine/TimeMachine.d.ts.map +1 -1
  139. package/dist/esm/TimeMachine/TimeMachine.js +8 -3
  140. package/dist/esm/TimeMachine/TimeMachine.js.map +1 -1
  141. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  142. package/package.json +31 -32
  143. package/.storybook/main.ts +0 -24
  144. package/CommandDialog/CommandDialog.stories.tsx +0 -25
  145. package/CommandDialog/CommandDialog.tsx +0 -161
  146. package/CommandDialog/index.ts +0 -4
  147. package/CommandForm/CommandForm.stories.tsx +0 -24
  148. package/CommandForm/CommandForm.tsx +0 -266
  149. package/CommandForm/CommandFormField.tsx +0 -27
  150. package/CommandForm/CommandFormFields.tsx +0 -142
  151. package/CommandForm/DatePickerField.tsx +0 -57
  152. package/CommandForm/DropdownField.tsx +0 -65
  153. package/CommandForm/InputTextField.tsx +0 -62
  154. package/CommandForm/SliderField.tsx +0 -68
  155. package/CommandForm/index.ts +0 -10
  156. package/Common/ErrorBoundary.stories.tsx +0 -10
  157. package/Common/ErrorBoundary.tsx +0 -41
  158. package/Common/FormElement.stories.tsx +0 -10
  159. package/Common/FormElement.tsx +0 -20
  160. package/Common/Page.stories.tsx +0 -10
  161. package/Common/Page.tsx +0 -21
  162. package/Common/index.ts +0 -6
  163. package/DataPage/DataPage.stories.tsx +0 -10
  164. package/DataPage/DataPage.tsx +0 -191
  165. package/DataPage/index.ts +0 -4
  166. package/DataTables/DataTableForObservableQuery.stories.tsx +0 -10
  167. package/DataTables/DataTableForObservableQuery.tsx +0 -97
  168. package/DataTables/DataTableForQuery.stories.tsx +0 -10
  169. package/DataTables/DataTableForQuery.tsx +0 -97
  170. package/DataTables/index.ts +0 -5
  171. package/Dialogs/BusyIndicatorDialog.stories.tsx +0 -26
  172. package/Dialogs/BusyIndicatorDialog.tsx +0 -26
  173. package/Dialogs/ConfirmationDialog.stories.tsx +0 -36
  174. package/Dialogs/ConfirmationDialog.tsx +0 -75
  175. package/Dialogs/index.ts +0 -5
  176. package/Dropdown/Dropdown.tsx +0 -23
  177. package/Dropdown/index.ts +0 -4
  178. package/PivotViewer/PivotViewer.stories.tsx +0 -24
  179. package/PivotViewer/PivotViewer.tsx +0 -791
  180. package/PivotViewer/components/AxisLabels.tsx +0 -69
  181. package/PivotViewer/components/DetailPanel.tsx +0 -108
  182. package/PivotViewer/components/FilterPanel.tsx +0 -189
  183. package/PivotViewer/components/FilterPanelContainer.tsx +0 -10
  184. package/PivotViewer/components/PivotCanvas.tsx +0 -660
  185. package/PivotViewer/components/PivotViewerMain.tsx +0 -229
  186. package/PivotViewer/components/RangeHistogramFilter.tsx +0 -220
  187. package/PivotViewer/components/Spinner.tsx +0 -21
  188. package/PivotViewer/components/Toolbar.tsx +0 -130
  189. package/PivotViewer/components/ToolbarContainer.tsx +0 -10
  190. package/PivotViewer/components/index.ts +0 -12
  191. package/PivotViewer/components/pivot/animation.ts +0 -108
  192. package/PivotViewer/components/pivot/buckets.ts +0 -152
  193. package/PivotViewer/components/pivot/colorResolver.ts +0 -67
  194. package/PivotViewer/components/pivot/constants.ts +0 -46
  195. package/PivotViewer/components/pivot/sprites.ts +0 -265
  196. package/PivotViewer/components/pivot/visibility.ts +0 -319
  197. package/PivotViewer/constants.ts +0 -9
  198. package/PivotViewer/engine/layout.ts +0 -149
  199. package/PivotViewer/engine/pivot.worker.ts +0 -86
  200. package/PivotViewer/engine/store.ts +0 -437
  201. package/PivotViewer/engine/types.ts +0 -255
  202. package/PivotViewer/hooks/index.ts +0 -13
  203. package/PivotViewer/hooks/useContainerDimensions.ts +0 -45
  204. package/PivotViewer/hooks/useDimensionState.ts +0 -53
  205. package/PivotViewer/hooks/useFilterOptions.ts +0 -36
  206. package/PivotViewer/hooks/useFilterPanelDrag.ts +0 -49
  207. package/PivotViewer/hooks/useFilterState.ts +0 -106
  208. package/PivotViewer/hooks/useFilteredData.ts +0 -119
  209. package/PivotViewer/hooks/usePanning.ts +0 -163
  210. package/PivotViewer/hooks/usePivotEngine.ts +0 -252
  211. package/PivotViewer/hooks/useSelectedItem.ts +0 -402
  212. package/PivotViewer/hooks/useWheelZoom.ts +0 -114
  213. package/PivotViewer/hooks/useZoomState.ts +0 -34
  214. package/PivotViewer/index.ts +0 -7
  215. package/PivotViewer/types.ts +0 -59
  216. package/PivotViewer/utils/animations.ts +0 -249
  217. package/PivotViewer/utils/constants.ts +0 -20
  218. package/PivotViewer/utils/index.ts +0 -6
  219. package/PivotViewer/utils/selection.ts +0 -292
  220. package/PivotViewer/utils/utils.ts +0 -259
  221. package/TimeMachine/EventsView.stories.tsx +0 -10
  222. package/TimeMachine/EventsView.tsx +0 -119
  223. package/TimeMachine/Properties.stories.tsx +0 -10
  224. package/TimeMachine/Properties.tsx +0 -98
  225. package/TimeMachine/ReadModelView.stories.tsx +0 -10
  226. package/TimeMachine/ReadModelView.tsx +0 -143
  227. package/TimeMachine/TimeMachine.stories.tsx +0 -10
  228. package/TimeMachine/TimeMachine.tsx +0 -244
  229. package/TimeMachine/index.ts +0 -8
  230. package/TimeMachine/types.ts +0 -23
  231. package/dist/cjs/CommandForm/DatePickerField.js +0 -31
  232. package/dist/cjs/CommandForm/DatePickerField.js.map +0 -1
  233. package/dist/cjs/CommandForm/DropdownField.js +0 -31
  234. package/dist/cjs/CommandForm/DropdownField.js.map +0 -1
  235. package/dist/cjs/CommandForm/InputTextField.js +0 -32
  236. package/dist/cjs/CommandForm/InputTextField.js.map +0 -1
  237. package/dist/cjs/CommandForm/SliderField.js +0 -34
  238. package/dist/cjs/CommandForm/SliderField.js.map +0 -1
  239. package/dist/esm/CommandForm/DatePickerField.d.ts +0 -20
  240. package/dist/esm/CommandForm/DatePickerField.d.ts.map +0 -1
  241. package/dist/esm/CommandForm/DatePickerField.js +0 -29
  242. package/dist/esm/CommandForm/DatePickerField.js.map +0 -1
  243. package/dist/esm/CommandForm/DropdownField.d.ts +0 -24
  244. package/dist/esm/CommandForm/DropdownField.d.ts.map +0 -1
  245. package/dist/esm/CommandForm/DropdownField.js +0 -29
  246. package/dist/esm/CommandForm/DropdownField.js.map +0 -1
  247. package/dist/esm/CommandForm/InputTextField.d.ts +0 -20
  248. package/dist/esm/CommandForm/InputTextField.d.ts.map +0 -1
  249. package/dist/esm/CommandForm/InputTextField.js +0 -30
  250. package/dist/esm/CommandForm/InputTextField.js.map +0 -1
  251. package/dist/esm/CommandForm/SliderField.d.ts +0 -23
  252. package/dist/esm/CommandForm/SliderField.d.ts.map +0 -1
  253. package/dist/esm/CommandForm/SliderField.js +0 -32
  254. package/dist/esm/CommandForm/SliderField.js.map +0 -1
  255. package/global.d.ts +0 -11
  256. package/index.ts +0 -22
  257. package/useOverlayZIndex.ts +0 -32
  258. package/vite.config.ts +0 -80
@@ -1,319 +0,0 @@
1
- // Copyright (c) Cratis. All rights reserved.
2
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
-
4
- import * as PIXI from 'pixi.js';
5
- import type { CardSprite } from './constants';
6
- import { CARD_GAP } from './constants';
7
- import type { LayoutResult } from '../../engine/types';
8
- import { destroySprite } from './sprites';
9
-
10
- export interface SyncParams<TItem> {
11
- root: PIXI.Container | null;
12
- container: HTMLDivElement | null;
13
- sprites: Map<string | number, CardSprite>;
14
- layout: LayoutResult;
15
- visibleIds: Uint32Array;
16
- items: TItem[];
17
- cardWidth: number;
18
- cardHeight: number;
19
- panX: number;
20
- panY: number;
21
- panDeltaX?: number;
22
- panDeltaY?: number;
23
- viewportWidth: number;
24
- viewportHeight: number;
25
- zoomLevel: number;
26
- createCardSprite: (id: string | number, x: number, y: number) => CardSprite;
27
- updateCardContent: (sprite: CardSprite, item: TItem) => void;
28
- isViewTransition?: boolean;
29
- prevLayout?: LayoutResult | null;
30
- }
31
-
32
- export function syncSpritesToViewport<TItem>(params: SyncParams<TItem>) {
33
- const { root, container, sprites, layout, visibleIds, items, cardWidth, cardHeight, panX, panY, panDeltaX, panDeltaY, viewportWidth, viewportHeight, createCardSprite, updateCardContent, zoomLevel, isViewTransition, prevLayout } = params;
34
- if (!root || !container) return;
35
-
36
- // `visibleIds` comes from callers but this module iterates `layout.positions`.
37
- // Keep a reference to avoid unused variable lint errors when callers include it.
38
- void visibleIds;
39
-
40
- // Apply pan delta to animating sprites to keep them visually stable during camera jumps
41
- if (isViewTransition && (panDeltaX || panDeltaY)) {
42
- const dx = (panDeltaX || 0) / (zoomLevel || 1);
43
- const dy = (panDeltaY || 0) / (zoomLevel || 1);
44
-
45
- for (const sprite of sprites.values()) {
46
- if (sprite.animationStartTime !== undefined) {
47
- if (sprite.startX !== undefined) sprite.startX += dx;
48
- if (sprite.startY !== undefined) sprite.startY += dy;
49
- sprite.currentX += dx;
50
- sprite.currentY += dy;
51
- sprite.container.position.set(sprite.currentX, sprite.currentY);
52
- }
53
- }
54
- }
55
-
56
- const visibleSet = new Set<string | number>();
57
-
58
- // Increase buffer (in world units) to reduce edge cases where rapid
59
- // scrolling skips sprite creation. Keep buffer in world units and convert
60
- // DOM pixel measurements into world coordinates below to avoid mixing
61
- // coordinate spaces which can cause precision drift at browser zooms.
62
- const baseBufferWorld = Math.max(cardWidth, cardHeight) * 4;
63
- // Ensure buffer scales with viewport size (in world units) so that when
64
- // zoomed out we still pre-create enough sprites ahead of the viewport.
65
- const invScale = zoomLevel && zoomLevel !== 0 ? 1 / zoomLevel : 1;
66
- // The layout positions are in world units; when the root container is scaled
67
- // (zoomed) the rendered pixel position = position * zoomLevel. The DOM
68
- // scroll positions (`container.scrollLeft/Top`) are the authoritative pixel
69
- // camera offsets; prefer them over the passed `panX/panY` to avoid stale
70
- // values or race conditions between React state and direct DOM updates.
71
- const effectivePanX = typeof container.scrollLeft === 'number' ? container.scrollLeft : (panX || 0);
72
- const effectivePanY = typeof container.scrollTop === 'number' ? container.scrollTop : (panY || 0);
73
-
74
- // Convert pixel-based DOM measurements into world units so we compare like
75
- // with like. root.position is set using -pixels, so the mapping
76
- // from DOM scroll (pixels) to world units is: world = pixels / zoomLevel.
77
- const panWorldX = effectivePanX * invScale;
78
- const panWorldY = effectivePanY * invScale;
79
-
80
- // Use the container's measured client size for the viewport dimensions
81
- // (in pixels). The passed `viewportWidth`/`viewportHeight` can be stale
82
- // when the browser/device zoom changes; `clientWidth/clientHeight` are
83
- // authoritative for the actual visible pixel area.
84
- const viewportPxWidth = container.clientWidth || viewportWidth;
85
- const viewportPxHeight = container.clientHeight || viewportHeight;
86
-
87
- const viewportWorldWidth = viewportPxWidth * invScale;
88
- const viewportWorldHeight = viewportPxHeight * invScale;
89
-
90
- // Ensure bufferWorld is calculated from the actual measured viewport
91
- // in world units (after converting client pixel dims using invScale).
92
- // Make buffer adaptive to zoom: when zoomed out (invScale > 1) a small
93
- // pixel scroll maps to a larger world delta, so increase the buffer.
94
- // Use the larger of width/height to ensure we buffer enough in both directions.
95
- const bufferWorld = Math.max(baseBufferWorld * invScale, Math.max(viewportWorldWidth, viewportWorldHeight) * 2.0, baseBufferWorld);
96
-
97
- // Do not clamp viewport edges to 0 — allow negative top/left values so the
98
- // visible window correctly follows the scroll even when the buffer is
99
- // larger than the current scroll offset.
100
- const viewportLeftWorld = panWorldX - bufferWorld;
101
- const viewportRightWorld = panWorldX + viewportWorldWidth + bufferWorld;
102
- const viewportTopWorld = panWorldY - bufferWorld;
103
- const viewportBottomWorld = panWorldY + viewportWorldHeight + bufferWorld;
104
-
105
- const inViewportIds: (string | number)[] = [];
106
- // Small tolerance in world units to avoid floating-point edge cases when
107
- // browser/device zoom or high scroll values produce tiny rounding errors.
108
- // Scale epsilon with invScale so tolerance grows when zoomed out.
109
- const worldEpsilon = Math.max(0.5, 0.5 * invScale);
110
-
111
- // Iterate layout positions directly to avoid depending on `visibleIds`
112
- // which may be calculated in a different coordinate space or with
113
- // different assumptions about zoom. Looping the positions map is
114
- // deterministic and uses world coordinates directly.
115
- for (const [id, position] of layout.positions) {
116
- if (!position) continue;
117
- const worldX = position.x;
118
- const worldY = position.y;
119
- const worldCardW = cardWidth;
120
- const worldCardH = cardHeight;
121
-
122
- if (
123
- worldX + worldCardW >= viewportLeftWorld - worldEpsilon &&
124
- worldX <= viewportRightWorld + worldEpsilon &&
125
- worldY + worldCardH >= viewportTopWorld - worldEpsilon &&
126
- worldY <= viewportBottomWorld + worldEpsilon
127
- ) {
128
- inViewportIds.push(id);
129
- visibleSet.add(id);
130
- }
131
- }
132
-
133
- // Ensure last rows are present when the user scrolls near the bottom.
134
- // Compute slot/row information and force-insert IDs from the last few
135
- // rows to avoid missing tiles due to rounding/precision at zoom levels.
136
- try {
137
- const slotHeight = cardHeight + (CARD_GAP || 8);
138
- const totalRows = Math.ceil((layout.totalHeight || 0) / slotHeight) || 0;
139
- // Determine how many rows are visible in the viewport (world units),
140
- // then prefetch a fraction of that adjusted by zoom (invScale).
141
- const rowsVisible = Math.max(1, Math.ceil(viewportWorldHeight / slotHeight));
142
- const prefetchMultiplier = 0.75; // fraction of viewport to prefetch
143
- const prefetchRows = Math.max(2, Math.ceil(rowsVisible * prefetchMultiplier * Math.max(1, invScale)));
144
- const lastRowThresholdY = Math.max(0, (totalRows - prefetchRows) * slotHeight);
145
- for (const [id, position] of layout.positions) {
146
- if (position.y >= lastRowThresholdY) {
147
- if (!visibleSet.has(id)) {
148
- inViewportIds.push(id);
149
- visibleSet.add(id);
150
- }
151
- }
152
- }
153
- } catch (e) {
154
- void e;
155
- }
156
-
157
- // If we detect a very large discrepancy between created sprites and the
158
- // computed in-viewport count, that's a signal our culling math may be
159
- // unstable (especially at non-100% zoom). In that case, skip hiding this
160
- // frame as a conservative safeguard to avoid mass disappearing tiles.
161
- // However, disable this safeguard during view transitions to ensure old sprites are cleaned up.
162
- const aggressiveCull = !isViewTransition && sprites.size > Math.max(120, Math.ceil(inViewportIds.length * 1.5));
163
-
164
- for (const [id, sprite] of sprites) {
165
- if (!visibleSet.has(id)) {
166
- // If view transition is active, check if this sprite has a valid target in the new layout
167
- // If so, keep it visible and animate it to the new position (even if off-screen)
168
- if (isViewTransition && layout.positions.has(id)) {
169
- const newPos = layout.positions.get(id);
170
- if (newPos) {
171
- sprite.targetX = newPos.x;
172
- sprite.targetY = newPos.y;
173
-
174
- // Trigger animation if not already animating
175
- if (sprite.animationStartTime === undefined) {
176
- sprite.startX = sprite.currentX;
177
- sprite.startY = sprite.currentY;
178
- sprite.animationStartTime = Date.now();
179
- sprite.animationDelay = Math.random() * 300;
180
- }
181
-
182
- try { if (sprite.container) sprite.container.visible = true; } catch (e) { void e; }
183
- // Don't mark as hidden, so it won't be swept
184
- if ((sprite as any).__lastHiddenAt) delete (sprite as any).__lastHiddenAt;
185
- continue;
186
- }
187
- }
188
-
189
- if (aggressiveCull) {
190
- // Keep sprite visible this frame to avoid visual holes
191
- try { if (sprite.container) sprite.container.visible = true; } catch (e) { void e; }
192
- continue;
193
- }
194
-
195
- try {
196
- if (sprite.container) {
197
- sprite.container.visible = false;
198
- }
199
- (sprite as any).__lastHiddenAt = Date.now();
200
- } catch (e) {
201
- void e;
202
- }
203
- } else {
204
- try {
205
- if (sprite.container) {
206
- sprite.container.visible = true;
207
- }
208
- if ((sprite as any).__lastHiddenAt) delete (sprite as any).__lastHiddenAt;
209
- } catch (e) { void e; }
210
- }
211
- }
212
-
213
- // Sweep: actually destroy sprites that have been hidden longer than threshold
214
- try {
215
- const SWEEP_MS = 500; // keep hidden sprites for 500ms before destruction
216
- const now = Date.now();
217
- for (const [id, sprite] of sprites) {
218
- const lastHidden = (sprite as any).__lastHiddenAt as number | undefined;
219
- if (lastHidden && now - lastHidden > SWEEP_MS) {
220
- try {
221
- // remove from parent if present
222
- if (sprite.container && sprite.container.parent) sprite.container.parent.removeChild(sprite.container);
223
- } catch (e) {
224
- void e;
225
- }
226
- try {
227
- destroySprite(sprite);
228
- } catch (e) {
229
- void e;
230
- }
231
- sprites.delete(id);
232
- }
233
- }
234
- } catch (e) {
235
- void e;
236
- }
237
-
238
- // Limit the number of sprites created per frame to avoid choking the GPU/CPU
239
- // when scrolling rapidly or zooming out significantly.
240
- const MAX_SPRITES_PER_FRAME = 50;
241
- let createdCount = 0;
242
-
243
- for (const id of inViewportIds) {
244
- const position = layout.positions.get(id);
245
- if (!position) continue;
246
-
247
- let sprite = sprites.get(id);
248
- if (!sprite) {
249
- if (createdCount >= MAX_SPRITES_PER_FRAME) continue;
250
- createdCount++;
251
-
252
- let startX = position.x;
253
- let startY = position.y;
254
- let shouldAnimate = false;
255
-
256
- // If view transition, try to find old position to fly in from
257
- if (isViewTransition && prevLayout && prevLayout.positions.has(id)) {
258
- const oldPos = prevLayout.positions.get(id);
259
- if (oldPos) {
260
- startX = oldPos.x;
261
- startY = oldPos.y;
262
-
263
- // If we have a pan delta (camera jump), we need to adjust the start position
264
- // so that the sprite appears at the same visual location relative to the NEW camera.
265
- // StartWorld = OldWorld + PanDelta
266
- if (panDeltaX || panDeltaY) {
267
- const dx = (panDeltaX || 0) / (zoomLevel || 1);
268
- const dy = (panDeltaY || 0) / (zoomLevel || 1);
269
- startX += dx;
270
- startY += dy;
271
- }
272
-
273
- shouldAnimate = true;
274
- }
275
- }
276
-
277
- sprite = createCardSprite(id, startX, startY);
278
- sprites.set(id, sprite);
279
- root.addChild(sprite.container);
280
- sprite.currentX = startX;
281
- sprite.currentY = startY;
282
- // Keep sprite.container positioned in world units; animation/update
283
- // loop will apply root.scale/position to convert to pixels.
284
- sprite.container.position.set(startX, startY);
285
-
286
- if (shouldAnimate) {
287
- sprite.targetX = position.x;
288
- sprite.targetY = position.y;
289
- sprite.startX = startX;
290
- sprite.startY = startY;
291
- sprite.animationStartTime = Date.now();
292
- sprite.animationDelay = Math.random() * 300;
293
- }
294
- }
295
-
296
- // Check if target changed to trigger animation
297
- if (sprite.targetX !== position.x || sprite.targetY !== position.y) {
298
- if (isViewTransition) {
299
- sprite.startX = sprite.currentX;
300
- sprite.startY = sprite.currentY;
301
- sprite.targetX = position.x;
302
- sprite.targetY = position.y;
303
- sprite.animationStartTime = Date.now();
304
- // Add random delay for "organic" fly effect
305
- sprite.animationDelay = Math.random() * 300;
306
- } else {
307
- sprite.targetX = position.x;
308
- sprite.targetY = position.y;
309
- delete sprite.animationStartTime;
310
- delete sprite.animationDelay;
311
- }
312
- }
313
-
314
- const item = (items as any)[String(id)];
315
- if (item) {
316
- updateCardContent(sprite, item);
317
- }
318
- }
319
- }
@@ -1,9 +0,0 @@
1
- // Copyright (c) Cratis. All rights reserved.
2
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
-
4
- export const BASE_CARD_WIDTH = 200;
5
- export const BASE_CARD_HEIGHT = 176;
6
- export const CARDS_PER_COLUMN = 5;
7
- export const GROUP_SPACING = 20;
8
- export const CARD_GAP = 10;
9
- export const CANVAS_PADDING = 20;
@@ -1,149 +0,0 @@
1
- // Copyright (c) Cratis. All rights reserved.
2
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
-
4
- import type {
5
- LayoutSpec,
6
- LayoutResult,
7
- ItemPosition,
8
- GroupingResult,
9
- ItemId,
10
- } from './types';
11
- import { CARD_GAP, CANVAS_PADDING } from '../constants';
12
-
13
- export function computeLayout(
14
- grouping: GroupingResult,
15
- spec: LayoutSpec
16
- ): LayoutResult {
17
- const positions = new Map<ItemId, ItemPosition>();
18
-
19
- if (spec.viewMode === 'collection') {
20
- return computeCollectionLayout(grouping, spec, positions);
21
- } else {
22
- return computeGroupedLayout(grouping, spec, positions);
23
- }
24
- }
25
-
26
- function computeCollectionLayout(
27
- grouping: GroupingResult,
28
- spec: LayoutSpec,
29
- positions: Map<ItemId, ItemPosition>
30
- ): LayoutResult {
31
- const { cardWidth, cardHeight, containerWidth } = spec;
32
- const slotWidth = cardWidth + CARD_GAP;
33
- const slotHeight = cardHeight + CARD_GAP;
34
-
35
- // Calculate how many cards fit per row based on container width (include gap)
36
- const cardsPerRow = Math.max(1, Math.floor((containerWidth + CARD_GAP - (CANVAS_PADDING * 2)) / slotWidth));
37
-
38
- let x = CANVAS_PADDING;
39
- let y = CANVAS_PADDING;
40
- let column = 0;
41
- let itemCount = 0;
42
-
43
- for (const group of grouping.groups) {
44
- for (let i = 0; i < group.ids.length; i++) {
45
- const id = group.ids[i];
46
-
47
- positions.set(id, {
48
- x,
49
- y,
50
- groupIndex: 0,
51
- });
52
-
53
- // Move to next position horizontally (left to right)
54
- column++;
55
- x += slotWidth;
56
-
57
- // Wrap to next row when we've filled the width
58
- if (column >= cardsPerRow) {
59
- column = 0;
60
- x = CANVAS_PADDING;
61
- y += slotHeight;
62
- }
63
-
64
- itemCount++;
65
- }
66
- }
67
-
68
- const rows = Math.ceil(itemCount / cardsPerRow);
69
- const contentWidth = Math.min(itemCount, cardsPerRow) * slotWidth;
70
-
71
- return {
72
- positions,
73
- totalWidth: Math.max(containerWidth, contentWidth + (CANVAS_PADDING * 2)),
74
- totalHeight: (rows * slotHeight) + (CANVAS_PADDING * 2),
75
- };
76
- }
77
-
78
- function computeGroupedLayout(
79
- grouping: GroupingResult,
80
- spec: LayoutSpec,
81
- positions: Map<ItemId, ItemPosition>
82
- ): LayoutResult {
83
- const { cardWidth, cardHeight, cardsPerColumn, groupSpacing } = spec;
84
- const slotWidth = cardWidth + CARD_GAP;
85
- const slotHeight = cardHeight + CARD_GAP;
86
-
87
- // Fixed bucket width: 2 columns of cards per bucket (always)
88
- const COLUMNS_PER_BUCKET = 2;
89
- const bucketWidth = COLUMNS_PER_BUCKET * slotWidth;
90
-
91
- let groupX = CANVAS_PADDING;
92
- // Use container height for layout, or fallback to cardsPerColumn height
93
- const layoutHeight = spec.containerHeight || (cardsPerColumn * slotHeight);
94
- const bucketWidths: number[] = [];
95
- let maxRows = 0;
96
-
97
- // First pass: calculate max rows to determine total height
98
- for (const group of grouping.groups) {
99
- const itemsInGroup = group.ids.length;
100
- const rowsInGroup = Math.ceil(itemsInGroup / COLUMNS_PER_BUCKET);
101
- maxRows = Math.max(maxRows, rowsInGroup);
102
- }
103
-
104
- // Calculate actual content height needed (ensure it's at least as tall as the container)
105
- const contentHeight = Math.max(layoutHeight, maxRows * slotHeight);
106
-
107
- for (let groupIndex = 0; groupIndex < grouping.groups.length; groupIndex++) {
108
- const group = grouping.groups[groupIndex];
109
-
110
- const itemsInGroup = group.ids.length;
111
-
112
- for (let i = 0; i < itemsInGroup; i++) {
113
- const id = group.ids[i];
114
-
115
- // Cards fill from left to right, bottom to top
116
- // For a 2-column bucket: i=0,1 in row 0; i=2,3 in row 1; etc.
117
- const col = i % COLUMNS_PER_BUCKET;
118
- const row = Math.floor(i / COLUMNS_PER_BUCKET);
119
-
120
- const x = groupX + col * slotWidth;
121
- // Stack from bottom of total content upward:
122
- // row 0 (first card) at bottom: contentHeight - slotHeight
123
- // row 1 above it: contentHeight - 2*slotHeight, etc.
124
- const y = contentHeight - (row + 1) * slotHeight + CANVAS_PADDING;
125
-
126
- positions.set(id, {
127
- x,
128
- y,
129
- groupIndex,
130
- });
131
- }
132
-
133
- // Always use fixed bucket width
134
- bucketWidths.push(bucketWidth);
135
-
136
- // Advance position by fixed bucket width + spacing
137
- groupX += bucketWidth;
138
- if (groupIndex < grouping.groups.length - 1) {
139
- groupX += groupSpacing;
140
- }
141
- }
142
-
143
- return {
144
- positions,
145
- totalWidth: groupX + CANVAS_PADDING,
146
- totalHeight: contentHeight + (CANVAS_PADDING * 2),
147
- bucketWidths,
148
- };
149
- }
@@ -1,86 +0,0 @@
1
- // Copyright (c) Cratis. All rights reserved.
2
- // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
-
4
- import type {
5
- PivotStore,
6
- PivotIndexes,
7
- WorkerInMessage,
8
- WorkerOutMessage,
9
- } from './types';
10
- import { buildIndexes, applyFilters, computeGrouping, sortIds } from './store';
11
-
12
- let store: PivotStore | null = null;
13
- let indexes: PivotIndexes | null = null;
14
-
15
- self.onmessage = (e: MessageEvent<WorkerInMessage>) => {
16
- const message = e.data;
17
-
18
- switch (message.type) {
19
- case 'buildIndexes': {
20
- store = message.store;
21
- indexes = buildIndexes(store, message.fields);
22
-
23
- const response: WorkerOutMessage = {
24
- type: 'indexesReady',
25
- indexes,
26
- };
27
- self.postMessage(response);
28
- break;
29
- }
30
-
31
- case 'applyFilters': {
32
- if (!store || !indexes) {
33
- console.error('Store or indexes not initialized');
34
- return;
35
- }
36
-
37
- const result = applyFilters(store, indexes, message.filters);
38
-
39
- const response: WorkerOutMessage = {
40
- type: 'filterResult',
41
- result,
42
- };
43
- self.postMessage(response);
44
- break;
45
- }
46
-
47
- case 'computeGrouping': {
48
- if (!store || !indexes) {
49
- console.error('Store or indexes not initialized');
50
- return;
51
- }
52
-
53
- const result = computeGrouping(
54
- store,
55
- indexes,
56
- message.visibleIds,
57
- message.groupBy
58
- );
59
-
60
- const response: WorkerOutMessage = {
61
- type: 'groupingResult',
62
- result,
63
- };
64
- self.postMessage(response);
65
- break;
66
- }
67
-
68
- case 'sort': {
69
- if (!store) {
70
- console.error('Store not initialized');
71
- return;
72
- }
73
-
74
- const result = sortIds(store, message.ids, message.sortBy);
75
-
76
- const response: WorkerOutMessage = {
77
- type: 'sortResult',
78
- result,
79
- };
80
- self.postMessage(response);
81
- break;
82
- }
83
- }
84
- };
85
-
86
- export {};