@watt-ui/react 0.1.1 → 0.1.3
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 +1 -0
- package/dist/components/alarm-feed.d.ts +2 -2
- package/dist/components/alarm-feed.d.ts.map +1 -1
- package/dist/components/alarm-feed.js +38 -16
- package/dist/components/alarm-feed.js.map +1 -1
- package/dist/components/asset-tree.d.ts +6 -2
- package/dist/components/asset-tree.d.ts.map +1 -1
- package/dist/components/asset-tree.js +15 -8
- package/dist/components/asset-tree.js.map +1 -1
- package/dist/components/battery-soc.d.ts +2 -2
- package/dist/components/battery-soc.d.ts.map +1 -1
- package/dist/components/battery-soc.js +4 -3
- package/dist/components/battery-soc.js.map +1 -1
- package/dist/components/data-freshness.d.ts +2 -2
- package/dist/components/data-freshness.d.ts.map +1 -1
- package/dist/components/data-freshness.js +8 -3
- package/dist/components/data-freshness.js.map +1 -1
- package/dist/components/power-metric.d.ts +5 -3
- package/dist/components/power-metric.d.ts.map +1 -1
- package/dist/components/power-metric.js +10 -5
- package/dist/components/power-metric.js.map +1 -1
- package/dist/components/status-indicator.d.ts +2 -2
- package/dist/components/status-indicator.d.ts.map +1 -1
- package/dist/components/status-indicator.js +8 -3
- package/dist/components/status-indicator.js.map +1 -1
- package/dist/components/telemetry-chart.d.ts +5 -7
- package/dist/components/telemetry-chart.d.ts.map +1 -1
- package/dist/components/telemetry-chart.js +30 -21
- package/dist/components/telemetry-chart.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/motion.d.ts +12 -0
- package/dist/motion.d.ts.map +1 -0
- package/dist/motion.js +90 -0
- package/dist/motion.js.map +1 -0
- package/dist/styles.css +69 -1
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/alarm-feed.tsx +97 -62
- package/src/components/asset-tree.tsx +56 -31
- package/src/components/battery-soc.tsx +29 -14
- package/src/components/data-freshness.tsx +34 -17
- package/src/components/power-metric.tsx +32 -17
- package/src/components/status-indicator.tsx +34 -15
- package/src/components/telemetry-chart.tsx +60 -38
- package/src/index.ts +5 -1
- package/src/motion.tsx +117 -0
- package/src/styles.css +69 -1
- 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>
|
|
34
|
-
React.HTMLAttributes<HTMLDivElement>,
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
(
|
|
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
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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)]
|
|
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 {
|
|
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
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
className
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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={
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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-motion watt-chart-legend-toggle inline-flex items-center gap-2"
|
|
436
445
|
>
|
|
437
446
|
<span
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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 {
|
|
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";
|