@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,229 +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 { ReactNode } from 'react';
5
- import type { ItemId, LayoutResult, GroupingResult } from '../engine/types';
6
- import type { ViewMode } from './Toolbar';
7
- import type { PivotDimensionFilter } from '../hooks/useDimensionState';
8
- import { Spinner } from './Spinner';
9
- import { PivotCanvas } from './PivotCanvas';
10
- import { AxisLabels } from './AxisLabels';
11
- import { DetailPanel } from './DetailPanel';
12
-
13
- export interface PivotViewerMainProps<TItem extends object> {
14
- data: TItem[];
15
- ready: boolean;
16
- isLoading: boolean;
17
- visibleIds: Uint32Array;
18
- grouping: GroupingResult;
19
- layout: LayoutResult;
20
- cardWidth: number;
21
- cardHeight: number;
22
- zoomLevel: number;
23
- scrollPosition: { x: number; y: number };
24
- containerDimensions: { width: number; height: number };
25
- selectedItem: TItem | null;
26
- hoveredGroupIndex: number | null;
27
- isZooming: boolean;
28
- viewMode: ViewMode;
29
- cardRenderer?: (item: TItem) => ReactNode;
30
- resolveId: (item: TItem, index: number) => ItemId;
31
- emptyContent?: ReactNode;
32
- dimensionFilter: PivotDimensionFilter;
33
- onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void;
34
- onPanStart: (e: React.MouseEvent) => void;
35
- onPanMove: (e: React.MouseEvent) => void;
36
- onPanEnd: () => void;
37
- onGroupHover: (index: number | null) => void;
38
- onAxisLabelClick: (value: string) => void;
39
- onCloseDetail: () => void;
40
- containerRef: React.RefObject<HTMLDivElement | null>;
41
- axisLabelsRef: React.RefObject<HTMLDivElement | null>;
42
- spacerRef: React.RefObject<HTMLDivElement | null>;
43
- }
44
-
45
- export function PivotViewerMain<TItem extends object>({
46
- data,
47
- ready,
48
- isLoading,
49
- visibleIds,
50
- grouping,
51
- layout,
52
- cardWidth,
53
- cardHeight,
54
- zoomLevel,
55
- scrollPosition,
56
- containerDimensions,
57
- selectedItem,
58
- hoveredGroupIndex,
59
- isZooming,
60
- viewMode,
61
- cardRenderer,
62
- resolveId,
63
- emptyContent,
64
- dimensionFilter,
65
- onCardClick,
66
- onPanStart,
67
- onPanMove,
68
- onPanEnd,
69
- onGroupHover,
70
- onAxisLabelClick,
71
- onCloseDetail,
72
- containerRef,
73
- axisLabelsRef,
74
- spacerRef,
75
- }: PivotViewerMainProps<TItem>) {
76
- const handleViewportClick = (e: React.MouseEvent) => {
77
- if (isZooming || !containerRef.current) return;
78
-
79
- const container = containerRef.current;
80
- const rect = container.getBoundingClientRect();
81
- // Use live DOM scroll position for accurate hit testing
82
- const scrollLeft = container.scrollLeft;
83
- const scrollTop = container.scrollTop;
84
-
85
- const clickX = e.clientX - rect.left + scrollLeft;
86
- const clickY = e.clientY - rect.top + scrollTop;
87
-
88
- const worldX = clickX / zoomLevel;
89
- const worldY = clickY / zoomLevel;
90
-
91
- // Check visible items
92
- for (let i = 0; i < visibleIds.length; i++) {
93
- const id = visibleIds[i];
94
- const pos = layout.positions.get(id);
95
- if (pos) {
96
- if (worldX >= pos.x && worldX <= pos.x + cardWidth &&
97
- worldY >= pos.y && worldY <= pos.y + cardHeight) {
98
- const item = data[id];
99
- if (item) {
100
- onCardClick(item, e.nativeEvent as unknown as MouseEvent, id);
101
- }
102
- return;
103
- }
104
- }
105
- }
106
- };
107
-
108
- const handleViewportMouseMove = (e: React.MouseEvent) => {
109
- if (isZooming || !containerRef.current) return;
110
-
111
- const container = containerRef.current;
112
- const rect = container.getBoundingClientRect();
113
- const scrollLeft = container.scrollLeft;
114
- const scrollTop = container.scrollTop;
115
-
116
- const mouseX = e.clientX - rect.left + scrollLeft;
117
- const mouseY = e.clientY - rect.top + scrollTop;
118
-
119
- const worldX = mouseX / zoomLevel;
120
- const worldY = mouseY / zoomLevel;
121
-
122
- let isOverCard = false;
123
- for (let i = 0; i < visibleIds.length; i++) {
124
- const id = visibleIds[i];
125
- const pos = layout.positions.get(id);
126
- if (pos) {
127
- if (worldX >= pos.x && worldX <= pos.x + cardWidth &&
128
- worldY >= pos.y && worldY <= pos.y + cardHeight) {
129
- isOverCard = true;
130
- break;
131
- }
132
- }
133
- }
134
-
135
- container.style.cursor = isOverCard ? 'pointer' : 'default';
136
- };
137
-
138
- return isLoading ? (
139
- <Spinner />
140
- ) : (
141
- <div className="pv-groups-wrapper">
142
- <div style={{ position: 'relative', flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
143
- <div
144
- className={`pv-viewport ${isZooming ? 'pv-zooming' : ''}`}
145
- ref={containerRef}
146
- style={{ overflow: 'auto', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 1 }}
147
- onClick={handleViewportClick}
148
- onMouseMove={handleViewportMouseMove}
149
- >
150
- {/* Spacer for scrolling - explicitly rendered to allow synchronous updates during animation */}
151
- <div
152
- ref={spacerRef}
153
- style={{
154
- position: 'absolute',
155
- top: 0,
156
- left: 0,
157
- width: layout.totalWidth * zoomLevel,
158
- height: layout.totalHeight * zoomLevel,
159
- pointerEvents: 'none'
160
- }}
161
- />
162
-
163
- {!ready && (
164
- <div className="pv-loading">Building indexes...</div>
165
- )}
166
-
167
- {ready && visibleIds.length === 0 && (
168
- <div className="pv-empty">
169
- {emptyContent ?? 'No items to display.'}
170
- </div>
171
- )}
172
-
173
- {ready && visibleIds.length > 0 && (
174
- <PivotCanvas
175
- items={data}
176
- layout={layout}
177
- grouping={grouping}
178
- visibleIds={visibleIds}
179
- cardWidth={cardWidth}
180
- cardHeight={cardHeight}
181
- zoomLevel={zoomLevel}
182
- panX={scrollPosition.x}
183
- panY={scrollPosition.y}
184
- viewportWidth={containerDimensions.width}
185
- viewportHeight={containerDimensions.height}
186
- selectedId={selectedItem ? resolveId(selectedItem, 0) : null}
187
- hoveredGroupIndex={hoveredGroupIndex}
188
- isZooming={isZooming}
189
- cardRenderer={cardRenderer}
190
- resolveId={resolveId}
191
- onCardClick={onCardClick}
192
- onPanStart={onPanStart as any}
193
- onPanMove={onPanMove as any}
194
- onPanEnd={onPanEnd}
195
- containerRef={containerRef}
196
- viewMode={viewMode}
197
- />
198
- )}
199
- </div>
200
- <DetailPanel
201
- selectedItem={selectedItem}
202
- onClose={onCloseDetail}
203
- />
204
- </div>
205
-
206
- {viewMode === 'grouped' && grouping.groups.length > 0 && (
207
- <AxisLabels
208
- groups={grouping.groups.map((g) => ({
209
- key: g.key,
210
- value: g.value,
211
- label: String(g.value),
212
- items: [],
213
- count: g.ids.length,
214
- }))}
215
- bucketWidths={layout.bucketWidths || []}
216
- zoomLevel={zoomLevel}
217
- dimensionFilter={dimensionFilter}
218
- hoveredGroup={hoveredGroupIndex !== null ? String(grouping.groups[hoveredGroupIndex]?.value) : null}
219
- onHover={(label) => {
220
- const index = grouping.groups.findIndex(g => String(g.value) === label);
221
- onGroupHover(index >= 0 ? index : null);
222
- }}
223
- onClick={onAxisLabelClick}
224
- containerRef={axisLabelsRef}
225
- />
226
- )}
227
- </div>
228
- );
229
- }
@@ -1,220 +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 { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
- import type { PivotPrimitive } from '../types';
6
-
7
- export interface RangeHistogramFilterProps {
8
- values: PivotPrimitive[];
9
- min: number;
10
- max: number;
11
- buckets?: number;
12
- selectedRange: [number, number] | null;
13
- onChange: (range: [number, number] | null) => void;
14
- }
15
-
16
- interface HistogramBucket {
17
- start: number;
18
- end: number;
19
- count: number;
20
- maxCount: number;
21
- }
22
-
23
- export function RangeHistogramFilter({
24
- values,
25
- min,
26
- max,
27
- buckets = 20,
28
- selectedRange,
29
- onChange,
30
- }: RangeHistogramFilterProps) {
31
- const containerRef = useRef<HTMLDivElement>(null);
32
- const [isDragging, setIsDragging] = useState<'left' | 'right' | 'range' | null>(null);
33
- const [dragStart, setDragStart] = useState<{ x: number; range: [number, number] } | null>(null);
34
-
35
- const numericValues = useMemo(() => {
36
- return values
37
- .map((v) => {
38
- if (typeof v === 'number') return v;
39
- if (v instanceof Date) return v.getTime();
40
- const parsed = Number(v);
41
- return Number.isNaN(parsed) ? null : parsed;
42
- })
43
- .filter((v): v is number => v !== null);
44
- }, [values]);
45
-
46
- const histogram = useMemo((): HistogramBucket[] => {
47
- const range = max - min;
48
- if (range <= 0 || numericValues.length === 0) {
49
- return [];
50
- }
51
-
52
- const bucketSize = range / buckets;
53
- const bucketCounts: number[] = Array(buckets).fill(0);
54
-
55
- numericValues.forEach((value) => {
56
- const bucketIndex = Math.min(
57
- Math.floor((value - min) / bucketSize),
58
- buckets - 1
59
- );
60
- if (bucketIndex >= 0 && bucketIndex < buckets) {
61
- bucketCounts[bucketIndex]++;
62
- }
63
- });
64
-
65
- const maxCount = Math.max(...bucketCounts, 1);
66
-
67
- return bucketCounts.map((count, i) => ({
68
- start: min + i * bucketSize,
69
- end: min + (i + 1) * bucketSize,
70
- count,
71
- maxCount,
72
- }));
73
- }, [numericValues, min, max, buckets]);
74
-
75
- const currentRange = selectedRange ?? [min, max];
76
-
77
- const getPositionFromValue = useCallback(
78
- (value: number) => {
79
- const range = max - min;
80
- if (range <= 0) return 0;
81
- return ((value - min) / range) * 100;
82
- },
83
- [min, max]
84
- );
85
-
86
- const handleMouseDown = (
87
- e: React.MouseEvent,
88
- handle: 'left' | 'right' | 'range'
89
- ) => {
90
- (e as any).preventDefault?.();
91
- setIsDragging(handle);
92
- setDragStart({ x: e.clientX, range: [...currentRange] as [number, number] });
93
- };
94
-
95
- useEffect(() => {
96
- if (!isDragging || !dragStart || !containerRef.current) return;
97
-
98
- const container = containerRef.current;
99
- const rect = container.getBoundingClientRect();
100
- const range = max - min;
101
-
102
- const handleMouseMove = (e: MouseEvent) => {
103
- const deltaX = e.clientX - dragStart.x;
104
- const deltaPercent = (deltaX / rect.width) * 100;
105
- const deltaValue = (deltaPercent / 100) * range;
106
-
107
- let newRange: [number, number] = [...dragStart.range];
108
-
109
- if (isDragging === 'left') {
110
- newRange[0] = Math.max(min, Math.min(dragStart.range[0] + deltaValue, newRange[1] - range * 0.01));
111
- } else if (isDragging === 'right') {
112
- newRange[1] = Math.min(max, Math.max(dragStart.range[1] + deltaValue, newRange[0] + range * 0.01));
113
- } else if (isDragging === 'range') {
114
- const rangeWidth = dragStart.range[1] - dragStart.range[0];
115
- let newStart = dragStart.range[0] + deltaValue;
116
- let newEnd = dragStart.range[1] + deltaValue;
117
-
118
- if (newStart < min) {
119
- newStart = min;
120
- newEnd = min + rangeWidth;
121
- }
122
- if (newEnd > max) {
123
- newEnd = max;
124
- newStart = max - rangeWidth;
125
- }
126
-
127
- newRange = [newStart, newEnd];
128
- }
129
-
130
- onChange(newRange);
131
- };
132
-
133
- const handleMouseUp = () => {
134
- setIsDragging(null);
135
- setDragStart(null);
136
- };
137
-
138
- document.addEventListener('mousemove', handleMouseMove);
139
- document.addEventListener('mouseup', handleMouseUp);
140
-
141
- return () => {
142
- document.removeEventListener('mousemove', handleMouseMove);
143
- document.removeEventListener('mouseup', handleMouseUp);
144
- };
145
- }, [isDragging, dragStart, min, max, onChange]);
146
-
147
- const handleBarClick = (bucket: HistogramBucket) => {
148
- onChange([bucket.start, bucket.end]);
149
- };
150
-
151
- const handleClear = () => {
152
- onChange(null);
153
- };
154
-
155
- const leftPos = getPositionFromValue(currentRange[0]);
156
- const rightPos = getPositionFromValue(currentRange[1]);
157
-
158
- const formatValue = (value: number) => {
159
- if (Number.isInteger(value)) return value.toString();
160
- return value.toFixed(1);
161
- };
162
-
163
- return (
164
- <div className="pv-range-histogram" ref={containerRef}>
165
- <div className="pv-histogram-bars">
166
- {histogram.map((bucket, i) => {
167
- const heightPercent = (bucket.count / bucket.maxCount) * 100;
168
- const isInRange =
169
- bucket.start >= currentRange[0] && bucket.end <= currentRange[1];
170
- const isPartiallyInRange =
171
- bucket.end > currentRange[0] && bucket.start < currentRange[1];
172
-
173
- return (
174
- <button
175
- key={i}
176
- className={`pv-histogram-bar ${isInRange ? 'in-range' : ''} ${isPartiallyInRange && !isInRange ? 'partial' : ''}`}
177
- style={{ height: `${heightPercent}%` }}
178
- onClick={() => handleBarClick(bucket)}
179
- title={`${formatValue(bucket.start)} - ${formatValue(bucket.end)}: ${bucket.count} items`}
180
- type="button"
181
- />
182
- );
183
- })}
184
- </div>
185
-
186
- <div className="pv-range-slider">
187
- <div className="pv-range-track" />
188
- <div
189
- className="pv-range-selection"
190
- style={{
191
- left: `${leftPos}%`,
192
- width: `${rightPos - leftPos}%`,
193
- }}
194
- onMouseDown={(e) => handleMouseDown(e, 'range')}
195
- />
196
- <div
197
- className="pv-range-handle pv-range-handle-left"
198
- style={{ left: `${leftPos}%` }}
199
- onMouseDown={(e) => handleMouseDown(e, 'left')}
200
- />
201
- <div
202
- className="pv-range-handle pv-range-handle-right"
203
- style={{ left: `${rightPos}%` }}
204
- onMouseDown={(e) => handleMouseDown(e, 'right')}
205
- />
206
- </div>
207
-
208
- <div className="pv-range-labels">
209
- <span className="pv-range-value">{formatValue(currentRange[0])}</span>
210
- <span className="pv-range-value">{formatValue(currentRange[1])}</span>
211
- </div>
212
-
213
- {selectedRange && (
214
- <button type="button" className="pv-range-clear" onClick={handleClear}>
215
- Clear Range
216
- </button>
217
- )}
218
- </div>
219
- );
220
- }
@@ -1,21 +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 './Spinner.css';
5
-
6
- export function Spinner() {
7
- return (
8
- <div className="pv-loading">
9
- <div className="pv-spinner">
10
- <div className="pv-spinner-ring" />
11
- <div className="pv-spinner-ring" />
12
- <div className="pv-spinner-ring" />
13
- <div className="pv-spinner-ring" />
14
- <div className="pv-spinner-ring" />
15
- <div className="pv-spinner-ring" />
16
- <div className="pv-spinner-ring" />
17
- <div className="pv-spinner-ring" />
18
- </div>
19
- </div>
20
- );
21
- }
@@ -1,130 +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 { PivotDimension } from '../types';
5
- import { ZOOM_MIN, ZOOM_MAX, ZOOM_STEP } from '../utils/utils';
6
-
7
- export type ViewMode = 'collection' | 'grouped';
8
-
9
- export interface ToolbarProps<TItem extends object> {
10
- hasFilters: boolean;
11
- filtersOpen: boolean;
12
- filteredCount: number;
13
- viewMode: ViewMode;
14
- zoomLevel: number;
15
- activeDimensionKey: string;
16
- dimensions: PivotDimension<TItem>[];
17
- activeFilterCount: number;
18
- onFiltersToggle: () => void;
19
- onViewModeChange: (mode: ViewMode) => void;
20
- onZoomIn: () => void;
21
- onZoomOut: () => void;
22
- onZoomSlider: (e: React.ChangeEvent<HTMLInputElement>) => void;
23
- onDimensionChange: (key: string) => void;
24
- filterButtonRef: React.RefObject<HTMLButtonElement | null>;
25
- }
26
-
27
- export function Toolbar<TItem extends object>({
28
- hasFilters,
29
- filtersOpen,
30
- filteredCount,
31
- viewMode,
32
- zoomLevel,
33
- activeDimensionKey,
34
- dimensions,
35
- activeFilterCount,
36
- onFiltersToggle,
37
- onViewModeChange,
38
- onZoomIn,
39
- onZoomOut,
40
- onZoomSlider,
41
- onDimensionChange,
42
- filterButtonRef,
43
- }: ToolbarProps<TItem>) {
44
- const labelText = 'Sort by';
45
-
46
- return (
47
- <header className="pv-toolbar">
48
- <div className="pv-toolbar-left">
49
- {hasFilters && (
50
- <button
51
- ref={filterButtonRef}
52
- type="button"
53
- className={`pv-filter-icon-button ${filtersOpen ? 'active' : ''}`}
54
- onClick={onFiltersToggle}
55
- title="Filters"
56
- >
57
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
58
- <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" />
59
- </svg>
60
- {activeFilterCount > 0 && (
61
- <span className="pv-filter-badge">{activeFilterCount}</span>
62
- )}
63
- </button>
64
- )}
65
- <h1>Pivot Viewer</h1>
66
- <span className="pv-count">{filteredCount} events</span>
67
- </div>
68
- <div className="pv-toolbar-right">
69
- <div className="pv-zoom-controls">
70
- <button
71
- type="button"
72
- onClick={onZoomOut}
73
- disabled={zoomLevel <= ZOOM_MIN}
74
- title="Zoom out"
75
- >
76
-
77
- </button>
78
- <input
79
- type="range"
80
- className="pv-zoom-slider"
81
- min={ZOOM_MIN}
82
- max={ZOOM_MAX}
83
- step={ZOOM_STEP}
84
- value={zoomLevel}
85
- onChange={onZoomSlider}
86
- title={`Zoom: ${Math.round(zoomLevel * 100)}%`}
87
- />
88
- <span className="pv-zoom-level">{Math.round(zoomLevel * 100)}%</span>
89
- <button
90
- type="button"
91
- onClick={onZoomIn}
92
- disabled={zoomLevel >= ZOOM_MAX}
93
- title="Zoom in"
94
- >
95
- +
96
- </button>
97
- </div>
98
- <div className="pv-view-toggle">
99
- <button
100
- type="button"
101
- className={viewMode === 'collection' ? 'active' : ''}
102
- onClick={() => onViewModeChange('collection')}
103
- >
104
- Collection
105
- </button>
106
- <button
107
- type="button"
108
- className={viewMode === 'grouped' ? 'active' : ''}
109
- onClick={() => onViewModeChange('grouped')}
110
- >
111
- Buckets
112
- </button>
113
- </div>
114
- <label className="pv-dimension-select">
115
- <span>{labelText}</span>
116
- <select
117
- value={activeDimensionKey}
118
- onChange={(event) => onDimensionChange(event.target.value)}
119
- >
120
- {dimensions.map((dimension) => (
121
- <option key={dimension.key} value={dimension.key}>
122
- {dimension.label}
123
- </option>
124
- ))}
125
- </select>
126
- </label>
127
- </div>
128
- </header>
129
- );
130
- }
@@ -1,10 +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 { Toolbar, type ToolbarProps } from './Toolbar';
5
-
6
- export type ToolbarContainerProps<TItem extends object> = ToolbarProps<TItem>;
7
-
8
- export function ToolbarContainer<TItem extends object>(props: ToolbarContainerProps<TItem>) {
9
- return <Toolbar {...props} />;
10
- }
@@ -1,12 +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 { PivotCanvas } from './PivotCanvas';
5
- export { FilterPanel } from './FilterPanel';
6
- export { Toolbar } from './Toolbar';
7
- export { DetailPanel } from './DetailPanel';
8
- export { AxisLabels } from './AxisLabels';
9
- export { Spinner } from './Spinner';
10
- export { RangeHistogramFilter } from './RangeHistogramFilter';
11
-
12
- export type { ViewMode } from './Toolbar';