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.
Files changed (211) hide show
  1. package/dist/components/Accordion/Accordion.module.css +121 -124
  2. package/dist/components/ActionRow/ActionRow.module.css +25 -24
  3. package/dist/components/Alert/Alert.module.css +74 -76
  4. package/dist/components/Avatar/Avatar.module.css +66 -66
  5. package/dist/components/Badge/Badge.module.css +50 -48
  6. package/dist/components/Breadcrumbs/Breadcrumbs.module.css +32 -33
  7. package/dist/components/Button/Button.d.ts +2 -2
  8. package/dist/components/Button/Button.js +1 -1
  9. package/dist/components/Button/Button.module.css +150 -152
  10. package/dist/components/Card/Card.module.css +37 -39
  11. package/dist/components/Chart/Chart.module.css +213 -245
  12. package/dist/components/Chart/behaviors/DraggablePuck.d.ts +36 -0
  13. package/dist/components/Chart/behaviors/DraggablePuck.js +94 -0
  14. package/dist/components/Chart/behaviors/Markers.js +6 -4
  15. package/dist/components/Chart/behaviors/SelectionUpdate.js +2 -4
  16. package/dist/components/Chart/behaviors/index.d.ts +1 -1
  17. package/dist/components/Chart/behaviors/index.js +1 -1
  18. package/dist/components/Chart/engine/CoordinateSystem.d.ts +59 -0
  19. package/dist/components/Chart/engine/CoordinateSystem.js +126 -0
  20. package/dist/components/Chart/engine/Engine.d.ts +102 -0
  21. package/dist/components/Chart/engine/Engine.js +226 -0
  22. package/dist/components/Chart/engine/Scheduler.d.ts +59 -0
  23. package/dist/components/Chart/engine/Scheduler.js +139 -0
  24. package/dist/components/Chart/engine/SpatialMap.d.ts +114 -0
  25. package/dist/components/Chart/engine/SpatialMap.js +270 -0
  26. package/dist/components/Chart/engine/index.d.ts +13 -0
  27. package/dist/components/Chart/engine/index.js +9 -0
  28. package/dist/components/Chart/engine/types.d.ts +137 -0
  29. package/dist/components/Chart/engine/types.js +47 -0
  30. package/dist/components/Chart/hooks/useEngine.d.ts +43 -0
  31. package/dist/components/Chart/hooks/useEngine.js +128 -0
  32. package/dist/components/Chart/sensors/DataHoverSensor/DataHoverSensor.d.ts +17 -19
  33. package/dist/components/Chart/sensors/DataHoverSensor/DataHoverSensor.js +38 -51
  34. package/dist/components/Chart/sensors/DragSensor/DragSensor.d.ts +38 -0
  35. package/dist/components/Chart/sensors/DragSensor/DragSensor.js +105 -0
  36. package/dist/components/Chart/sensors/DragSensor/index.d.ts +2 -0
  37. package/dist/components/Chart/sensors/DragSensor/index.js +1 -0
  38. package/dist/components/Chart/sensors/{KeyboardSensor.d.ts → KeyboardSensor/KeyboardSensor.d.ts} +1 -1
  39. package/dist/components/Chart/sensors/KeyboardSensor/KeyboardSensor.js +86 -0
  40. package/dist/components/Chart/sensors/KeyboardSensor/index.d.ts +1 -0
  41. package/dist/components/Chart/sensors/KeyboardSensor/index.js +1 -0
  42. package/dist/components/Chart/sensors/{SelectionSensor.d.ts → SelectionSensor/SelectionSensor.d.ts} +2 -2
  43. package/dist/components/Chart/sensors/SelectionSensor/SelectionSensor.js +39 -0
  44. package/dist/components/Chart/sensors/SelectionSensor/index.d.ts +1 -0
  45. package/dist/components/Chart/sensors/SelectionSensor/index.js +1 -0
  46. package/dist/components/Chart/sensors/SensorManager/SensorManager.js +36 -41
  47. package/dist/components/Chart/sensors/index.d.ts +1 -0
  48. package/dist/components/Chart/sensors/index.js +3 -2
  49. package/dist/components/Chart/sensors/utils/search.d.ts +1 -1
  50. package/dist/components/Chart/sensors/utils/search.js +25 -4
  51. package/dist/components/Chart/state/store/chart.store.js +18 -0
  52. package/dist/components/Chart/state/store/slices/series.slice.d.ts +1 -0
  53. package/dist/components/Chart/state/store/slices/series.slice.js +3 -2
  54. package/dist/components/Chart/subcomponents/Axis/Axis.module.css +32 -33
  55. package/dist/components/Chart/subcomponents/BarSeries/BarSeries.js +6 -1
  56. package/dist/components/Chart/subcomponents/BarSeries/BarSeries.module.css +11 -9
  57. package/dist/components/Chart/subcomponents/Cursor/Cursor.js +8 -1
  58. package/dist/components/Chart/subcomponents/Cursor/Cursor.module.css +14 -13
  59. package/dist/components/Chart/subcomponents/CustomSeries/CustomSeries.js +4 -0
  60. package/dist/components/Chart/subcomponents/CustomSeries/CustomSeries.module.css +5 -3
  61. package/dist/components/Chart/subcomponents/Footer/Footer.module.css +5 -3
  62. package/dist/components/Chart/subcomponents/Grid/Grid.module.css +12 -11
  63. package/dist/components/Chart/subcomponents/Header/Header.module.css +8 -7
  64. package/dist/components/Chart/subcomponents/InteractionLayer/InteractionLayer.d.ts +4 -4
  65. package/dist/components/Chart/subcomponents/InteractionLayer/InteractionLayer.js +39 -76
  66. package/dist/components/Chart/subcomponents/Legend/Legend.module.css +30 -32
  67. package/dist/components/Chart/subcomponents/LineSeries/LineSeries.js +9 -3
  68. package/dist/components/Chart/subcomponents/LineSeries/LineSeries.module.css +21 -21
  69. package/dist/components/Chart/subcomponents/Root/Root.js +113 -7
  70. package/dist/components/Chart/subcomponents/Root/Root.module.css +70 -82
  71. package/dist/components/Chart/subcomponents/ScatterSeries/ScatterSeries.js +6 -1
  72. package/dist/components/Chart/subcomponents/ScatterSeries/ScatterSeries.module.css +7 -5
  73. package/dist/components/Chart/subcomponents/Series/Series.module.css +118 -128
  74. package/dist/components/Chart/subcomponents/SeriesPoint/SeriesPoint.module.css +10 -8
  75. package/dist/components/Chart/subcomponents/Tooltip/Tooltip.js +2 -3
  76. package/dist/components/Chart/subcomponents/Tooltip/Tooltip.module.css +52 -67
  77. package/dist/components/Chart/types/context.d.ts +9 -0
  78. package/dist/components/Chart/types/events.d.ts +5 -7
  79. package/dist/components/Chart/types/interaction.d.ts +24 -2
  80. package/dist/components/Chart/types/interaction.js +1 -0
  81. package/dist/components/Checkbox/Checkbox.module.css +57 -59
  82. package/dist/components/Chip/Chip.module.css +105 -115
  83. package/dist/components/Combobox/Combobox.d.ts +2 -1
  84. package/dist/components/Combobox/Combobox.js +2 -2
  85. package/dist/components/Combobox/Combobox.module.css +233 -210
  86. package/dist/components/CopyButton/CopyButton.module.css +84 -90
  87. package/dist/components/Drawer/Drawer.module.css +126 -145
  88. package/dist/components/Dropdown/Dropdown.d.ts +3 -1
  89. package/dist/components/Dropdown/Dropdown.js +3 -3
  90. package/dist/components/Dropdown/Dropdown.module.css +52 -32
  91. package/dist/components/FileUpload/FileUpload.js +24 -0
  92. package/dist/components/FileUpload/FileUpload.module.css +295 -313
  93. package/dist/components/Form/Form.module.css +35 -39
  94. package/dist/components/Image/Image.module.css +53 -54
  95. package/dist/components/Input/Input.d.ts +4 -2
  96. package/dist/components/Input/Input.js +2 -2
  97. package/dist/components/Input/Input.module.css +135 -119
  98. package/dist/components/Label/Label.module.css +17 -15
  99. package/dist/components/Layout/Layout.module.css +95 -111
  100. package/dist/components/Link/Link.module.css +67 -65
  101. package/dist/components/Modal/Modal.module.css +112 -132
  102. package/dist/components/Page/Page.module.css +21 -21
  103. package/dist/components/Pagination/Pagination.module.css +56 -56
  104. package/dist/components/Popover/Popover.module.css +17 -16
  105. package/dist/components/ProgressBar/ProgressBar.module.css +36 -37
  106. package/dist/components/RadioGroup/RadioGroup.module.css +74 -77
  107. package/dist/components/Select/Select.d.ts +2 -1
  108. package/dist/components/Select/Select.js +2 -2
  109. package/dist/components/Select/Select.module.css +133 -98
  110. package/dist/components/Sheet/Sheet.module.css +134 -154
  111. package/dist/components/Sidebar/Sidebar.module.css +72 -74
  112. package/dist/components/Sidebar/subcomponents/Footer/Footer.module.css +7 -5
  113. package/dist/components/Sidebar/subcomponents/Group/Group.module.css +80 -85
  114. package/dist/components/Sidebar/subcomponents/Header/Header.module.css +12 -10
  115. package/dist/components/Sidebar/subcomponents/Item/Item.module.css +54 -55
  116. package/dist/components/Sidebar/subcomponents/MobileOverlay/MobileOverlay.module.css +38 -38
  117. package/dist/components/Sidebar/subcomponents/MobileTrigger/MobileTrigger.module.css +5 -3
  118. package/dist/components/Sidebar/subcomponents/Nav/Nav.module.css +13 -11
  119. package/dist/components/Sidebar/subcomponents/Rail/Rail.module.css +62 -63
  120. package/dist/components/Sidebar/subcomponents/Section/Section.module.css +86 -91
  121. package/dist/components/Skeleton/Skeleton.module.css +28 -26
  122. package/dist/components/Slat/Slat.module.css +93 -94
  123. package/dist/components/Slider/Slider.module.css +116 -121
  124. package/dist/components/Spinner/Spinner.module.css +28 -27
  125. package/dist/components/SplitButton/SplitButton.d.ts +3 -1
  126. package/dist/components/SplitButton/SplitButton.js +2 -2
  127. package/dist/components/SplitButton/SplitButton.module.css +104 -87
  128. package/dist/components/Switch/Switch.module.css +64 -63
  129. package/dist/components/Table/FilterBuilder/FilterBuilder.module.css +36 -36
  130. package/dist/components/Table/FilterBuilder/FilterConditionRow.js +1 -1
  131. package/dist/components/Table/FilterBuilder/FilterConditionRow.module.css +21 -22
  132. package/dist/components/Table/FilterBuilder/FilterGroup.js +4 -4
  133. package/dist/components/Table/FilterBuilder/FilterGroup.module.css +355 -389
  134. package/dist/components/Table/FilterBuilder/FilterSheet.module.css +68 -71
  135. package/dist/components/Table/Table.d.ts +4 -2
  136. package/dist/components/Table/Table.js +50 -13
  137. package/dist/components/Table/Table.module.css +210 -188
  138. package/dist/components/Table/TableHeaderFilter.js +1 -1
  139. package/dist/components/Table/TableHeaderFilter.module.css +51 -57
  140. package/dist/components/Tabs/Tabs.module.css +79 -80
  141. package/dist/components/Text/Text.module.css +108 -131
  142. package/dist/components/Textarea/Textarea.d.ts +3 -1
  143. package/dist/components/Textarea/Textarea.js +2 -2
  144. package/dist/components/Textarea/Textarea.module.css +114 -94
  145. package/dist/components/Toast/Toast.module.css +82 -82
  146. package/dist/components/Tooltip/Tooltip.module.css +17 -16
  147. package/dist/styles/globals.css +1677 -1691
  148. package/dist/styles/palettes.d.ts +0 -5
  149. package/dist/styles/palettes.js +0 -8
  150. package/dist/styles/themes/definitions.d.ts +0 -8
  151. package/dist/styles/themes/definitions.js +117 -5
  152. package/dist/styles/types.d.ts +2 -0
  153. package/dist/styles/types.js +1 -0
  154. package/dist/tsconfig.build.tsbuildinfo +1 -1
  155. package/package.json +4 -4
  156. package/.agent/skills/doom/a2ui/a2ui-examples.md +0 -289
  157. package/.agent/skills/doom/a2ui/a2ui-principles.md +0 -49
  158. package/.agent/skills/doom/a2ui/a2ui-protocol.md +0 -191
  159. package/.agent/skills/doom/components/a2ui.md +0 -46
  160. package/.agent/skills/doom/components/accordion.md +0 -44
  161. package/.agent/skills/doom/components/actionrow.md +0 -33
  162. package/.agent/skills/doom/components/alert.md +0 -35
  163. package/.agent/skills/doom/components/avatar.md +0 -36
  164. package/.agent/skills/doom/components/badge.md +0 -29
  165. package/.agent/skills/doom/components/breadcrumbs.md +0 -33
  166. package/.agent/skills/doom/components/button.md +0 -43
  167. package/.agent/skills/doom/components/card.md +0 -41
  168. package/.agent/skills/doom/components/chart.md +0 -106
  169. package/.agent/skills/doom/components/checkbox.md +0 -38
  170. package/.agent/skills/doom/components/chip.md +0 -35
  171. package/.agent/skills/doom/components/combobox.md +0 -50
  172. package/.agent/skills/doom/components/copybutton.md +0 -41
  173. package/.agent/skills/doom/components/drawer.md +0 -69
  174. package/.agent/skills/doom/components/dropdown.md +0 -33
  175. package/.agent/skills/doom/components/fileupload.md +0 -49
  176. package/.agent/skills/doom/components/form.md +0 -55
  177. package/.agent/skills/doom/components/icon.md +0 -47
  178. package/.agent/skills/doom/components/image.md +0 -48
  179. package/.agent/skills/doom/components/input.md +0 -54
  180. package/.agent/skills/doom/components/label.md +0 -32
  181. package/.agent/skills/doom/components/layout.md +0 -92
  182. package/.agent/skills/doom/components/link.md +0 -39
  183. package/.agent/skills/doom/components/modal.md +0 -71
  184. package/.agent/skills/doom/components/page.md +0 -41
  185. package/.agent/skills/doom/components/pagination.md +0 -32
  186. package/.agent/skills/doom/components/popover.md +0 -45
  187. package/.agent/skills/doom/components/progressbar.md +0 -37
  188. package/.agent/skills/doom/components/radiogroup.md +0 -45
  189. package/.agent/skills/doom/components/select.md +0 -43
  190. package/.agent/skills/doom/components/sheet.md +0 -71
  191. package/.agent/skills/doom/components/sidebar.md +0 -92
  192. package/.agent/skills/doom/components/skeleton.md +0 -35
  193. package/.agent/skills/doom/components/slat.md +0 -46
  194. package/.agent/skills/doom/components/slider.md +0 -51
  195. package/.agent/skills/doom/components/spinner.md +0 -34
  196. package/.agent/skills/doom/components/splitbutton.md +0 -35
  197. package/.agent/skills/doom/components/switch.md +0 -40
  198. package/.agent/skills/doom/components/table.md +0 -82
  199. package/.agent/skills/doom/components/tabs.md +0 -65
  200. package/.agent/skills/doom/components/text.md +0 -42
  201. package/.agent/skills/doom/components/textarea.md +0 -46
  202. package/.agent/skills/doom/components/toast.md +0 -59
  203. package/.agent/skills/doom/components/tooltip.md +0 -35
  204. package/.agent/skills/doom/index.md +0 -167
  205. package/.agent/skills/doom/styles/aesthetic.md +0 -151
  206. package/.agent/skills/doom/styles/css-variables.md +0 -80
  207. package/.agent/skills/doom/styles/mixins.md +0 -97
  208. package/.agent/skills/doom/styles/tokens.md +0 -129
  209. package/.agent/skills/doom/styles/utilities.md +0 -84
  210. package/dist/components/Chart/sensors/KeyboardSensor.js +0 -82
  211. package/dist/components/Chart/sensors/SelectionSensor.js +0 -41
