@watt-ui/react 0.1.1 → 0.1.2

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 (50) hide show
  1. package/README.md +1 -0
  2. package/dist/components/alarm-feed.d.ts +2 -2
  3. package/dist/components/alarm-feed.d.ts.map +1 -1
  4. package/dist/components/alarm-feed.js +38 -16
  5. package/dist/components/alarm-feed.js.map +1 -1
  6. package/dist/components/asset-tree.d.ts +6 -2
  7. package/dist/components/asset-tree.d.ts.map +1 -1
  8. package/dist/components/asset-tree.js +15 -8
  9. package/dist/components/asset-tree.js.map +1 -1
  10. package/dist/components/battery-soc.d.ts +2 -2
  11. package/dist/components/battery-soc.d.ts.map +1 -1
  12. package/dist/components/battery-soc.js +4 -3
  13. package/dist/components/battery-soc.js.map +1 -1
  14. package/dist/components/data-freshness.d.ts +2 -2
  15. package/dist/components/data-freshness.d.ts.map +1 -1
  16. package/dist/components/data-freshness.js +8 -3
  17. package/dist/components/data-freshness.js.map +1 -1
  18. package/dist/components/power-metric.d.ts +5 -3
  19. package/dist/components/power-metric.d.ts.map +1 -1
  20. package/dist/components/power-metric.js +10 -5
  21. package/dist/components/power-metric.js.map +1 -1
  22. package/dist/components/status-indicator.d.ts +2 -2
  23. package/dist/components/status-indicator.d.ts.map +1 -1
  24. package/dist/components/status-indicator.js +8 -3
  25. package/dist/components/status-indicator.js.map +1 -1
  26. package/dist/components/telemetry-chart.d.ts +5 -7
  27. package/dist/components/telemetry-chart.d.ts.map +1 -1
  28. package/dist/components/telemetry-chart.js +30 -21
  29. package/dist/components/telemetry-chart.js.map +1 -1
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/motion.d.ts +12 -0
  33. package/dist/motion.d.ts.map +1 -0
  34. package/dist/motion.js +90 -0
  35. package/dist/motion.js.map +1 -0
  36. package/dist/styles.css +36 -1
  37. package/dist/types.d.ts +10 -1
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/components/alarm-feed.tsx +97 -62
  41. package/src/components/asset-tree.tsx +56 -31
  42. package/src/components/battery-soc.tsx +29 -14
  43. package/src/components/data-freshness.tsx +34 -17
  44. package/src/components/power-metric.tsx +32 -17
  45. package/src/components/status-indicator.tsx +34 -15
  46. package/src/components/telemetry-chart.tsx +60 -38
  47. package/src/index.ts +5 -1
  48. package/src/motion.tsx +117 -0
  49. package/src/styles.css +36 -1
  50. package/src/types.ts +30 -4
@@ -11,8 +11,10 @@ import {
11
11
  } from "@headless-tree/core";
12
12
  import { useVirtualizer } from "@tanstack/react-virtual";
13
13
  import { cx } from "../internal.js";
14
+ import { Slot } from "@radix-ui/react-slot";
15
+ import { MotionProvider, motionStyle } from "../motion.js";
14
16
  import type { ForwardedRef, RefAttributes, ReactElement } from "react";
15
- import type { Density, PlantAsset } from "../types.js";
17
+ import type { Density, MotionProps, PartProps, PlantAsset } from "../types.js";
16
18
  export interface AssetTreeRow<T> {
17
19
  item: T;
18
20
  level: number;
@@ -30,10 +32,9 @@ export type AssetTreeItem<T extends PlantAsset> =
30
32
  ? never
31
33
  : AssetTreeItem<C>
32
34
  : never);
