@dschz/solid-uplot 0.1.7 → 0.3.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/README.md CHANGED
@@ -462,26 +462,35 @@ const MyDashboard = () => {
462
462
  ### SolidUplot Component
463
463
 
464
464
  ```tsx
465
+ type SolidUplotEvents = {
466
+ /** Callback fired when the uPlot instance is created */
467
+ readonly onCreate?: (u: uPlot, meta: OnCreateMeta) => void;
468
+ /** Callback fired when the cursor moves */
469
+ readonly onCursorMove?: (params: OnCursorMoveParams) => void;
470
+ };
471
+
465
472
  // Main component props (extends all uPlot.Options except plugins, width, height)
466
- type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> & {
467
- // Ref callback to access the chart container element
468
- ref?: (el: HTMLDivElement) => void;
473
+ type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> &
474
+ SolidUplotEvents & {
475
+ // Ref callback to access the chart container element
476
+ ref?: Ref<HTMLDivElement>;
469
477
 
470
- // Callback fired when the uPlot instance is created
471
- onCreate?: (u: uPlot, meta: { seriesData: SeriesDatum[] }) => void;
478
+ // CSS class name for the chart container (default: "solid-uplot")
479
+ // Additional classes will be appended to the default class
480
+ class?: string;
472
481
 
473
- // Whether to reset scales when chart data is updated (default: true)
474
- resetScales?: boolean;
482
+ // CSS styles for the chart container (position is managed internally)
483
+ style?: Omit<JSX.CSSProperties, "position">;
475
484
 
476
- // CSS styles for the chart container (position is managed internally)
477
- style?: Omit<JSX.CSSProperties, "position">;
485
+ // Enable automatic resizing to fit container (default: false)
486
+ autoResize?: boolean;
478
487
 
479
- // Where to place children components relative to the chart (default: "top")
480
- childrenPlacement?: "top" | "bottom";
488
+ // Whether to reset scales when chart data is updated (default: true)
489
+ resetScales?: boolean;
481
490
 
482
- // Enable automatic resizing to fit container (default: false)
483
- autoResize?: boolean;
484
- };
491
+ // Where to place children components relative to the chart (default: "top")
492
+ childrenPlacement?: "top" | "bottom";
493
+ };
485
494
 
486
495
  // Configuration options extending uPlot.Options with SolidJS enhancements
487
496
  type SolidUplotOptions<T extends VoidStruct = VoidStruct> = Omit<
@@ -0,0 +1,24 @@
1
+ import 'uplot';
2
+
3
+ // src/utils/getColorString.ts
4
+ var getColorString = (fillOrStroke, fallback = "#888") => {
5
+ return typeof fillOrStroke === "string" ? fillOrStroke : fallback;
6
+ };
7
+ function getNewCalendarDayIndices(u) {
8
+ const xValues = u.data[0];
9
+ if (!xValues || !xValues.length) return [];
10
+ const seen = /* @__PURE__ */ new Set();
11
+ const indices = [];
12
+ for (let i = 0; i < xValues.length; i++) {
13
+ const ts = xValues[i];
14
+ const date = new Date(ts);
15
+ const dayString = date.toISOString().split("T")[0];
16
+ if (!seen.has(dayString)) {
17
+ seen.add(dayString);
18
+ indices.push(i);
19
+ }
20
+ }
21
+ return indices;
22
+ }
23
+
24
+ export { getColorString, getNewCalendarDayIndices };
@@ -1,3 +1,17 @@
1
+ // src/utils/getCursorData.ts
2
+ var getCursorData = (u) => {
3
+ const idx = u.cursor.idx;
4
+ const xValues = u.data[0];
5
+ const isValid = idx != null && xValues && idx < xValues.length;
6
+ return !isValid ? void 0 : {
7
+ plotId: u.root.id,
8
+ idx,
9
+ xValue: xValues[idx],
10
+ visible: Boolean(u.cursor.show),
11
+ position: { left: u.cursor.left || 0, top: u.cursor.top || 0 }
12
+ };
13
+ };
14
+
1
15
  // src/utils/getSeriesData.ts
2
16
  var getSeriesData = (u, options = {}) => {
3
17
  const series = [];
@@ -20,8 +34,6 @@ var getSeriesData = (u, options = {}) => {
20
34
  }
21
35
  return series;
22
36
  };
23
-
24
- export {
25
- getSeriesData
26
- };
27
37
  // istanbul ignore next -- @preserve
38
+
39
+ export { getCursorData, getSeriesData };
@@ -1,3 +1,46 @@
1
+ type CursorPosition = {
2
+ /**
3
+ * The cursor position left offset in CSS pixels (relative to plotting area)
4
+ */
5
+ readonly left: number;
6
+ /**
7
+ * The cursor position top offset in CSS pixels (relative to plotting area)
8
+ */
9
+ readonly top: number;
10
+ };
11
+ /**
12
+ * The cursor index data for a given uPlot instance.
13
+ */
14
+ type CursorData = {
15
+ /**
16
+ * The id of the plot instance that the cursor message originates from.
17
+ */
18
+ readonly plotId: string;
19
+ /**
20
+ * The closest x-axis data index to cursor (closestIdx)
21
+ */
22
+ readonly idx: number;
23
+ /**
24
+ * The x-axis value of the cursor idx.
25
+ */
26
+ readonly xValue: number;
27
+ /**
28
+ * The position of the cursor.
29
+ */
30
+ readonly position: CursorPosition;
31
+ /**
32
+ * The visibility of the cursor.
33
+ */
34
+ readonly visible: boolean;
35
+ };
36
+ /**
37
+ * Get the cursor index data for a given uPlot instance.
38
+ *
39
+ * @param u - The uPlot instance.
40
+ * @returns The cursor data.
41
+ */
42
+ declare const getCursorData: (u: uPlot) => CursorData | undefined;
43
+
1
44
  /**
2
45
  * Summary data for a given series.
3
46
  */
@@ -34,4 +77,4 @@ type GetSeriesDataOptions = {
34
77
  */
35
78
  declare const getSeriesData: (u: uPlot, options?: GetSeriesDataOptions) => SeriesDatum[];
36
79
 
37
- export { type SeriesDatum as S, getSeriesData as g };
80
+ export { type CursorData as C, type SeriesDatum as S, getSeriesData as a, getCursorData as g };
@@ -1,10 +1,20 @@
1
1
  import { V as VoidStruct, S as SolidUplotPluginBus, U as UplotPluginFactory } from '../createPluginBus-DdrjQANs.js';
2
2
  export { a as UplotPluginFactoryContext, c as createPluginBus } from '../createPluginBus-DdrjQANs.js';
3
+ import { Ref } from '@solid-primitives/refs';
3
4
  import { ParentProps, JSX } from 'solid-js';
4
- import uPlot from 'uplot';
5
- import { S as SeriesDatum } from '../getSeriesData-D1zBqQ9Y.js';
5
+ import uPlot$1 from 'uplot';
6
+ import { C as CursorData, S as SeriesDatum } from '../getSeriesData-04wGQord.js';
6
7
  import 'solid-js/store';
7
8
 
9
+ type OnCursorMoveParams = {
10
+ /** The uPlot instance */
11
+ readonly u: uPlot;
12
+ /** The cursor data */
13
+ readonly cursor: CursorData;
14
+ /** Array of series data extracted from the chart configuration */
15
+ readonly seriesData: SeriesDatum[];
16
+ };
17
+
8
18
  /** Placement options for children components relative to the chart */
9
19
  type ChildrenPlacement = "top" | "bottom";
10
20
  /**
@@ -13,18 +23,20 @@ type ChildrenPlacement = "top" | "bottom";
13
23
  *
14
24
  * @template T - The type of the plugin bus data structure
15
25
  */
16
- type SolidUplotPlugin<T extends VoidStruct = VoidStruct> = uPlot.Plugin | UplotPluginFactory<T>;
26
+ type SolidUplotPlugin<T extends VoidStruct = VoidStruct> = uPlot$1.Plugin | UplotPluginFactory<T>;
17
27
  /**
18
28
  * Configuration options for the SolidUplot component, extending uPlot.Options
19
29
  * with SolidJS-specific enhancements
20
30
  *
21
31
  * @template T - The type of the plugin bus data structure
22
32
  */
23
- type SolidUplotOptions<T extends VoidStruct = VoidStruct> = Omit<uPlot.Options, "plugins" | "width" | "height"> & {
33
+ type SolidUplotOptions<T extends VoidStruct = VoidStruct> = Omit<uPlot$1.Options, "plugins" | "width" | "height" | "data"> & {
24
34
  /** Chart width in pixels */
25
35
  readonly width?: number;
26
36
  /** Chart height in pixels */
27
37
  readonly height?: number;
38
+ /** Chart data - accepts AlignedData or number[][] */
39
+ readonly data?: uPlot$1.AlignedData | number[][];
28
40
  /** Plugin communication bus for coordinating between plugins */
29
41
  readonly pluginBus?: SolidUplotPluginBus<T>;
30
42
  /** Array of plugins to apply to the chart */
@@ -37,28 +49,27 @@ type OnCreateMeta = {
37
49
  /** Array of series data extracted from the chart configuration */
38
50
  readonly seriesData: SeriesDatum[];
39
51
  };
52
+ /**
53
+ * Events that can be passed to the SolidUplot component
54
+ */
55
+ type SolidUplotEvents = {
56
+ /** Callback fired when the uPlot instance is created */
57
+ readonly onCreate?: (u: uPlot$1, meta: OnCreateMeta) => void;
58
+ /** Callback fired when the cursor moves */
59
+ readonly onCursorMove?: (params: OnCursorMoveParams) => void;
60
+ };
40
61
  /**
41
62
  * Props for the SolidUplot component
42
63
  *
43
64
  * @template T - The type of the plugin bus data structure
44
65
  */
45
- type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> & {
46
- /** Ref callback to access the chart container element */
47
- readonly ref?: (el: HTMLDivElement) => void;
48
- /** Callback fired when the uPlot instance is created */
49
- readonly onCreate?: (u: uPlot, meta: OnCreateMeta) => void;
50
- /**
51
- * Whether to reset scales when chart data is updated
52
- * @default true
53
- */
54
- readonly resetScales?: boolean;
66
+ type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> & SolidUplotEvents & {
67
+ /** Class name for the chart container */
68
+ readonly class?: string;
55
69
  /** CSS styles for the chart container (position is managed internally) */
56
70
  readonly style?: Omit<JSX.CSSProperties, "position">;
57
- /**
58
- * Where to place children components relative to the chart
59
- * @default "top"
60
- */
61
- readonly childrenPlacement?: ChildrenPlacement;
71
+ /** Ref callback to access the chart container element */
72
+ readonly ref?: Ref<HTMLDivElement>;
62
73
  /**
63
74
  * Enable automatic resizing to fit container.
64
75
  *
@@ -70,6 +81,16 @@ type SolidUplotProps<T extends VoidStruct = VoidStruct> = SolidUplotOptions<T> &
70
81
  * @default false
71
82
  */
72
83
  readonly autoResize?: boolean;
84
+ /**
85
+ * Whether to reset scales when chart data is updated
86
+ * @default true
87
+ */
88
+ readonly resetScales?: boolean;
89
+ /**
90
+ * Where to place children components relative to the chart
91
+ * @default "top"
92
+ */
93
+ readonly childrenPlacement?: ChildrenPlacement;
73
94
  };
74
95
  /**
75
96
  * A SolidJS wrapper component for uPlot charts with enhanced features
@@ -1,17 +1,31 @@
1
- import { getSeriesData } from '../chunk/ZISGD6FJ.js';
1
+ import '../chunk/IV6D7K4M.js';
2
+ import { getSeriesData, getCursorData } from '../chunk/QELYARMN.js';
2
3
  import { createStore } from 'solid-js/store';
3
- import { template, use, insert, effect, style } from 'solid-js/web';
4
+ import { template, use, insert, effect, className, style } from 'solid-js/web';
4
5
  import 'uplot/dist/uPlot.min.css';
6
+ import { mergeRefs } from '@solid-primitives/refs';
5
7
  import { mergeProps, createUniqueId, splitProps, createMemo, createEffect, untrack, onCleanup } from 'solid-js';
6
8
  import uPlot from 'uplot';
7
9
 
8
10
  var createPluginBus = (initialData = {}) => {
9
11
  const [data, setData] = createStore(initialData);
12
+ return { data, setData };
13
+ };
14
+
15
+ // src/eventPlugins.ts
16
+ var createCursorMovePlugin = (onCursorMove) => {
10
17
  return {
11
- data,
12
- setData
18
+ hooks: {
19
+ setCursor: (u) => {
20
+ const cursor = getCursorData(u);
21
+ if (!cursor) return;
22
+ onCursorMove?.({ u, cursor, seriesData: getSeriesData(u) });
23
+ }
24
+ }
13
25
  };
14
26
  };
27
+
28
+ // src/SolidUplot.tsx
15
29
  var _tmpl$ = /* @__PURE__ */ template(`<div id=solid-uplot-root>`);
16
30
  var SolidUplot = (props) => {
17
31
  let container;
@@ -28,7 +42,7 @@ var SolidUplot = (props) => {
28
42
  show: false
29
43
  }
30
44
  }, props);
31
- const [local, options] = splitProps(_props, ["children", "childrenPlacement", "autoResize", "onCreate", "style", "ref"]);
45
+ const [local, options] = splitProps(_props, ["children", "childrenPlacement", "class", "autoResize", "onCreate", "onCursorMove", "style", "ref"]);
32
46
  const [updateableOptions, newChartOptions] = splitProps(options, ["data", "width", "height", "resetScales"]);
33
47
  const [system, chartOptions] = splitProps(newChartOptions, ["pluginBus", "plugins"]);
34
48
  const size = () => ({
@@ -36,9 +50,13 @@ var SolidUplot = (props) => {
36
50
  height: updateableOptions.height
37
51
  });
38
52
  const chartPlugins = createMemo(() => {
39
- return system.plugins.map((plugin) => typeof plugin === "function" ? plugin({
53
+ const plugins = system.plugins.map((plugin) => typeof plugin === "function" ? plugin({
40
54
  bus: system.pluginBus
41
55
  }) : plugin);
56
+ if (local.onCursorMove) {
57
+ plugins.push(createCursorMovePlugin(local.onCursorMove));
58
+ }
59
+ return plugins;
42
60
  });
43
61
  createEffect(() => {
44
62
  const getInitialSize = () => {
@@ -91,25 +109,32 @@ var SolidUplot = (props) => {
91
109
  chart.destroy();
92
110
  });
93
111
  });
112
+ const classes = () => local.class ? `solid-uplot ${local.class}` : "solid-uplot";
94
113
  return (() => {
95
114
  var _el$ = _tmpl$();
96
- use((el) => {
97
- container = el;
98
- local.ref?.(el);
99
- }, _el$);
115
+ var _ref$ = mergeRefs(local.ref, (el) => container = el);
116
+ typeof _ref$ === "function" && use(_ref$, _el$);
100
117
  insert(_el$, () => local.children);
101
- effect((_$p) => style(_el$, {
102
- display: "flex",
103
- "flex-direction": local.childrenPlacement === "top" ? "column" : "column-reverse",
104
- // When autoResize is enabled, fill the parent container
105
- ...local.autoResize && {
106
- width: "100%",
107
- height: "100%",
108
- "min-width": "0",
109
- "min-height": "0"
110
- },
111
- ...local.style
112
- }, _$p));
118
+ effect((_p$) => {
119
+ var _v$ = classes(), _v$2 = {
120
+ display: "flex",
121
+ "flex-direction": local.childrenPlacement === "top" ? "column" : "column-reverse",
122
+ // When autoResize is enabled, fill the parent container
123
+ ...local.autoResize && {
124
+ width: "100%",
125
+ height: "100%",
126
+ "min-width": "0",
127
+ "min-height": "0"
128
+ },
129
+ ...local.style
130
+ };
131
+ _v$ !== _p$.e && className(_el$, _p$.e = _v$);
132
+ _p$.t = style(_el$, _v$2, _p$.t);
133
+ return _p$;
134
+ }, {
135
+ e: void 0,
136
+ t: void 0
137
+ });
113
138
  return _el$;
114
139
  })();
115
140
  };
@@ -1,8 +1,7 @@
1
1
  import { U as UplotPluginFactory, S as SolidUplotPluginBus } from '../createPluginBus-DdrjQANs.js';
2
- import { C as CursorData } from '../getCursorData-2qxURGuZ.js';
2
+ import { C as CursorData, S as SeriesDatum } from '../getSeriesData-04wGQord.js';
3
3
  import { Component, JSX } from 'solid-js';
4
- import uPlot from 'uplot';
5
- import { S as SeriesDatum } from '../getSeriesData-D1zBqQ9Y.js';
4
+ import uPlot$1 from 'uplot';
6
5
  import 'solid-js/store';
7
6
 
8
7
  /**
@@ -157,7 +156,7 @@ type FocusSeriesPluginMessageBus = {
157
156
  /**
158
157
  * Configuration options for the focus series plugin.
159
158
  */
160
- type FocusSeriesPluginOptions = {
159
+ type FocusSeriesPluginOptions<T extends CursorPluginMessageBus & FocusSeriesPluginMessageBus> = {
161
160
  /**
162
161
  * The vertical distance in pixels required to focus a series.
163
162
  * If the cursor's Y position is within this threshold of a Y value, that series is considered focused.
@@ -184,6 +183,24 @@ type FocusSeriesPluginOptions = {
184
183
  * @default false
185
184
  */
186
185
  readonly rebuildPaths?: boolean;
186
+ /**
187
+ *
188
+ * For charts that are configured with this plugin, this callback is used to determine if the chart
189
+ * should redraw when the plugin bus updates. It is an additional condition to the base condition `cursor?.sourceId !== u.root.id`
190
+ * which is always applied first to prevent the source chart from redrawing twice.
191
+ *
192
+ * This can be used to add filtering logic, such as only reacting to specific source charts.
193
+ *
194
+ * @default undefined (always redraw for non-source charts)
195
+ */
196
+ readonly shouldRedrawOnBusUpdate?: (params: {
197
+ /** The current chart instance (the one evaluating whether to redraw) */
198
+ readonly u: uPlot;
199
+ /** Current cursor state from the bus (may be from another chart) */
200
+ readonly cursor: T["cursor"];
201
+ /** Current focus series state from the bus */
202
+ readonly focusSeries: T["focusSeries"];
203
+ }) => boolean;
187
204
  };
188
205
  /**
189
206
  * Creates a focus series plugin that visually highlights series based on cursor proximity.
@@ -220,7 +237,7 @@ type FocusSeriesPluginOptions = {
220
237
  * @param options - Configuration options for focus behavior
221
238
  * @returns A plugin factory function that creates the focus series plugin instance
222
239
  */
223
- declare const focusSeries: (options?: FocusSeriesPluginOptions) => UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
240
+ declare const focusSeries: (options?: FocusSeriesPluginOptions<CursorPluginMessageBus & FocusSeriesPluginMessageBus>) => UplotPluginFactory<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
224
241
 
225
242
  /**
226
243
  * Simple legend placement options - only top corners to avoid axis conflicts
@@ -234,7 +251,7 @@ type LegendPlacement = "top-left" | "top-right";
234
251
  * data flow where series data changes immediately trigger legend updates with fresh data information.
235
252
  */
236
253
  type LegendProps = {
237
- readonly u: uPlot;
254
+ readonly u: uPlot$1;
238
255
  readonly seriesData: SeriesDatum[];
239
256
  readonly bus: SolidUplotPluginBus<CursorPluginMessageBus & FocusSeriesPluginMessageBus>;
240
257
  };
@@ -307,7 +324,7 @@ type TooltipCursorPlacement = "top-left" | "top-right" | "bottom-left" | "bottom
307
324
  */
308
325
  type TooltipProps = {
309
326
  /** The uPlot instance for accessing chart configuration and data */
310
- readonly u: uPlot;
327
+ readonly u: uPlot$1;
311
328
  /**
312
329
  * Current cursor data including position and data index, automatically updated
313
330
  * by the cursor plugin and passed via the plugin bus system
@@ -361,6 +378,12 @@ type TooltipConfigOptions = {
361
378
  * @default "top-left"
362
379
  */
363
380
  readonly placement?: TooltipCursorPlacement;
381
+ /**
382
+ * Use fixed positioning for the tooltip. Set to true when the chart is in a
383
+ * fixed positioning context (like a dialog or modal) to prevent tooltip clipping.
384
+ * @default false
385
+ */
386
+ readonly fixed?: boolean;
364
387
  };
365
388
  /**
366
389
  * Combined options for the tooltip plugin including container props and behavior config.
@@ -1,5 +1,4 @@
1
- import { getCursorData } from '../chunk/3TJS44N7.js';
2
- import { getSeriesData } from '../chunk/ZISGD6FJ.js';
1
+ import { getCursorData, getSeriesData } from '../chunk/QELYARMN.js';
3
2
  import { createRoot, createEffect, mergeProps, splitProps, Show } from 'solid-js';
4
3
  import { render, createComponent, template, use, insert, effect, setAttribute, className, style } from 'solid-js/web';
5
4
 
@@ -59,7 +58,7 @@ var seriesFocusRedraw = (u, options = {}) => {
59
58
  } = options;
60
59
  for (let i = 1; i < u.series.length; i++) {
61
60
  const s = u.series[i];
62
- if (!focusTargets || !focusTargets.length) {
61
+ if (!focusTargets?.length) {
63
62
  s.alpha = 1;
64
63
  continue;
65
64
  }
@@ -105,13 +104,15 @@ var focusSeries = (options = {}) => {
105
104
  dispose = createRoot((dispose2) => {
106
105
  createEffect(() => {
107
106
  const cursor2 = bus.data.cursor;
108
- const focus = bus.data.focusSeries;
109
- if (cursor2?.sourceId !== u.root.id) {
107
+ const focusSeries2 = bus.data.focusSeries;
108
+ const isNotSourceChart = cursor2?.sourceId !== u.root.id;
109
+ const userRedrawCondition = options.shouldRedrawOnBusUpdate?.({ u, cursor: cursor2, focusSeries: focusSeries2 }) ?? true;
110
+ if (isNotSourceChart && userRedrawCondition) {
110
111
  seriesFocusRedraw(u, {
111
112
  unfocusedAlpha,
112
113
  focusedAlpha,
113
114
  rebuildPaths,
114
- focusTargets: focus?.targets
115
+ focusTargets: focusSeries2?.targets
115
116
  });
116
117
  }
117
118
  });
@@ -232,11 +233,11 @@ var legend = (Component, options = {}) => {
232
233
  var _tmpl$2 = /* @__PURE__ */ template(`<div role=tooltip aria-label="Chart tooltip">`);
233
234
  var TOOLTIP_OFFSET_X = 8;
234
235
  var TOOLTIP_OFFSET_Y = 8;
235
- var getTooltipPosition = (placement, left, top, tooltipWidth, tooltipHeight) => {
236
+ var getTooltipPosition = (placement, left, top, tooltipWidth, tooltipHeight, isFixed = false) => {
236
237
  const baseX = placement.includes("left") ? left - tooltipWidth - TOOLTIP_OFFSET_X : left + TOOLTIP_OFFSET_X;
237
238
  const baseY = placement.includes("top") ? top - tooltipHeight - TOOLTIP_OFFSET_Y : top + TOOLTIP_OFFSET_Y;
238
- const viewportX = baseX - window.scrollX;
239
- const viewportY = baseY - window.scrollY;
239
+ const viewportX = isFixed ? baseX : baseX - window.scrollX;
240
+ const viewportY = isFixed ? baseY : baseY - window.scrollY;
240
241
  const overflowsLeft = viewportX < 0;
241
242
  const overflowsRight = viewportX + tooltipWidth > window.innerWidth;
242
243
  const overflowsTop = viewportY < 0;
@@ -272,12 +273,13 @@ var tooltip = (Component, options = {}) => {
272
273
  const TooltipRoot = () => {
273
274
  const _options = mergeProps({
274
275
  placement: "top-left",
276
+ fixed: false,
275
277
  id: "solid-uplot-tooltip-root",
276
278
  style: {},
277
279
  zIndex: 20
278
280
  }, options);
279
281
  const chartCursorData = () => bus.data.cursor?.state[u.root.id];
280
- const [tooltipOptions, containerProps] = splitProps(_options, ["placement"]);
282
+ const [tooltipOptions, containerProps] = splitProps(_options, ["placement", "fixed"]);
281
283
  return createComponent(Show, {
282
284
  get when() {
283
285
  return chartCursorData();
@@ -285,11 +287,13 @@ var tooltip = (Component, options = {}) => {
285
287
  children: (cursor2) => {
286
288
  const position = () => {
287
289
  const overRect = u.over.getBoundingClientRect();
288
- const absoluteLeft = overRect.left + cursor2().position.left + window.scrollX;
289
- const absoluteTop = overRect.top + cursor2().position.top + window.scrollY;
290
290
  const tooltipWidth = tooltipRoot.offsetWidth ?? 0;
291
291
  const tooltipHeight = tooltipRoot.offsetHeight ?? 0;
292
- return getTooltipPosition(tooltipOptions.placement, absoluteLeft, absoluteTop, tooltipWidth, tooltipHeight);
292
+ const cursorLeft = overRect.left + cursor2().position.left;
293
+ const cursorTop = overRect.top + cursor2().position.top;
294
+ const absoluteLeft = tooltipOptions.fixed ? cursorLeft : cursorLeft + window.scrollX;
295
+ const absoluteTop = tooltipOptions.fixed ? cursorTop : cursorTop + window.scrollY;
296
+ return getTooltipPosition(tooltipOptions.placement, absoluteLeft, absoluteTop, tooltipWidth, tooltipHeight, tooltipOptions.fixed);
293
297
  };
294
298
  return (() => {
295
299
  var _el$ = _tmpl$2();
@@ -307,11 +311,11 @@ var tooltip = (Component, options = {}) => {
307
311
  }));
308
312
  effect((_p$) => {
309
313
  var _v$ = containerProps.id, _v$2 = containerProps.class, _v$3 = {
310
- position: "absolute",
314
+ position: tooltipOptions.fixed ? "fixed" : "absolute",
311
315
  "z-index": containerProps.zIndex,
316
+ "pointer-events": "none",
312
317
  left: `${position().left}px`,
313
318
  top: `${position().top}px`,
314
- "pointer-events": "none",
315
319
  ...containerProps.style
316
320
  };
317
321
  _v$ !== _p$.e && setAttribute(_el$, "id", _p$.e = _v$);
@@ -1,6 +1,5 @@
1
- export { C as CursorData, g as getCursorData } from '../getCursorData-2qxURGuZ.js';
1
+ export { C as CursorData, S as SeriesDatum, g as getCursorData, a as getSeriesData } from '../getSeriesData-04wGQord.js';
2
2
  import uPlot from 'uplot';
3
- export { S as SeriesDatum, g as getSeriesData } from '../getSeriesData-D1zBqQ9Y.js';
4
3
 
5
4
  /**
6
5
  * Utility to get a color string from uPlot stroke or fill values
@@ -1,26 +1,2 @@
1
- export { getCursorData } from '../chunk/3TJS44N7.js';
2
- export { getSeriesData } from '../chunk/ZISGD6FJ.js';
3
- import 'uplot';
4
-
5
- // src/utils/getColorString.ts
6
- var getColorString = (fillOrStroke, fallback = "#888") => {
7
- return typeof fillOrStroke === "string" ? fillOrStroke : fallback;
8
- };
9
- function getNewCalendarDayIndices(u) {
10
- const xValues = u.data[0];
11
- if (!xValues || !xValues.length) return [];
12
- const seen = /* @__PURE__ */ new Set();
13
- const indices = [];
14
- for (let i = 0; i < xValues.length; i++) {
15
- const ts = xValues[i];
16
- const date = new Date(ts);
17
- const dayString = date.toISOString().split("T")[0];
18
- if (!seen.has(dayString)) {
19
- seen.add(dayString);
20
- indices.push(i);
21
- }
22
- }
23
- return indices;
24
- }
25
-
26
- export { getColorString, getNewCalendarDayIndices };
1
+ export { getColorString, getNewCalendarDayIndices } from '../chunk/IV6D7K4M.js';
2
+ export { getCursorData, getSeriesData } from '../chunk/QELYARMN.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dschz/solid-uplot",
3
- "version": "0.1.7",
3
+ "version": "0.3.0",
4
4
  "description": "SolidJS wrapper for uPlot — ultra-fast, tiny time-series & charting library",
5
5
  "type": "module",
6
6
  "author": "Daniel Sanchez <dsanc89@pm.me>",
@@ -59,14 +59,12 @@
59
59
  "browser": {},
60
60
  "exports": {
61
61
  ".": {
62
- "solid": "./dist/index/index.jsx",
63
62
  "import": {
64
63
  "types": "./dist/index/index.d.ts",
65
64
  "default": "./dist/index/index.js"
66
65
  }
67
66
  },
68
67
  "./plugins": {
69
- "solid": "./dist/plugins/index.jsx",
70
68
  "import": {
71
69
  "types": "./dist/plugins/index.d.ts",
72
70
  "default": "./dist/plugins/index.js"
@@ -107,44 +105,46 @@
107
105
  "typecheck": "tsc --noEmit"
108
106
  },
109
107
  "devDependencies": {
110
- "@changesets/cli": "^2.29.3",
111
- "@dschz/solid-auto-sizer": "^0.1.0",
112
- "@solidjs/router": "^0.15.3",
108
+ "@changesets/cli": "^2.29.7",
109
+ "@dschz/solid-auto-sizer": "^0.1.3",
110
+ "@solidjs/router": "^0.15.4",
113
111
  "@solidjs/testing-library": "^0.8.10",
114
- "@tailwindcss/vite": "^4.1.5",
115
- "@testing-library/jest-dom": "^6.6.3",
112
+ "@tailwindcss/vite": "^4.1.17",
113
+ "@testing-library/jest-dom": "^6.9.1",
116
114
  "@testing-library/user-event": "^14.6.1",
117
- "@types/bun": "^1.2.12",
118
- "@types/prismjs": "^1.26.3",
119
- "@typescript-eslint/eslint-plugin": "^8.32.0",
120
- "@typescript-eslint/parser": "^8.32.0",
121
- "@vitest/coverage-istanbul": "^3.1.3",
115
+ "@types/bun": "^1.3.3",
116
+ "@types/prismjs": "^1.26.5",
117
+ "@typescript-eslint/eslint-plugin": "^8.47.0",
118
+ "@typescript-eslint/parser": "^8.47.0",
119
+ "@vitest/coverage-istanbul": "^4.0.13",
122
120
  "@wessberg/pointer-events": "^1.0.9",
123
- "canvas": "^3.1.0",
124
- "eslint": "^9.26.0",
121
+ "canvas": "^3.2.0",
122
+ "eslint": "^9.39.1",
125
123
  "eslint-plugin-simple-import-sort": "^12.1.1",
126
124
  "eslint-plugin-solid": "^0.14.5",
127
- "globals": "^16.1.0",
128
- "jiti": "^2.4.2",
129
- "jsdom": "^26.1.0",
130
- "path2d": "^0.2.2",
131
- "prettier": "^3.5.3",
132
- "prismjs": "^1.29.0",
133
- "solid-js": "^1.9.6",
134
- "solid-prism-editor": "^2.0.0",
135
- "tailwindcss": "^4.1.5",
136
- "tsup": "^8.4.0",
125
+ "globals": "^16.5.0",
126
+ "jiti": "^2.6.1",
127
+ "jsdom": "^27.2.0",
128
+ "path2d": "^0.3.1",
129
+ "prettier": "^3.6.2",
130
+ "prismjs": "^1.30.0",
131
+ "solid-js": "^1.9.10",
132
+ "solid-prism-editor": "^2.1.0",
133
+ "tailwindcss": "^4.1.17",
134
+ "tsup": "^8.5.1",
137
135
  "tsup-preset-solid": "^2.2.0",
138
- "typescript": "^5.8.3",
139
- "typescript-eslint": "^8.32.0",
136
+ "typescript": "^5.9.3",
137
+ "typescript-eslint": "^8.47.0",
140
138
  "uplot": "^1.6.32",
141
- "vite": "^6.3.5",
142
- "vite-plugin-solid": "^2.11.6",
143
- "vitest": "^3.1.3"
139
+ "vite": "^7.2.4",
140
+ "vite-plugin-solid": "^2.11.10",
141
+ "vitest": "^4.0.13"
144
142
  },
145
143
  "peerDependencies": {
146
144
  "solid-js": ">=1.6.0",
147
145
  "uplot": ">=1.6.32"
148
146
  },
149
- "dependencies": {}
147
+ "dependencies": {
148
+ "@solid-primitives/refs": "^1.1.2"
149
+ }
150
150
  }
@@ -1,15 +0,0 @@
1
- // src/utils/getCursorData.ts
2
- var getCursorData = (u) => {
3
- const idx = u.cursor.idx;
4
- const xValues = u.data[0];
5
- const isValid = idx != null && xValues && idx < xValues.length;
6
- return !isValid ? void 0 : {
7
- plotId: u.root.id,
8
- idx,
9
- xValue: xValues[idx],
10
- visible: Boolean(u.cursor.show),
11
- position: { left: u.cursor.left || 0, top: u.cursor.top || 0 }
12
- };
13
- };
14
-
15
- export { getCursorData };
@@ -1,25 +0,0 @@
1
- // src/utils/getSeriesData.ts
2
- var getSeriesData = (u, options = {}) => {
3
- const series = [];
4
- for (let i = 1; i < u.series.length; i++) {
5
- const s = u.series[i];
6
- const stroke = typeof s.stroke === "function" ? s.stroke(u, i) : s.stroke;
7
- const fill = typeof s.fill === "function" ? s.fill(u, i) : s.fill;
8
- const label = options.labelTransform?.(s.label) || s.label?.toString() || `Series ${i}`;
9
- series.push({
10
- idx: i - 1,
11
- seriesIdx: i,
12
- label,
13
- stroke: stroke ?? "#000",
14
- fill: fill ?? "transparent",
15
- width: s.width,
16
- dash: s.dash,
17
- scale: s.scale,
18
- visible: Boolean(s.show)
19
- });
20
- }
21
- return series;
22
- };
23
- // istanbul ignore next -- @preserve
24
-
25
- export { getSeriesData };
@@ -1,44 +0,0 @@
1
- type CursorPosition = {
2
- /**
3
- * The cursor position left offset in CSS pixels (relative to plotting area)
4
- */
5
- readonly left: number;
6
- /**
7
- * The cursor position top offset in CSS pixels (relative to plotting area)
8
- */
9
- readonly top: number;
10
- };
11
- /**
12
- * The cursor index data for a given uPlot instance.
13
- */
14
- type CursorData = {
15
- /**
16
- * The id of the plot instance that the cursor message originates from.
17
- */
18
- readonly plotId: string;
19
- /**
20
- * The closest x-axis data index to cursor (closestIdx)
21
- */
22
- readonly idx: number;
23
- /**
24
- * The x-axis value of the cursor idx.
25
- */
26
- readonly xValue: number;
27
- /**
28
- * The position of the cursor.
29
- */
30
- readonly position: CursorPosition;
31
- /**
32
- * The visibility of the cursor.
33
- */
34
- readonly visible: boolean;
35
- };
36
- /**
37
- * Get the cursor index data for a given uPlot instance.
38
- *
39
- * @param u - The uPlot instance.
40
- * @returns The cursor data.
41
- */
42
- declare const getCursorData: (u: uPlot) => CursorData | undefined;
43
-
44
- export { type CursorData as C, getCursorData as g };
@@ -1,135 +0,0 @@
1
- import {
2
- getSeriesData
3
- } from "../chunk/A3AZKFSW.jsx";
4
-
5
- // src/createPluginBus.tsx
6
- import { createStore } from "solid-js/store";
7
- var createPluginBus = (initialData = {}) => {
8
- const [data, setData] = createStore(initialData);
9
- return { data, setData };
10
- };
11
-
12
- // src/SolidUplot.tsx
13
- import "uplot/dist/uPlot.min.css";
14
- import {
15
- createEffect,
16
- createMemo,
17
- createUniqueId,
18
- mergeProps,
19
- onCleanup,
20
- splitProps,
21
- untrack
22
- } from "solid-js";
23
- import uPlot from "uplot";
24
- var SolidUplot = (props) => {
25
- let container;
26
- const _props = mergeProps(
27
- {
28
- id: createUniqueId(),
29
- childrenPlacement: "top",
30
- width: 600,
31
- height: 300,
32
- autoResize: false,
33
- data: [],
34
- resetScales: true,
35
- plugins: [],
36
- legend: {
37
- show: false
38
- }
39
- },
40
- props
41
- );
42
- const [local, options] = splitProps(_props, [
43
- "children",
44
- "childrenPlacement",
45
- "autoResize",
46
- "onCreate",
47
- "style",
48
- "ref"
49
- ]);
50
- const [updateableOptions, newChartOptions] = splitProps(options, [
51
- "data",
52
- "width",
53
- "height",
54
- "resetScales"
55
- ]);
56
- const [system, chartOptions] = splitProps(newChartOptions, ["pluginBus", "plugins"]);
57
- const size = () => ({ width: updateableOptions.width, height: updateableOptions.height });
58
- const chartPlugins = createMemo(() => {
59
- return system.plugins.map(
60
- (plugin) => typeof plugin === "function" ? plugin({ bus: system.pluginBus }) : plugin
61
- );
62
- });
63
- createEffect(() => {
64
- const getInitialSize = () => {
65
- if (local.autoResize) {
66
- const rect = container.getBoundingClientRect();
67
- return {
68
- width: rect.width > 0 ? Math.floor(rect.width) : 600,
69
- height: rect.height > 0 ? Math.floor(rect.height) : 300
70
- };
71
- }
72
- return untrack(size);
73
- };
74
- const initialSize = getInitialSize();
75
- const initialData = untrack(() => updateableOptions.data);
76
- const chart = new uPlot(
77
- {
78
- ...chartOptions,
79
- ...initialSize,
80
- plugins: chartPlugins()
81
- },
82
- initialData,
83
- container
84
- );
85
- local.onCreate?.(chart, { seriesData: getSeriesData(chart) });
86
- createEffect(() => {
87
- if (local.autoResize) return;
88
- chart.setSize(size());
89
- });
90
- createEffect(() => {
91
- if (!local.autoResize) return;
92
- const resizeObserver = new ResizeObserver((entries) => {
93
- for (const entry of entries) {
94
- const { width, height } = entry.contentRect;
95
- chart.setSize({ width: Math.floor(width), height: Math.floor(height) });
96
- }
97
- });
98
- resizeObserver.observe(container);
99
- onCleanup(() => {
100
- resizeObserver.disconnect();
101
- });
102
- });
103
- createEffect(() => {
104
- chart.setData(updateableOptions.data, updateableOptions.resetScales);
105
- });
106
- onCleanup(() => {
107
- chart.destroy();
108
- });
109
- });
110
- return <div
111
- id="solid-uplot-root"
112
- style={{
113
- display: "flex",
114
- "flex-direction": local.childrenPlacement === "top" ? "column" : "column-reverse",
115
- // When autoResize is enabled, fill the parent container
116
- ...local.autoResize && {
117
- width: "100%",
118
- height: "100%",
119
- "min-width": "0",
120
- "min-height": "0"
121
- },
122
- ...local.style
123
- }}
124
- ref={(el) => {
125
- container = el;
126
- local.ref?.(el);
127
- }}
128
- >
129
- {local.children}
130
- </div>;
131
- };
132
- export {
133
- SolidUplot,
134
- createPluginBus
135
- };
@@ -1,347 +0,0 @@
1
- import {
2
- getSeriesData
3
- } from "../chunk/A3AZKFSW.jsx";
4
-
5
- // src/utils/getCursorData.ts
6
- var getCursorData = (u) => {
7
- const idx = u.cursor.idx;
8
- const xValues = u.data[0];
9
- const isValid = idx != null && xValues && idx < xValues.length;
10
- return !isValid ? void 0 : {
11
- plotId: u.root.id,
12
- idx,
13
- xValue: xValues[idx],
14
- visible: Boolean(u.cursor.show),
15
- position: { left: u.cursor.left || 0, top: u.cursor.top || 0 }
16
- };
17
- };
18
-
19
- // src/plugins/cursor.ts
20
- var cursor = () => {
21
- return ({ bus }) => {
22
- if (!bus) {
23
- return { hooks: {} };
24
- }
25
- bus.setData("cursor", {
26
- state: {}
27
- });
28
- let pointerEnter;
29
- let pointerLeave;
30
- return {
31
- hooks: {
32
- ready: (u) => {
33
- pointerEnter = () => {
34
- bus.setData("cursor", { sourceId: u.root.id });
35
- };
36
- pointerLeave = () => {
37
- bus.setData("cursor", { sourceId: void 0 });
38
- };
39
- u.over.addEventListener("pointerenter", pointerEnter);
40
- u.over.addEventListener("pointerleave", pointerLeave);
41
- },
42
- setCursor: (u) => {
43
- bus.setData("cursor", "state", u.root.id, getCursorData(u));
44
- },
45
- setData: (u) => {
46
- bus.setData("cursor", (prev) => ({
47
- ...prev ?? {},
48
- state: {
49
- ...prev?.state ?? {},
50
- [u.root.id]: getCursorData(u)
51
- }
52
- }));
53
- },
54
- destroy: (u) => {
55
- bus.setData("cursor", "state", u.root.id, void 0);
56
- u.over.removeEventListener("pointerenter", pointerEnter);
57
- u.over.removeEventListener("pointerleave", pointerLeave);
58
- }
59
- }
60
- };
61
- };
62
- };
63
-
64
- // src/plugins/focusSeries.ts
65
- import { createEffect, createRoot } from "solid-js";
66
- var DEFAULT_UNFOCUSED_ALPHA = 0.1;
67
- var DEFAULT_FOCUSED_ALPHA = 1;
68
- var DEFAULT_REBUILD_PATHS = false;
69
- var seriesFocusRedraw = (u, options = {}) => {
70
- const {
71
- unfocusedAlpha = DEFAULT_UNFOCUSED_ALPHA,
72
- focusedAlpha = DEFAULT_FOCUSED_ALPHA,
73
- rebuildPaths = DEFAULT_REBUILD_PATHS,
74
- focusTargets
75
- } = options;
76
- for (let i = 1; i < u.series.length; i++) {
77
- const s = u.series[i];
78
- if (!focusTargets || !focusTargets.length) {
79
- s.alpha = 1;
80
- continue;
81
- }
82
- const target = focusTargets.find((t) => {
83
- if ("label" in t) return t.label === s.label;
84
- if ("zeroIndex" in t) return t.zeroIndex === i - 1;
85
- if ("index" in t) return t.index === i;
86
- });
87
- s.alpha = target ? focusedAlpha : unfocusedAlpha;
88
- }
89
- u.redraw(rebuildPaths);
90
- };
91
- var focusSeries = (options = {}) => {
92
- return ({ bus }) => {
93
- if (!bus) {
94
- return { hooks: {} };
95
- }
96
- const {
97
- pxThreshold = 5,
98
- unfocusedAlpha = DEFAULT_UNFOCUSED_ALPHA,
99
- focusedAlpha = DEFAULT_FOCUSED_ALPHA,
100
- rebuildPaths = DEFAULT_REBUILD_PATHS
101
- } = options;
102
- let dispose;
103
- let pointerLeave;
104
- return {
105
- hooks: {
106
- ready: (u) => {
107
- pointerLeave = () => {
108
- bus.setData("focusSeries", void 0);
109
- };
110
- queueMicrotask(() => {
111
- if (bus.data.focusSeries) {
112
- seriesFocusRedraw(u, {
113
- unfocusedAlpha,
114
- focusedAlpha,
115
- rebuildPaths,
116
- focusTargets: bus.data.focusSeries.targets
117
- });
118
- }
119
- });
120
- u.over.addEventListener("pointerleave", pointerLeave);
121
- dispose = createRoot((dispose2) => {
122
- createEffect(() => {
123
- const cursor2 = bus.data.cursor;
124
- const focus = bus.data.focusSeries;
125
- if (cursor2?.sourceId !== u.root.id) {
126
- seriesFocusRedraw(u, {
127
- unfocusedAlpha,
128
- focusedAlpha,
129
- rebuildPaths,
130
- focusTargets: focus?.targets
131
- });
132
- }
133
- });
134
- return dispose2;
135
- });
136
- },
137
- setCursor: (u) => {
138
- const cursor2 = bus.data.cursor;
139
- const chartCursor = cursor2?.state[u.root.id];
140
- if (!cursor2 || !chartCursor || cursor2.sourceId !== u.root.id) return;
141
- const focusTargets = [];
142
- for (let i = 1; i < u.series.length; i++) {
143
- const s = u.series[i];
144
- const yVals = u.data[i];
145
- const val = yVals?.[chartCursor.idx];
146
- if (!s.show || !yVals || val == null) continue;
147
- const yPos = u.valToPos(val, s.scale);
148
- const dist = Math.abs(yPos - chartCursor.position.top);
149
- if (dist <= pxThreshold) {
150
- if (s.label != null) {
151
- focusTargets.push({ label: s.label });
152
- } else {
153
- focusTargets.push({ index: i });
154
- }
155
- }
156
- }
157
- seriesFocusRedraw(u, {
158
- unfocusedAlpha,
159
- focusedAlpha,
160
- rebuildPaths,
161
- focusTargets
162
- });
163
- bus.setData("focusSeries", {
164
- sourceId: u.root.id,
165
- targets: focusTargets
166
- });
167
- },
168
- destroy: (u) => {
169
- dispose();
170
- u.over.removeEventListener("pointerleave", pointerLeave);
171
- }
172
- }
173
- };
174
- };
175
- };
176
-
177
- // src/plugins/legend.tsx
178
- import { mergeProps, splitProps } from "solid-js";
179
- import { render } from "solid-js/web";
180
- import "uplot";
181
- var legend = (Component, options = {}) => {
182
- return ({ bus }) => {
183
- if (!bus) {
184
- return { hooks: {} };
185
- }
186
- let legendRoot;
187
- let dispose;
188
- return {
189
- hooks: {
190
- ready: (u) => {
191
- const seriesData = getSeriesData(u);
192
- const LegendRoot = () => {
193
- const _options = mergeProps(
194
- {
195
- placement: "top-left",
196
- pxOffset: 8,
197
- id: "solid-uplot-legend-root",
198
- zIndex: 10
199
- },
200
- options
201
- );
202
- const [legendOptions, containerProps] = splitProps(_options, ["placement", "pxOffset"]);
203
- const containerStyle = () => {
204
- const overRect = u.over.getBoundingClientRect();
205
- const offset = legendOptions.pxOffset;
206
- return {
207
- position: "absolute",
208
- [legendOptions.placement === "top-left" ? "left" : "right"]: `${offset}px`,
209
- top: `${offset}px`,
210
- "max-width": `${overRect.width - offset * 2}px`,
211
- "max-height": `${overRect.height - offset * 2}px`,
212
- "z-index": containerProps.zIndex,
213
- "pointer-events": "auto",
214
- overflow: "auto",
215
- ...containerProps.style
216
- };
217
- };
218
- return <div
219
- ref={legendRoot}
220
- id={containerProps.id}
221
- class={containerProps.class}
222
- role="group"
223
- aria-label="Chart legend"
224
- style={containerStyle()}
225
- >
226
- <Component u={u} seriesData={seriesData} bus={bus} />
227
- </div>;
228
- };
229
- dispose = render(() => <LegendRoot />, u.over);
230
- },
231
- destroy: () => {
232
- dispose();
233
- legendRoot?.remove();
234
- }
235
- }
236
- };
237
- };
238
- };
239
-
240
- // src/plugins/tooltip.tsx
241
- import { mergeProps as mergeProps2, Show, splitProps as splitProps2 } from "solid-js";
242
- import { render as render2 } from "solid-js/web";
243
- import "uplot";
244
- var TOOLTIP_OFFSET_X = 8;
245
- var TOOLTIP_OFFSET_Y = 8;
246
- var getTooltipPosition = (placement, left, top, tooltipWidth, tooltipHeight) => {
247
- const baseX = placement.includes("left") ? left - tooltipWidth - TOOLTIP_OFFSET_X : left + TOOLTIP_OFFSET_X;
248
- const baseY = placement.includes("top") ? top - tooltipHeight - TOOLTIP_OFFSET_Y : top + TOOLTIP_OFFSET_Y;
249
- const viewportX = baseX - window.scrollX;
250
- const viewportY = baseY - window.scrollY;
251
- const overflowsLeft = viewportX < 0;
252
- const overflowsRight = viewportX + tooltipWidth > window.innerWidth;
253
- const overflowsTop = viewportY < 0;
254
- const overflowsBottom = viewportY + tooltipHeight > window.innerHeight;
255
- let flipX = false;
256
- let flipY = false;
257
- if (placement.includes("left") && overflowsLeft) flipX = true;
258
- if (placement.includes("right") && overflowsRight) flipX = true;
259
- if (placement.includes("top") && overflowsTop) flipY = true;
260
- if (placement.includes("bottom") && overflowsBottom) flipY = true;
261
- const finalX = flipX && placement.includes("left") ? left + TOOLTIP_OFFSET_X : flipX && placement.includes("right") ? left - tooltipWidth - TOOLTIP_OFFSET_X : baseX;
262
- const finalY = flipY && placement.includes("top") ? top + TOOLTIP_OFFSET_Y : flipY && placement.includes("bottom") ? top - tooltipHeight - TOOLTIP_OFFSET_Y : baseY;
263
- return {
264
- left: finalX,
265
- top: finalY
266
- };
267
- };
268
- var tooltip = (Component, options = {}) => {
269
- return ({ bus }) => {
270
- if (!bus) {
271
- return { hooks: {} };
272
- }
273
- let tooltipRoot;
274
- let dispose;
275
- return {
276
- hooks: {
277
- ready: (u) => {
278
- const seriesData = getSeriesData(u);
279
- const TooltipRoot = () => {
280
- const _options = mergeProps2(
281
- {
282
- placement: "top-left",
283
- id: "solid-uplot-tooltip-root",
284
- style: {},
285
- zIndex: 20
286
- },
287
- options
288
- );
289
- const chartCursorData = () => bus.data.cursor?.state[u.root.id];
290
- const [tooltipOptions, containerProps] = splitProps2(_options, ["placement"]);
291
- return <Show when={chartCursorData()}>
292
- {(cursor2) => {
293
- const position = () => {
294
- const overRect = u.over.getBoundingClientRect();
295
- const absoluteLeft = overRect.left + cursor2().position.left + window.scrollX;
296
- const absoluteTop = overRect.top + cursor2().position.top + window.scrollY;
297
- const tooltipWidth = tooltipRoot.offsetWidth ?? 0;
298
- const tooltipHeight = tooltipRoot.offsetHeight ?? 0;
299
- return getTooltipPosition(
300
- tooltipOptions.placement,
301
- absoluteLeft,
302
- absoluteTop,
303
- tooltipWidth,
304
- tooltipHeight
305
- );
306
- };
307
- return <div
308
- ref={tooltipRoot}
309
- id={containerProps.id}
310
- class={containerProps.class}
311
- role="tooltip"
312
- aria-label="Chart tooltip"
313
- style={{
314
- position: "absolute",
315
- "z-index": containerProps.zIndex,
316
- left: `${position().left}px`,
317
- top: `${position().top}px`,
318
- "pointer-events": "none",
319
- ...containerProps.style
320
- }}
321
- >
322
- <Component
323
- u={u}
324
- seriesData={seriesData}
325
- cursor={cursor2()}
326
- focusedSeries={bus.data.focusSeries}
327
- />
328
- </div>;
329
- }}
330
- </Show>;
331
- };
332
- dispose = render2(() => <TooltipRoot />, u.root);
333
- },
334
- destroy: () => {
335
- dispose();
336
- tooltipRoot?.remove();
337
- }
338
- }
339
- };
340
- };
341
- };
342
- export {
343
- cursor,
344
- focusSeries,
345
- legend,
346
- tooltip
347
- };