@@ -0,0 +1,47 @@
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
+ // INPUT SIGNAL (The Universal Input Format)
9
+ // =============================================================================
10
+ /**
11
+ * The source of an input signal. Used for multiplayer and debugging.
12
+ */
13
+ export var InputSource;
14
+ (function (InputSource) {
15
+ InputSource["MOUSE"] = "mouse";
16
+ InputSource["TOUCH"] = "touch";
17
+ InputSource["KEYBOARD"] = "keyboard";
18
+ InputSource["REMOTE"] = "remote";
19
+ })(InputSource || (InputSource = {}));
20
+ /**
21
+ * The type of input action being performed.
22
+ */
23
+ export var InputAction;
24
+ (function (InputAction) {
25
+ InputAction["START"] = "START";
26
+ InputAction["MOVE"] = "MOVE";
27
+ InputAction["END"] = "END";
28
+ InputAction["CANCEL"] = "CANCEL";
29
+ InputAction["KEY"] = "KEY";
30
+ })(InputAction || (InputAction = {}));
31
+ // =============================================================================
32
+ // SCHEDULER TYPES
33
+ // =============================================================================
34
+ /**
35
+ * Priority levels for the scheduler.
36
+ * Critical tasks run synchronously to enable preventDefault().
37
+ * Visual tasks are batched to requestAnimationFrame.
38
+ */
39
+ export var TaskPriority;
40
+ (function (TaskPriority) {
41
+ /** Must run immediately (pointer down, to enable preventDefault) */
42
+ TaskPriority[TaskPriority["CRITICAL"] = 0] = "CRITICAL";
43
+ /** Can be batched to next frame (hover updates, tooltip positioning) */
44
+ TaskPriority[TaskPriority["VISUAL"] = 1] = "VISUAL";
45
+ /** Low priority, can be deferred (analytics, logging) */
46
+ TaskPriority[TaskPriority["IDLE"] = 2] = "IDLE";
47
+ })(TaskPriority || (TaskPriority = {}));
@@ -0,0 +1,43 @@
1
+ /**
2
+ * useEngine Hook
3
+ *
4
+ * React hook that manages the Engine lifecycle, connecting the
5
+ * pure Engine to React's component tree.
6
+ *
7
+ * Responsibilities:
8
+ * - Create and dispose Engine instance
9
+ * - Sync data to spatial index
10
+ * - Handle container attachment and resize
11
+ * - Wire up event handler with stable callback pattern
12
+ */
13
+ import { Engine, EngineEvent } from "../engine";
14
+ export interface UseEngineOptions<T = unknown> {
15
+ /** Data points to index for spatial queries */
16
+ data?: T[];
17
+ /** Function to get X coordinate from data item */
18
+ getX?: (d: T) => number;
19
+ /** Function to get Y coordinate from data item */
20
+ getY?: (d: T) => number;
21
+ /** Function to get series ID from data item */
22
+ getSeriesId?: (d: T) => string;
23
+ /** Function to get data index from data item */
24
+ getDataIndex?: (d: T, index: number) => number;
25
+ /** Plot bounds for coordinate calculations */
26
+ plotBounds?: {
27
+ x: number;
28
+ y: number;
29
+ width: number;
30
+ height: number;
31
+ };
32
+ /** Event handler for engine events */
33
+ onEvent?: (event: EngineEvent<T>) => void;
34
+ /** Magnetic radius for snapping (pixels) */
35
+ magneticRadius?: number;
36
+ }
37
+ export interface UseEngineResult<T = unknown> {
38
+ /** The Engine instance */
39
+ engine: Engine<T>;
40
+ /** Ref to attach to the container element */
41
+ containerRef: React.RefObject<HTMLElement | null>;
42
+ }
43
+ export declare function useEngine<T = unknown>(options?: UseEngineOptions<T>): UseEngineResult<T>;
@@ -0,0 +1,128 @@
1
+ /**
2
+ * useEngine Hook
3
+ *
4
+ * React hook that manages the Engine lifecycle, connecting the
5
+ * pure Engine to React's component tree.
6
+ *
7
+ * Responsibilities:
8
+ * - Create and dispose Engine instance
9
+ * - Sync data to spatial index
10
+ * - Handle container attachment and resize
11
+ * - Wire up event handler with stable callback pattern
12
+ */
13
+ import { useEffect, useMemo, useRef, useState } from "react";
14
+ import { Engine } from "../engine/index.js";
15
+ // =============================================================================
16
+ // HOOK IMPLEMENTATION
17
+ // =============================================================================
18
+ export function useEngine(options = {}) {
19
+ const { data, getX, getY, getSeriesId, getDataIndex, plotBounds, onEvent, magneticRadius, } = options;
20
+ // Container ref for DOM attachment
21
+ const containerRef = useRef(null);
22
+ // Track container element state for triggering effects
23
+ const [containerElement, setContainerElement] = useState(null);
24
+ // =========================================================================
25
+ // Engine Creation (Stable)
26
+ // =========================================================================
27
+ const engine = useMemo(() => new Engine({
28
+ magneticRadius,
29
+ useDomHitTesting: true,
30
+ }),
31
+ // eslint-disable-next-line react-hooks/exhaustive-deps
32
+ []);
33
+ // =========================================================================
34
+ // Event Handler (Stable Callback Pattern)
35
+ // =========================================================================
36
+ const onEventRef = useRef(onEvent);
37
+ onEventRef.current = onEvent;
38
+ useEffect(() => {
39
+ engine.setHandler((event) => {
40
+ var _a;
41
+ (_a = onEventRef.current) === null || _a === void 0 ? void 0 : _a.call(onEventRef, event);
42
+ });
43
+ }, [engine]);
44
+ // =========================================================================
45
+ // Plot Bounds Sync
46
+ // =========================================================================
47
+ useEffect(() => {
48
+ if (!plotBounds) {
49
+ return;
50
+ }
51
+ const currentRect = engine.getContainerRect();
52
+ if (currentRect) {
53
+ engine.updateBounds(currentRect, plotBounds);
54
+ }
55
+ else {
56
+ engine.setContainer(null, null, plotBounds);
57
+ }
58
+ }, [engine, plotBounds]);
59
+ // =========================================================================
60
+ // Data Sync to Spatial Index
61
+ // =========================================================================
62
+ useEffect(() => {
63
+ if (!data || !getX || !getY) {
64
+ engine.updateData([]);
65
+ return;
66
+ }
67
+ const points = data.map((d, i) => {
68
+ var _a, _b;
69
+ return ({
70
+ x: getX(d),
71
+ y: getY(d),
72
+ data: d,
73
+ seriesId: (_a = getSeriesId === null || getSeriesId === void 0 ? void 0 : getSeriesId(d)) !== null && _a !== void 0 ? _a : "default",
74
+ dataIndex: (_b = getDataIndex === null || getDataIndex === void 0 ? void 0 : getDataIndex(d, i)) !== null && _b !== void 0 ? _b : i,
75
+ });
76
+ });
77
+ engine.updateData(points);
78
+ }, [engine, data, getX, getY, getSeriesId, getDataIndex]);
79
+ // =========================================================================
80
+ // Container Ref Detection
81
+ // Refs don't trigger re-renders, so we poll on RAF to detect changes
82
+ // =========================================================================
83
+ useEffect(() => {
84
+ if (containerRef.current && containerRef.current !== containerElement) {
85
+ setContainerElement(containerRef.current);
86
+ }
87
+ const rafId = requestAnimationFrame(() => {
88
+ if (containerRef.current !== containerElement) {
89
+ setContainerElement(containerRef.current);
90
+ }
91
+ });
92
+ return () => cancelAnimationFrame(rafId);
93
+ }, [containerElement]);
94
+ // =========================================================================
95
+ // Container Attachment & ResizeObserver
96
+ // =========================================================================
97
+ useEffect(() => {
98
+ if (!containerElement) {
99
+ return;
100
+ }
101
+ engine.setContainer(containerElement, null, plotBounds);
102
+ const resizeObserver = new ResizeObserver((entries) => {
103
+ for (const entry of entries) {
104
+ const rect = entry.target.getBoundingClientRect();
105
+ engine.updateBounds(rect, plotBounds);
106
+ }
107
+ });
108
+ resizeObserver.observe(containerElement);
109
+ return () => {
110
+ resizeObserver.disconnect();
111
+ };
112
+ }, [engine, containerElement, plotBounds]);
113
+ // =========================================================================
114
+ // Cleanup on Unmount
115
+ // =========================================================================
116
+ useEffect(() => {
117
+ return () => {
118
+ engine.dispose();
119
+ };
120
+ }, [engine]);
121
+ // =========================================================================
122
+ // Return Value
123
+ // =========================================================================
124
+ return {
125
+ engine,
126
+ containerRef,
127
+ };
128
+ }
@@ -1,31 +1,29 @@
1
1
  import { Sensor } from "../../types/events";
