@cratis/components 0.1.9 → 0.1.10

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 (101) hide show
  1. package/dist/cjs/PivotViewer/PivotViewer.css +1258 -0
  2. package/dist/cjs/PivotViewer/components/Spinner.css +77 -0
  3. package/dist/cjs/TimeMachine/EventsView.css +213 -0
  4. package/dist/cjs/TimeMachine/TimeMachine.css +567 -0
  5. package/dist/esm/PivotViewer/PivotViewer.css +1258 -0
  6. package/dist/esm/PivotViewer/components/Spinner.css +77 -0
  7. package/dist/esm/TimeMachine/EventsView.css +213 -0
  8. package/dist/esm/TimeMachine/TimeMachine.css +567 -0
  9. package/package.json +3 -4
  10. package/.storybook/main.ts +0 -24
  11. package/CommandDialog/CommandDialog.stories.tsx +0 -25
  12. package/CommandDialog/CommandDialog.tsx +0 -161
  13. package/CommandDialog/index.ts +0 -4
  14. package/CommandForm/CommandForm.stories.tsx +0 -24
  15. package/CommandForm/CommandForm.tsx +0 -266
  16. package/CommandForm/CommandFormField.tsx +0 -27
  17. package/CommandForm/CommandFormFields.tsx +0 -142
  18. package/CommandForm/DatePickerField.tsx +0 -57
  19. package/CommandForm/DropdownField.tsx +0 -65
  20. package/CommandForm/InputTextField.tsx +0 -62
  21. package/CommandForm/SliderField.tsx +0 -68
  22. package/CommandForm/index.ts +0 -10
  23. package/Common/ErrorBoundary.stories.tsx +0 -10
  24. package/Common/ErrorBoundary.tsx +0 -41
  25. package/Common/FormElement.stories.tsx +0 -10
  26. package/Common/FormElement.tsx +0 -20
  27. package/Common/Page.stories.tsx +0 -10
  28. package/Common/Page.tsx +0 -21
  29. package/Common/index.ts +0 -6
  30. package/DataPage/DataPage.stories.tsx +0 -10
  31. package/DataPage/DataPage.tsx +0 -191
  32. package/DataPage/index.ts +0 -4
  33. package/DataTables/DataTableForObservableQuery.stories.tsx +0 -10
  34. package/DataTables/DataTableForObservableQuery.tsx +0 -97
  35. package/DataTables/DataTableForQuery.stories.tsx +0 -10
  36. package/DataTables/DataTableForQuery.tsx +0 -97
  37. package/DataTables/index.ts +0 -5
  38. package/Dialogs/BusyIndicatorDialog.stories.tsx +0 -26
  39. package/Dialogs/BusyIndicatorDialog.tsx +0 -26
  40. package/Dialogs/ConfirmationDialog.stories.tsx +0 -36
  41. package/Dialogs/ConfirmationDialog.tsx +0 -75
  42. package/Dialogs/index.ts +0 -5
  43. package/Dropdown/Dropdown.tsx +0 -23
  44. package/Dropdown/index.ts +0 -4
  45. package/PivotViewer/PivotViewer.stories.tsx +0 -24
  46. package/PivotViewer/PivotViewer.tsx +0 -791
  47. package/PivotViewer/components/AxisLabels.tsx +0 -69
  48. package/PivotViewer/components/DetailPanel.tsx +0 -108
  49. package/PivotViewer/components/FilterPanel.tsx +0 -189
  50. package/PivotViewer/components/FilterPanelContainer.tsx +0 -10
  51. package/PivotViewer/components/PivotCanvas.tsx +0 -660
  52. package/PivotViewer/components/PivotViewerMain.tsx +0 -229
  53. package/PivotViewer/components/RangeHistogramFilter.tsx +0 -220
  54. package/PivotViewer/components/Spinner.tsx +0 -21
  55. package/PivotViewer/components/Toolbar.tsx +0 -130
  56. package/PivotViewer/components/ToolbarContainer.tsx +0 -10
  57. package/PivotViewer/components/index.ts +0 -12
  58. package/PivotViewer/components/pivot/animation.ts +0 -108
  59. package/PivotViewer/components/pivot/buckets.ts +0 -152
  60. package/PivotViewer/components/pivot/colorResolver.ts +0 -67
  61. package/PivotViewer/components/pivot/constants.ts +0 -46
  62. package/PivotViewer/components/pivot/sprites.ts +0 -265
  63. package/PivotViewer/components/pivot/visibility.ts +0 -319
  64. package/PivotViewer/constants.ts +0 -9
  65. package/PivotViewer/engine/layout.ts +0 -149
  66. package/PivotViewer/engine/pivot.worker.ts +0 -86
  67. package/PivotViewer/engine/store.ts +0 -437
  68. package/PivotViewer/engine/types.ts +0 -255
  69. package/PivotViewer/hooks/index.ts +0 -13
  70. package/PivotViewer/hooks/useContainerDimensions.ts +0 -45
  71. package/PivotViewer/hooks/useDimensionState.ts +0 -53
  72. package/PivotViewer/hooks/useFilterOptions.ts +0 -36
  73. package/PivotViewer/hooks/useFilterPanelDrag.ts +0 -49
  74. package/PivotViewer/hooks/useFilterState.ts +0 -106
  75. package/PivotViewer/hooks/useFilteredData.ts +0 -119
  76. package/PivotViewer/hooks/usePanning.ts +0 -163
  77. package/PivotViewer/hooks/usePivotEngine.ts +0 -252
  78. package/PivotViewer/hooks/useSelectedItem.ts +0 -402
  79. package/PivotViewer/hooks/useWheelZoom.ts +0 -114
  80. package/PivotViewer/hooks/useZoomState.ts +0 -34
  81. package/PivotViewer/index.ts +0 -7
  82. package/PivotViewer/types.ts +0 -59
  83. package/PivotViewer/utils/animations.ts +0 -249
  84. package/PivotViewer/utils/constants.ts +0 -20
  85. package/PivotViewer/utils/index.ts +0 -6
  86. package/PivotViewer/utils/selection.ts +0 -292
  87. package/PivotViewer/utils/utils.ts +0 -259
  88. package/TimeMachine/EventsView.stories.tsx +0 -10
  89. package/TimeMachine/EventsView.tsx +0 -119
  90. package/TimeMachine/Properties.stories.tsx +0 -10
  91. package/TimeMachine/Properties.tsx +0 -98
  92. package/TimeMachine/ReadModelView.stories.tsx +0 -10
  93. package/TimeMachine/ReadModelView.tsx +0 -143
  94. package/TimeMachine/TimeMachine.stories.tsx +0 -10
  95. package/TimeMachine/TimeMachine.tsx +0 -244
  96. package/TimeMachine/index.ts +0 -8
  97. package/TimeMachine/types.ts +0 -23
  98. package/global.d.ts +0 -11
  99. package/index.ts +0 -22
  100. package/useOverlayZIndex.ts +0 -32
  101. package/vite.config.ts +0 -80
