@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.
- package/dist/cjs/CommandForm/CommandFormFields.js +9 -3
- package/dist/cjs/CommandForm/CommandFormFields.js.map +1 -1
- package/dist/cjs/CommandForm/ValidationMessage.js +24 -0
- package/dist/cjs/CommandForm/ValidationMessage.js.map +1 -0
- package/dist/cjs/CommandForm/asCommandFormField.js +47 -0
- package/dist/cjs/CommandForm/asCommandFormField.js.map +1 -0
- package/dist/cjs/CommandForm/fields/CheckboxField.js +13 -0
- package/dist/cjs/CommandForm/fields/CheckboxField.js.map +1 -0
- package/dist/cjs/CommandForm/fields/DropdownField.js +13 -0
- package/dist/cjs/CommandForm/fields/DropdownField.js.map +1 -0
- package/dist/cjs/CommandForm/fields/InputTextField.js +13 -0
- package/dist/cjs/CommandForm/fields/InputTextField.js.map +1 -0
- package/dist/cjs/CommandForm/fields/NumberField.js +13 -0
- package/dist/cjs/CommandForm/fields/NumberField.js.map +1 -0
- package/dist/cjs/CommandForm/fields/SliderField.js +17 -0
- package/dist/cjs/CommandForm/fields/SliderField.js.map +1 -0
- package/dist/cjs/CommandForm/fields/TextAreaField.js +13 -0
- package/dist/cjs/CommandForm/fields/TextAreaField.js.map +1 -0
- package/dist/cjs/CommandForm/index.js +15 -7
- package/dist/cjs/CommandForm/index.js.map +1 -1
- package/dist/cjs/PivotViewer/PivotViewer.css +1258 -0
- package/dist/cjs/PivotViewer/PivotViewer.js +14 -0
- package/dist/cjs/PivotViewer/PivotViewer.js.map +1 -1
- package/dist/cjs/PivotViewer/components/PivotCanvas.js +33 -10
- package/dist/cjs/PivotViewer/components/PivotCanvas.js.map +1 -1
- package/dist/cjs/PivotViewer/components/PivotViewerMain.js +1 -1
- package/dist/cjs/PivotViewer/components/PivotViewerMain.js.map +1 -1
- package/dist/cjs/PivotViewer/components/Spinner.css +77 -0
- package/dist/cjs/PivotViewer/components/pivot/sprites.js +79 -15
- package/dist/cjs/PivotViewer/components/pivot/sprites.js.map +1 -1
- package/dist/cjs/PivotViewer/components/pivot/visibility.js +36 -10
- package/dist/cjs/PivotViewer/components/pivot/visibility.js.map +1 -1
- package/dist/cjs/PivotViewer/engine/layout.js +2 -1
- package/dist/cjs/PivotViewer/engine/layout.js.map +1 -1
- package/dist/cjs/PivotViewer/hooks/usePivotEngine.js +37 -2
- package/dist/cjs/PivotViewer/hooks/usePivotEngine.js.map +1 -1
- package/dist/cjs/PivotViewer/index.js +3 -0
- package/dist/cjs/PivotViewer/index.js.map +1 -1
- package/dist/cjs/PivotViewer/types.js +22 -0
- package/dist/cjs/PivotViewer/types.js.map +1 -0
- package/dist/cjs/TimeMachine/EventsView.css +213 -0
- package/dist/cjs/TimeMachine/TimeMachine.css +567 -0
- package/dist/cjs/TimeMachine/TimeMachine.js +8 -3
- package/dist/cjs/TimeMachine/TimeMachine.js.map +1 -1
- package/dist/esm/CommandForm/CommandForm.stories.d.ts +1 -0
- package/dist/esm/CommandForm/CommandForm.stories.d.ts.map +1 -1
- package/dist/esm/CommandForm/CommandForm.stories.js +34 -1
- package/dist/esm/CommandForm/CommandForm.stories.js.map +1 -1
- package/dist/esm/CommandForm/CommandFormFields.d.ts.map +1 -1
- package/dist/esm/CommandForm/CommandFormFields.js +9 -3
- package/dist/esm/CommandForm/CommandFormFields.js.map +1 -1
- package/dist/esm/CommandForm/UserRegistrationCommand.d.ts +63 -0
- package/dist/esm/CommandForm/UserRegistrationCommand.d.ts.map +1 -0
- package/dist/esm/CommandForm/UserRegistrationCommand.js +143 -0
- package/dist/esm/CommandForm/UserRegistrationCommand.js.map +1 -0
- package/dist/esm/CommandForm/ValidationMessage.d.ts +8 -0
- package/dist/esm/CommandForm/ValidationMessage.d.ts.map +1 -0
- package/dist/esm/CommandForm/ValidationMessage.js +22 -0
- package/dist/esm/CommandForm/ValidationMessage.js.map +1 -0
- package/dist/esm/CommandForm/asCommandFormField.d.ts +32 -0
- package/dist/esm/CommandForm/asCommandFormField.d.ts.map +1 -0
- package/dist/esm/CommandForm/asCommandFormField.js +45 -0
- package/dist/esm/CommandForm/asCommandFormField.js.map +1 -0
- package/dist/esm/CommandForm/fields/CheckboxField.d.ts +10 -0
- package/dist/esm/CommandForm/fields/CheckboxField.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/CheckboxField.js +11 -0
- package/dist/esm/CommandForm/fields/CheckboxField.js.map +1 -0
- package/dist/esm/CommandForm/fields/DropdownField.d.ts +15 -0
- package/dist/esm/CommandForm/fields/DropdownField.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/DropdownField.js +11 -0
- package/dist/esm/CommandForm/fields/DropdownField.js.map +1 -0
- package/dist/esm/CommandForm/fields/InputTextField.d.ts +11 -0
- package/dist/esm/CommandForm/fields/InputTextField.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/InputTextField.js +11 -0
- package/dist/esm/CommandForm/fields/InputTextField.js.map +1 -0
- package/dist/esm/CommandForm/fields/NumberField.d.ts +13 -0
- package/dist/esm/CommandForm/fields/NumberField.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/NumberField.js +11 -0
- package/dist/esm/CommandForm/fields/NumberField.js.map +1 -0
- package/dist/esm/CommandForm/fields/SliderField.d.ts +12 -0
- package/dist/esm/CommandForm/fields/SliderField.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/SliderField.js +15 -0
- package/dist/esm/CommandForm/fields/SliderField.js.map +1 -0
- package/dist/esm/CommandForm/fields/TextAreaField.d.ts +12 -0
- package/dist/esm/CommandForm/fields/TextAreaField.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/TextAreaField.js +11 -0
- package/dist/esm/CommandForm/fields/TextAreaField.js.map +1 -0
- package/dist/esm/CommandForm/fields/index.d.ts +7 -0
- package/dist/esm/CommandForm/fields/index.d.ts.map +1 -0
- package/dist/esm/CommandForm/fields/index.js +7 -0
- package/dist/esm/CommandForm/fields/index.js.map +1 -0
- package/dist/esm/CommandForm/index.d.ts +3 -4
- package/dist/esm/CommandForm/index.d.ts.map +1 -1
- package/dist/esm/CommandForm/index.js +8 -4
- package/dist/esm/CommandForm/index.js.map +1 -1
- package/dist/esm/PivotViewer/PivotViewer.css +1258 -0
- package/dist/esm/PivotViewer/PivotViewer.d.ts.map +1 -1
- package/dist/esm/PivotViewer/PivotViewer.js +14 -0
- package/dist/esm/PivotViewer/PivotViewer.js.map +1 -1
- package/dist/esm/PivotViewer/PivotViewer.stories.d.ts +1 -0
- package/dist/esm/PivotViewer/PivotViewer.stories.d.ts.map +1 -1
- package/dist/esm/PivotViewer/PivotViewer.stories.js +43 -3
- package/dist/esm/PivotViewer/PivotViewer.stories.js.map +1 -1
- package/dist/esm/PivotViewer/components/PivotCanvas.d.ts.map +1 -1
- package/dist/esm/PivotViewer/components/PivotCanvas.js +33 -10
- package/dist/esm/PivotViewer/components/PivotCanvas.js.map +1 -1
- package/dist/esm/PivotViewer/components/PivotViewerMain.js +1 -1
- package/dist/esm/PivotViewer/components/PivotViewerMain.js.map +1 -1
- package/dist/esm/PivotViewer/components/Spinner.css +77 -0
- package/dist/esm/PivotViewer/components/pivot/sprites.d.ts.map +1 -1
- package/dist/esm/PivotViewer/components/pivot/sprites.js +79 -15
- package/dist/esm/PivotViewer/components/pivot/sprites.js.map +1 -1
- package/dist/esm/PivotViewer/components/pivot/visibility.d.ts.map +1 -1
- package/dist/esm/PivotViewer/components/pivot/visibility.js +36 -10
- package/dist/esm/PivotViewer/components/pivot/visibility.js.map +1 -1
- package/dist/esm/PivotViewer/engine/layout.js +2 -1
- package/dist/esm/PivotViewer/engine/layout.js.map +1 -1
- package/dist/esm/PivotViewer/engine/pivot.worker.d.ts.map +1 -1
- package/dist/esm/PivotViewer/engine/pivot.worker.js +22 -7
- package/dist/esm/PivotViewer/engine/pivot.worker.js.map +1 -1
- package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts +2 -2
- package/dist/esm/PivotViewer/hooks/useFilteredData.d.ts.map +1 -1
- package/dist/esm/PivotViewer/hooks/useFilteredData.js +4 -2
- package/dist/esm/PivotViewer/hooks/useFilteredData.js.map +1 -1
- package/dist/esm/PivotViewer/hooks/usePivotEngine.d.ts.map +1 -1
- package/dist/esm/PivotViewer/hooks/usePivotEngine.js +37 -2
- package/dist/esm/PivotViewer/hooks/usePivotEngine.js.map +1 -1
- package/dist/esm/PivotViewer/index.d.ts +2 -1
- package/dist/esm/PivotViewer/index.d.ts.map +1 -1
- package/dist/esm/PivotViewer/index.js +1 -0
- package/dist/esm/PivotViewer/index.js.map +1 -1
- package/dist/esm/PivotViewer/types.d.ts +4 -1
- package/dist/esm/PivotViewer/types.d.ts.map +1 -1
- package/dist/esm/PivotViewer/types.js +19 -2
- package/dist/esm/PivotViewer/types.js.map +1 -1
- package/dist/esm/TimeMachine/EventsView.css +213 -0
- package/dist/esm/TimeMachine/TimeMachine.css +567 -0
- package/dist/esm/TimeMachine/TimeMachine.d.ts.map +1 -1
- package/dist/esm/TimeMachine/TimeMachine.js +8 -3
- package/dist/esm/TimeMachine/TimeMachine.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +31 -32
- package/.storybook/main.ts +0 -24
- package/CommandDialog/CommandDialog.stories.tsx +0 -25
- package/CommandDialog/CommandDialog.tsx +0 -161
- package/CommandDialog/index.ts +0 -4
- package/CommandForm/CommandForm.stories.tsx +0 -24
- package/CommandForm/CommandForm.tsx +0 -266
- package/CommandForm/CommandFormField.tsx +0 -27
- package/CommandForm/CommandFormFields.tsx +0 -142
- package/CommandForm/DatePickerField.tsx +0 -57
- package/CommandForm/DropdownField.tsx +0 -65
- package/CommandForm/InputTextField.tsx +0 -62
- package/CommandForm/SliderField.tsx +0 -68
- package/CommandForm/index.ts +0 -10
- package/Common/ErrorBoundary.stories.tsx +0 -10
- package/Common/ErrorBoundary.tsx +0 -41
- package/Common/FormElement.stories.tsx +0 -10
- package/Common/FormElement.tsx +0 -20
- package/Common/Page.stories.tsx +0 -10
- package/Common/Page.tsx +0 -21
- package/Common/index.ts +0 -6
- package/DataPage/DataPage.stories.tsx +0 -10
- package/DataPage/DataPage.tsx +0 -191
- package/DataPage/index.ts +0 -4
- package/DataTables/DataTableForObservableQuery.stories.tsx +0 -10
- package/DataTables/DataTableForObservableQuery.tsx +0 -97
- package/DataTables/DataTableForQuery.stories.tsx +0 -10
- package/DataTables/DataTableForQuery.tsx +0 -97
- package/DataTables/index.ts +0 -5
- package/Dialogs/BusyIndicatorDialog.stories.tsx +0 -26
- package/Dialogs/BusyIndicatorDialog.tsx +0 -26
- package/Dialogs/ConfirmationDialog.stories.tsx +0 -36
- package/Dialogs/ConfirmationDialog.tsx +0 -75
- package/Dialogs/index.ts +0 -5
- package/Dropdown/Dropdown.tsx +0 -23
- package/Dropdown/index.ts +0 -4
- package/PivotViewer/PivotViewer.stories.tsx +0 -24
- package/PivotViewer/PivotViewer.tsx +0 -791
- package/PivotViewer/components/AxisLabels.tsx +0 -69
- package/PivotViewer/components/DetailPanel.tsx +0 -108
- package/PivotViewer/components/FilterPanel.tsx +0 -189
- package/PivotViewer/components/FilterPanelContainer.tsx +0 -10
- package/PivotViewer/components/PivotCanvas.tsx +0 -660
- package/PivotViewer/components/PivotViewerMain.tsx +0 -229
- package/PivotViewer/components/RangeHistogramFilter.tsx +0 -220
- package/PivotViewer/components/Spinner.tsx +0 -21
- package/PivotViewer/components/Toolbar.tsx +0 -130
- package/PivotViewer/components/ToolbarContainer.tsx +0 -10
- package/PivotViewer/components/index.ts +0 -12
- package/PivotViewer/components/pivot/animation.ts +0 -108
- package/PivotViewer/components/pivot/buckets.ts +0 -152
- package/PivotViewer/components/pivot/colorResolver.ts +0 -67
- package/PivotViewer/components/pivot/constants.ts +0 -46
- package/PivotViewer/components/pivot/sprites.ts +0 -265
- package/PivotViewer/components/pivot/visibility.ts +0 -319
- package/PivotViewer/constants.ts +0 -9
- package/PivotViewer/engine/layout.ts +0 -149
- package/PivotViewer/engine/pivot.worker.ts +0 -86
- package/PivotViewer/engine/store.ts +0 -437
- package/PivotViewer/engine/types.ts +0 -255
- package/PivotViewer/hooks/index.ts +0 -13
- package/PivotViewer/hooks/useContainerDimensions.ts +0 -45
- package/PivotViewer/hooks/useDimensionState.ts +0 -53
- package/PivotViewer/hooks/useFilterOptions.ts +0 -36
- package/PivotViewer/hooks/useFilterPanelDrag.ts +0 -49
- package/PivotViewer/hooks/useFilterState.ts +0 -106
- package/PivotViewer/hooks/useFilteredData.ts +0 -119
- package/PivotViewer/hooks/usePanning.ts +0 -163
- package/PivotViewer/hooks/usePivotEngine.ts +0 -252
- package/PivotViewer/hooks/useSelectedItem.ts +0 -402
- package/PivotViewer/hooks/useWheelZoom.ts +0 -114
- package/PivotViewer/hooks/useZoomState.ts +0 -34
- package/PivotViewer/index.ts +0 -7
- package/PivotViewer/types.ts +0 -59
- package/PivotViewer/utils/animations.ts +0 -249
- package/PivotViewer/utils/constants.ts +0 -20
- package/PivotViewer/utils/index.ts +0 -6
- package/PivotViewer/utils/selection.ts +0 -292
- package/PivotViewer/utils/utils.ts +0 -259
- package/TimeMachine/EventsView.stories.tsx +0 -10
- package/TimeMachine/EventsView.tsx +0 -119
- package/TimeMachine/Properties.stories.tsx +0 -10
- package/TimeMachine/Properties.tsx +0 -98
- package/TimeMachine/ReadModelView.stories.tsx +0 -10
- package/TimeMachine/ReadModelView.tsx +0 -143
- package/TimeMachine/TimeMachine.stories.tsx +0 -10
- package/TimeMachine/TimeMachine.tsx +0 -244
- package/TimeMachine/index.ts +0 -8
- package/TimeMachine/types.ts +0 -23
- package/dist/cjs/CommandForm/DatePickerField.js +0 -31
- package/dist/cjs/CommandForm/DatePickerField.js.map +0 -1
- package/dist/cjs/CommandForm/DropdownField.js +0 -31
- package/dist/cjs/CommandForm/DropdownField.js.map +0 -1
- package/dist/cjs/CommandForm/InputTextField.js +0 -32
- package/dist/cjs/CommandForm/InputTextField.js.map +0 -1
- package/dist/cjs/CommandForm/SliderField.js +0 -34
- package/dist/cjs/CommandForm/SliderField.js.map +0 -1
- package/dist/esm/CommandForm/DatePickerField.d.ts +0 -20
- package/dist/esm/CommandForm/DatePickerField.d.ts.map +0 -1
- package/dist/esm/CommandForm/DatePickerField.js +0 -29
- package/dist/esm/CommandForm/DatePickerField.js.map +0 -1
- package/dist/esm/CommandForm/DropdownField.d.ts +0 -24
- package/dist/esm/CommandForm/DropdownField.d.ts.map +0 -1
- package/dist/esm/CommandForm/DropdownField.js +0 -29
- package/dist/esm/CommandForm/DropdownField.js.map +0 -1
- package/dist/esm/CommandForm/InputTextField.d.ts +0 -20
- package/dist/esm/CommandForm/InputTextField.d.ts.map +0 -1
- package/dist/esm/CommandForm/InputTextField.js +0 -30
- package/dist/esm/CommandForm/InputTextField.js.map +0 -1
- package/dist/esm/CommandForm/SliderField.d.ts +0 -23
- package/dist/esm/CommandForm/SliderField.d.ts.map +0 -1
- package/dist/esm/CommandForm/SliderField.js +0 -32
- package/dist/esm/CommandForm/SliderField.js.map +0 -1
- package/global.d.ts +0 -11
- package/index.ts +0 -22
- package/useOverlayZIndex.ts +0 -32
- package/vite.config.ts +0 -80
|
@@ -1,292 +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 { animateZoomAndScroll, calculateCenterScrollPosition, smoothScrollTo } from './animations';
|
|
5
|
-
import { ZOOM_MAX, MIN_ZOOM_ON_SELECT, ZOOM_MULTIPLIER, DETAIL_PANEL_WIDTH } from './constants';
|
|
6
|
-
import type { ViewMode } from '../components/Toolbar';
|
|
7
|
-
|
|
8
|
-
export interface SelectionState {
|
|
9
|
-
zoom: number;
|
|
10
|
-
scrollLeft: number;
|
|
11
|
-
scrollTop: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface CardClickHandlerParams<TItem> {
|
|
15
|
-
item: TItem;
|
|
16
|
-
itemId: string | number;
|
|
17
|
-
selectedItemId: string | number | null;
|
|
18
|
-
container: HTMLElement;
|
|
19
|
-
cardPosition: { x: number; y: number; width: number; height: number } | null;
|
|
20
|
-
targetCardPosition?: { x: number; y: number; width: number; height: number } | null;
|
|
21
|
-
getCardPositionAtZoom?: (zoom: number) => { x: number; y: number; width: number; height: number } | null;
|
|
22
|
-
getLayoutSizeAtZoom?: (zoom: number) => { width: number; height: number };
|
|
23
|
-
spacer?: HTMLElement | null;
|
|
24
|
-
viewMode: ViewMode;
|
|
25
|
-
zoomLevel: number;
|
|
26
|
-
totalHeight?: number;
|
|
27
|
-
preSelectionState: SelectionState | null;
|
|
28
|
-
startScrollPosition?: { x: number; y: number };
|
|
29
|
-
setZoomLevel: (zoom: number) => void;
|
|
30
|
-
setIsZooming: (isZooming: boolean) => void;
|
|
31
|
-
setSelectedItem: (item: TItem | null) => void;
|
|
32
|
-
setPreSelectionState: (state: SelectionState | null) => void;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Handle card click for selection with zoom and scroll animation
|
|
37
|
-
*/
|
|
38
|
-
export function handleCardSelection<TItem>({
|
|
39
|
-
itemId,
|
|
40
|
-
selectedItemId,
|
|
41
|
-
container,
|
|
42
|
-
cardPosition,
|
|
43
|
-
targetCardPosition,
|
|
44
|
-
getCardPositionAtZoom,
|
|
45
|
-
getLayoutSizeAtZoom,
|
|
46
|
-
spacer,
|
|
47
|
-
viewMode,
|
|
48
|
-
zoomLevel,
|
|
49
|
-
totalHeight,
|
|
50
|
-
preSelectionState,
|
|
51
|
-
startScrollPosition,
|
|
52
|
-
setZoomLevel,
|
|
53
|
-
setIsZooming,
|
|
54
|
-
setSelectedItem,
|
|
55
|
-
setPreSelectionState,
|
|
56
|
-
item,
|
|
57
|
-
}: CardClickHandlerParams<TItem>): void {
|
|
58
|
-
// Clicking the same card - deselect
|
|
59
|
-
if (selectedItemId === itemId) {
|
|
60
|
-
deselectCard({
|
|
61
|
-
container,
|
|
62
|
-
cardPosition,
|
|
63
|
-
targetCardPosition,
|
|
64
|
-
getCardPositionAtZoom,
|
|
65
|
-
getLayoutSizeAtZoom,
|
|
66
|
-
spacer,
|
|
67
|
-
viewMode,
|
|
68
|
-
zoomLevel,
|
|
69
|
-
preSelectionState,
|
|
70
|
-
setZoomLevel,
|
|
71
|
-
setIsZooming,
|
|
72
|
-
setSelectedItem,
|
|
73
|
-
setPreSelectionState,
|
|
74
|
-
});
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// First selection - save state and zoom in
|
|
79
|
-
const isFirstSelection = selectedItemId === null;
|
|
80
|
-
|
|
81
|
-
if (isFirstSelection) {
|
|
82
|
-
setPreSelectionState({
|
|
83
|
-
zoom: zoomLevel,
|
|
84
|
-
scrollLeft: container.scrollLeft,
|
|
85
|
-
scrollTop: container.scrollTop,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
setSelectedItem(item);
|
|
90
|
-
|
|
91
|
-
if (isFirstSelection) {
|
|
92
|
-
if (viewMode === 'collection') {
|
|
93
|
-
// Collection mode: just smooth scroll to center, no zoom
|
|
94
|
-
if (cardPosition) {
|
|
95
|
-
// In collection mode, we don't have a detail panel width offset because the panel is an overlay or separate
|
|
96
|
-
// But if we want to center it, we should consider if the detail panel pushes content
|
|
97
|
-
// For now, assume 0 offset as per original code
|
|
98
|
-
const { scrollLeft, scrollTop } = calculateCenterScrollPosition(container, cardPosition, zoomLevel, 0, totalHeight);
|
|
99
|
-
smoothScrollTo(container, scrollLeft, scrollTop, true);
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
// Grouped mode: animate zoom and scroll
|
|
103
|
-
zoomAndCenterCard({
|
|
104
|
-
container,
|
|
105
|
-
cardPosition,
|
|
106
|
-
targetCardPosition,
|
|
107
|
-
getCardPositionAtZoom,
|
|
108
|
-
getLayoutSizeAtZoom,
|
|
109
|
-
spacer,
|
|
110
|
-
itemId,
|
|
111
|
-
zoomLevel,
|
|
112
|
-
totalHeight,
|
|
113
|
-
startScrollPosition,
|
|
114
|
-
setZoomLevel,
|
|
115
|
-
setIsZooming,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
// Subsequent selections: just center the new card
|
|
120
|
-
if (cardPosition) {
|
|
121
|
-
// In collection mode, we don't zoom, so we just center.
|
|
122
|
-
// In grouped mode, we might be zoomed in, so we center with offset.
|
|
123
|
-
const detailWidth = viewMode === 'collection' ? 0 : DETAIL_PANEL_WIDTH;
|
|
124
|
-
const { scrollLeft, scrollTop } = calculateCenterScrollPosition(container, cardPosition, zoomLevel, detailWidth, totalHeight);
|
|
125
|
-
smoothScrollTo(container, scrollLeft, scrollTop, true);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
interface DeselectParams {
|
|
131
|
-
container: HTMLElement;
|
|
132
|
-
cardPosition: { x: number; y: number; width: number; height: number } | null;
|
|
133
|
-
targetCardPosition?: { x: number; y: number; width: number; height: number } | null;
|
|
134
|
-
getCardPositionAtZoom?: (zoom: number) => { x: number; y: number; width: number; height: number } | null;
|
|
135
|
-
getLayoutSizeAtZoom?: (zoom: number) => { width: number; height: number };
|
|
136
|
-
spacer?: HTMLElement | null;
|
|
137
|
-
viewMode: ViewMode;
|
|
138
|
-
zoomLevel: number;
|
|
139
|
-
preSelectionState: SelectionState | null;
|
|
140
|
-
setZoomLevel: (zoom: number) => void;
|
|
141
|
-
setIsZooming: (isZooming: boolean) => void;
|
|
142
|
-
setSelectedItem: (item: null) => void;
|
|
143
|
-
setPreSelectionState: (state: SelectionState | null) => void;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Deselect card with zoom-out animation if needed
|
|
148
|
-
*/
|
|
149
|
-
function deselectCard({
|
|
150
|
-
container,
|
|
151
|
-
cardPosition,
|
|
152
|
-
targetCardPosition,
|
|
153
|
-
getCardPositionAtZoom,
|
|
154
|
-
getLayoutSizeAtZoom,
|
|
155
|
-
spacer,
|
|
156
|
-
viewMode,
|
|
157
|
-
zoomLevel,
|
|
158
|
-
preSelectionState,
|
|
159
|
-
setZoomLevel,
|
|
160
|
-
setIsZooming,
|
|
161
|
-
setSelectedItem,
|
|
162
|
-
setPreSelectionState,
|
|
163
|
-
}: DeselectParams): void {
|
|
164
|
-
if (!preSelectionState) {
|
|
165
|
-
setSelectedItem(null);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Collection mode: just scroll back
|
|
170
|
-
if (viewMode === 'collection') {
|
|
171
|
-
setSelectedItem(null);
|
|
172
|
-
smoothScrollTo(container, preSelectionState.scrollLeft, preSelectionState.scrollTop, true);
|
|
173
|
-
setPreSelectionState(null);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Grouped mode: check if zoom changed
|
|
178
|
-
const zoomChanged = Math.abs(preSelectionState.zoom - zoomLevel) > 0.001;
|
|
179
|
-
|
|
180
|
-
if (!zoomChanged || !cardPosition) {
|
|
181
|
-
setSelectedItem(null);
|
|
182
|
-
smoothScrollTo(container, preSelectionState.scrollLeft, preSelectionState.scrollTop, true);
|
|
183
|
-
setPreSelectionState(null);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Animate zoom out
|
|
188
|
-
setIsZooming(true);
|
|
189
|
-
|
|
190
|
-
animateZoomAndScroll({
|
|
191
|
-
container,
|
|
192
|
-
cardPosition,
|
|
193
|
-
targetCardPosition,
|
|
194
|
-
getCardPositionAtZoom,
|
|
195
|
-
getLayoutSizeAtZoom,
|
|
196
|
-
spacer,
|
|
197
|
-
startZoom: zoomLevel,
|
|
198
|
-
targetZoom: preSelectionState.zoom,
|
|
199
|
-
targetScrollLeft: preSelectionState.scrollLeft,
|
|
200
|
-
targetScrollTop: preSelectionState.scrollTop,
|
|
201
|
-
onUpdate: setZoomLevel,
|
|
202
|
-
onComplete: () => {
|
|
203
|
-
setIsZooming(false);
|
|
204
|
-
setSelectedItem(null);
|
|
205
|
-
setPreSelectionState(null);
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
interface ZoomAndCenterParams {
|
|
211
|
-
container: HTMLElement;
|
|
212
|
-
cardPosition: { x: number; y: number; width: number; height: number } | null;
|
|
213
|
-
targetCardPosition?: { x: number; y: number; width: number; height: number } | null;
|
|
214
|
-
getCardPositionAtZoom?: (zoom: number) => { x: number; y: number; width: number; height: number } | null;
|
|
215
|
-
getLayoutSizeAtZoom?: (zoom: number) => { width: number; height: number };
|
|
216
|
-
spacer?: HTMLElement | null;
|
|
217
|
-
itemId: string | number;
|
|
218
|
-
zoomLevel: number;
|
|
219
|
-
totalHeight?: number;
|
|
220
|
-
startScrollPosition?: { x: number; y: number };
|
|
221
|
-
setZoomLevel: (zoom: number) => void;
|
|
222
|
-
setIsZooming: (isZooming: boolean) => void;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Zoom in and center a card
|
|
227
|
-
*/
|
|
228
|
-
function zoomAndCenterCard({
|
|
229
|
-
container,
|
|
230
|
-
cardPosition,
|
|
231
|
-
targetCardPosition,
|
|
232
|
-
getCardPositionAtZoom,
|
|
233
|
-
getLayoutSizeAtZoom,
|
|
234
|
-
spacer,
|
|
235
|
-
zoomLevel,
|
|
236
|
-
totalHeight,
|
|
237
|
-
startScrollPosition,
|
|
238
|
-
setZoomLevel,
|
|
239
|
-
setIsZooming,
|
|
240
|
-
}: ZoomAndCenterParams): void {
|
|
241
|
-
const targetZoom = Math.min(ZOOM_MAX, Math.max(MIN_ZOOM_ON_SELECT, zoomLevel * ZOOM_MULTIPLIER));
|
|
242
|
-
const shouldZoom = Math.abs(targetZoom - zoomLevel) > 0.001;
|
|
243
|
-
|
|
244
|
-
if (!shouldZoom || !cardPosition) {
|
|
245
|
-
if (cardPosition) {
|
|
246
|
-
const { scrollLeft, scrollTop } = calculateCenterScrollPosition(container, cardPosition, zoomLevel, DETAIL_PANEL_WIDTH, totalHeight);
|
|
247
|
-
smoothScrollTo(container, scrollLeft, scrollTop, true);
|
|
248
|
-
}
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
setIsZooming(true);
|
|
253
|
-
|
|
254
|
-
// Use targetCardPosition if available, otherwise fallback to cardPosition
|
|
255
|
-
const { scrollLeft: targetScrollLeft, scrollTop: targetScrollTop } = calculateCenterScrollPosition(
|
|
256
|
-
container,
|
|
257
|
-
targetCardPosition || cardPosition,
|
|
258
|
-
targetZoom,
|
|
259
|
-
DETAIL_PANEL_WIDTH,
|
|
260
|
-
totalHeight
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
animateZoomAndScroll({
|
|
264
|
-
container,
|
|
265
|
-
cardPosition,
|
|
266
|
-
targetCardPosition,
|
|
267
|
-
getCardPositionAtZoom,
|
|
268
|
-
getLayoutSizeAtZoom,
|
|
269
|
-
spacer,
|
|
270
|
-
startZoom: zoomLevel,
|
|
271
|
-
targetZoom,
|
|
272
|
-
targetScrollLeft,
|
|
273
|
-
targetScrollTop,
|
|
274
|
-
startScrollLeft: startScrollPosition?.x,
|
|
275
|
-
startScrollTop: startScrollPosition?.y,
|
|
276
|
-
onUpdate: setZoomLevel,
|
|
277
|
-
onComplete: () => {
|
|
278
|
-
setIsZooming(false);
|
|
279
|
-
},
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Get card element by ID from container
|
|
285
|
-
*/
|
|
286
|
-
export function getCardElementById(_container: HTMLElement, _itemId: string | number): HTMLElement | null {
|
|
287
|
-
// Deprecated: Pixi renderer doesn't use DOM elements for cards
|
|
288
|
-
// Keep parameter names prefixed to indicate intentional non-use.
|
|
289
|
-
void _container;
|
|
290
|
-
void _itemId;
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
@@ -1,259 +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
|
-
PivotDimension,
|
|
6
|
-
PivotFilter,
|
|
7
|
-
PivotFilterOption,
|
|
8
|
-
PivotGroup,
|
|
9
|
-
PivotPrimitive,
|
|
10
|
-
} from '../types';
|
|
11
|
-
|
|
12
|
-
export type FilterState = Record<string, Set<string>>;
|
|
13
|
-
export type RangeFilterState = Record<string, [number, number] | null>;
|
|
14
|
-
|
|
15
|
-
// Zoom configuration
|
|
16
|
-
export const ZOOM_MIN = 0.1;
|
|
17
|
-
export const ZOOM_MAX = 3;
|
|
18
|
-
export const ZOOM_STEP = 0.05;
|
|
19
|
-
export const CARDS_PER_COLUMN = 5;
|
|
20
|
-
export const BASE_CARD_WIDTH = 180;
|
|
21
|
-
export const BASE_CARD_HEIGHT = 140;
|
|
22
|
-
|
|
23
|
-
export const toKey = (value: PivotPrimitive): string => {
|
|
24
|
-
if (value === undefined) return 'undefined';
|
|
25
|
-
if (value === null) return 'null';
|
|
26
|
-
if (value instanceof Date) return value.toISOString();
|
|
27
|
-
if (typeof value === 'number' && Number.isNaN(value)) return 'nan';
|
|
28
|
-
return String(value);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const defaultFormat = (value: PivotPrimitive): string => {
|
|
32
|
-
if (value === undefined) return 'Unknown';
|
|
33
|
-
if (value === null) return 'None';
|
|
34
|
-
if (value instanceof Date) return value.toLocaleString();
|
|
35
|
-
if (typeof value === 'boolean') return value ? 'Yes' : 'No';
|
|
36
|
-
return String(value);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const buildFilterState = <TItem extends object>(
|
|
40
|
-
filters: PivotFilter<TItem>[] | undefined,
|
|
41
|
-
): FilterState => {
|
|
42
|
-
const state: FilterState = {};
|
|
43
|
-
filters?.forEach((filter) => {
|
|
44
|
-
state[filter.key] = new Set<string>();
|
|
45
|
-
});
|
|
46
|
-
return state;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export const buildRangeFilterState = <TItem extends object>(
|
|
50
|
-
filters: PivotFilter<TItem>[] | undefined,
|
|
51
|
-
): RangeFilterState => {
|
|
52
|
-
const state: RangeFilterState = {};
|
|
53
|
-
filters?.forEach((filter) => {
|
|
54
|
-
if (filter.type === 'number') {
|
|
55
|
-
state[filter.key] = null;
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
return state;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const applyFilters = <TItem extends object>(
|
|
62
|
-
data: TItem[],
|
|
63
|
-
filters: PivotFilter<TItem>[] | undefined,
|
|
64
|
-
state: FilterState,
|
|
65
|
-
rangeState: RangeFilterState,
|
|
66
|
-
skipKey?: string,
|
|
67
|
-
): TItem[] => {
|
|
68
|
-
if (!filters?.length) {
|
|
69
|
-
return data;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Pre-compute active filters to avoid checking empty Sets repeatedly
|
|
73
|
-
const activeFilters: Array<{
|
|
74
|
-
filter: PivotFilter<TItem>;
|
|
75
|
-
selections?: Set<string>;
|
|
76
|
-
range?: [number, number];
|
|
77
|
-
}> = [];
|
|
78
|
-
|
|
79
|
-
for (const filter of filters) {
|
|
80
|
-
if (filter.key === skipKey) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (filter.type === 'number') {
|
|
85
|
-
const range = rangeState[filter.key];
|
|
86
|
-
if (range) {
|
|
87
|
-
activeFilters.push({ filter, range });
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
const selections = state[filter.key];
|
|
91
|
-
if (selections && selections.size > 0) {
|
|
92
|
-
activeFilters.push({ filter, selections });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Early exit if no active filters
|
|
98
|
-
if (activeFilters.length === 0) {
|
|
99
|
-
return data;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return data.filter((item) => {
|
|
103
|
-
for (const { filter, selections, range } of activeFilters) {
|
|
104
|
-
if (range) {
|
|
105
|
-
const value = filter.getValue(item);
|
|
106
|
-
const numValue = typeof value === 'number' ? value : Number(value);
|
|
107
|
-
if (!Number.isNaN(numValue) && (numValue < range[0] || numValue > range[1])) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
} else if (selections) {
|
|
111
|
-
const valueKey = toKey(filter.getValue(item));
|
|
112
|
-
if (!selections.has(valueKey)) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return true;
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export const computeFilterOptions = <TItem extends object>(
|
|
122
|
-
data: TItem[],
|
|
123
|
-
filter: PivotFilter<TItem>,
|
|
124
|
-
): PivotFilterOption[] => {
|
|
125
|
-
if (filter.options?.length) {
|
|
126
|
-
return filter.options;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const counts = new Map<string, { option: PivotFilterOption; count: number }>();
|
|
130
|
-
|
|
131
|
-
data.forEach((item) => {
|
|
132
|
-
const value = filter.getValue(item);
|
|
133
|
-
const key = toKey(value);
|
|
134
|
-
const existing = counts.get(key);
|
|
135
|
-
if (existing) {
|
|
136
|
-
existing.count += 1;
|
|
137
|
-
} else {
|
|
138
|
-
counts.set(key, {
|
|
139
|
-
option: {
|
|
140
|
-
key,
|
|
141
|
-
label: defaultFormat(value),
|
|
142
|
-
value,
|
|
143
|
-
count: 1,
|
|
144
|
-
},
|
|
145
|
-
count: 1,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
const options = Array.from(counts.values()).map(({ option, count }) => ({
|
|
151
|
-
...option,
|
|
152
|
-
count,
|
|
153
|
-
}));
|
|
154
|
-
|
|
155
|
-
return filter.sort ? options.sort(filter.sort) : options.sort((a, b) => b.count - a.count);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export const renderOptionCount = (count: number | undefined): string | number =>
|
|
159
|
-
typeof count === 'number' ? count : '';
|
|
160
|
-
|
|
161
|
-
export const computeNumericRange = <TItem extends object>(
|
|
162
|
-
data: TItem[],
|
|
163
|
-
filter: PivotFilter<TItem>,
|
|
164
|
-
): { min: number; max: number; values: PivotPrimitive[] } => {
|
|
165
|
-
const values: PivotPrimitive[] = [];
|
|
166
|
-
let min = Infinity;
|
|
167
|
-
let max = -Infinity;
|
|
168
|
-
|
|
169
|
-
data.forEach((item) => {
|
|
170
|
-
const value = filter.getValue(item);
|
|
171
|
-
values.push(value);
|
|
172
|
-
const numValue = typeof value === 'number' ? value : Number(value);
|
|
173
|
-
if (!Number.isNaN(numValue)) {
|
|
174
|
-
min = Math.min(min, numValue);
|
|
175
|
-
max = Math.max(max, numValue);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
min: min === Infinity ? 0 : min,
|
|
181
|
-
max: max === -Infinity ? 100 : max,
|
|
182
|
-
values,
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
export const groupData = <TItem extends object>(
|
|
187
|
-
data: TItem[],
|
|
188
|
-
dimension: PivotDimension<TItem>,
|
|
189
|
-
): PivotGroup<TItem>[] => {
|
|
190
|
-
const groups = new Map<string, PivotGroup<TItem>>();
|
|
191
|
-
|
|
192
|
-
// Cache the formatValue function if it exists
|
|
193
|
-
const formatValue = dimension.formatValue;
|
|
194
|
-
|
|
195
|
-
data.forEach((item) => {
|
|
196
|
-
const rawValue = dimension.getValue(item);
|
|
197
|
-
const key = toKey(rawValue) || 'default';
|
|
198
|
-
const group = groups.get(key);
|
|
199
|
-
|
|
200
|
-
if (group) {
|
|
201
|
-
group.items.push(item);
|
|
202
|
-
} else {
|
|
203
|
-
const label = formatValue ? formatValue(rawValue) : defaultFormat(rawValue);
|
|
204
|
-
groups.set(key, {
|
|
205
|
-
key,
|
|
206
|
-
label,
|
|
207
|
-
value: rawValue,
|
|
208
|
-
items: [item],
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const result = Array.from(groups.values());
|
|
214
|
-
return dimension.sort ? result.sort(dimension.sort) : result.sort((a, b) => a.label.localeCompare(b.label));
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Reorder items for a CSS Grid that displays bottom-up, left-to-right.
|
|
219
|
-
* CSS Grid fills top-to-bottom, left-to-right by default.
|
|
220
|
-
*
|
|
221
|
-
* Visual goal (items fill bottom row first, then row above, etc.):
|
|
222
|
-
* Grid Row 0 (top): [4] [5] [6] [ ] [ ] <- partial top row, empties on RIGHT
|
|
223
|
-
* Grid Row 1 (bottom): [0] [1] [2] [3] [4] <- full bottom row
|
|
224
|
-
*
|
|
225
|
-
* Returns array with nulls for empty cells that need placeholder rendering.
|
|
226
|
-
*/
|
|
227
|
-
export const reorderForBottomUpGrid = <TItem,>(items: TItem[], columns: number): (TItem | null)[] => {
|
|
228
|
-
if (items.length === 0) return [];
|
|
229
|
-
|
|
230
|
-
const totalRows = Math.ceil(items.length / columns);
|
|
231
|
-
const itemsInTopRow = items.length % columns || columns;
|
|
232
|
-
|
|
233
|
-
const result: (TItem | null)[] = [];
|
|
234
|
-
|
|
235
|
-
// Top row: items from the "overflow" portion, plus empty cells on the right
|
|
236
|
-
const topRowStartIndex = items.length - itemsInTopRow;
|
|
237
|
-
for (let col = 0; col < columns; col++) {
|
|
238
|
-
if (col < itemsInTopRow) {
|
|
239
|
-
result.push(items[topRowStartIndex + col]);
|
|
240
|
-
} else {
|
|
241
|
-
result.push(null); // Empty cell on right side of top row
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Remaining rows (from second-to-top down to bottom)
|
|
246
|
-
// These are full rows, containing items 0 to (items.length - itemsInTopRow - 1)
|
|
247
|
-
for (let row = 1; row < totalRows; row++) {
|
|
248
|
-
// This grid row corresponds to visual row (totalRows - 1 - row) from bottom
|
|
249
|
-
// Visual row 0 = bottom, visual row (totalRows-1) = top
|
|
250
|
-
const visualRowFromBottom = totalRows - 1 - row;
|
|
251
|
-
const rowStartIndex = visualRowFromBottom * columns;
|
|
252
|
-
|
|
253
|
-
for (let col = 0; col < columns; col++) {
|
|
254
|
-
result.push(items[rowStartIndex + col]);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return result;
|
|
259
|
-
};
|
|
@@ -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 React from 'react';
|
|
5
|
-
import * as Comp from './EventsView';
|
|
6
|
-
const Component: React.ComponentType<any> | undefined = (Comp as any).default || (Object.values(Comp)[0] as any);
|
|
7
|
-
|
|
8
|
-
export default { title: 'TimeMachine/EventsView', component: Component };
|
|
9
|
-
|
|
10
|
-
export const Default = () => (Component ? <Component /> : <div>Unable to render component</div>);
|
|
@@ -1,119 +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 React, { useEffect, useRef, useState } from 'react';
|
|
5
|
-
import { Timeline } from 'primereact/timeline';
|
|
6
|
-
import type { Event } from './types';
|
|
7
|
-
import { Properties } from './Properties';
|
|
8
|
-
import './EventsView.css';
|
|
9
|
-
|
|
10
|
-
interface EventsViewProps {
|
|
11
|
-
events: Event[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const EventsView: React.FC<EventsViewProps> = ({ events }) => {
|
|
15
|
-
// Use test data if no events provided
|
|
16
|
-
const displayEvents = events.length > 0 ? events : [];
|
|
17
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
18
|
-
const [canScrollUp, setCanScrollUp] = useState(false);
|
|
19
|
-
const [canScrollDown, setCanScrollDown] = useState(false);
|
|
20
|
-
|
|
21
|
-
const updateScrollState = () => {
|
|
22
|
-
const container = containerRef.current;
|
|
23
|
-
if (!container) {
|
|
24
|
-
setCanScrollUp(false);
|
|
25
|
-
setCanScrollDown(false);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
30
|
-
const epsilon = 1;
|
|
31
|
-
setCanScrollUp(scrollTop > epsilon);
|
|
32
|
-
setCanScrollDown(scrollTop + clientHeight < scrollHeight - epsilon);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
updateScrollState();
|
|
37
|
-
const container = containerRef.current;
|
|
38
|
-
if (!container) return;
|
|
39
|
-
|
|
40
|
-
const handleScroll = () => updateScrollState();
|
|
41
|
-
container.addEventListener('scroll', handleScroll);
|
|
42
|
-
return () => container.removeEventListener('scroll', handleScroll);
|
|
43
|
-
}, [displayEvents.length]);
|
|
44
|
-
|
|
45
|
-
const renderEventCard = (event: Event, position: 'left' | 'right') => {
|
|
46
|
-
const isLeft = position === 'left';
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<div className={`events-view-event-card events-view-event-card--${position}`}>
|
|
50
|
-
<div className={`events-view-event-header ${isLeft ? 'events-view-event-header--right' : ''}`}>
|
|
51
|
-
<h3 className={`events-view-event-name ${isLeft ? 'events-view-event-name--right' : ''}`}>{event.type}</h3>
|
|
52
|
-
<div className={`events-view-event-timestamp ${isLeft ? 'events-view-event-timestamp--right' : ''}`}>
|
|
53
|
-
{event.occurred.toLocaleString()}
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
<div className="events-view-event-properties">
|
|
57
|
-
<Properties data={event.content || {}} align="left" />
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const customContent = (event: Event, index: number) => {
|
|
64
|
-
// PrimeReact places even indices (0,2,4,...) on the right, odd on the left
|
|
65
|
-
const position = index % 2 === 0 ? 'right' : 'left';
|
|
66
|
-
return renderEventCard(event, position);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const customMarker = () => {
|
|
70
|
-
return (
|
|
71
|
-
<div className="events-view-marker">
|
|
72
|
-
<div className="events-view-marker-dot"></div>
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const scrollToTop = () => containerRef.current?.scrollTo({ top: 0, behavior: 'smooth' });
|
|
78
|
-
const scrollToBottom = () => {
|
|
79
|
-
const container = containerRef.current;
|
|
80
|
-
if (!container) return;
|
|
81
|
-
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<div className="events-view-container" ref={containerRef}>
|
|
86
|
-
{canScrollUp && (
|
|
87
|
-
<div className="events-view-scroll-button-wrapper events-view-scroll-button-wrapper--top">
|
|
88
|
-
<button
|
|
89
|
-
type="button"
|
|
90
|
-
className="events-view-scroll-button events-view-scroll-button--top"
|
|
91
|
-
onClick={scrollToTop}
|
|
92
|
-
aria-label="Scroll to top"
|
|
93
|
-
>
|
|
94
|
-
<i className="pi pi-arrow-up" />
|
|
95
|
-
</button>
|
|
96
|
-
</div>
|
|
97
|
-
)}
|
|
98
|
-
<Timeline
|
|
99
|
-
value={displayEvents}
|
|
100
|
-
align="alternate"
|
|
101
|
-
content={customContent}
|
|
102
|
-
marker={customMarker}
|
|
103
|
-
className="events-view-timeline"
|
|
104
|
-
/>
|
|
105
|
-
{canScrollDown && (
|
|
106
|
-
<div className="events-view-scroll-button-wrapper events-view-scroll-button-wrapper--bottom">
|
|
107
|
-
<button
|
|
108
|
-
type="button"
|
|
109
|
-
className="events-view-scroll-button events-view-scroll-button--bottom"
|
|
110
|
-
onClick={scrollToBottom}
|
|
111
|
-
aria-label="Scroll to bottom"
|
|
112
|
-
>
|
|
113
|
-
<i className="pi pi-arrow-down" />
|
|
114
|
-
</button>
|
|
115
|
-
</div>
|
|
116
|
-
)}
|
|
117
|
-
</div>
|
|
118
|
-
);
|
|
119
|
-
};
|
|
@@ -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 React from 'react';
|
|
5
|
-
import * as Comp from './Properties';
|
|
6
|
-
const Component: React.ComponentType<any> | undefined = (Comp as any).default || (Object.values(Comp)[0] as any);
|
|
7
|
-
|
|
8
|
-
export default { title: 'TimeMachine/Properties', component: Component };
|
|
9
|
-
|
|
10
|
-
export const Default = () => (Component ? <Component /> : <div>Unable to render component</div>);
|