doom-design-system 0.5.0 → 0.6.0
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/components/Accordion/Accordion.module.css +121 -124
- package/dist/components/ActionRow/ActionRow.module.css +25 -24
- package/dist/components/Alert/Alert.module.css +74 -76
- package/dist/components/Avatar/Avatar.module.css +66 -66
- package/dist/components/Badge/Badge.module.css +50 -48
- package/dist/components/Breadcrumbs/Breadcrumbs.module.css +32 -33
- package/dist/components/Button/Button.d.ts +2 -2
- package/dist/components/Button/Button.js +1 -1
- package/dist/components/Button/Button.module.css +150 -152
- package/dist/components/Card/Card.module.css +37 -39
- package/dist/components/Chart/Chart.module.css +213 -245
- package/dist/components/Chart/behaviors/DraggablePuck.d.ts +36 -0
- package/dist/components/Chart/behaviors/DraggablePuck.js +94 -0
- package/dist/components/Chart/behaviors/Markers.js +6 -4
- package/dist/components/Chart/behaviors/SelectionUpdate.js +2 -4
- package/dist/components/Chart/behaviors/index.d.ts +1 -1
- package/dist/components/Chart/behaviors/index.js +1 -1
- package/dist/components/Chart/engine/CoordinateSystem.d.ts +59 -0
- package/dist/components/Chart/engine/CoordinateSystem.js +126 -0
- package/dist/components/Chart/engine/Engine.d.ts +102 -0
- package/dist/components/Chart/engine/Engine.js +226 -0
- package/dist/components/Chart/engine/Scheduler.d.ts +59 -0
- package/dist/components/Chart/engine/Scheduler.js +139 -0
- package/dist/components/Chart/engine/SpatialMap.d.ts +114 -0
- package/dist/components/Chart/engine/SpatialMap.js +270 -0
- package/dist/components/Chart/engine/index.d.ts +13 -0
- package/dist/components/Chart/engine/index.js +9 -0
- package/dist/components/Chart/engine/types.d.ts +137 -0
- package/dist/components/Chart/engine/types.js +47 -0
- package/dist/components/Chart/hooks/useEngine.d.ts +43 -0
- package/dist/components/Chart/hooks/useEngine.js +128 -0
- package/dist/components/Chart/sensors/DataHoverSensor/DataHoverSensor.d.ts +17 -19
- package/dist/components/Chart/sensors/DataHoverSensor/DataHoverSensor.js +38 -51
- package/dist/components/Chart/sensors/DragSensor/DragSensor.d.ts +38 -0
- package/dist/components/Chart/sensors/DragSensor/DragSensor.js +105 -0
- package/dist/components/Chart/sensors/DragSensor/index.d.ts +2 -0
- package/dist/components/Chart/sensors/DragSensor/index.js +1 -0
- package/dist/components/Chart/sensors/{KeyboardSensor.d.ts → KeyboardSensor/KeyboardSensor.d.ts} +1 -1
- package/dist/components/Chart/sensors/KeyboardSensor/KeyboardSensor.js +86 -0
- package/dist/components/Chart/sensors/KeyboardSensor/index.d.ts +1 -0
- package/dist/components/Chart/sensors/KeyboardSensor/index.js +1 -0
- package/dist/components/Chart/sensors/{SelectionSensor.d.ts → SelectionSensor/SelectionSensor.d.ts} +2 -2
- package/dist/components/Chart/sensors/SelectionSensor/SelectionSensor.js +39 -0
- package/dist/components/Chart/sensors/SelectionSensor/index.d.ts +1 -0
- package/dist/components/Chart/sensors/SelectionSensor/index.js +1 -0
- package/dist/components/Chart/sensors/SensorManager/SensorManager.js +36 -41
- package/dist/components/Chart/sensors/index.d.ts +1 -0
- package/dist/components/Chart/sensors/index.js +3 -2
- package/dist/components/Chart/sensors/utils/search.d.ts +1 -1
- package/dist/components/Chart/sensors/utils/search.js +25 -4
- package/dist/components/Chart/state/store/chart.store.js +18 -0
- package/dist/components/Chart/state/store/slices/series.slice.d.ts +1 -0
- package/dist/components/Chart/state/store/slices/series.slice.js +3 -2
- package/dist/components/Chart/subcomponents/Axis/Axis.module.css +32 -33
- package/dist/components/Chart/subcomponents/BarSeries/BarSeries.js +6 -1
- package/dist/components/Chart/subcomponents/BarSeries/BarSeries.module.css +11 -9
- package/dist/components/Chart/subcomponents/Cursor/Cursor.js +8 -1
- package/dist/components/Chart/subcomponents/Cursor/Cursor.module.css +14 -13
- package/dist/components/Chart/subcomponents/CustomSeries/CustomSeries.js +4 -0
- package/dist/components/Chart/subcomponents/CustomSeries/CustomSeries.module.css +5 -3
- package/dist/components/Chart/subcomponents/Footer/Footer.module.css +5 -3
- package/dist/components/Chart/subcomponents/Grid/Grid.module.css +12 -11
- package/dist/components/Chart/subcomponents/Header/Header.module.css +8 -7
- package/dist/components/Chart/subcomponents/InteractionLayer/InteractionLayer.d.ts +4 -4
- package/dist/components/Chart/subcomponents/InteractionLayer/InteractionLayer.js +39 -76
- package/dist/components/Chart/subcomponents/Legend/Legend.module.css +30 -32
- package/dist/components/Chart/subcomponents/LineSeries/LineSeries.js +9 -3
- package/dist/components/Chart/subcomponents/LineSeries/LineSeries.module.css +21 -21
- package/dist/components/Chart/subcomponents/Root/Root.js +113 -7
- package/dist/components/Chart/subcomponents/Root/Root.module.css +70 -82
- package/dist/components/Chart/subcomponents/ScatterSeries/ScatterSeries.js +6 -1
- package/dist/components/Chart/subcomponents/ScatterSeries/ScatterSeries.module.css +7 -5
- package/dist/components/Chart/subcomponents/Series/Series.module.css +118 -128
- package/dist/components/Chart/subcomponents/SeriesPoint/SeriesPoint.module.css +10 -8
- package/dist/components/Chart/subcomponents/Tooltip/Tooltip.js +2 -3
- package/dist/components/Chart/subcomponents/Tooltip/Tooltip.module.css +52 -67
- package/dist/components/Chart/types/context.d.ts +9 -0
- package/dist/components/Chart/types/events.d.ts +5 -7
- package/dist/components/Chart/types/interaction.d.ts +24 -2
- package/dist/components/Chart/types/interaction.js +1 -0
- package/dist/components/Checkbox/Checkbox.module.css +57 -59
- package/dist/components/Chip/Chip.module.css +105 -115
- package/dist/components/Combobox/Combobox.d.ts +2 -1
- package/dist/components/Combobox/Combobox.js +2 -2
- package/dist/components/Combobox/Combobox.module.css +233 -210
- package/dist/components/CopyButton/CopyButton.module.css +84 -90
- package/dist/components/Drawer/Drawer.module.css +126 -145
- package/dist/components/Dropdown/Dropdown.d.ts +3 -1
- package/dist/components/Dropdown/Dropdown.js +3 -3
- package/dist/components/Dropdown/Dropdown.module.css +52 -32
- package/dist/components/FileUpload/FileUpload.js +24 -0
- package/dist/components/FileUpload/FileUpload.module.css +295 -313
- package/dist/components/Form/Form.module.css +35 -39
- package/dist/components/Image/Image.module.css +53 -54
- package/dist/components/Input/Input.d.ts +4 -2
- package/dist/components/Input/Input.js +2 -2
- package/dist/components/Input/Input.module.css +135 -119
- package/dist/components/Label/Label.module.css +17 -15
- package/dist/components/Layout/Layout.module.css +95 -111
- package/dist/components/Link/Link.module.css +67 -65
- package/dist/components/Modal/Modal.module.css +112 -132
- package/dist/components/Page/Page.module.css +21 -21
- package/dist/components/Pagination/Pagination.module.css +56 -56
- package/dist/components/Popover/Popover.module.css +17 -16
- package/dist/components/ProgressBar/ProgressBar.module.css +36 -37
- package/dist/components/RadioGroup/RadioGroup.module.css +74 -77
- package/dist/components/Select/Select.d.ts +2 -1
- package/dist/components/Select/Select.js +2 -2
- package/dist/components/Select/Select.module.css +133 -98
- package/dist/components/Sheet/Sheet.module.css +134 -154
- package/dist/components/Sidebar/Sidebar.module.css +72 -74
- package/dist/components/Sidebar/subcomponents/Footer/Footer.module.css +7 -5
- package/dist/components/Sidebar/subcomponents/Group/Group.module.css +80 -85
- package/dist/components/Sidebar/subcomponents/Header/Header.module.css +12 -10
- package/dist/components/Sidebar/subcomponents/Item/Item.module.css +54 -55
- package/dist/components/Sidebar/subcomponents/MobileOverlay/MobileOverlay.module.css +38 -38
- package/dist/components/Sidebar/subcomponents/MobileTrigger/MobileTrigger.module.css +5 -3
- package/dist/components/Sidebar/subcomponents/Nav/Nav.module.css +13 -11
- package/dist/components/Sidebar/subcomponents/Rail/Rail.module.css +62 -63
- package/dist/components/Sidebar/subcomponents/Section/Section.module.css +86 -91
- package/dist/components/Skeleton/Skeleton.module.css +28 -26
- package/dist/components/Slat/Slat.module.css +93 -94
- package/dist/components/Slider/Slider.module.css +116 -121
- package/dist/components/Spinner/Spinner.module.css +28 -27
- package/dist/components/SplitButton/SplitButton.d.ts +3 -1
- package/dist/components/SplitButton/SplitButton.js +2 -2
- package/dist/components/SplitButton/SplitButton.module.css +104 -87
- package/dist/components/Switch/Switch.module.css +64 -63
- package/dist/components/Table/FilterBuilder/FilterBuilder.module.css +36 -36
- package/dist/components/Table/FilterBuilder/FilterConditionRow.js +1 -1
- package/dist/components/Table/FilterBuilder/FilterConditionRow.module.css +21 -22
- package/dist/components/Table/FilterBuilder/FilterGroup.js +4 -4
- package/dist/components/Table/FilterBuilder/FilterGroup.module.css +355 -389
- package/dist/components/Table/FilterBuilder/FilterSheet.module.css +68 -71
- package/dist/components/Table/Table.d.ts +4 -2
- package/dist/components/Table/Table.js +50 -13
- package/dist/components/Table/Table.module.css +210 -188
- package/dist/components/Table/TableHeaderFilter.js +1 -1
- package/dist/components/Table/TableHeaderFilter.module.css +51 -57
- package/dist/components/Tabs/Tabs.module.css +79 -80
- package/dist/components/Text/Text.module.css +108 -131
- package/dist/components/Textarea/Textarea.d.ts +3 -1
- package/dist/components/Textarea/Textarea.js +2 -2
- package/dist/components/Textarea/Textarea.module.css +114 -94
- package/dist/components/Toast/Toast.module.css +82 -82
- package/dist/components/Tooltip/Tooltip.module.css +17 -16
- package/dist/styles/globals.css +1677 -1691
- package/dist/styles/palettes.d.ts +0 -5
- package/dist/styles/palettes.js +0 -8
- package/dist/styles/themes/definitions.d.ts +0 -8
- package/dist/styles/themes/definitions.js +117 -5
- package/dist/styles/types.d.ts +2 -0
- package/dist/styles/types.js +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/.agent/skills/doom/a2ui/a2ui-examples.md +0 -289
- package/.agent/skills/doom/a2ui/a2ui-principles.md +0 -49
- package/.agent/skills/doom/a2ui/a2ui-protocol.md +0 -191
- package/.agent/skills/doom/components/a2ui.md +0 -46
- package/.agent/skills/doom/components/accordion.md +0 -44
- package/.agent/skills/doom/components/actionrow.md +0 -33
- package/.agent/skills/doom/components/alert.md +0 -35
- package/.agent/skills/doom/components/avatar.md +0 -36
- package/.agent/skills/doom/components/badge.md +0 -29
- package/.agent/skills/doom/components/breadcrumbs.md +0 -33
- package/.agent/skills/doom/components/button.md +0 -43
- package/.agent/skills/doom/components/card.md +0 -41
- package/.agent/skills/doom/components/chart.md +0 -106
- package/.agent/skills/doom/components/checkbox.md +0 -38
- package/.agent/skills/doom/components/chip.md +0 -35
- package/.agent/skills/doom/components/combobox.md +0 -50
- package/.agent/skills/doom/components/copybutton.md +0 -41
- package/.agent/skills/doom/components/drawer.md +0 -69
- package/.agent/skills/doom/components/dropdown.md +0 -33
- package/.agent/skills/doom/components/fileupload.md +0 -49
- package/.agent/skills/doom/components/form.md +0 -55
- package/.agent/skills/doom/components/icon.md +0 -47
- package/.agent/skills/doom/components/image.md +0 -48
- package/.agent/skills/doom/components/input.md +0 -54
- package/.agent/skills/doom/components/label.md +0 -32
- package/.agent/skills/doom/components/layout.md +0 -92
- package/.agent/skills/doom/components/link.md +0 -39
- package/.agent/skills/doom/components/modal.md +0 -71
- package/.agent/skills/doom/components/page.md +0 -41
- package/.agent/skills/doom/components/pagination.md +0 -32
- package/.agent/skills/doom/components/popover.md +0 -45
- package/.agent/skills/doom/components/progressbar.md +0 -37
- package/.agent/skills/doom/components/radiogroup.md +0 -45
- package/.agent/skills/doom/components/select.md +0 -43
- package/.agent/skills/doom/components/sheet.md +0 -71
- package/.agent/skills/doom/components/sidebar.md +0 -92
- package/.agent/skills/doom/components/skeleton.md +0 -35
- package/.agent/skills/doom/components/slat.md +0 -46
- package/.agent/skills/doom/components/slider.md +0 -51
- package/.agent/skills/doom/components/spinner.md +0 -34
- package/.agent/skills/doom/components/splitbutton.md +0 -35
- package/.agent/skills/doom/components/switch.md +0 -40
- package/.agent/skills/doom/components/table.md +0 -82
- package/.agent/skills/doom/components/tabs.md +0 -65
- package/.agent/skills/doom/components/text.md +0 -42
- package/.agent/skills/doom/components/textarea.md +0 -46
- package/.agent/skills/doom/components/toast.md +0 -59
- package/.agent/skills/doom/components/tooltip.md +0 -35
- package/.agent/skills/doom/index.md +0 -167
- package/.agent/skills/doom/styles/aesthetic.md +0 -151
- package/.agent/skills/doom/styles/css-variables.md +0 -80
- package/.agent/skills/doom/styles/mixins.md +0 -97
- package/.agent/skills/doom/styles/tokens.md +0 -129
- package/.agent/skills/doom/styles/utilities.md +0 -84
- package/dist/components/Chart/sensors/KeyboardSensor.js +0 -82
- package/dist/components/Chart/sensors/SelectionSensor.js +0 -41
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler
|
|
3
|
+
*
|
|
4
|
+
* Manages the execution timing of engine tasks.
|
|
5
|
+
* Critical tasks (pointer down) run synchronously.
|
|
6
|
+
* Visual tasks (hover) are batched to requestAnimationFrame.
|
|
7
|
+
*/
|
|
8
|
+
import { TaskPriority } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* The Scheduler controls when events are dispatched to sensors.
|
|
11
|
+
* It uses a priority-based system to ensure critical interactions
|
|
12
|
+
* (like drag start) happen immediately, while visual updates
|
|
13
|
+
* (like hover highlights) are batched for performance.
|
|
14
|
+
*/
|
|
15
|
+
export class Scheduler {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.visualQueue = [];
|
|
18
|
+
this.idleQueue = [];
|
|
19
|
+
this.rafId = null;
|
|
20
|
+
this.handler = null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register the handler that will process events.
|
|
24
|
+
* In the full system, this is the EventBus.dispatch method.
|
|
25
|
+
*/
|
|
26
|
+
setHandler(handler) {
|
|
27
|
+
this.handler = handler;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Schedule a task for execution.
|
|
31
|
+
*
|
|
32
|
+
* @param priority - The priority level of the task
|
|
33
|
+
* @param event - The engine event to dispatch
|
|
34
|
+
*/
|
|
35
|
+
schedule(priority, event) {
|
|
36
|
+
const task = {
|
|
37
|
+
priority,
|
|
38
|
+
event,
|
|
39
|
+
timestamp: performance.now(),
|
|
40
|
+
};
|
|
41
|
+
switch (priority) {
|
|
42
|
+
case TaskPriority.CRITICAL:
|
|
43
|
+
// Critical tasks execute immediately (synchronously)
|
|
44
|
+
this.executeCritical(task);
|
|
45
|
+
break;
|
|
46
|
+
case TaskPriority.VISUAL:
|
|
47
|
+
// Visual tasks are batched to the next animation frame
|
|
48
|
+
this.visualQueue.push(task);
|
|
49
|
+
this.scheduleVisualFlush();
|
|
50
|
+
break;
|
|
51
|
+
case TaskPriority.IDLE:
|
|
52
|
+
// Idle tasks are deferred even further
|
|
53
|
+
this.idleQueue.push(task);
|
|
54
|
+
this.scheduleIdleFlush();
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Execute a critical task immediately.
|
|
60
|
+
* This is synchronous to allow preventDefault() on the original event.
|
|
61
|
+
*/
|
|
62
|
+
executeCritical(task) {
|
|
63
|
+
if (this.handler) {
|
|
64
|
+
this.handler(task.event);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Schedule a flush of the visual queue on the next animation frame.
|
|
69
|
+
*/
|
|
70
|
+
scheduleVisualFlush() {
|
|
71
|
+
if (this.rafId !== null) {
|
|
72
|
+
return;
|
|
73
|
+
} // Already scheduled
|
|
74
|
+
this.rafId = requestAnimationFrame(() => {
|
|
75
|
+
this.flushVisualQueue();
|
|
76
|
+
this.rafId = null;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Process all queued visual tasks.
|
|
81
|
+
* Only the most recent event per input ID is processed (coalescing).
|
|
82
|
+
*/
|
|
83
|
+
flushVisualQueue() {
|
|
84
|
+
if (!this.handler || this.visualQueue.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Coalesce: Group by input ID, keep only the latest
|
|
88
|
+
const latestByInputId = new Map();
|
|
89
|
+
for (const task of this.visualQueue) {
|
|
90
|
+
const existing = latestByInputId.get(task.event.signal.id);
|
|
91
|
+
if (!existing || task.timestamp > existing.timestamp) {
|
|
92
|
+
latestByInputId.set(task.event.signal.id, task);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Clear the queue
|
|
96
|
+
this.visualQueue = [];
|
|
97
|
+
// Dispatch coalesced events
|
|
98
|
+
for (const task of Array.from(latestByInputId.values())) {
|
|
99
|
+
this.handler(task.event);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Schedule a flush of the idle queue using requestIdleCallback.
|
|
104
|
+
*/
|
|
105
|
+
scheduleIdleFlush() {
|
|
106
|
+
if (typeof requestIdleCallback !== "undefined") {
|
|
107
|
+
requestIdleCallback(() => this.flushIdleQueue());
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// Fallback for browsers without requestIdleCallback
|
|
111
|
+
setTimeout(() => this.flushIdleQueue(), 50);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Process all queued idle tasks.
|
|
116
|
+
*/
|
|
117
|
+
flushIdleQueue() {
|
|
118
|
+
if (!this.handler || this.idleQueue.length === 0) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const tasks = this.idleQueue;
|
|
122
|
+
this.idleQueue = [];
|
|
123
|
+
for (const task of tasks) {
|
|
124
|
+
this.handler(task.event);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Cancel all pending tasks and clean up.
|
|
129
|
+
*/
|
|
130
|
+
dispose() {
|
|
131
|
+
if (this.rafId !== null) {
|
|
132
|
+
cancelAnimationFrame(this.rafId);
|
|
133
|
+
this.rafId = null;
|
|
134
|
+
}
|
|
135
|
+
this.visualQueue = [];
|
|
136
|
+
this.idleQueue = [];
|
|
137
|
+
this.handler = null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpatialMap
|
|
3
|
+
*
|
|
4
|
+
* The "Hybrid Radar" that finds interaction candidates.
|
|
5
|
+
* Combines DOM-based hit testing with Quadtree spatial queries.
|
|
6
|
+
*/
|
|
7
|
+
import { InteractionCandidate } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Standard data attributes used to tag interactive DOM elements.
|
|
10
|
+
* These are read by the SpatialMap during DOM-based hit testing.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CHART_DATA_ATTRS: {
|
|
13
|
+
readonly TYPE: "data-chart-type";
|
|
14
|
+
readonly SERIES_ID: "data-chart-series";
|
|
15
|
+
readonly INDEX: "data-chart-index";
|
|
16
|
+
readonly DRAGGABLE: "data-chart-draggable";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A point that has been indexed in the Quadtree.
|
|
20
|
+
* This is stored in memory for fast spatial queries.
|
|
21
|
+
*/
|
|
22
|
+
export interface IndexedPoint<T = unknown> {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
data: T;
|
|
26
|
+
seriesId: string;
|
|
27
|
+
dataIndex: number;
|
|
28
|
+
seriesColor?: string;
|
|
29
|
+
draggable?: boolean;
|
|
30
|
+
suppressMarker?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface SpatialMapOptions {
|
|
33
|
+
/**
|
|
34
|
+
* The radius (in pixels) for "magnetic" snapping to data points.
|
|
35
|
+
* Points within this radius will be returned as candidates.
|
|
36
|
+
* @default 20
|
|
37
|
+
*/
|
|
38
|
+
magneticRadius?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Whether to use DOM-based hit testing (elementsFromPoint).
|
|
41
|
+
* Disable for unit testing without a DOM.
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
useDomHitTesting?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* SpatialMap provides the "Hybrid Radar" for finding interaction targets.
|
|
48
|
+
*
|
|
49
|
+
* It combines two strategies:
|
|
50
|
+
* 1. **DOM Hit Testing (Broad Phase):** Uses `elementsFromPoint` to find
|
|
51
|
+
* large, complex shapes (bars, areas, labels) accurately.
|
|
52
|
+
* 2. **Quadtree (Fine Phase):** Uses a spatial tree to find nearby data
|
|
53
|
+
* points with "magnetic" snapping (great for scatter/line charts).
|
|
54
|
+
*/
|
|
55
|
+
export declare class SpatialMap<T = unknown> {
|
|
56
|
+
private tree;
|
|
57
|
+
private points;
|
|
58
|
+
private xBuckets;
|
|
59
|
+
private containerElement;
|
|
60
|
+
private options;
|
|
61
|
+
constructor(options?: SpatialMapOptions);
|
|
62
|
+
/**
|
|
63
|
+
* Set the container element for DOM-based hit testing.
|
|
64
|
+
* Elements outside this container are ignored.
|
|
65
|
+
*/
|
|
66
|
+
setContainer(element: Element | null): void;
|
|
67
|
+
/**
|
|
68
|
+
* Update the spatial index with new data points.
|
|
69
|
+
* Call this when data changes.
|
|
70
|
+
*
|
|
71
|
+
* @param points - The data points with their pixel coordinates
|
|
72
|
+
*/
|
|
73
|
+
updateIndex(points: IndexedPoint<T>[]): void;
|
|
74
|
+
/**
|
|
75
|
+
* Clear the spatial index.
|
|
76
|
+
*/
|
|
77
|
+
clear(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Find all indexed points sharing the given X coordinate (O(k) bucket lookup).
|
|
80
|
+
* Returns every point at that X regardless of Y distance — intended for
|
|
81
|
+
* multi-series vertical-slice hover.
|
|
82
|
+
*
|
|
83
|
+
* @param x - The exact X pixel coordinate (as stored in the index)
|
|
84
|
+
*/
|
|
85
|
+
findAllAtX(x: number): InteractionCandidate<T>[];
|
|
86
|
+
/**
|
|
87
|
+
* Find all interaction candidates near a point.
|
|
88
|
+
*
|
|
89
|
+
* @param x - X coordinate (relative to plot area for Quadtree)
|
|
90
|
+
* @param y - Y coordinate (relative to plot area for Quadtree)
|
|
91
|
+
* @param containerPoint - Optional container-relative coordinates for DOM hit testing
|
|
92
|
+
* @returns Sorted array of candidates (closest first)
|
|
93
|
+
*/
|
|
94
|
+
find(x: number, y: number, containerPoint?: {
|
|
95
|
+
x: number;
|
|
96
|
+
y: number;
|
|
97
|
+
}): InteractionCandidate<T>[];
|
|
98
|
+
/**
|
|
99
|
+
* Find candidates using DOM elementsFromPoint.
|
|
100
|
+
*/
|
|
101
|
+
private findFromDOM;
|
|
102
|
+
/**
|
|
103
|
+
* Convert a DOM element into an InteractionCandidate.
|
|
104
|
+
*/
|
|
105
|
+
private hydrateElement;
|
|
106
|
+
/**
|
|
107
|
+
* Find candidates using the Quadtree (nearby data points).
|
|
108
|
+
*/
|
|
109
|
+
private findFromTree;
|
|
110
|
+
/**
|
|
111
|
+
* Get the current options.
|
|
112
|
+
*/
|
|
113
|
+
getOptions(): Required<SpatialMapOptions>;
|
|
114
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpatialMap
|
|
3
|
+
*
|
|
4
|
+
* The "Hybrid Radar" that finds interaction candidates.
|
|
5
|
+
* Combines DOM-based hit testing with Quadtree spatial queries.
|
|
6
|
+
*/
|
|
7
|
+
import { quadtree } from "d3-quadtree";
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// DATA ATTRIBUTES (For DOM Element Tagging)
|
|
10
|
+
// =============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Standard data attributes used to tag interactive DOM elements.
|
|
13
|
+
* These are read by the SpatialMap during DOM-based hit testing.
|
|
14
|
+
*/
|
|
15
|
+
export const CHART_DATA_ATTRS = {
|
|
16
|
+
TYPE: "data-chart-type",
|
|
17
|
+
SERIES_ID: "data-chart-series",
|
|
18
|
+
INDEX: "data-chart-index",
|
|
19
|
+
DRAGGABLE: "data-chart-draggable",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* SpatialMap provides the "Hybrid Radar" for finding interaction targets.
|
|
23
|
+
*
|
|
24
|
+
* It combines two strategies:
|
|
25
|
+
* 1. **DOM Hit Testing (Broad Phase):** Uses `elementsFromPoint` to find
|
|
26
|
+
* large, complex shapes (bars, areas, labels) accurately.
|
|
27
|
+
* 2. **Quadtree (Fine Phase):** Uses a spatial tree to find nearby data
|
|
28
|
+
* points with "magnetic" snapping (great for scatter/line charts).
|
|
29
|
+
*/
|
|
30
|
+
export class SpatialMap {
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
this.tree = null;
|
|
34
|
+
this.points = [];
|
|
35
|
+
this.xBuckets = new Map();
|
|
36
|
+
this.containerElement = null;
|
|
37
|
+
this.options = {
|
|
38
|
+
magneticRadius: (_a = options.magneticRadius) !== null && _a !== void 0 ? _a : 40,
|
|
39
|
+
useDomHitTesting: (_b = options.useDomHitTesting) !== null && _b !== void 0 ? _b : true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Set the container element for DOM-based hit testing.
|
|
44
|
+
* Elements outside this container are ignored.
|
|
45
|
+
*/
|
|
46
|
+
setContainer(element) {
|
|
47
|
+
this.containerElement = element;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Update the spatial index with new data points.
|
|
51
|
+
* Call this when data changes.
|
|
52
|
+
*
|
|
53
|
+
* @param points - The data points with their pixel coordinates
|
|
54
|
+
*/
|
|
55
|
+
updateIndex(points) {
|
|
56
|
+
var _a;
|
|
57
|
+
this.points = points;
|
|
58
|
+
if (points.length === 0) {
|
|
59
|
+
this.tree = null;
|
|
60
|
+
this.xBuckets = new Map();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.tree = quadtree()
|
|
64
|
+
.x((d) => d.x)
|
|
65
|
+
.y((d) => d.y)
|
|
66
|
+
.addAll(points);
|
|
67
|
+
// Build the X-bucket index for O(k) vertical-slice lookups
|
|
68
|
+
this.xBuckets = new Map();
|
|
69
|
+
for (const p of points) {
|
|
70
|
+
const bucket = (_a = this.xBuckets.get(p.x)) !== null && _a !== void 0 ? _a : [];
|
|
71
|
+
bucket.push(p);
|
|
72
|
+
this.xBuckets.set(p.x, bucket);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Clear the spatial index.
|
|
77
|
+
*/
|
|
78
|
+
clear() {
|
|
79
|
+
this.points = [];
|
|
80
|
+
this.tree = null;
|
|
81
|
+
this.xBuckets = new Map();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Find all indexed points sharing the given X coordinate (O(k) bucket lookup).
|
|
85
|
+
* Returns every point at that X regardless of Y distance — intended for
|
|
86
|
+
* multi-series vertical-slice hover.
|
|
87
|
+
*
|
|
88
|
+
* @param x - The exact X pixel coordinate (as stored in the index)
|
|
89
|
+
*/
|
|
90
|
+
findAllAtX(x) {
|
|
91
|
+
var _a;
|
|
92
|
+
const bucket = (_a = this.xBuckets.get(x)) !== null && _a !== void 0 ? _a : [];
|
|
93
|
+
return bucket.map((p) => ({
|
|
94
|
+
type: "data-point",
|
|
95
|
+
data: p.data,
|
|
96
|
+
seriesId: p.seriesId,
|
|
97
|
+
dataIndex: p.dataIndex,
|
|
98
|
+
coordinate: { x: p.x, y: p.y },
|
|
99
|
+
distance: 0,
|
|
100
|
+
seriesColor: p.seriesColor,
|
|
101
|
+
draggable: p.draggable,
|
|
102
|
+
suppressMarker: p.suppressMarker,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Find all interaction candidates near a point.
|
|
107
|
+
*
|
|
108
|
+
* @param x - X coordinate (relative to plot area for Quadtree)
|
|
109
|
+
* @param y - Y coordinate (relative to plot area for Quadtree)
|
|
110
|
+
* @param containerPoint - Optional container-relative coordinates for DOM hit testing
|
|
111
|
+
* @returns Sorted array of candidates (closest first)
|
|
112
|
+
*/
|
|
113
|
+
find(x, y, containerPoint) {
|
|
114
|
+
const candidates = [];
|
|
115
|
+
// Phase 1: DOM Hit Testing (Broad Phase)
|
|
116
|
+
if (this.options.useDomHitTesting) {
|
|
117
|
+
// Use container coordinates if available, otherwise assume x/y are container-relative
|
|
118
|
+
// (This fallback retains backward compatibility but might be wrong if x/y are plot-relative)
|
|
119
|
+
const domX = containerPoint ? containerPoint.x : x;
|
|
120
|
+
const domY = containerPoint ? containerPoint.y : y;
|
|
121
|
+
const domCandidates = this.findFromDOM(domX, domY);
|
|
122
|
+
candidates.push(...domCandidates);
|
|
123
|
+
}
|
|
124
|
+
// Phase 2: Quadtree (Fine Phase)
|
|
125
|
+
const treeCandidates = this.findFromTree(x, y);
|
|
126
|
+
candidates.push(...treeCandidates);
|
|
127
|
+
candidates.sort((a, b) => {
|
|
128
|
+
var _a, _b;
|
|
129
|
+
const zDiff = ((_a = b.zIndex) !== null && _a !== void 0 ? _a : 0) - ((_b = a.zIndex) !== null && _b !== void 0 ? _b : 0);
|
|
130
|
+
if (zDiff !== 0) {
|
|
131
|
+
return zDiff;
|
|
132
|
+
}
|
|
133
|
+
return a.distance - b.distance;
|
|
134
|
+
});
|
|
135
|
+
return candidates;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Find candidates using DOM elementsFromPoint.
|
|
139
|
+
*/
|
|
140
|
+
findFromDOM(x, y) {
|
|
141
|
+
if (!this.containerElement) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
const rect = this.containerElement.getBoundingClientRect();
|
|
145
|
+
const viewportX = rect.left + x;
|
|
146
|
+
const viewportY = rect.top + y;
|
|
147
|
+
const elements = document.elementsFromPoint(viewportX, viewportY);
|
|
148
|
+
const candidates = [];
|
|
149
|
+
for (const element of elements) {
|
|
150
|
+
if (!this.containerElement.contains(element)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const chartType = element.getAttribute(CHART_DATA_ATTRS.TYPE);
|
|
154
|
+
if (!chartType) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const candidate = this.hydrateElement(element, chartType, x, y);
|
|
158
|
+
if (candidate) {
|
|
159
|
+
candidates.push(candidate);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return candidates;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Convert a DOM element into an InteractionCandidate.
|
|
166
|
+
*/
|
|
167
|
+
hydrateElement(element, type, pointerX, pointerY) {
|
|
168
|
+
var _a, _b;
|
|
169
|
+
const seriesId = (_a = element.getAttribute(CHART_DATA_ATTRS.SERIES_ID)) !== null && _a !== void 0 ? _a : undefined;
|
|
170
|
+
const indexStr = element.getAttribute(CHART_DATA_ATTRS.INDEX);
|
|
171
|
+
const draggable = element.getAttribute(CHART_DATA_ATTRS.DRAGGABLE) === "true";
|
|
172
|
+
const rect = element.getBoundingClientRect();
|
|
173
|
+
const containerRect = (_b = this.containerElement) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
|
|
174
|
+
if (!containerRect) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const elementCenterX = rect.left + rect.width / 2 - containerRect.left;
|
|
178
|
+
const elementCenterY = rect.top + rect.height / 2 - containerRect.top;
|
|
179
|
+
const distance = Math.hypot(pointerX - elementCenterX, pointerY - elementCenterY);
|
|
180
|
+
let data;
|
|
181
|
+
let dataIndex;
|
|
182
|
+
if (indexStr !== null && seriesId) {
|
|
183
|
+
dataIndex = parseInt(indexStr, 10);
|
|
184
|
+
const point = this.points.find((p) => p.seriesId === seriesId && p.dataIndex === dataIndex);
|
|
185
|
+
if (point) {
|
|
186
|
+
data = point.data;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Fall back to D3's __data__ binding for custom renders that don't index their points
|
|
190
|
+
if (!data) {
|
|
191
|
+
const d3Data = element.__data__;
|
|
192
|
+
if (d3Data && !Array.isArray(d3Data)) {
|
|
193
|
+
data = d3Data;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const zIndex = parseZIndex(element);
|
|
197
|
+
if (!data) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
type: type,
|
|
202
|
+
data,
|
|
203
|
+
seriesId,
|
|
204
|
+
dataIndex,
|
|
205
|
+
coordinate: { x: elementCenterX, y: elementCenterY },
|
|
206
|
+
distance,
|
|
207
|
+
element,
|
|
208
|
+
draggable,
|
|
209
|
+
zIndex,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Find candidates using the Quadtree (nearby data points).
|
|
214
|
+
*/
|
|
215
|
+
findFromTree(x, y) {
|
|
216
|
+
if (!this.tree) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
const candidates = [];
|
|
220
|
+
const radius = this.options.magneticRadius;
|
|
221
|
+
this.tree.visit((node, x0, y0, x1, y1) => {
|
|
222
|
+
if (x0 > x + radius ||
|
|
223
|
+
x1 < x - radius ||
|
|
224
|
+
y0 > y + radius ||
|
|
225
|
+
y1 < y - radius) {
|
|
226
|
+
return true; // Skip this branch
|
|
227
|
+
}
|
|
228
|
+
// Leaf nodes don't have the 'length' property defined
|
|
229
|
+
if (!("length" in node)) {
|
|
230
|
+
let current = node;
|
|
231
|
+
while (current) {
|
|
232
|
+
const point = current.data;
|
|
233
|
+
if (point) {
|
|
234
|
+
const distance = Math.hypot(point.x - x, point.y - y);
|
|
235
|
+
if (distance <= radius) {
|
|
236
|
+
candidates.push({
|
|
237
|
+
type: "data-point",
|
|
238
|
+
data: point.data,
|
|
239
|
+
seriesId: point.seriesId,
|
|
240
|
+
dataIndex: point.dataIndex,
|
|
241
|
+
coordinate: { x: point.x, y: point.y },
|
|
242
|
+
distance,
|
|
243
|
+
seriesColor: point.seriesColor,
|
|
244
|
+
draggable: point.draggable,
|
|
245
|
+
suppressMarker: point.suppressMarker,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
current = current.next;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return false; // Continue visiting
|
|
253
|
+
});
|
|
254
|
+
return candidates;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get the current options.
|
|
258
|
+
*/
|
|
259
|
+
getOptions() {
|
|
260
|
+
return Object.assign({}, this.options);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Parse the z-index from an element's computed style.
|
|
265
|
+
*/
|
|
266
|
+
function parseZIndex(element) {
|
|
267
|
+
const style = getComputedStyle(element);
|
|
268
|
+
const zIndex = parseInt(style.zIndex, 10);
|
|
269
|
+
return isNaN(zIndex) ? 0 : zIndex;
|
|
270
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine Module
|
|
3
|
+
*
|
|
4
|
+
* Public API for the Chart interaction engine.
|
|
5
|
+
*/
|
|
6
|
+
export type { EngineOptions } from "./Engine";
|
|
7
|
+
export { Engine } from "./Engine";
|
|
8
|
+
export type { TaskHandler } from "./Scheduler";
|
|
9
|
+
export { Scheduler } from "./Scheduler";
|
|
10
|
+
export type { IndexedPoint, SpatialMapOptions } from "./SpatialMap";
|
|
11
|
+
export { CHART_DATA_ATTRS, SpatialMap } from "./SpatialMap";
|
|
12
|
+
export type { CandidateType, EngineEvent, InputSignal, InteractionCandidate, ScheduledTask, TaskPriority, } from "./types";
|
|
13
|
+
export { InputAction, InputSource, TaskPriority as TaskPriorityEnum, } from "./types";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine Module
|
|
3
|
+
*
|
|
4
|
+
* Public API for the Chart interaction engine.
|
|
5
|
+
*/
|
|
6
|
+
export { Engine } from "./Engine.js";
|
|
7
|
+
export { Scheduler } from "./Scheduler.js";
|
|
8
|
+
export { CHART_DATA_ATTRS, SpatialMap } from "./SpatialMap.js";
|
|
9
|
+
export { InputAction, InputSource, TaskPriority as TaskPriorityEnum, } from "./types.js";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* These types define the core data structures for the Hyper-Engine architecture.
|
|
5
|
+
* They are intentionally decoupled from React and DOM to enable pure logic testing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* The source of an input signal. Used for multiplayer and debugging.
|
|
9
|
+
*/
|
|
10
|
+
export declare enum InputSource {
|
|
11
|
+
MOUSE = "mouse",
|
|
12
|
+
TOUCH = "touch",
|
|
13
|
+
KEYBOARD = "keyboard",
|
|
14
|
+
REMOTE = "remote"
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The type of input action being performed.
|
|
18
|
+
*/
|
|
19
|
+
export declare enum InputAction {
|
|
20
|
+
START = "START",
|
|
21
|
+
MOVE = "MOVE",
|
|
22
|
+
END = "END",
|
|
23
|
+
CANCEL = "CANCEL",
|
|
24
|
+
KEY = "KEY"
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* InputSignal is the normalized, DOM-independent representation of a user input.
|
|
28
|
+
* This is what the Engine processes - NOT raw PointerEvents.
|
|
29
|
+
*
|
|
30
|
+
* Think of this as the "packet" that travels through the engine pipeline.
|
|
31
|
+
*/
|
|
32
|
+
export interface InputSignal {
|
|
33
|
+
/** Unique identifier for this input stream (e.g., pointerId for multi-touch) */
|
|
34
|
+
id: number;
|
|
35
|
+
/** The action being performed */
|
|
36
|
+
action: InputAction;
|
|
37
|
+
/** Where this input came from */
|
|
38
|
+
source: InputSource;
|
|
39
|
+
/** X coordinate relative to the chart container (NOT the viewport) */
|
|
40
|
+
x: number;
|
|
41
|
+
/** Y coordinate relative to the chart container */
|
|
42
|
+
y: number;
|
|
43
|
+
/** High-resolution timestamp (performance.now()) for frame scheduling */
|
|
44
|
+
timestamp: number;
|
|
45
|
+
/** User identifier for multiplayer support. 'local' for the current user. */
|
|
46
|
+
userId: string;
|
|
47
|
+
/** Optional: For keyboard events, the key that was pressed */
|
|
48
|
+
key?: string;
|
|
49
|
+
/** Optional: Modifier keys state */
|
|
50
|
+
modifiers?: {
|
|
51
|
+
shift: boolean;
|
|
52
|
+
ctrl: boolean;
|
|
53
|
+
alt: boolean;
|
|
54
|
+
meta: boolean;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The type of element that was hit by the spatial query.
|
|
59
|
+
*/
|
|
60
|
+
export type CandidateType = "data-point" | "bar" | "area" | "label" | "axis" | "legend-item" | "custom";
|
|
61
|
+
/**
|
|
62
|
+
* InteractionCandidate is a "hydrated" hit result.
|
|
63
|
+
* It contains everything a Sensor needs to make a decision.
|
|
64
|
+
*/
|
|
65
|
+
export interface InteractionCandidate<T = unknown> {
|
|
66
|
+
/** What kind of element this is */
|
|
67
|
+
type: CandidateType;
|
|
68
|
+
/** The underlying data object (if applicable) */
|
|
69
|
+
data?: T;
|
|
70
|
+
/** The series this belongs to (if applicable) */
|
|
71
|
+
seriesId?: string;
|
|
72
|
+
/** The index within the series (for data points) */
|
|
73
|
+
dataIndex?: number;
|
|
74
|
+
/** The color of the series (for styling behaviors) */
|
|
75
|
+
seriesColor?: string;
|
|
76
|
+
/** The pixel coordinates of this candidate (for snapping) */
|
|
77
|
+
coordinate: {
|
|
78
|
+
x: number;
|
|
79
|
+
y: number;
|
|
80
|
+
};
|
|
81
|
+
/** Distance from the pointer (for sorting by proximity) */
|
|
82
|
+
distance: number;
|
|
83
|
+
/** The DOM element that was hit (for DOM-sourced candidates) */
|
|
84
|
+
element?: Element;
|
|
85
|
+
/** Whether this candidate is draggable */
|
|
86
|
+
draggable?: boolean;
|
|
87
|
+
/** Whether this candidate should suppress marker rendering */
|
|
88
|
+
suppressMarker?: boolean;
|
|
89
|
+
/** Z-index for layering (higher = on top) */
|
|
90
|
+
zIndex?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* EngineEvent is the fully processed event that gets dispatched to Sensors.
|
|
94
|
+
* It combines the raw input with the spatial query results.
|
|
95
|
+
*/
|
|
96
|
+
export interface EngineEvent<T = unknown> {
|
|
97
|
+
/** The normalized input that triggered this event */
|
|
98
|
+
signal: InputSignal;
|
|
99
|
+
/** All candidates found by the spatial query, sorted by relevance */
|
|
100
|
+
candidates: InteractionCandidate<T>[];
|
|
101
|
+
/** The primary (closest/most relevant) candidate, if any */
|
|
102
|
+
primaryCandidate?: InteractionCandidate<T>;
|
|
103
|
+
/**
|
|
104
|
+
* All indexed points sharing the primary candidate's X position, regardless
|
|
105
|
+
* of Y distance. Pre-computed via an O(k) bucket lookup (k = number of series
|
|
106
|
+
* at that X). Always an array; empty when there is no primary candidate.
|
|
107
|
+
* Intended for multi-series vertical-slice hover.
|
|
108
|
+
*/
|
|
109
|
+
sliceCandidates: InteractionCandidate<T>[];
|
|
110
|
+
/** X coordinate relative to the plot area (after margins) */
|
|
111
|
+
chartX: number;
|
|
112
|
+
/** Y coordinate relative to the plot area */
|
|
113
|
+
chartY: number;
|
|
114
|
+
/** Whether the pointer is within the plot area bounds */
|
|
115
|
+
isWithinPlot: boolean;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Priority levels for the scheduler.
|
|
119
|
+
* Critical tasks run synchronously to enable preventDefault().
|
|
120
|
+
* Visual tasks are batched to requestAnimationFrame.
|
|
121
|
+
*/
|
|
122
|
+
export declare enum TaskPriority {
|
|
123
|
+
/** Must run immediately (pointer down, to enable preventDefault) */
|
|
124
|
+
CRITICAL = 0,
|
|
125
|
+
/** Can be batched to next frame (hover updates, tooltip positioning) */
|
|
126
|
+
VISUAL = 1,
|
|
127
|
+
/** Low priority, can be deferred (analytics, logging) */
|
|
128
|
+
IDLE = 2
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A task to be scheduled by the engine.
|
|
132
|
+
*/
|
|
133
|
+
export interface ScheduledTask<T = unknown> {
|
|
134
|
+
priority: TaskPriority;
|
|
135
|
+
event: EngineEvent<T>;
|
|
136
|
+
timestamp: number;
|
|
137
|
+
}
|