@@ -1,108 +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 { CardSprite } from './constants';
5
- import type * as PIXI from 'pixi.js';
6
-
7
- export function updatePositions(sprites: Map<unknown, CardSprite>, isViewTransitionRef: { current: boolean }, animationSpeed = 0.15) {
8
- let isAnimating = false;
9
- const threshold = 0.5;
10
- const shouldAnimate = isViewTransitionRef.current;
11
- const now = Date.now();
12
- const DURATION = 600; // ms
13
-
14
- for (const sprite of sprites.values()) {
15
- // Handle explicit time-based animation (view transitions)
16
- if (sprite.animationStartTime !== undefined) {
17
- const elapsed = now - sprite.animationStartTime;
18
- const delay = sprite.animationDelay || 0;
19
-
20
- if (elapsed < delay) {
21
- isAnimating = true;
22
- continue;
23
- }
24
-
25
- const progress = Math.min(1, (elapsed - delay) / DURATION);
26
- // Ease out cubic: 1 - (1-t)^3
27
- const t = 1 - Math.pow(1 - progress, 3);
28
-
29
- if (sprite.startX !== undefined && sprite.startY !== undefined) {
30
- sprite.currentX = sprite.startX + (sprite.targetX - sprite.startX) * t;
31
- sprite.currentY = sprite.startY + (sprite.targetY - sprite.startY) * t;
32
- }
33
-
34
- sprite.container.position.set(sprite.currentX, sprite.currentY);
35
-
36
- if (progress < 1) {
37
- isAnimating = true;
38
- } else {
39
- // Animation finished, clean up
40
- sprite.currentX = sprite.targetX;
41
- sprite.currentY = sprite.targetY;
42
- delete sprite.animationStartTime;
43
- delete sprite.animationDelay;
44
- delete sprite.startX;
45
- delete sprite.startY;
46
- }
47
- continue;
48
- }
49
-
50
- const dx = sprite.targetX - sprite.currentX;
51
- const dy = sprite.targetY - sprite.currentY;
52
- const distance = Math.sqrt(dx * dx + dy * dy);
53
-
54
- if (shouldAnimate && distance > threshold) {
55
- sprite.currentX += dx * animationSpeed;
56
- sprite.currentY += dy * animationSpeed;
57
- sprite.container.position.set(sprite.currentX, sprite.currentY);
58
- isAnimating = true;
59
- } else if (distance > 0) {
60
- sprite.currentX = sprite.targetX;
61
- sprite.currentY = sprite.targetY;
62
- sprite.container.position.set(sprite.currentX, sprite.currentY);
63
- }
64
- }
65
-
66
- if (!isAnimating && isViewTransitionRef.current) {
67
- isViewTransitionRef.current = false;
68
- }
69
-
70
- return isAnimating;
71
- }
72
-
73
- export function startAnimationLoop(
74
- refs: {
75
- mountedRef: { current: boolean };
76
- appRef: { current: PIXI.Application | null };
77
- animationFrameRef: { current: number };
78
- isAnimatingRef: { current: boolean };
79
- needsRenderRef: { current: boolean };
80
- spritesRef: { current: Map<unknown, CardSprite> };
81
- isViewTransitionRef: { current: boolean };
82
- },
83
- animationSpeed = 0.15,
84
- ) {
85
- const { mountedRef, appRef, animationFrameRef, isAnimatingRef, needsRenderRef, spritesRef, isViewTransitionRef } = refs;
86
-
87
- const animate = () => {
88
- if (!mountedRef.current) return;
89
-
90
- const stillAnimating = updatePositions(spritesRef.current, isViewTransitionRef, animationSpeed);
91
-
92
- if (stillAnimating || needsRenderRef.current) {
93
- appRef.current?.renderer.render(appRef.current.stage);
94
- needsRenderRef.current = false;
95
- }
96
-
97
- if (stillAnimating) {
98
- animationFrameRef.current = requestAnimationFrame(animate);
99
- } else {
100
- isAnimatingRef.current = false;
101
- }
102
- };
103
-
104
- if (!isAnimatingRef.current) {
105
- isAnimatingRef.current = true;
106
- animate();
107
- }
108
- }
@@ -1,152 +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 { CardColors } from './constants';
6
- import type { GroupingResult, LayoutResult } from '../../engine/types';
7
-
8
- export function updateBucketBackgrounds(
9
- bucketsContainer: PIXI.Container | null,
10
- container: HTMLDivElement | null,
11
- grouping: GroupingResult,
12
- layout: LayoutResult,
13
- zoomLevel: number,
14
- cardColors: CardColors,
15
- viewMode: string,
16
- ) {
17
- if (!bucketsContainer) return;
18
-
19
- // keep parameter referenced to avoid unused param lint when callers pass zoomLevel
20
- void zoomLevel;
21
-
22
- if (!container || grouping.groups.length === 0 || viewMode === 'collection') {
23
- // If we shouldn't show anything, hide all existing backgrounds
24
- // We keep the highlight if it exists
25
- for (const child of bucketsContainer.children) {
26
- if ((child as any).name !== 'highlight') {
27
- child.visible = false;
28
- }
29
- }
30
- return;
31
- }
32
-
33
- // Use layout's total height if available so backgrounds match sprite positions
34
- const worldHeight = layout.totalHeight || container.clientHeight;
35
-
36
- // Get existing background graphics (excluding highlight)
37
- const backgroundGraphics = bucketsContainer.children.filter(c => (c as any).name !== 'highlight') as PIXI.Graphics[];
38
- let bgIndex = 0;
39
-
40
- // Instead of re-deriving bucket geometry from constants, compute bucket bounds
41
- // directly from the positions stored in the layout so backgrounds align exactly.
42
- for (let index = 0; index < grouping.groups.length; index++) {
43
- const group = grouping.groups[index];
44
- let minX = Infinity;
45
- let maxX = -Infinity;
46
-
47
- for (let j = 0; j < group.ids.length; j++) {
48
- const id = group.ids[j];
49
- const pos = layout.positions.get(id);
50
- if (pos) {
51
- minX = Math.min(minX, pos.x);
52
- maxX = Math.max(maxX, pos.x);
53
- }
54
- }
55
-
56
- if (minX === Infinity) continue;
57
-
58
- // Prefer explicit bucket width from layout when available
59
- const bucketWidths = layout.bucketWidths || [];
60
- const widthFromLayout = bucketWidths[index];
61
- const width = widthFromLayout && widthFromLayout > 0 ? widthFromLayout : (maxX - minX) || 0;
62
-
63
- if (index % 2 === 0 && width > 0) {
64
- let bg: PIXI.Graphics;
65
-
66
- if (bgIndex < backgroundGraphics.length) {
67
- bg = backgroundGraphics[bgIndex];
68
- bg.clear();
69
- } else {
70
- bg = new PIXI.Graphics();
71
- // Insert before highlight if it exists, otherwise at end
72
- const highlightIndex = bucketsContainer.children.findIndex(c => (c as any).name === 'highlight');
73
- if (highlightIndex >= 0) {
74
- bucketsContainer.addChildAt(bg, highlightIndex);
75
- } else {
76
- bucketsContainer.addChild(bg);
77
- }
78
- }
79
-
80
- bg.rect(minX, 0, width, worldHeight);
81
- bg.fill(cardColors.base);
82
- bg.alpha = 0.15;
83
- bg.visible = true;
84
- bgIndex++;
85
- }
86
- }
87
-
88
- // Hide unused background graphics
89
- for (let i = bgIndex; i < backgroundGraphics.length; i++) {
90
- backgroundGraphics[i].visible = false;
91
- }
92
- }
93
-
94
- export function updateHighlight(
95
- bucketsContainer: PIXI.Container | null,
96
- container: HTMLDivElement | null,
97
- grouping: GroupingResult,
98
- layout: LayoutResult,
99
- hoveredGroupIndex: number | null,
100
- cardWidth: number,
101
- zoomLevel: number,
102
- ) {
103
- if (!bucketsContainer || !container || grouping.groups.length === 0) return;
104
-
105
- // `zoomLevel` is part of the signature for future use; reference it
106
- // to avoid unused-parameter lint errors when callers pass it.
107
- void zoomLevel;
108
-
109
- let highlight = bucketsContainer.children.find(child => (child as any).name === 'highlight') as PIXI.Graphics;
110
-
111
- if (!highlight) {
112
- highlight = new PIXI.Graphics();
113
- (highlight as any).name = 'highlight';
114
- bucketsContainer.addChild(highlight);
115
- }
116
-
117
- highlight.clear();
118
-
119
- if (hoveredGroupIndex === null || hoveredGroupIndex < 0) {
120
- highlight.visible = false;
121
- return;
122
- }
123
-
124
- const group = grouping.groups[hoveredGroupIndex];
125
- if (!group || group.ids.length === 0) {
126
- highlight.visible = false;
127
- return;
128
- }
129
-
130
- let minX = Infinity;
131
- let maxX = -Infinity;
132
-
133
- for (let j = 0; j < group.ids.length; j++) {
134
- const id = group.ids[j];
135
- const pos = layout.positions.get(id);
136
- if (pos) {
137
- minX = Math.min(minX, pos.x);
138
- maxX = Math.max(maxX, pos.x + cardWidth);
139
- }
140
- }
141
-
142
- if (minX === Infinity) {
143
- highlight.visible = false;
144
- return;
145
- }
146
-
147
- const worldHeight = layout.totalHeight || container.clientHeight;
148
- highlight.rect(minX, 0, maxX - minX, worldHeight);
149
- highlight.fill(0xffffff);
150
- highlight.alpha = 0.05;
151
- highlight.visible = true;
152
- }
@@ -1,67 +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 { DEFAULT_COLORS, type CardColors } from './constants';
5
-
6
- export function createCssColorResolver() {
7
- const canvas = document.createElement('canvas');
8
- const ctx = canvas.getContext('2d');
9
-
10
- const parseHex = (val: string): number | null => {
11
- if (!val.startsWith('#')) return null;
12
- const hex = val.slice(1);
13
- const normalized = hex.length === 3
14
- ? hex.split('').map((c) => c + c).join('')
15
- : hex.length === 4
16
- ? hex.slice(0, 3).split('').map((c) => c + c).join('')
17
- : hex.length === 6
18
- ? hex
19
- : hex.length === 8
20
- ? hex.slice(0, 6)
21
- : null;
22
- return normalized ? Number.parseInt(normalized, 16) : null;
23
- };
24
-
25
- return (cssValue: string, fallback: number): number => {
26
- if (!ctx) {
27
- return fallback;
28
- }
29
- ctx.fillStyle = cssValue.trim();
30
- const resolved = ctx.fillStyle; // normalized by canvas
31
- const hex = parseHex(resolved);
32
- if (hex !== null && Number.isFinite(hex)) {
33
- return hex;
34
- }
35
-
36
- if (resolved.startsWith('rgb')) {
37
- const nums = resolved
38
- .replace(/rgba?\(/, '')
39
- .replace(/\)/, '')
40
- .split(',')
41
- .map((n) => Number.parseFloat(n.trim()))
42
- .filter((n) => !Number.isNaN(n));
43
- if (nums.length >= 3) {
44
- const [r, g, b] = nums;
45
- return ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
46
- }
47
- }
48
-
49
- return fallback;
50
- };
51
- }
52
-
53
- export function resolveCardColors(cssColorResolver: (s: string, f: number) => number): CardColors {
54
- const root = document.documentElement;
55
- const style = getComputedStyle(root);
56
- const getVar = (name: string, fallback: number) =>
57
- cssColorResolver(style.getPropertyValue(name) || name, fallback);
58
-
59
- return {
60
- base: getVar('--surface-b', DEFAULT_COLORS.base),
61
- mid: getVar('--surface-a', DEFAULT_COLORS.mid),
62
- gradient: getVar('--surface-ground', DEFAULT_COLORS.gradient),
63
- border: getVar('--surface-border', DEFAULT_COLORS.border),
64
- text: getVar('--text-color', DEFAULT_COLORS.text),
65
- textSecondary: getVar('--text-color-secondary', DEFAULT_COLORS.textSecondary),
66
- };
67
- }
@@ -1,46 +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 * as PIXI from 'pixi.js';
5
-
6
- export const ANIMATION_SPEED = 0.15;
7
- export const CARD_PADDING = 10;
8
- export const CARD_RADIUS = 12;
9
- export const CARD_GAP = 8; // Gap between cards (must match layout gap)
10
-
11
- export const DEFAULT_COLORS = {
12
- base: 0x1b2b2f,
13
- mid: 0x234445,
14
- gradient: 0x2f5b56,
15
- border: 0x5ad1a0,
16
- text: 0xffffff,
17
- textSecondary: 0x8899aa,
18
- };
19
-
20
- export type CardColors = typeof DEFAULT_COLORS;
21
-
22
- export interface CardSprite {
23
- container: PIXI.Container;
24
- graphics: PIXI.Graphics;
25
- titleText: PIXI.Text;
26
- labelsText: PIXI.Text;
27
- valuesText: PIXI.Text;
28
- itemId: number | string;
29
- targetX: number;
30
- targetY: number;
31
- currentX: number;
32
- currentY: number;
33
- // Animation state
34
- animationStartTime?: number;
35
- animationDelay?: number;
36
- startX?: number;
37
- startY?: number;
38
- // Cache state to avoid unnecessary redraws
39
- lastSelectedId?: string | number | null;
40
- lastCardColors?: CardColors;
41
- lastTitle?: string;
42
- lastLabels?: string;
43
- lastValues?: string;
44
- }
45
-
46
- export default {};
@@ -1,265 +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 { CARD_GAP, CARD_PADDING, CARD_RADIUS } from './constants';
6
- import type { CardSprite, CardColors } from './constants';
7
-
8
- const spritePool: CardSprite[] = [];
9
-
10
- export function createCardSprite<TItem extends object>(
11
- id: number | string,
12
- x: number,
13
- y: number,
14
- items: TItem[],
15
- onCardClick: (item: TItem, e: MouseEvent, id: number | string) => void,
16
- onPanStart: (e: MouseEvent) => void,
17
- cardWidth: number,
18
- cardHeight: number,
19
- cardColors: CardColors,
20
- ): CardSprite {
21
- if (spritePool.length > 0) {
22
- const sprite = spritePool.pop()!;
23
- sprite.container.visible = true;
24
- sprite.container.alpha = 1;
25
- sprite.container.position.set(x, y);
26
- sprite.itemId = id;
27
- sprite.targetX = x;
28
- sprite.targetY = y;
29
- sprite.currentX = x;
30
- sprite.currentY = y;
31
-
32
- // Reset animation state
33
- delete sprite.animationStartTime;
34
- delete sprite.animationDelay;
35
- delete sprite.startX;
36
- delete sprite.startY;
37
-
38
- // Reset cache
39
- sprite.lastSelectedId = null;
40
- sprite.lastCardColors = undefined;
41
- sprite.lastTitle = undefined;
42
- sprite.lastLabels = undefined;
43
- sprite.lastValues = undefined;
44
-
45
- // Update event context
46
- (sprite.container as any)._eventContext = { items, onCardClick, id };
47
-
48
- return sprite;
49
- }
50
-
51
- const container = new PIXI.Container();
52
- container.eventMode = 'static';
53
- container.cursor = 'pointer';
54
- container.position.set(x, y);
55
-
56
- // Store context for event handlers
57
- (container as any)._eventContext = { items, onCardClick, id };
58
-
59
- const graphics = new PIXI.Graphics();
60
-
61
- const actualWidth = cardWidth - CARD_GAP;
62
- const actualHeight = cardHeight - CARD_GAP;
63
- const offsetX = CARD_GAP / 2;
64
- const offsetY = CARD_GAP / 2;
65
-
66
- const gradient = new PIXI.FillGradient(0, offsetY, 0, offsetY + actualHeight);
67
- gradient.addColorStop(0, cardColors.mid);
68
- gradient.addColorStop(1, cardColors.base);
69
-
70
- graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, CARD_RADIUS);
71
- graphics.fill(gradient);
72
-
73
- container.addChild(graphics);
74
-
75
- const titleText = new PIXI.Text('', {
76
- fontSize: 13,
77
- fill: cardColors.text as any,
78
- fontWeight: '600',
79
- lineHeight: 18,
80
- wordWrap: false,
81
- } as any);
82
- titleText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING);
83
- container.addChild(titleText);
84
-
85
- const labelsText = new PIXI.Text('', {
86
- fontSize: 11,
87
- fill: cardColors.textSecondary as any,
88
- fontWeight: '400',
89
- lineHeight: 18,
90
- } as any);
91
- labelsText.position.set(offsetX + CARD_PADDING, offsetY + CARD_PADDING + 40);
92
- container.addChild(labelsText);
93
-
94
- const valuesText = new PIXI.Text('', {
95
- fontSize: 11,
96
- fill: cardColors.text as any,
97
- fontWeight: '500',
98
- lineHeight: 18,
99
- wordWrap: false,
100
- } as any);
101
- valuesText.position.set(offsetX + CARD_PADDING + 65, offsetY + CARD_PADDING + 40);
102
- container.addChild(valuesText);
103
-
104
- container.on('click', (e: PIXI.FederatedPointerEvent) => {
105
- e.stopPropagation();
106
- const ctx = (container as any)._eventContext as { items: any; onCardClick: (item: any, e: MouseEvent, id: number | string) => void; id: number | string };
107
- const itemsMap = ctx.items as Record<string, any>;
108
- const item = itemsMap[String(ctx.id)];
109
- if (item) {
110
- ctx.onCardClick(item, e.nativeEvent as MouseEvent, ctx.id);
111
- }
112
- });
113
-
114
- container.on('pointerdown', (e: PIXI.FederatedPointerEvent) => {
115
- e.stopPropagation();
116
- // onPanStart(e.nativeEvent as MouseEvent);
117
- });
118
-
119
- return {
120
- container,
121
- graphics,
122
- titleText,
123
- labelsText,
124
- valuesText,
125
- itemId: id,
126
- targetX: x,
127
- targetY: y,
128
- currentX: x,
129
- currentY: y,
130
- };
131
- }
132
-
133
- export function destroySprite(sprite: CardSprite) {
134
- if (sprite.container && sprite.container.parent) {
135
- sprite.container.parent.removeChild(sprite.container);
136
- }
137
- // Reset visibility to ensure it doesn't ghost if something goes wrong
138
- if (sprite.container) {
139
- sprite.container.visible = false;
140
- }
141
- spritePool.push(sprite);
142
- }
143
-
144
- export function clearSpritePool() {
145
- for (const sprite of spritePool) {
146
- try {
147
- sprite.graphics?.destroy();
148
- sprite.titleText?.destroy();
149
- sprite.labelsText?.destroy();
150
- sprite.valuesText?.destroy();
151
- sprite.container?.destroy();
152
- } catch (e) {
153
- void e;
154
- }
155
- }
156
- spritePool.length = 0;
157
- }
158
-
159
- export function updateCardContent<TItem extends object>(
160
- sprite: CardSprite,
161
- item: TItem,
162
- selectedId: string | number | null,
163
- cardWidth: number,
164
- cardHeight: number,
165
- cardColors: CardColors,
166
- ) {
167
- if (!item) return;
168
-
169
- const event = item as any;
170
- const eventType = event.type || event.name || event.title || 'Event';
171
-
172
- const timeStr = event.occurred ? new Date(event.occurred).toLocaleString('en-US', {
173
- month: '2-digit',
174
- day: '2-digit',
175
- year: 'numeric',
176
- hour: '2-digit',
177
- minute: '2-digit',
178
- hour12: false
179
- }).replace(',', '') : '';
180
-
181
- const correlation = event.correlationId || event.correlation || '';
182
- const correlationShort = correlation ? String(correlation).substring(0, 12) + '...' : '';
183
-
184
- const maxTitleLength = 20;
185
- const titleDisplay = eventType.length > maxTitleLength
186
- ? eventType.substring(0, maxTitleLength) + '...'
187
- : eventType;
188
-
189
- const maxTypeLength = 16;
190
- const typeDisplay = eventType.length > maxTypeLength
191
- ? eventType.substring(0, maxTypeLength) + '...'
192
- : eventType;
193
-
194
- const colors = cardColors;
195
- const labelsText = 'Type\nOccurred\nCorrelation';
196
- const valuesText = `${typeDisplay}\n${timeStr}\n${correlationShort}`;
197
- const colorsChanged = sprite.lastCardColors !== colors;
198
-
199
- if (sprite.lastTitle !== titleDisplay) {
200
- sprite.titleText.text = titleDisplay;
201
- sprite.lastTitle = titleDisplay;
202
- }
203
-
204
- if (sprite.lastLabels !== labelsText) {
205
- sprite.labelsText.text = labelsText;
206
- sprite.lastLabels = labelsText;
207
- }
208
-
209
- if (colorsChanged) {
210
- (sprite.labelsText.style as any).fill = colors.textSecondary as any;
211
- }
212
-
213
- if (sprite.lastValues !== valuesText) {
214
- sprite.valuesText.text = valuesText;
215
- sprite.lastValues = valuesText;
216
- }
217
-
218
- if (colorsChanged) {
219
- (sprite.valuesText.style as any).fill = colors.text as any;
220
- }
221
-
222
- sprite.titleText.visible = true;
223
- sprite.labelsText.visible = true;
224
- sprite.valuesText.visible = true;
225
-
226
- const isSelected = sprite.itemId === selectedId;
227
-
228
- // Only redraw graphics if selection state or colors changed
229
- if (sprite.lastSelectedId === selectedId && !colorsChanged && sprite.graphics) {
230
- return;
231
- }
232
-
233
- sprite.lastSelectedId = selectedId;
234
- sprite.lastCardColors = cardColors;
235
-
236
- if (sprite.graphics) {
237
- sprite.graphics.clear();
238
- } else {
239
- sprite.graphics = new PIXI.Graphics();
240
- sprite.container.addChildAt(sprite.graphics, 0);
241
- }
242
-
243
- const actualWidth = cardWidth - CARD_GAP;
244
- const actualHeight = cardHeight - CARD_GAP;
245
- const offsetX = CARD_GAP / 2;
246
- const offsetY = CARD_GAP / 2;
247
-
248
- const gradient = new PIXI.FillGradient(0, offsetY, 0, offsetY + actualHeight);
249
- if (isSelected) {
250
- gradient.addColorStop(0, colors.gradient);
251
- gradient.addColorStop(1, colors.mid);
252
- } else {
253
- gradient.addColorStop(0, colors.mid);
254
- gradient.addColorStop(1, colors.base);
255
- }
256
-
257
- sprite.graphics.roundRect(offsetX, offsetY, actualWidth, actualHeight, CARD_RADIUS);
258
- sprite.graphics.fill(gradient);
259
-
260
- if (isSelected) {
261
- sprite.graphics.stroke({ width: 2, color: colors.border });
262
- } else {
263
- sprite.graphics.stroke({ width: 1, color: colors.border, alpha: 0.35 });
264
- }
265
- }