2
- import { HoverMode, InteractionChannel } from "../../types/interaction";
2
+ import { InteractionChannel } from "../../types/interaction";
3
3
  export interface HoverSensorOptions {
4
- /**
5
- * How to find the target.
6
- */
7
- mode: HoverMode;
8
4
  /**
9
5
  * Name for the interaction channel.
10
6
  * Defaults to 'primary-hover'.
11
7
  */
12
8
  name?: InteractionChannel | string;
9
+ /**
10
+ * When true, only fire when the pointer is directly over a tagged DOM element
11
+ * (DOM hit-test match). Proximal quadtree candidates that have no backing
12
+ * element are ignored. Useful for area-fill charts like treemaps where
13
+ * magnetic snapping across empty gaps is undesirable.
14
+ * @default false
15
+ */
16
+ exactHit?: boolean;
17
+ /**
18
+ * When true, all series at the primary candidate's X position are included
19
+ * as targets (vertical-slice behaviour). Useful for multi-series line/bar/area
20
+ * charts where series share the same X domain values.
21
+ * @default false
22
+ */
23
+ verticalSlice?: boolean;
13
24
  }
14
25
  /**
15
26
  * The DataHoverSensor detects pointer movements over the chart plot
16
- * and identifies the closest data targets based on the specified mode.
17
- *
18
- * It coordinates with the interaction store to trigger hover-based
19
- * behaviors like tooltips and markers.
20
- *
21
- * @example
22
- * ```tsx
23
- * Root({ sensors: [DataHoverSensor()] })
24
- * ```
25
- *
26
- * @example
27
- * ```tsx
28
- * DataHoverSensor({ mode: 'exact' })
29
- * ```
27
+ * and identifies the closest data targets.
30
28
  */