33
- export interface AssetTreeProps<T extends PlantAsset = PlantAsset> extends Omit<
34
- React.HTMLAttributes<HTMLDivElement>,
35
- "children"
36
- > {
35
+ export interface AssetTreeProps<T extends PlantAsset = PlantAsset>
36
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "children">,
37
+ MotionProps {
37
38
  items: readonly T[];
38
39
  children: (row: AssetTreeRow<AssetTreeItem<T>>) => React.ReactNode;
39
40
  expandedIds?: string[];
@@ -64,6 +65,7 @@ function Root<T extends PlantAsset>(
64
65
  height = 360,
65
66
  rowHeight = density === "compact" ? 36 : 48,
66
67
  empty,
68
+ motion,
67
69
  className,
68
70
  style,
69
71
  ...props
@@ -203,7 +205,8 @@ function Root<T extends PlantAsset>(
203
205
  data-slot="asset-tree-row"
204
206
  data-index={index}
205
207
  data-selected={item.isSelected() || undefined}
206
- className="absolute top-0 left-0 flex w-full items-center border-b border-[var(--watt-border)] outline-offset-[-2px] data-[selected]:bg-[var(--watt-subtle)] focus-visible:outline-2 focus-visible:outline-[var(--watt-accent)]"
208
+ data-expanded={item.isExpanded()}
209
+ className="watt-motion absolute top-0 left-0 flex w-full items-center border-b border-[var(--watt-border)] outline-offset-[-2px] data-[selected]:bg-[var(--watt-subtle)] focus-visible:outline-2 focus-visible:outline-[var(--watt-accent)]"
207
210
  style={{
208
211
  height: rowHeight,
209
212
  paddingInlineStart: item.getItemMeta().level * 20,
@@ -224,34 +227,56 @@ function Root<T extends PlantAsset>(
224
227
  );
225
228
  };
226
229
  return (
227
- <div
228
- {...containerProps}
229
- {...props}
230
- ref={hostRef}
231
- data-slot="asset-tree"
232
- data-density={density}
233
- className={cx("watt-root overflow-auto text-sm", className)}
234
- style={{ height, ...style }}
235
- >
236
- {!items.length ? (
237
- empty
238
- ) : (
239
- <div
240
- role="presentation"
241
- style={{ height: virtualizer.getTotalSize(), position: "relative" }}
242
- >
243
- {virtualizer.getVirtualItems().map((row) => {
244
- const item = visible[row.index];
245
- return item && indexed.has(item.getId())
246
- ? renderRow(item, row.index, row.start)
247
- : null;
248
- })}
249
- </div>
250
- )}
251
- </div>
230
+ <MotionProvider motion={motion}>
231
+ <div
232
+ {...containerProps}
233
+ {...props}
234
+ ref={hostRef}
235
+ data-slot="asset-tree"
236
+ data-density={density}
237
+ className={cx("watt-root overflow-auto text-sm", className)}
238
+ style={{ height, ...style, ...motionStyle(motion) }}
239
+ >
240
+ {!items.length ? (
241
+ empty
242
+ ) : (
243
+ <div
244
+ role="presentation"
245
+ style={{ height: virtualizer.getTotalSize(), position: "relative" }}
246
+ >
247
+ {virtualizer.getVirtualItems().map((row) => {
248
+ const item = visible[row.index];
249
+ return item && indexed.has(item.getId())
250
+ ? renderRow(item, row.index, row.start)
251
+ : null;
252
+ })}
253
+ </div>
254
+ )}
255
+ </div>
256
+ </MotionProvider>
252
257
  );
253
258
  }
259
+ export interface AssetTreeIndicatorProps extends PartProps {
260
+ expanded: boolean;
261
+ }
262
+ const Indicator = forwardRef<HTMLElement, AssetTreeIndicatorProps>(
263
+ ({ expanded, asChild, className, style, ...props }, ref) => {
264
+ const Component = asChild ? Slot : "span";
265
+ return (
266
+ <Component
267
+ {...props}
268
+ ref={ref as never}
269
+ aria-hidden={props["aria-hidden"] ?? true}
270
+ data-slot="asset-tree-indicator"
271
+ data-expanded={expanded}
272
+ className={cx("watt-motion inline-flex shrink-0", className)}
273
+ style={{ transform: `rotate(${expanded ? 90 : 0}deg)`, ...style }}
274
+ />
275
+ );
276
+ },
277
+ );
254
278
  export const AssetTree = {
279
+ Indicator,
255
280
  Root: forwardRef(Root) as <T extends PlantAsset = PlantAsset>(
256
281
  props: AssetTreeProps<T> & RefAttributes<HTMLDivElement>,
257
282
  ) => ReactElement,
@@ -2,7 +2,8 @@
2
2
  import { createContext, forwardRef, useContext } from "react";
3
3
  import { Slot } from "@radix-ui/react-slot";
4
4
  import { cx, part } from "../internal.js";
5
- import type { PartProps, RootProps, Density } from "../types.js";
5
+ import type { PartProps, RootProps, Density, MotionProps } from "../types.js";
6
+ import { MotionProvider, motionStyle } from "../motion.js";
6
7
  const Context = createContext<{
7
8
  value: number | null;
8
9
  density: Density;
@@ -13,25 +14,39 @@ function useSOC() {
13
14
  throw new Error("BatterySOC parts require BatterySOC.Root");
14
15
  return value;
15
16
  }
16
- export interface BatterySOCProps extends RootProps {
17
+ export interface BatterySOCProps extends RootProps, MotionProps {
17
18
  value: number | null;
18
19
  }
19
20
  const Root = forwardRef<HTMLElement, BatterySOCProps>(
20
- ({ value, asChild, density = "comfortable", className, ...props }, ref) => {
21
+ (
22
+ {
23
+ value,
24
+ asChild,
25
+ density = "comfortable",
26
+ motion,
27
+ className,
28
+ style,
29
+ ...props
30
+ },
31
+ ref,
32
+ ) => {
21
33
  const Component = asChild ? Slot : "div";
22
34
  const valid =
23
35
  value !== null && Number.isFinite(value) && value >= 0 && value <= 100;
24
36
  return (
25
- <Context.Provider value={{ value: valid ? value : null, density }}>
26
- <Component
27
- {...props}
28
- ref={ref as never}
29
- data-slot="battery-soc"
30
- data-invalid={!valid || undefined}
31
- data-density={density}
32
- className={cx("watt-root flex flex-col gap-3", className)}
33
- />
34
- </Context.Provider>
37
+ <MotionProvider motion={motion}>
38
+ <Context.Provider value={{ value: valid ? value : null, density }}>
39
+ <Component
40
+ {...props}
41
+ ref={ref as never}
42
+ data-slot="battery-soc"
43
+ data-invalid={!valid || undefined}
44
+ data-density={density}
45
+ style={{ ...style, ...motionStyle(motion) }}
46
+ className={cx("watt-root flex flex-col gap-3", className)}
47
+ />
48
+ </Context.Provider>
49
+ </MotionProvider>
35
50
  );
36
51
  },
37
52
  );
@@ -68,7 +83,7 @@ const Fill = forwardRef<HTMLElement, PartProps>(
68
83
  aria-hidden="true"
69
84
  data-slot="battery-soc-fill"
70
85
  className={cx(
71
- "h-full bg-[var(--watt-accent)] transition-[width] duration-500 motion-reduce:transition-none",
86
+ "watt-motion watt-soc-fill h-full bg-[var(--watt-accent)]",
72
87
  className,
73
88
  )}
74
89
  style={{ width: `${value ?? 0}%`, ...style }}
@@ -1,9 +1,17 @@
1
1
  "use client";
2
- import { createContext, forwardRef, useContext } from "react";
2
+ import { createContext, forwardRef, useContext, useRef } from "react";
3
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
3
4
  import { Slot } from "@radix-ui/react-slot";
4
5
  import { cx, part, useNow } from "../internal.js";
5
6
  import { parseTimestamp, formatTime } from "../domain.js";
6
- import type { DataQuality, PartProps, RootProps, Timestamp } from "../types.js";
7
+ import type {
8
+ DataQuality,
9
+ MotionProps,
10
+ PartProps,
11
+ RootProps,
12
+ Timestamp,
13
+ } from "../types.js";
14
+ import { MotionProvider, motionStyle, useChangeFeedback } from "../motion.js";
7
15
  interface Freshness {
8
16
  timestamp: Timestamp | null;
9
17
  quality?: DataQuality;
@@ -14,7 +22,7 @@ function useFreshness() {
14
22
  if (!ctx) throw new Error("DataFreshness parts require DataFreshness.Root");
15
23
  return ctx;
16
24
  }
17
- export interface DataFreshnessProps extends RootProps, Freshness {}
25
+ export interface DataFreshnessProps extends RootProps, Freshness, MotionProps {}
18
26
  const Root = forwardRef<HTMLElement, DataFreshnessProps>(
19
27
  (
20
28
  {
@@ -23,29 +31,38 @@ const Root = forwardRef<HTMLElement, DataFreshnessProps>(
23
31
  density = "comfortable",
24
32
  asChild,
25
33
  className,
34
+ motion,
35
+ style,
26
36
  ...props
27
37
  },
28
38
  ref,
29
39
  ) => {
40
+ const host = useRef<HTMLElement>(null);
41
+ const hostRef = useComposedRefs(host, ref);
42
+ useChangeFeedback(host, quality, motion);
30
43
  const Component = asChild ? Slot : "span";
31
44
  return (
32
- <Context.Provider value={{ timestamp, quality }}>
33
- <Component
34
- {...props}
35
- ref={ref as never}
36
- data-slot="data-freshness"
37
- data-density={density}
38
- data-quality={quality}
39
- className={cx(
40
- "watt-root inline-flex items-center gap-2 text-sm text-[var(--watt-muted)] data-[density=compact]:gap-1 data-[density=compact]:text-xs",
41
- className,
42
- )}
43
- />
44
- </Context.Provider>
45
+ <MotionProvider motion={motion}>
46
+ <Context.Provider value={{ timestamp, quality }}>
47
+ <Component
48
+ {...props}
49
+ ref={hostRef as never}
50
+ data-slot="data-freshness"
51
+ data-density={density}
52
+ data-quality={quality}
53
+ style={{ ...style, ...motionStyle(motion) }}
54
+ className={cx(
55
+ "watt-root watt-motion inline-flex items-center gap-2 text-sm text-[var(--watt-muted)] data-[density=compact]:gap-1 data-[density=compact]:text-xs",
56
+ className,
57
+ )}
58
+ />
59
+ </Context.Provider>
60
+ </MotionProvider>
45
61
  );
46
62
  },
47
63
  );
48
- export interface FreshnessTimeProps extends React.ComponentPropsWithoutRef<"time"> {
64
+ export interface FreshnessTimeProps
65
+ extends React.ComponentPropsWithoutRef<"time"> {
49
66
  timeZone: string;
50
67
  locale?: string;
51
68
  formatter?: (timestamp: number) => React.ReactNode;
@@ -1,27 +1,36 @@
1
1
  "use client";
2
- import { forwardRef } from "react";
2
+ import { forwardRef, useRef } from "react";
3
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
3
4
  import { Slot } from "@radix-ui/react-slot";
4
5
  import { cx, part } from "../internal.js";
5
6
  import { formatNumber } from "../domain.js";
6
- import type { PartProps, RootProps } from "../types.js";
7
- const Root = forwardRef<HTMLElement, RootProps>(
8
- ({ asChild, density = "comfortable", className, ...props }, ref) => {
7
+ import type { MotionProps, PartProps, RootProps } from "../types.js";
8
+ import { MotionProvider, motionStyle, useChangeFeedback } from "../motion.js";
9
+ export interface PowerMetricProps extends RootProps, MotionProps {}
10
+ const Root = forwardRef<HTMLElement, PowerMetricProps>(
11
+ (
12
+ { asChild, density = "comfortable", motion, className, style, ...props },
13
+ ref,
14
+ ) => {
9
15
  const Component = asChild ? Slot : "div";
10
16
  return (
11
- <Component
12
- {...props}
13
- ref={ref as never}
14
- data-slot="power-metric"
15
- data-density={density}
16
- className={cx(
17
- "watt-root flex flex-col gap-2 data-[density=compact]:gap-1",
18
- className,
19
- )}
20
- />
17
+ <MotionProvider motion={motion}>
18
+ <Component
19
+ {...props}
20
+ ref={ref as never}
21
+ data-slot="power-metric"
22
+ data-density={density}
23
+ style={{ ...style, ...motionStyle(motion) }}
24
+ className={cx(
25
+ "watt-root flex flex-col gap-2 data-[density=compact]:gap-1",
26
+ className,
27
+ )}
28
+ />
29
+ </MotionProvider>
21
30
  );
22
31
  },
23
32
  );
24
- export interface MetricValueProps extends PartProps {
33
+ export interface MetricValueProps extends PartProps, MotionProps {
25
34
  value: number | null;
26
35
  formatter?: (value: number) => React.ReactNode;
27
36
  fallback?: React.ReactNode;
@@ -35,20 +44,26 @@ const Value = forwardRef<HTMLElement, MetricValueProps>(
35
44
  fallback = "—",
36
45
  children,
37
46
  className,
47
+ motion,
48
+ style,
38
49
  ...props
39
50
  },
40
51
  ref,
41
52
  ) => {
53
+ const host = useRef<HTMLElement>(null);
54
+ const hostRef = useComposedRefs(host, ref);
55
+ useChangeFeedback(host, value, motion);
42
56
  const Component = asChild ? Slot : "span";
43
57
  return (
44
58
  <Component
45
59
  {...props}
46
- ref={ref as never}
60
+ ref={hostRef as never}
47
61
  data-slot="power-metric-value"
48
62
  className={cx(
49
- "text-5xl font-medium tabular-nums tracking-tight",
63
+ "watt-motion text-5xl font-medium tabular-nums tracking-tight",
50
64
  className,
51
65
  )}
66
+ style={{ ...style, ...motionStyle(motion) }}
52
67
  >
53
68
  {children ??
54
69
  (value !== null && Number.isFinite(value)
@@ -1,26 +1,45 @@
1
1
  "use client";
2
- import { forwardRef } from "react";
2
+ import { forwardRef, useRef } from "react";
3
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
3
4
  import { Slot } from "@radix-ui/react-slot";
4
5
  import { cx, part } from "../internal.js";
5
- import type { EquipmentStatus, RootProps } from "../types.js";
6
- export interface StatusIndicatorProps extends RootProps {
6
+ import type { EquipmentStatus, MotionProps, RootProps } from "../types.js";
7
+ import { MotionProvider, motionStyle, useChangeFeedback } from "../motion.js";
8
+ export interface StatusIndicatorProps extends RootProps, MotionProps {
7
9
  status: EquipmentStatus;
8
10
  }
9
11
  const Root = forwardRef<HTMLElement, StatusIndicatorProps>(
10
- ({ status, density = "comfortable", asChild, className, ...props }, ref) => {
12
+ (
13
+ {
14
+ status,
15
+ density = "comfortable",
16
+ asChild,
17
+ motion,
18
+ className,
19
+ style,
20
+ ...props
21
+ },
22
+ ref,
23
+ ) => {
24
+ const host = useRef<HTMLElement>(null);
25
+ const hostRef = useComposedRefs(host, ref);
26
+ useChangeFeedback(host, status, motion);
11
27
  const Component = asChild ? Slot : "span";
12
28
  return (
13
- <Component
14
- {...props}
15
- ref={ref as never}
16
- data-slot="status-indicator"
17
- data-status={status}
18
- data-density={density}
19
- className={cx(
20
- "watt-status inline-flex items-center gap-2 text-sm data-[density=compact]:gap-1 data-[density=compact]:text-xs",
21
- className,
22
- )}
23
- />
29
+ <MotionProvider motion={motion}>
30
+ <Component
31
+ {...props}
32
+ ref={hostRef as never}
33
+ data-slot="status-indicator"
34
+ data-status={status}
35
+ data-density={density}
36
+ style={{ ...style, ...motionStyle(motion) }}
37
+ className={cx(
38
+ "watt-status watt-motion inline-flex items-center gap-2 text-sm data-[density=compact]:gap-1 data-[density=compact]:text-xs",
39
+ className,
40
+ )}
41
+ />
42
+ </MotionProvider>
24
43
  );
25
44
  },
26
45
  );
@@ -13,6 +13,7 @@ import {
13
13
  } from "react";
14
14
  import type { EChartsCoreOption, EChartsType } from "echarts/core";
15
15
  import { cx, part } from "../internal.js";
16
+ import { MotionProvider, motionStyle, useChangeFeedback } from "../motion.js";
16
17
  import { nearestIndex, prepareSeries, lowerBound } from "../chart/data.js";
17
18
  import {
18
19
  chartOption,
@@ -24,6 +25,7 @@ import type {
24
25
  TelemetrySeries,
25
26
  TimeRange,
26
27
  DataQuality,
28
+ MotionProps,
27
29
  } from "../types.js";
28
30
  export interface ChartInspection {
29
31
  timestamp: number;
@@ -36,7 +38,9 @@ export interface ChartInspection {
36
38
  quality?: DataQuality;
37
39
  }[];
38
40
  }
39
- export interface TelemetryChartProps extends Omit<RootProps, "onError"> {
41
+ export interface TelemetryChartProps
42
+ extends Omit<RootProps, "onError">,
43
+ MotionProps {
40
44
  series: readonly TelemetrySeries[];
41
45
  timeZone: string;
42
46
  locale?: string;
@@ -83,6 +87,8 @@ const Root = forwardRef<HTMLDivElement, TelemetryChartProps>(
83
87
  asChild,
84
88
  children,
85
89
  className,
90
+ style,
91
+ motion,
86
92
  ...props
87
93
  },
88
94
  ref,
@@ -142,34 +148,37 @@ const Root = forwardRef<HTMLDivElement, TelemetryChartProps>(
142
148
  }, [options, prepared, range, timeZone]);
143
149
  const Component = asChild ? Slot : "div";
144
150
  return (
145
- <Context.Provider
146
- value={{
147
- series: prepared,
148
- timeZone,
149
- locale,
150
- range,
151
- onRangeChange,
152
- sampling,
153
- options,
154
- events,
155
- inspection,
156
- inspect,
157
- hidden,
158
- toggle,
159
- onReady,
160
- onError,
161
- }}
162
- >
163
- <Component
164
- {...props}
165
- ref={ref}
166
- data-slot="telemetry-chart"
167
- data-density={density}
168
- className={cx("watt-root min-w-0", className)}
151
+ <MotionProvider motion={motion}>
152
+ <Context.Provider
153
+ value={{
154
+ series: prepared,
155
+ timeZone,
156
+ locale,
157
+ range,
158
+ onRangeChange,
159
+ sampling,
160
+ options,
161
+ events,
162
+ inspection,
163
+ inspect,
164
+ hidden,
165
+ toggle,
166
+ onReady,
167
+ onError,
168
+ }}
169
169
  >
170
- {children}
171
- </Component>
172
- </Context.Provider>
170
+ <Component
171
+ {...props}
172
+ ref={ref}
173
+ data-slot="telemetry-chart"
174
+ data-density={density}
175
+ style={{ ...style, ...motionStyle(motion) }}
176
+ className={cx("watt-root min-w-0", className)}
177
+ >
178
+ {children}
179
+ </Component>
180
+ </Context.Provider>
181
+ </MotionProvider>
173
182
  );
174
183
  },
175
184
  );
@@ -432,11 +441,18 @@ const Legend = forwardRef<
432
441
  type="button"
433
442
  aria-pressed={!ctx.hidden.has(s.id)}
434
443
  onClick={() => ctx.toggle(s.id)}
435
- className="inline-flex items-center gap-2"
444
+ className="watt-chart-legend-toggle inline-flex items-center gap-2"
436
445
  >
437
446
  <span
438
- className="h-2 w-2 rounded-full"
439
- style={{ background: s.color ?? "var(--watt-accent)" }}
447
+ aria-hidden="true"
448
+ data-slot="telemetry-chart-legend-marker"
449
+ className="watt-motion h-2 w-2 rounded-full border border-current"
450
+ style={{
451
+ color: s.color ?? "var(--watt-accent)",
452
+ background: ctx.hidden.has(s.id)
453
+ ? "transparent"
454
+ : "currentColor",
455
+ }}
440
456
  />
441
457
  {s.label}
442
458
  </button>
@@ -446,24 +462,30 @@ const Legend = forwardRef<
446
462
  </div>
447
463
  );
448
464
  });
449
- function Tooltip({
450
- children,
451
- ...props
452
- }: Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
453
- children: (inspection: ChartInspection | null) => React.ReactNode;
454
- }) {
465
+ const Tooltip = forwardRef<
466
+ HTMLDivElement,
467
+ Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
468
+ children: (inspection: ChartInspection | null) => React.ReactNode;
469
+ }
470
+ >(function Tooltip({ children, className, ...props }, ref) {
455
471
  const { inspection } = useChart();
472
+ const host = useRef<HTMLDivElement>(null);
473
+ const hostRef = useComposedRefs(host, ref);
474
+ useChangeFeedback(host, inspection !== null);
456
475
  return (
457
476
  <div
458
477
  {...props}
478
+ ref={hostRef}
459
479
  data-slot="telemetry-chart-tooltip"
480
+ data-state={inspection ? "active" : "idle"}
481
+ className={cx("watt-motion", className)}
460
482
  aria-live={props["aria-live"] ?? "polite"}
461
483
  aria-atomic={props["aria-atomic"] ?? true}
462
484
  >
463
485
  {children(inspection)}
464
486
  </div>
465
487
  );
466
- }
488
+ });
467
489
  export const TelemetryChart = {
468
490
  Root,
469
491
  Canvas,
package/src/index.ts CHANGED
@@ -8,7 +8,10 @@ export { AlarmFeed } from "./components/alarm-feed.js";
8
8
  export { AssetTree } from "./components/asset-tree.js";
9
9
  export { formatNumber, formatTime, parseTimestamp } from "./domain.js";
10
10
  export type * from "./types.js";
11
- export type { MetricValueProps } from "./components/power-metric.js";
11
+ export type {
12
+ PowerMetricProps,
13
+ MetricValueProps,
14
+ } from "./components/power-metric.js";
12
15
  export type { StatusIndicatorProps } from "./components/status-indicator.js";
13
16
  export type { BatterySOCProps } from "./components/battery-soc.js";
14
17
  export type {
@@ -34,4 +37,5 @@ export type {
34
37
  AssetTreeProps,
35
38
  AssetTreeRow,
36
39
  AssetTreeItem,
40
+ AssetTreeIndicatorProps,
37
41
  } from "./components/asset-tree.js";