31
29
  export declare const DataHoverSensor: (options?: HoverSensorOptions) => Sensor;
@@ -1,58 +1,45 @@
1
+ import { InputAction } from "../../engine/index.js";
1
2
  import { InteractionChannel } from "../../types/interaction.js";
2
- import { findClosestTargets } from "../utils/search.js";
3
3
  /**
4
4
  * The DataHoverSensor detects pointer movements over the chart plot
5
- * and identifies the closest data targets based on the specified mode.
6
- *
7
- * It coordinates with the interaction store to trigger hover-based
8
- * behaviors like tooltips and markers.
9
- *
10
- * @example
11
- * ```tsx
12
- * Root({ sensors: [DataHoverSensor()] })
13
- * ```
14
- *
15
- * @example
16
- * ```tsx
17
- * DataHoverSensor({ mode: 'exact' })
18
- * ```
5
+ * and identifies the closest data targets.
19
6
  */
20
- export const DataHoverSensor = (options = { mode: "nearest-x" }) => {
21
- const { mode, name = InteractionChannel.PRIMARY_HOVER } = options;
22
- return ({ on, off, getChartContext, upsertInteraction, removeInteraction, }) => {
23
- const handleMove = (event) => {
24
- const ctx = getChartContext();
25
- if (!ctx || !ctx.chartStore) {
26
- return;
27
- }
28
- const state = ctx.chartStore.getState();
29
- const targets = findClosestTargets(event, mode, state);
30
- if (targets.length > 0) {
31
- const isTouch = "touches" in event.nativeEvent ||
32
- "changedTouches" in event.nativeEvent;
33
- upsertInteraction(name, {
34
- pointer: {
35
- x: event.coordinates.chartX,
36
- y: event.coordinates.chartY,
37
- containerX: event.coordinates.containerX,
38
- containerY: event.coordinates.containerY,
39
- isTouch,
40
- },
41
- targets,
42
- });
43
- }
44
- else {
45
- removeInteraction(name);
46
- }
47
- };
48
- const handleLeave = () => {
7
+ export const DataHoverSensor = (options = {}) => {
8
+ const { name = InteractionChannel.PRIMARY_HOVER, exactHit = false, verticalSlice = false, } = options;
9
+ return (event, { upsertInteraction, removeInteraction }) => {
10
+ const { signal, primaryCandidate, sliceCandidates, chartX, chartY, isWithinPlot, } = event;
11
+ if (signal.action !== InputAction.MOVE &&
12
+ signal.action !== InputAction.CANCEL) {
13
+ return;
14
+ }
15
+ if (signal.action === InputAction.CANCEL ||
16
+ (!isWithinPlot && signal.source !== "touch")) {
49
17
  removeInteraction(name);
50
- };
51
- on("CHART_POINTER_MOVE", handleMove);
52
- on("CHART_POINTER_LEAVE", handleLeave);
53
- return () => {
54
- off("CHART_POINTER_MOVE", handleMove);
55
- off("CHART_POINTER_LEAVE", handleLeave);
56
- };
18
+ return;
19
+ }
20
+ // When exactHit is true, discard proximal (quadtree-only) candidates that
21
+ // have no backing DOM element — the pointer is over empty space.
22
+ const candidate = exactHit && primaryCandidate && !primaryCandidate.element
23
+ ? undefined
24
+ : primaryCandidate;
25
+ if (candidate) {
26
+ const isTouch = signal.source === "touch";
27
+ const targets = verticalSlice && sliceCandidates.length > 0
28
+ ? sliceCandidates
29
+ : [candidate];
30
+ upsertInteraction(name, {
31
+ pointer: {
32
+ x: chartX,
33
+ y: chartY,
34
+ containerX: signal.x,
35
+ containerY: signal.y,
36
+ isTouch,
37
+ },
38
+ targets,
39
+ });
40
+ }
41
+ else {
42
+ removeInteraction(name);
43
+ }
57
44
  };
58
45
  };
@@ -0,0 +1,38 @@
1
+ import { Sensor } from "../../types/events";
2
+ import { InteractionChannel } from "../../types/interaction";
3
+ export interface DragSensorOptions<T = any> {
4
+ /**
5
+ * Interaction channel name.
6
+ * @default InteractionChannel.DRAG
7
+ */
8
+ name?: InteractionChannel | string;
9
+ /**
10
+ * Callback fired when a drag ends.
11
+ */
12
+ onDragEnd?: (originalData: T, newValue: {
13
+ x: any;
14
+ y: any;
15
+ }, pixelPosition: {
16
+ x: number;
17
+ y: number;
18
+ }) => void;
19
+ /**
20
+ * Callback fired during drag.
21
+ */
22
+ onDrag?: (originalData: T, currentValue: {
23
+ x: any;
24
+ y: any;
25
+ }, pixelPosition: {
26
+ x: number;
27
+ y: number;
28
+ }) => void;
29
+ /**
30
+ * Radius in pixels for hit detection.
31
+ * @default 20
32
+ */
33
+ hitRadius?: number;
34
+ }
35
+ /**
36
+ * DragSensor enables dragging data points ("pucks") on a chart.
37
+ */
38
+ export declare const DragSensor: <T = any>(options?: DragSensorOptions<T>) => Sensor<T>;
@@ -0,0 +1,105 @@
1
+ import { InputAction } from "../../engine/index.js";
2
+ import { InteractionChannel, } from "../../types/interaction.js";
3
+ /**
4
+ * DragSensor enables dragging data points ("pucks") on a chart.
5
+ */
6
+ export const DragSensor = (options = {}) => {
7
+ const { name = InteractionChannel.DRAG, onDragEnd, onDrag, hitRadius = 20, } = options;
8
+ let isDragging = false;
9
+ let dragTarget = null;
10
+ let startPosition = null;
11
+ return (event, { getChartContext, upsertInteraction, removeInteraction }) => {
12
+ const { signal, primaryCandidate, chartX, chartY } = event;
13
+ // 1. START DRAG
14
+ if (signal.action === InputAction.START) {
15
+ if (primaryCandidate && primaryCandidate.distance <= hitRadius) {
16
+ // Start dragging exact target
17
+ const target = primaryCandidate;
18
+ const initialData = target.data;
19
+ if (initialData) {
20
+ isDragging = true;
21
+ startPosition = { x: chartX, y: chartY };
22
+ const interactionTarget = {
23
+ data: initialData,
24
+ coordinate: target.coordinate,
25
+ seriesId: target.seriesId,
26
+ };
27
+ dragTarget = interactionTarget;
28
+ // Register interaction
29
+ const interaction = {
30
+ target: interactionTarget,
31
+ startPosition,
32
+ currentPosition: startPosition,
33
+ currentValue: { x: null, y: null },
34
+ isDragging: true,
35
+ };
36
+ upsertInteraction(name, interaction);
37
+ }
38
+ }
39
+ return;
40
+ }
41
+ // 2. MOVE DRAG
42
+ if (signal.action === InputAction.MOVE) {
43
+ if (!isDragging || !dragTarget || !startPosition) {
44
+ return;
45
+ }
46
+ const currentPosition = { x: chartX, y: chartY };
47
+ const ctx = getChartContext();
48
+ if (!ctx || !ctx.chartStore) {
49
+ return;
50
+ }
51
+ const state = ctx.chartStore.getState();
52
+ const { x: xScale, y: yScale } = state.scales;
53
+ // Invert
54
+ let xValue = null;
55
+ let yValue = null;
56
+ if (xScale && "invert" in xScale) {
57
+ xValue = xScale.invert(currentPosition.x);
58
+ }
59
+ if (yScale && "invert" in yScale) {
60
+ yValue = yScale.invert(currentPosition.y);
61
+ }
62
+ const currentValue = { x: xValue, y: yValue };
63
+ // Update interaction
64
+ const interaction = {
65
+ target: dragTarget,
66
+ startPosition,
67
+ currentPosition,
68
+ currentValue,
69
+ isDragging: true,
70
+ };
71
+ upsertInteraction(name, interaction);
72
+ if (onDrag) {
73
+ onDrag(dragTarget.data, currentValue, currentPosition);
74
+ }
75
+ return;
76
+ }
77
+ // 3. END / CANCEL DRAG
78
+ if (signal.action === InputAction.END ||
79
+ signal.action === InputAction.CANCEL) {
80
+ if (!isDragging || !dragTarget) {
81
+ return;
82
+ }
83
+ const finalPosition = { x: chartX, y: chartY };
84
+ const ctx = getChartContext();
85
+ const state = ctx.chartStore.getState();
86
+ const { x: xScale, y: yScale } = state.scales;
87
+ let xValue = null;
88
+ let yValue = null;
89
+ if (xScale && "invert" in xScale) {
90
+ xValue = xScale.invert(finalPosition.x);
91
+ }
92
+ if (yScale && "invert" in yScale) {
93
+ yValue = yScale.invert(finalPosition.y);
94
+ }
95
+ if (signal.action === InputAction.END && onDragEnd) {
96
+ onDragEnd(dragTarget.data, { x: xValue, y: yValue }, finalPosition);
97
+ }
98
+ // Cleanup
99
+ isDragging = false;
100
+ dragTarget = null;
101
+ startPosition = null;
102
+ removeInteraction(name);
103
+ }
104
+ };
105
+ };
@@ -0,0 +1,2 @@
1
+ export type { DragSensorOptions } from "./DragSensor";
2
+ export { DragSensor } from "./DragSensor";
@@ -0,0 +1 @@
1
+ export { DragSensor } from "./DragSensor.js";
@@ -1,4 +1,4 @@
1
- import { Sensor } from "../types/events";
1
+ import { Sensor } from "../../types/events";
2
2
  /**
3
3
  * Professional-grade Keyboard Sensor for A11y.
4
4
  * Allows navigating data points using ArrowKeys.
@@ -0,0 +1,86 @@
1
+ import { InputAction } from "../../engine/index.js";
2
+ import { resolveAccessor } from "../../types/accessors.js";
3
+ import { InteractionChannel } from "../../types/interaction.js";
4
+ /**
5
+ * Professional-grade Keyboard Sensor for A11y.
6
+ * Allows navigating data points using ArrowKeys.
7
+ */
8
+ export const KeyboardSensor = (options = {}) => {
9
+ const { name = InteractionChannel.PRIMARY_HOVER } = options;
10
+ let focusedIndex = -1;
11
+ return (event, { getChartContext, upsertInteraction, removeInteraction }) => {
12
+ const { signal } = event;
13
+ // Only handle KEY actions
14
+ if (signal.action !== InputAction.KEY || !signal.key) {
15
+ return;
16
+ }
17
+ const ctx = getChartContext();
18
+ const { chartStore } = ctx;
19
+ const state = chartStore.getState();
20
+ const { data, scales, config } = state;
21
+ if (!data || data.length === 0 || !scales.x) {
22
+ return;
23
+ }
24
+ // Update Focus
25
+ let changed = false;
26
+ if (signal.key === "ArrowRight") {
27
+ focusedIndex = Math.min(focusedIndex + 1, data.length - 1);
28
+ changed = true;
29
+ }
30
+ else if (signal.key === "ArrowLeft") {
31
+ focusedIndex = Math.max(focusedIndex - 1, 0);
32
+ changed = true;
33
+ if (focusedIndex < 0) {
34
+ focusedIndex = 0;
35
+ }
36
+ }
37
+ else if (signal.key === "Escape") {
38
+ focusedIndex = -1;
39
+ removeInteraction(name);
40
+ return;
41
+ }
42
+ if (changed && focusedIndex >= 0) {
43
+ const d = data[focusedIndex];
44
+ const { x: xScale, y: yScale } = scales;
45
+ // Resolve coordinates
46
+ // TODO: Accessor resolution should be centralized or consistent with Root
47
+ const xAcc = config.x || ((v) => v[0]); // fallback
48
+ const yAcc = config.y || ((v) => v[1]);
49
+ const getX = resolveAccessor(xAcc);
50
+ const getY = resolveAccessor(yAcc);
51
+ const xVal = getX(d);
52
+ const yVal = getY(d);
53
+ const xPos = xScale(xVal) || 0;
54
+ const yPos = yScale(yVal) || 0;
55
+ // Query Engine for targets at this location (Vertical Slice)
56
+ // We use a small search radius to mimic "nearest" behavior if needed,
57
+ // but typically we want everything at this X.
58
+ // The spatial index might be quadtree.
59
+ // For now, let's use the primary data point we just found as the target
60
+ // + any others the engine finds nearby.
61
+ // Simplest interaction: Just highlight the data point we navigated to.
62
+ // If we want multi-series, we should query engine.
63
+ // const candidates = engine.spatialIndex?.find(xPos, yPos, 10) || [];
64
+ // Construct target manually since we know the data point
65
+ const primaryTarget = {
66
+ type: "data-point",
67
+ data: d,
68
+ seriesId: "default", // TODO: Multi-series support
69
+ dataIndex: focusedIndex,
70
+ coordinate: { x: xPos, y: yPos },
71
+ distance: 0,
72
+ };
73
+ upsertInteraction(name, {
74
+ pointer: {
75
+ x: xPos,
76
+ y: yPos,
77
+ containerX: xPos + state.dimensions.margin.left,
78
+ containerY: yPos + state.dimensions.margin.top,
79
+ isTouch: false,
80
+ },
81
+ targets: [primaryTarget], // Cast to InteractionTarget
82
+ target: primaryTarget,
83
+ });
84
+ }
85
+ };
86
+ };
@@ -0,0 +1 @@
1
+ export * from "./KeyboardSensor";
@@ -0,0 +1 @@
1
+ export * from "./KeyboardSensor.js";
@@ -1,7 +1,7 @@
1
- import { Sensor } from "../types/events";
1
+ import { Sensor } from "../../types/events";
2
2
  /**
3
3
  * Professional-grade Selection Sensor.
4
- * Coordinates with search logic to choose data points on click.
4
+ * Coordinates with Engine to choose data points on click/start.
5
5
  */
6
6
  export declare const SelectionSensor: (options?: {
7
7
  name?: string;
@@ -0,0 +1,39 @@
1
+ import { InputAction } from "../../engine/index.js";
2
+ import { InteractionChannel } from "../../types/interaction.js";
3
+ /**
4
+ * Professional-grade Selection Sensor.
5
+ * Coordinates with Engine to choose data points on click/start.
6
+ */
7
+ export const SelectionSensor = (options = {}) => {
8
+ const { name = InteractionChannel.SELECTION } = options;
9
+ return ({ signal, primaryCandidate }, { getChartContext, upsertInteraction }) => {
10
+ // Only handle START action (roughly equivalent to mouse down)
11
+ if (signal.action !== InputAction.START) {
12
+ return;
13
+ }
14
+ if (!primaryCandidate) {
15
+ return;
16
+ }
17
+ const ctx = getChartContext();
18
+ const { chartStore } = ctx;
19
+ const state = chartStore.getState();
20
+ const selectedDatum = primaryCandidate.data;
21
+ const currentInteraction = state.interactions.get(name);
22
+ const currentSelection = (currentInteraction === null || currentInteraction === void 0 ? void 0 : currentInteraction.selection) || [];
23
+ // Simple toggle logic
24
+ // TODO: support multi-select with shift key?
25
+ // signal.originalEvent is the native event if we need modifiers.
26
+ const isAlreadySelected = currentSelection.includes(selectedDatum);
27
+ let nextSelection;
28
+ if (isAlreadySelected) {
29
+ nextSelection = currentSelection.filter((d) => d !== selectedDatum);
30
+ }
31
+ else {
32
+ nextSelection = [...currentSelection, selectedDatum];
33
+ }
34
+ upsertInteraction(name, {
35
+ selection: nextSelection,
36
+ mode: "discrete", // or "continuous"
37
+ });
38
+ };
39
+ };
@@ -0,0 +1 @@
1
+ export * from "./SelectionSensor";