@epilot/spark-ui 1.0.0-alpha.3 → 1.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accent-colors-Ch2J0EEY.js +13 -0
- package/dist/accent-colors-Ch2J0EEY.js.map +1 -0
- package/dist/accent-colors-CjHCf8fH.cjs +18 -0
- package/dist/accent-colors-CjHCf8fH.cjs.map +1 -0
- package/dist/components/accordion.cjs +1 -1
- package/dist/components/accordion.d.ts +1 -1
- package/dist/components/accordion.js +1 -1
- package/dist/components/attachment.d.cts +1 -1
- package/dist/components/attachment.d.ts +1 -1
- package/dist/components/badge.cjs +12 -7
- package/dist/components/badge.cjs.map +1 -1
- package/dist/components/badge.d.cts +5 -1
- package/dist/components/badge.d.ts +5 -1
- package/dist/components/badge.js +12 -7
- package/dist/components/badge.js.map +1 -1
- package/dist/components/bar-chart.cjs +60 -56
- package/dist/components/bar-chart.cjs.map +1 -1
- package/dist/components/bar-chart.d.cts +7 -2
- package/dist/components/bar-chart.d.ts +7 -2
- package/dist/components/bar-chart.js +60 -56
- package/dist/components/bar-chart.js.map +1 -1
- package/dist/components/battery-progress-row.cjs +10 -16
- package/dist/components/battery-progress-row.cjs.map +1 -1
- package/dist/components/battery-progress-row.d.cts +1 -1
- package/dist/components/battery-progress-row.d.ts +2 -2
- package/dist/components/battery-progress-row.js +10 -16
- package/dist/components/battery-progress-row.js.map +1 -1
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button.cjs +5 -2
- package/dist/components/button.cjs.map +1 -1
- package/dist/components/button.d.cts +16 -7
- package/dist/components/button.d.ts +16 -7
- package/dist/components/button.js +5 -2
- package/dist/components/button.js.map +1 -1
- package/dist/components/callout.d.cts +1 -1
- package/dist/components/callout.d.ts +1 -1
- package/dist/components/card.d.cts +1 -1
- package/dist/components/card.d.ts +1 -1
- package/dist/components/chart.cjs +1 -1
- package/dist/components/chart.cjs.map +1 -1
- package/dist/components/chart.js +1 -1
- package/dist/components/chart.js.map +1 -1
- package/dist/components/checkbox.cjs +30 -23
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.d.cts +5 -1
- package/dist/components/checkbox.d.ts +5 -1
- package/dist/components/checkbox.js +30 -23
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/circular-gauge.d.ts +1 -1
- package/dist/components/day-of-week-picker.cjs +2 -2
- package/dist/components/day-of-week-picker.cjs.map +1 -1
- package/dist/components/day-of-week-picker.js +2 -2
- package/dist/components/day-of-week-picker.js.map +1 -1
- package/dist/components/device-card.d.ts +1 -1
- package/dist/components/donut-chart.cjs +3 -3
- package/dist/components/donut-chart.cjs.map +1 -1
- package/dist/components/donut-chart.js +3 -3
- package/dist/components/donut-chart.js.map +1 -1
- package/dist/components/energy-bar-chart.cjs +2 -1
- package/dist/components/energy-bar-chart.cjs.map +1 -1
- package/dist/components/energy-bar-chart.d.ts +1 -1
- package/dist/components/energy-bar-chart.js +2 -1
- package/dist/components/energy-bar-chart.js.map +1 -1
- package/dist/components/energy-breakdown.cjs +1 -1
- package/dist/components/energy-breakdown.cjs.map +1 -1
- package/dist/components/energy-breakdown.js +1 -1
- package/dist/components/energy-breakdown.js.map +1 -1
- package/dist/components/energy-history-chart.cjs +14 -6
- package/dist/components/energy-history-chart.cjs.map +1 -1
- package/dist/components/energy-history-chart.d.cts +11 -3
- package/dist/components/energy-history-chart.d.ts +11 -3
- package/dist/components/energy-history-chart.js +14 -6
- package/dist/components/energy-history-chart.js.map +1 -1
- package/dist/components/feed-in-hero.d.ts +1 -1
- package/dist/components/field.cjs +1 -1
- package/dist/components/field.d.cts +1 -1
- package/dist/components/field.d.ts +1 -1
- package/dist/components/field.js +1 -1
- package/dist/components/info-block.d.cts +1 -1
- package/dist/components/info-block.d.ts +1 -1
- package/dist/components/input-group.cjs +1 -1
- package/dist/components/input-group.js +1 -1
- package/dist/components/input-otp.cjs +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/input.cjs +1 -1
- package/dist/components/input.d.cts +1 -1
- package/dist/components/input.d.ts +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/line-chart.cjs +61 -19
- package/dist/components/line-chart.cjs.map +1 -1
- package/dist/components/line-chart.d.cts +7 -0
- package/dist/components/line-chart.d.ts +7 -0
- package/dist/components/line-chart.js +61 -19
- package/dist/components/line-chart.js.map +1 -1
- package/dist/components/metering-row.d.ts +1 -1
- package/dist/components/money-saved-block.cjs +1 -1
- package/dist/components/money-saved-block.cjs.map +1 -1
- package/dist/components/money-saved-block.js +1 -1
- package/dist/components/money-saved-block.js.map +1 -1
- package/dist/components/number-flow.d.ts +1 -1
- package/dist/components/power-history-chart.cjs +23 -44
- package/dist/components/power-history-chart.cjs.map +1 -1
- package/dist/components/power-history-chart.d.cts +10 -1
- package/dist/components/power-history-chart.d.ts +10 -1
- package/dist/components/power-history-chart.js +24 -45
- package/dist/components/power-history-chart.js.map +1 -1
- package/dist/components/power-history.cjs +1 -1
- package/dist/components/power-history.cjs.map +1 -1
- package/dist/components/power-history.js +1 -1
- package/dist/components/power-history.js.map +1 -1
- package/dist/components/power-network.cjs +36 -11
- package/dist/components/power-network.cjs.map +1 -1
- package/dist/components/power-network.js +36 -11
- package/dist/components/power-network.js.map +1 -1
- package/dist/components/progress.cjs +1 -1
- package/dist/components/progress.cjs.map +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio-card-group.cjs +26 -1
- package/dist/components/radio-card-group.cjs.map +1 -1
- package/dist/components/radio-card-group.js +26 -1
- package/dist/components/radio-card-group.js.map +1 -1
- package/dist/components/radio.cjs +11 -4
- package/dist/components/radio.cjs.map +1 -1
- package/dist/components/radio.d.cts +15 -3
- package/dist/components/radio.d.ts +15 -3
- package/dist/components/radio.js +11 -4
- package/dist/components/radio.js.map +1 -1
- package/dist/components/sankey-chart.cjs +12 -45
- package/dist/components/sankey-chart.cjs.map +1 -1
- package/dist/components/sankey-chart.d.cts +8 -0
- package/dist/components/sankey-chart.d.ts +9 -1
- package/dist/components/sankey-chart.js +12 -45
- package/dist/components/sankey-chart.js.map +1 -1
- package/dist/components/schedule-list.cjs +3 -3
- package/dist/components/schedule-list.cjs.map +1 -1
- package/dist/components/schedule-list.js +3 -3
- package/dist/components/schedule-list.js.map +1 -1
- package/dist/components/segmented-control.cjs +44 -0
- package/dist/components/segmented-control.cjs.map +1 -0
- package/dist/components/segmented-control.d.cts +33 -0
- package/dist/components/segmented-control.d.ts +33 -0
- package/dist/components/segmented-control.js +40 -0
- package/dist/components/segmented-control.js.map +1 -0
- package/dist/components/select.cjs +1 -1
- package/dist/components/select.d.cts +1 -1
- package/dist/components/select.d.ts +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/separator.cjs +1 -1
- package/dist/components/separator.cjs.map +1 -1
- package/dist/components/separator.js +1 -1
- package/dist/components/separator.js.map +1 -1
- package/dist/components/series-legend.cjs +1 -1
- package/dist/components/series-legend.cjs.map +1 -1
- package/dist/components/series-legend.js +1 -1
- package/dist/components/series-legend.js.map +1 -1
- package/dist/components/slider.cjs +6 -19
- package/dist/components/slider.cjs.map +1 -1
- package/dist/components/slider.d.cts +0 -13
- package/dist/components/slider.d.ts +0 -13
- package/dist/components/slider.js +6 -19
- package/dist/components/slider.js.map +1 -1
- package/dist/components/solar-forecast-chart.cjs +4 -3
- package/dist/components/solar-forecast-chart.cjs.map +1 -1
- package/dist/components/solar-forecast-chart.d.cts +4 -1
- package/dist/components/solar-forecast-chart.d.ts +4 -1
- package/dist/components/solar-forecast-chart.js +4 -3
- package/dist/components/solar-forecast-chart.js.map +1 -1
- package/dist/components/stepper.cjs +2 -2
- package/dist/components/stepper.cjs.map +1 -1
- package/dist/components/stepper.js +2 -2
- package/dist/components/stepper.js.map +1 -1
- package/dist/components/switch.cjs +8 -3
- package/dist/components/switch.cjs.map +1 -1
- package/dist/components/switch.d.cts +9 -4
- package/dist/components/switch.d.ts +9 -4
- package/dist/components/switch.js +8 -3
- package/dist/components/switch.js.map +1 -1
- package/dist/components/tabs.cjs +4 -4
- package/dist/components/tabs.cjs.map +1 -1
- package/dist/components/tabs.js +4 -4
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/theme.cjs +1 -1
- package/dist/components/theme.d.cts +1 -1
- package/dist/components/theme.d.ts +1 -1
- package/dist/components/theme.js +1 -1
- package/dist/components/time-picker.cjs +1 -1
- package/dist/components/time-picker.cjs.map +1 -1
- package/dist/components/time-picker.js +1 -1
- package/dist/components/time-picker.js.map +1 -1
- package/dist/components/tooltip.cjs +1 -1
- package/dist/components/tooltip.cjs.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/hooks/use-reduced-motion.cjs +22 -0
- package/dist/hooks/use-reduced-motion.cjs.map +1 -0
- package/dist/hooks/use-reduced-motion.d.cts +7 -0
- package/dist/hooks/use-reduced-motion.d.ts +7 -0
- package/dist/hooks/use-reduced-motion.js +20 -0
- package/dist/hooks/use-reduced-motion.js.map +1 -0
- package/dist/{input-box-variants-Dr1K7uYE.js → input-box-variants-DcajYXvo.js} +1 -1
- package/dist/{input-box-variants-Dr1K7uYE.js.map → input-box-variants-DcajYXvo.js.map} +1 -1
- package/dist/{input-box-variants-BMDdwvqg.cjs → input-box-variants-ZCtgs-1S.cjs} +1 -1
- package/dist/{input-box-variants-BMDdwvqg.cjs.map → input-box-variants-ZCtgs-1S.cjs.map} +1 -1
- package/dist/{number-flow-DSbucQ_K.d.ts → number-flow-D9zsxH0-.d.ts} +1 -1
- package/dist/styles/index.css +39 -0
- package/dist/styles/spark-default.css +123 -40
- package/dist/styles/theme.css +78 -0
- package/dist/{theme-B3WtiByA.js → theme-8PDO9E0S.js} +2 -2
- package/dist/theme-8PDO9E0S.js.map +1 -0
- package/dist/{theme-C9_OSe0S.cjs → theme-ClRJhexF.cjs} +2 -2
- package/dist/theme-ClRJhexF.cjs.map +1 -0
- package/dist/{theme-types-DoROIXpi.d.ts → theme-types-BDdoRxXa.d.ts} +2 -3
- package/dist/{theme-types-r20EJfEC.d.cts → theme-types-BwSJKdsB.d.cts} +2 -3
- package/dist/{tones-DyCkL71h.d.ts → tones-PXJGlSpe.d.ts} +1 -1
- package/dist/utils/build-sankey-nodes.cjs +16 -3
- package/dist/utils/build-sankey-nodes.cjs.map +1 -1
- package/dist/utils/build-sankey-nodes.js +16 -3
- package/dist/utils/build-sankey-nodes.js.map +1 -1
- package/dist/utils/chart-scale.cjs +43 -0
- package/dist/utils/chart-scale.cjs.map +1 -0
- package/dist/utils/chart-scale.d.cts +22 -0
- package/dist/utils/chart-scale.d.ts +22 -0
- package/dist/utils/chart-scale.js +41 -0
- package/dist/utils/chart-scale.js.map +1 -0
- package/package.json +1 -1
- package/dist/theme-B3WtiByA.js.map +0 -1
- package/dist/theme-C9_OSe0S.cjs.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useReducedMotion } from "../hooks/use-reduced-motion.js";
|
|
2
3
|
import { cn } from "../utils/cn.js";
|
|
3
4
|
import { MaterialSymbol } from "../icons/material-symbol.js";
|
|
4
5
|
import { SectionTitle } from "./typography.js";
|
|
@@ -24,14 +25,14 @@ const SOURCE_VARIANTS = {
|
|
|
24
25
|
initialDashGap: 7
|
|
25
26
|
},
|
|
26
27
|
neutral: {
|
|
27
|
-
activeClassName: "border-
|
|
28
|
-
flowStroke: "var(--
|
|
28
|
+
activeClassName: "border-gray-a9 bg-gray-a1 shadow-gray-a3",
|
|
29
|
+
flowStroke: "var(--neutral-a9)",
|
|
29
30
|
initialDashGap: 9
|
|
30
31
|
}
|
|
31
32
|
};
|
|
32
|
-
const DEVICE_FLOW_STROKE = "var(--
|
|
33
|
+
const DEVICE_FLOW_STROKE = "var(--neutral-a9)";
|
|
33
34
|
const DEVICE_INITIAL_DASH_GAP = 9;
|
|
34
|
-
const SKELETON_STROKE = "var(--
|
|
35
|
+
const SKELETON_STROKE = "var(--neutral-a6)";
|
|
35
36
|
const STROKE_WIDTH = 2;
|
|
36
37
|
const STRAIGHT_TOLERANCE_PX = 3;
|
|
37
38
|
const SOURCE_HOME_OFFSET_STEP_PX = 10;
|
|
@@ -79,6 +80,7 @@ function connectorPathsEqual(a, b) {
|
|
|
79
80
|
}
|
|
80
81
|
function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
81
82
|
const containerRef = useRef(null);
|
|
83
|
+
const reducedMotion = useReducedMotion();
|
|
82
84
|
const [nodes, setNodes] = useState(() => /* @__PURE__ */ new Map());
|
|
83
85
|
const [connectorPaths, setConnectorPaths] = useState([]);
|
|
84
86
|
const connectorPathsRef = useRef([]);
|
|
@@ -190,7 +192,7 @@ function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
|
190
192
|
targetFlowVisualsRef.current = targets;
|
|
191
193
|
}, [nodes, totalSourceWatts]);
|
|
192
194
|
useEffect(() => {
|
|
193
|
-
if (loading) return;
|
|
195
|
+
if (loading || reducedMotion) return;
|
|
194
196
|
let frameId = 0;
|
|
195
197
|
let lastFrameTime = performance.now();
|
|
196
198
|
const animate = (now) => {
|
|
@@ -223,7 +225,23 @@ function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
|
223
225
|
};
|
|
224
226
|
frameId = window.requestAnimationFrame(animate);
|
|
225
227
|
return () => window.cancelAnimationFrame(frameId);
|
|
226
|
-
}, [loading]);
|
|
228
|
+
}, [loading, reducedMotion]);
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (loading || !reducedMotion) return;
|
|
231
|
+
for (const connectorPath of connectorPathsRef.current) {
|
|
232
|
+
const target = targetFlowVisualsRef.current[connectorPath.id];
|
|
233
|
+
const pathElement = flowPathRefs.current[connectorPath.id];
|
|
234
|
+
if (!target || !pathElement) continue;
|
|
235
|
+
pathElement.setAttribute("stroke-dasharray", `${FLOW_DASH_SOLID} ${target.gap.toFixed(3)}`);
|
|
236
|
+
pathElement.setAttribute("stroke-dashoffset", "0");
|
|
237
|
+
pathElement.setAttribute("opacity", target.opacity.toFixed(3));
|
|
238
|
+
}
|
|
239
|
+
}, [
|
|
240
|
+
loading,
|
|
241
|
+
reducedMotion,
|
|
242
|
+
nodes,
|
|
243
|
+
totalSourceWatts
|
|
244
|
+
]);
|
|
227
245
|
const contextValue = useMemo(() => ({
|
|
228
246
|
loading,
|
|
229
247
|
register,
|
|
@@ -257,7 +275,7 @@ function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
|
257
275
|
return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("path", {
|
|
258
276
|
d: path.d,
|
|
259
277
|
fill: "none",
|
|
260
|
-
stroke: "var(--
|
|
278
|
+
stroke: "var(--neutral-a1)",
|
|
261
279
|
strokeWidth: STROKE_WIDTH,
|
|
262
280
|
strokeLinecap: "round"
|
|
263
281
|
}), /* @__PURE__ */ jsx("path", {
|
|
@@ -271,7 +289,8 @@ function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
|
271
289
|
},
|
|
272
290
|
strokeDasharray: `${FLOW_DASH_SOLID} ${initialDashGap}`,
|
|
273
291
|
strokeDashoffset: 0,
|
|
274
|
-
opacity: 0
|
|
292
|
+
opacity: 0,
|
|
293
|
+
style: reducedMotion ? { transition: "opacity 300ms ease" } : void 0
|
|
275
294
|
})] }, path.id);
|
|
276
295
|
})
|
|
277
296
|
}),
|
|
@@ -392,7 +411,7 @@ function PowerDevice({ id: idProp, watts, icon, className }) {
|
|
|
392
411
|
className: cn("flex flex-col items-center justify-center gap-1", className),
|
|
393
412
|
children: [/* @__PURE__ */ jsx("div", {
|
|
394
413
|
ref: setElement,
|
|
395
|
-
className: "flex aspect-square items-center justify-center rounded-circle border border-gray bg-
|
|
414
|
+
className: "flex aspect-square items-center justify-center rounded-circle border border-gray bg-gray-a1 p-4",
|
|
396
415
|
children: icon
|
|
397
416
|
}), /* @__PURE__ */ jsx(NumberFlow, {
|
|
398
417
|
value: watts,
|
|
@@ -445,6 +464,7 @@ function PowerNetworkHome({ watts, autarky, className }) {
|
|
|
445
464
|
});
|
|
446
465
|
}
|
|
447
466
|
function HomeSection({ anchorRef, autarky, className, watts }) {
|
|
467
|
+
const reducedMotion = useReducedMotion();
|
|
448
468
|
const targetAutarky = autarky == null ? 0 : Math.max(0, Math.min(100, autarky * 100));
|
|
449
469
|
const [animatedAutarky, setAnimatedAutarky] = useState(targetAutarky);
|
|
450
470
|
const animatedAutarkyRef = useRef(animatedAutarky);
|
|
@@ -457,6 +477,11 @@ function HomeSection({ anchorRef, autarky, className, watts }) {
|
|
|
457
477
|
animatedAutarkyRef.current = animatedAutarky;
|
|
458
478
|
}, [animatedAutarky]);
|
|
459
479
|
useEffect(() => {
|
|
480
|
+
if (reducedMotion) {
|
|
481
|
+
animatedAutarkyRef.current = targetAutarky;
|
|
482
|
+
setAnimatedAutarky(targetAutarky);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
460
485
|
let frameId = 0;
|
|
461
486
|
let lastTime = performance.now();
|
|
462
487
|
const animate = (now) => {
|
|
@@ -474,7 +499,7 @@ function HomeSection({ anchorRef, autarky, className, watts }) {
|
|
|
474
499
|
};
|
|
475
500
|
frameId = window.requestAnimationFrame(animate);
|
|
476
501
|
return () => window.cancelAnimationFrame(frameId);
|
|
477
|
-
}, [targetAutarky]);
|
|
502
|
+
}, [targetAutarky, reducedMotion]);
|
|
478
503
|
return /* @__PURE__ */ jsxs("div", {
|
|
479
504
|
ref: anchorRef,
|
|
480
505
|
className: cn("relative mx-auto size-[78px]", className),
|
|
@@ -486,7 +511,7 @@ function HomeSection({ anchorRef, autarky, className, watts }) {
|
|
|
486
511
|
cy: nodeSize / 2,
|
|
487
512
|
r: radius,
|
|
488
513
|
fill: "none",
|
|
489
|
-
stroke: "var(--
|
|
514
|
+
stroke: "var(--neutral-a5)",
|
|
490
515
|
strokeWidth: ringStrokeWidth
|
|
491
516
|
}), progressLength > 0 && /* @__PURE__ */ jsx("circle", {
|
|
492
517
|
cx: nodeSize / 2,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-network.js","names":[],"sources":["../../src/components/power-network.tsx"],"sourcesContent":["import {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { MaterialSymbol } from '../icons/material-symbol'\nimport { cn } from '../utils/cn'\nimport NumberFlow from './number-flow'\nimport { Skeleton } from './skeleton'\nimport { SectionTitle } from './typography'\n\n// --- Variants -----------------------------------------------------------\n\nexport type PowerSourceVariant = 'grid' | 'solar' | 'battery' | 'neutral'\n\ninterface SourceVariantStyles {\n activeClassName: string\n flowStroke: string\n initialDashGap: number\n}\n\nconst SOURCE_VARIANTS: Record<PowerSourceVariant, SourceVariantStyles> = {\n grid: {\n activeClassName: 'border-blue-a9 bg-blue-a1 shadow-blue-a4',\n flowStroke: 'var(--blue-11)',\n initialDashGap: 10,\n },\n solar: {\n activeClassName: 'border-orange-a9 bg-orange-a1 shadow-orange-a3',\n flowStroke: 'var(--orange-a9)',\n initialDashGap: 5,\n },\n battery: {\n activeClassName: 'border-green-a9 bg-green-a1 shadow-green-a3',\n flowStroke: 'var(--green-a9)',\n initialDashGap: 7,\n },\n neutral: {\n activeClassName: 'border-slate-a9 bg-slate-a1 shadow-slate-a3',\n flowStroke: 'var(--slate-a9)',\n initialDashGap: 9,\n },\n}\n\nconst DEVICE_FLOW_STROKE = 'var(--slate-a9)'\nconst DEVICE_INITIAL_DASH_GAP = 9\n\nconst SKELETON_STROKE = 'var(--gray-a6)'\nconst STROKE_WIDTH = 2\nconst STRAIGHT_TOLERANCE_PX = 3\nconst SOURCE_HOME_OFFSET_STEP_PX = 10\nconst DEVICE_HOME_OFFSET_STEP_PX = 10\n\n// --- Flow animation parameters ------------------------------------------\n\nconst FLOW_DASH_SOLID = 1\nconst FLOW_GAP_MAX = 14\nconst FLOW_GAP_MIN = 5\nconst FLOW_SPEED_MIN = 10\nconst FLOW_SPEED_MAX = 56\nconst FLOW_SMOOTHING = 5.5\n\ninterface FlowVisual {\n gap: number\n speed: number\n opacity: number\n}\n\ninterface FlowAnimationState extends FlowVisual {\n offset: number\n}\n\n// --- Context ------------------------------------------------------------\n\ntype NodeKind = 'source' | 'device' | 'home'\n\ninterface RegisteredNode {\n id: string\n kind: NodeKind\n variant?: PowerSourceVariant\n watts: number\n element: HTMLDivElement | null\n}\n\ninterface PowerNetworkContextValue {\n loading: boolean\n register: (node: RegisteredNode) => void\n unregister: (id: string) => void\n totalSourceWatts: number\n}\n\nconst PowerNetworkContext = createContext<PowerNetworkContextValue | null>(null)\n\nfunction usePowerNetwork(): PowerNetworkContextValue {\n const ctx = useContext(PowerNetworkContext)\n if (!ctx) {\n throw new Error(\n 'Power network sub-components must be used inside <PowerNetworkDiagram>.',\n )\n }\n return ctx\n}\n\n// --- Geometry helpers ---------------------------------------------------\n\ninterface Point {\n x: number\n y: number\n}\n\nfunction anchorTop(rect: DOMRect, containerRect: DOMRect): Point {\n return {\n x: rect.left + rect.width / 2 - containerRect.left,\n y: rect.top - containerRect.top,\n }\n}\n\nfunction anchorBottom(rect: DOMRect, containerRect: DOMRect): Point {\n return {\n x: rect.left + rect.width / 2 - containerRect.left,\n y: rect.bottom - containerRect.top,\n }\n}\n\nfunction buildAdaptivePath(from: Point, to: Point): string {\n if (Math.abs(from.x - to.x) < STRAIGHT_TOLERANCE_PX) {\n return `M ${from.x} ${from.y} L ${to.x} ${to.y}`\n }\n const dy = to.y - from.y\n const c1y = from.y + dy * 0.56\n const c2y = to.y - dy * 0.58\n return `M ${from.x} ${from.y} C ${from.x} ${c1y} ${to.x} ${c2y} ${to.x} ${to.y}`\n}\n\n// --- PowerNetworkDiagram ------------------------------------------------\n\nexport interface PowerNetworkDiagramProps {\n title?: string\n loading?: boolean\n className?: string\n children?: ReactNode\n}\n\ninterface ConnectorPath {\n id: string\n d: string\n kind: NodeKind\n variant?: PowerSourceVariant\n reversed: boolean\n}\n\nfunction connectorPathsEqual(a: ConnectorPath[], b: ConnectorPath[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n const ap = a[i]\n const bp = b[i]\n if (!ap || !bp) return false\n if (\n ap.id !== bp.id ||\n ap.d !== bp.d ||\n ap.variant !== bp.variant ||\n ap.kind !== bp.kind ||\n ap.reversed !== bp.reversed\n ) {\n return false\n }\n }\n return true\n}\n\nexport function PowerNetworkDiagram({\n title,\n loading = false,\n className,\n children,\n}: PowerNetworkDiagramProps): ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [nodes, setNodes] = useState<Map<string, RegisteredNode>>(\n () => new Map(),\n )\n const [connectorPaths, setConnectorPaths] = useState<ConnectorPath[]>([])\n const connectorPathsRef = useRef<ConnectorPath[]>([])\n const flowPathRefs = useRef<Record<string, SVGPathElement | null>>({})\n const currentFlowVisualsRef = useRef<Record<string, FlowAnimationState>>({})\n const targetFlowVisualsRef = useRef<Record<string, FlowVisual>>({})\n\n const register = useCallback((node: RegisteredNode) => {\n setNodes((prev) => {\n const existing = prev.get(node.id)\n if (\n existing &&\n existing.kind === node.kind &&\n existing.variant === node.variant &&\n existing.watts === node.watts &&\n existing.element === node.element\n ) {\n return prev\n }\n const next = new Map(prev)\n next.set(node.id, node)\n return next\n })\n }, [])\n\n const unregister = useCallback((id: string) => {\n setNodes((prev) => {\n if (!prev.has(id)) return prev\n const next = new Map(prev)\n next.delete(id)\n return next\n })\n }, [])\n\n const totalSourceWatts = useMemo(() => {\n let total = 0\n for (const node of nodes.values()) {\n if (node.kind === 'source') total += node.watts\n }\n return total\n }, [nodes])\n\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n\n let homeNode: RegisteredNode | undefined\n const sources: RegisteredNode[] = []\n const devices: RegisteredNode[] = []\n for (const node of nodes.values()) {\n if (node.kind === 'home') {\n homeNode = node\n } else if (node.element) {\n if (node.kind === 'source') sources.push(node)\n else devices.push(node)\n }\n }\n\n const updatePaths = () => {\n if (!homeNode || !homeNode.element) {\n setConnectorPaths((prev) => (prev.length === 0 ? prev : []))\n return\n }\n const containerRect = container.getBoundingClientRect()\n const homeRect = homeNode.element.getBoundingClientRect()\n const homeTop = anchorTop(homeRect, containerRect)\n const homeBottom = anchorBottom(homeRect, containerRect)\n const next: ConnectorPath[] = []\n\n const sourceCount = sources.length\n const deviceCount = devices.length\n\n sources.forEach((source, index) => {\n if (!source.element) return\n const from = anchorBottom(\n source.element.getBoundingClientRect(),\n containerRect,\n )\n const offsetX =\n SOURCE_HOME_OFFSET_STEP_PX * (index - (sourceCount - 1) / 2)\n const to = { x: homeTop.x + offsetX, y: homeTop.y }\n next.push({\n id: source.id,\n d: buildAdaptivePath(from, to),\n kind: 'source',\n variant: source.variant,\n reversed: source.watts < 0,\n })\n })\n\n devices.forEach((device, index) => {\n if (!device.element) return\n const to = anchorTop(\n device.element.getBoundingClientRect(),\n containerRect,\n )\n const offsetX =\n DEVICE_HOME_OFFSET_STEP_PX * (index - (deviceCount - 1) / 2)\n const from = { x: homeBottom.x + offsetX, y: homeBottom.y }\n next.push({\n id: device.id,\n d: buildAdaptivePath(from, to),\n kind: 'device',\n reversed: false,\n })\n })\n\n setConnectorPaths((prev) =>\n connectorPathsEqual(prev, next) ? prev : next,\n )\n }\n\n updatePaths()\n\n const resizeObserver = new ResizeObserver(updatePaths)\n resizeObserver.observe(container)\n for (const node of nodes.values()) {\n if (node.element) resizeObserver.observe(node.element)\n }\n window.addEventListener('resize', updatePaths)\n\n return () => {\n resizeObserver.disconnect()\n window.removeEventListener('resize', updatePaths)\n }\n }, [nodes])\n\n useEffect(() => {\n connectorPathsRef.current = connectorPaths\n }, [connectorPaths])\n\n useEffect(() => {\n const targets: Record<string, FlowVisual> = {}\n const homeWatts = totalSourceWatts\n for (const node of nodes.values()) {\n if (node.kind === 'home') continue\n const watts = node.watts\n const relative =\n homeWatts > 0\n ? Math.max(0, Math.min(1, Math.abs(watts) / homeWatts))\n : 0\n targets[node.id] = {\n gap: FLOW_GAP_MAX - (FLOW_GAP_MAX - FLOW_GAP_MIN) * relative,\n speed: FLOW_SPEED_MIN + (FLOW_SPEED_MAX - FLOW_SPEED_MIN) * relative,\n opacity: Math.abs(watts) > 0 ? 1 : 0,\n }\n }\n targetFlowVisualsRef.current = targets\n }, [nodes, totalSourceWatts])\n\n useEffect(() => {\n if (loading) return\n let frameId = 0\n let lastFrameTime = performance.now()\n\n const animate = (now: number) => {\n const deltaSeconds = Math.max(0.001, (now - lastFrameTime) / 1000)\n lastFrameTime = now\n const smoothing = 1 - Math.exp(-FLOW_SMOOTHING * deltaSeconds)\n\n for (const connectorPath of connectorPathsRef.current) {\n const id = connectorPath.id\n const target = targetFlowVisualsRef.current[id]\n const pathElement = flowPathRefs.current[id]\n if (!target || !pathElement) continue\n\n const current = currentFlowVisualsRef.current[id] ?? {\n gap: target.gap,\n offset: 0,\n speed: target.speed,\n opacity: target.opacity,\n }\n\n current.gap += (target.gap - current.gap) * smoothing\n current.speed += (target.speed - current.speed) * smoothing\n current.opacity += (target.opacity - current.opacity) * smoothing\n\n const dashCycle = FLOW_DASH_SOLID + Math.max(0.001, current.gap)\n const direction = connectorPath.reversed ? 1 : -1\n const nextOffset =\n current.offset + direction * current.speed * deltaSeconds\n const wrappedOffset = ((nextOffset % dashCycle) + dashCycle) % dashCycle\n current.offset = wrappedOffset - dashCycle\n\n pathElement.setAttribute(\n 'stroke-dasharray',\n `${FLOW_DASH_SOLID} ${current.gap.toFixed(3)}`,\n )\n pathElement.setAttribute('stroke-dashoffset', current.offset.toFixed(3))\n pathElement.setAttribute('opacity', current.opacity.toFixed(3))\n\n currentFlowVisualsRef.current[id] = current\n }\n\n frameId = window.requestAnimationFrame(animate)\n }\n\n frameId = window.requestAnimationFrame(animate)\n return () => window.cancelAnimationFrame(frameId)\n }, [loading])\n\n const contextValue = useMemo<PowerNetworkContextValue>(\n () => ({ loading, register, unregister, totalSourceWatts }),\n [loading, register, unregister, totalSourceWatts],\n )\n\n return (\n <PowerNetworkContext.Provider value={contextValue}>\n <div\n ref={containerRef}\n className={cn('@container relative flex flex-col', className)}\n >\n {title && <SectionTitle>{title}</SectionTitle>}\n {connectorPaths.length > 0 && (\n <svg className=\"pointer-events-none absolute inset-0 size-full\">\n {connectorPaths.map((path) => {\n if (loading) {\n return (\n <path\n key={path.id}\n d={path.d}\n fill=\"none\"\n stroke={SKELETON_STROKE}\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n />\n )\n }\n\n const flowStroke =\n path.kind === 'source' && path.variant\n ? SOURCE_VARIANTS[path.variant].flowStroke\n : DEVICE_FLOW_STROKE\n const initialDashGap =\n path.kind === 'source' && path.variant\n ? SOURCE_VARIANTS[path.variant].initialDashGap\n : DEVICE_INITIAL_DASH_GAP\n\n return (\n <g key={path.id}>\n <path\n d={path.d}\n fill=\"none\"\n stroke=\"var(--gray-a1)\"\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n />\n <path\n d={path.d}\n fill=\"none\"\n stroke={flowStroke}\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n ref={(el) => {\n flowPathRefs.current[path.id] = el\n }}\n strokeDasharray={`${FLOW_DASH_SOLID} ${initialDashGap}`}\n strokeDashoffset={0}\n opacity={0}\n />\n </g>\n )\n })}\n </svg>\n )}\n <div className=\"flex flex-col gap-y-14\">{children}</div>\n </div>\n </PowerNetworkContext.Provider>\n )\n}\n\n// --- Row containers -----------------------------------------------------\n\nexport interface PowerNetworkRowProps {\n className?: string\n children?: ReactNode\n}\n\nexport function PowerNetworkSources({\n className,\n children,\n}: PowerNetworkRowProps): ReactElement {\n return (\n <div\n className={cn(\n 'flex items-center justify-center gap-[clamp(1.5rem,10cqw,4rem)]',\n className,\n )}\n >\n {children}\n </div>\n )\n}\n\nexport function PowerNetworkDevices({\n className,\n children,\n}: PowerNetworkRowProps): ReactElement {\n return (\n <div\n className={cn(\n 'flex items-center justify-center gap-[clamp(1.5rem,10cqw,4rem)]',\n className,\n )}\n >\n {children}\n </div>\n )\n}\n\n// --- PowerSource --------------------------------------------------------\n\nexport interface PowerSourceProps {\n id?: string\n watts: number\n icon: ReactNode\n variant?: PowerSourceVariant\n className?: string\n /**\n * Optional 0–100 progress (e.g. battery SoC). When set, a thin colored arc\n * is drawn around the icon circle as a non-intrusive indicator.\n */\n progress?: number\n}\n\nexport function PowerSource({\n id: idProp,\n watts,\n icon,\n variant = 'neutral',\n className,\n progress,\n}: PowerSourceProps): ReactElement {\n const autoId = useId()\n const id = idProp ?? autoId\n const { register, unregister, loading } = usePowerNetwork()\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!element) return\n register({ id, kind: 'source', variant, watts, element })\n }, [element, register, id, variant, watts])\n\n useEffect(() => {\n return () => unregister(id)\n }, [unregister, id])\n\n if (loading) {\n return <SourceSkeleton anchorRef={setElement} />\n }\n\n const isActive = watts !== 0\n const variantStyles = SOURCE_VARIANTS[variant]\n const clampedProgress =\n progress == null ? null : Math.max(0, Math.min(100, progress))\n const showRing = clampedProgress != null\n const RING_RADIUS = 48\n const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS\n const ringDash =\n clampedProgress != null ? (clampedProgress / 100) * RING_CIRCUMFERENCE : 0\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-1 transition-opacity duration-500',\n !isActive && 'opacity-45',\n className,\n )}\n >\n <NumberFlow\n value={Math.abs(watts)}\n suffix=\"W\"\n className=\"mt-1 text-sm leading-none font-medium\"\n />\n <div\n ref={setElement}\n className={cn(\n 'relative flex aspect-square items-center justify-center rounded-circle border p-4 shadow-sm',\n isActive\n ? variantStyles.activeClassName\n : 'border-gray bg-gray-a3 shadow-gray-a4 saturate-0',\n )}\n >\n {showRing && (\n <svg\n className=\"pointer-events-none absolute inset-0 size-full -rotate-90\"\n viewBox=\"0 0 100 100\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"50\"\n cy=\"50\"\n r={RING_RADIUS}\n fill=\"none\"\n stroke={variantStyles.flowStroke}\n strokeWidth=\"4\"\n strokeLinecap=\"round\"\n strokeDasharray={`${ringDash} ${RING_CIRCUMFERENCE}`}\n opacity={isActive ? 1 : 0.5}\n />\n </svg>\n )}\n {icon}\n </div>\n </div>\n )\n}\n\nfunction SourceSkeleton({\n anchorRef,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n}): ReactElement {\n return (\n <div className=\"flex flex-col items-center justify-center gap-1\">\n <Skeleton className=\"h-3.5 w-10\" />\n <div\n ref={anchorRef}\n className=\"flex aspect-square animate-pulse items-center justify-center rounded-circle border border-gray bg-base-gray-soft p-4 motion-reduce:animate-none\"\n >\n <div className=\"size-7\" />\n </div>\n </div>\n )\n}\n\n// --- PowerDevice --------------------------------------------------------\n\nexport interface PowerDeviceProps {\n id?: string\n watts: number\n icon: ReactNode\n className?: string\n}\n\nexport function PowerDevice({\n id: idProp,\n watts,\n icon,\n className,\n}: PowerDeviceProps): ReactElement {\n const autoId = useId()\n const id = idProp ?? autoId\n const { register, unregister, loading } = usePowerNetwork()\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!element) return\n register({ id, kind: 'device', watts, element })\n }, [element, register, id, watts])\n\n useEffect(() => {\n return () => unregister(id)\n }, [unregister, id])\n\n if (loading) {\n return <DeviceSkeleton anchorRef={setElement} />\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-1',\n className,\n )}\n >\n <div\n ref={setElement}\n className=\"flex aspect-square items-center justify-center rounded-circle border border-gray bg-slate-a1 p-4\"\n >\n {icon}\n </div>\n <NumberFlow\n value={watts}\n suffix=\"W\"\n className=\"text-sm font-medium tabular-nums\"\n />\n </div>\n )\n}\n\nfunction DeviceSkeleton({\n anchorRef,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n}): ReactElement {\n return (\n <div className=\"flex flex-col items-center justify-center gap-1\">\n <div\n ref={anchorRef}\n className=\"flex aspect-square animate-pulse items-center justify-center rounded-circle border border-gray bg-base-gray-soft p-4 motion-reduce:animate-none\"\n >\n <div className=\"size-5\" />\n </div>\n <Skeleton className=\"h-3.5 w-10\" />\n </div>\n )\n}\n\n// --- PowerNetworkHome ---------------------------------------------------\n\nexport interface PowerNetworkHomeProps {\n /** Override total power. Defaults to the sum of registered source watts. */\n watts?: number\n /** Self-sufficiency ratio in 0..1. Renders a green ring when provided. */\n autarky?: number\n className?: string\n}\n\nexport function PowerNetworkHome({\n watts,\n autarky,\n className,\n}: PowerNetworkHomeProps): ReactElement {\n const autoId = useId()\n const { register, unregister, loading, totalSourceWatts } = usePowerNetwork()\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n const effectiveWatts = watts ?? totalSourceWatts\n\n useEffect(() => {\n if (!element) return\n register({ id: autoId, kind: 'home', watts: effectiveWatts, element })\n }, [element, register, autoId, effectiveWatts])\n\n useEffect(() => {\n return () => unregister(autoId)\n }, [unregister, autoId])\n\n if (loading) {\n return <HomeSkeleton anchorRef={setElement} className={className} />\n }\n\n return (\n <HomeSection\n anchorRef={setElement}\n autarky={autarky ?? null}\n watts={effectiveWatts}\n className={className}\n />\n )\n}\n\nfunction HomeSection({\n anchorRef,\n autarky,\n className,\n watts,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n autarky: number | null\n className?: string\n watts: number\n}): ReactElement {\n const targetAutarky =\n autarky == null ? 0 : Math.max(0, Math.min(100, autarky * 100))\n const [animatedAutarky, setAnimatedAutarky] = useState(targetAutarky)\n const animatedAutarkyRef = useRef(animatedAutarky)\n const nodeSize = 78\n const ringStrokeWidth = 2\n const radius = (nodeSize - ringStrokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n const progressLength = (animatedAutarky / 100) * circumference\n\n useEffect(() => {\n animatedAutarkyRef.current = animatedAutarky\n }, [animatedAutarky])\n\n useEffect(() => {\n let frameId = 0\n let lastTime = performance.now()\n\n const animate = (now: number) => {\n const deltaSeconds = Math.max(0.001, (now - lastTime) / 1000)\n lastTime = now\n const smoothing = 1 - Math.exp(-7 * deltaSeconds)\n const next =\n animatedAutarkyRef.current +\n (targetAutarky - animatedAutarkyRef.current) * smoothing\n\n animatedAutarkyRef.current = next\n setAnimatedAutarky(next)\n\n if (Math.abs(targetAutarky - next) > 0.05) {\n frameId = window.requestAnimationFrame(animate)\n } else {\n animatedAutarkyRef.current = targetAutarky\n setAnimatedAutarky(targetAutarky)\n }\n }\n\n frameId = window.requestAnimationFrame(animate)\n return () => window.cancelAnimationFrame(frameId)\n }, [targetAutarky])\n\n return (\n <div\n ref={anchorRef}\n className={cn('relative mx-auto size-[78px]', className)}\n >\n {autarky != null && (\n <svg\n className=\"absolute inset-0 size-full -rotate-90\"\n viewBox={`0 0 ${nodeSize} ${nodeSize}`}\n >\n <circle\n cx={nodeSize / 2}\n cy={nodeSize / 2}\n r={radius}\n fill=\"none\"\n stroke=\"var(--gray-a5)\"\n strokeWidth={ringStrokeWidth}\n />\n {progressLength > 0 && (\n <circle\n cx={nodeSize / 2}\n cy={nodeSize / 2}\n r={radius}\n fill=\"none\"\n stroke=\"var(--green-a9)\"\n strokeLinecap=\"round\"\n strokeWidth={ringStrokeWidth}\n strokeDasharray={`${progressLength} ${circumference}`}\n />\n )}\n </svg>\n )}\n <div className=\"absolute inset-0 flex flex-col items-center justify-center rounded-circle border border-gray-a5 shadow-sm shadow-gray-a1\">\n <MaterialSymbol className=\"text-[14px]\" name=\"home\" />\n <NumberFlow\n value={watts}\n suffix=\"W\"\n className=\"mt-1 text-sm leading-none font-medium\"\n />\n </div>\n </div>\n )\n}\n\nfunction HomeSkeleton({\n anchorRef,\n className,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n className?: string\n}): ReactElement {\n return (\n <div\n ref={anchorRef}\n className={cn(\n 'mx-auto size-[78px] animate-pulse rounded-circle border border-gray motion-reduce:animate-none',\n className,\n )}\n />\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"power-network.js","names":[],"sources":["../../src/components/power-network.tsx"],"sourcesContent":["import {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { MaterialSymbol } from '../icons/material-symbol'\nimport { cn } from '../utils/cn'\nimport NumberFlow from './number-flow'\nimport { Skeleton } from './skeleton'\nimport { SectionTitle } from './typography'\n\n// --- Variants -----------------------------------------------------------\n\nexport type PowerSourceVariant = 'grid' | 'solar' | 'battery' | 'neutral'\n\ninterface SourceVariantStyles {\n activeClassName: string\n flowStroke: string\n initialDashGap: number\n}\n\nconst SOURCE_VARIANTS: Record<PowerSourceVariant, SourceVariantStyles> = {\n grid: {\n activeClassName: 'border-blue-a9 bg-blue-a1 shadow-blue-a4',\n flowStroke: 'var(--blue-11)',\n initialDashGap: 10,\n },\n solar: {\n activeClassName: 'border-orange-a9 bg-orange-a1 shadow-orange-a3',\n flowStroke: 'var(--orange-a9)',\n initialDashGap: 5,\n },\n battery: {\n activeClassName: 'border-green-a9 bg-green-a1 shadow-green-a3',\n flowStroke: 'var(--green-a9)',\n initialDashGap: 7,\n },\n neutral: {\n activeClassName: 'border-gray-a9 bg-gray-a1 shadow-gray-a3',\n flowStroke: 'var(--neutral-a9)',\n initialDashGap: 9,\n },\n}\n\nconst DEVICE_FLOW_STROKE = 'var(--neutral-a9)'\nconst DEVICE_INITIAL_DASH_GAP = 9\n\nconst SKELETON_STROKE = 'var(--neutral-a6)'\nconst STROKE_WIDTH = 2\nconst STRAIGHT_TOLERANCE_PX = 3\nconst SOURCE_HOME_OFFSET_STEP_PX = 10\nconst DEVICE_HOME_OFFSET_STEP_PX = 10\n\n// --- Flow animation parameters ------------------------------------------\n\nconst FLOW_DASH_SOLID = 1\nconst FLOW_GAP_MAX = 14\nconst FLOW_GAP_MIN = 5\nconst FLOW_SPEED_MIN = 10\nconst FLOW_SPEED_MAX = 56\nconst FLOW_SMOOTHING = 5.5\n\ninterface FlowVisual {\n gap: number\n speed: number\n opacity: number\n}\n\ninterface FlowAnimationState extends FlowVisual {\n offset: number\n}\n\n// --- Context ------------------------------------------------------------\n\ntype NodeKind = 'source' | 'device' | 'home'\n\ninterface RegisteredNode {\n id: string\n kind: NodeKind\n variant?: PowerSourceVariant\n watts: number\n element: HTMLDivElement | null\n}\n\ninterface PowerNetworkContextValue {\n loading: boolean\n register: (node: RegisteredNode) => void\n unregister: (id: string) => void\n totalSourceWatts: number\n}\n\nconst PowerNetworkContext = createContext<PowerNetworkContextValue | null>(null)\n\nfunction usePowerNetwork(): PowerNetworkContextValue {\n const ctx = useContext(PowerNetworkContext)\n if (!ctx) {\n throw new Error(\n 'Power network sub-components must be used inside <PowerNetworkDiagram>.',\n )\n }\n return ctx\n}\n\n// --- Geometry helpers ---------------------------------------------------\n\ninterface Point {\n x: number\n y: number\n}\n\nfunction anchorTop(rect: DOMRect, containerRect: DOMRect): Point {\n return {\n x: rect.left + rect.width / 2 - containerRect.left,\n y: rect.top - containerRect.top,\n }\n}\n\nfunction anchorBottom(rect: DOMRect, containerRect: DOMRect): Point {\n return {\n x: rect.left + rect.width / 2 - containerRect.left,\n y: rect.bottom - containerRect.top,\n }\n}\n\nfunction buildAdaptivePath(from: Point, to: Point): string {\n if (Math.abs(from.x - to.x) < STRAIGHT_TOLERANCE_PX) {\n return `M ${from.x} ${from.y} L ${to.x} ${to.y}`\n }\n const dy = to.y - from.y\n const c1y = from.y + dy * 0.56\n const c2y = to.y - dy * 0.58\n return `M ${from.x} ${from.y} C ${from.x} ${c1y} ${to.x} ${c2y} ${to.x} ${to.y}`\n}\n\n// --- PowerNetworkDiagram ------------------------------------------------\n\nexport interface PowerNetworkDiagramProps {\n title?: string\n loading?: boolean\n className?: string\n children?: ReactNode\n}\n\ninterface ConnectorPath {\n id: string\n d: string\n kind: NodeKind\n variant?: PowerSourceVariant\n reversed: boolean\n}\n\nfunction connectorPathsEqual(a: ConnectorPath[], b: ConnectorPath[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n const ap = a[i]\n const bp = b[i]\n if (!ap || !bp) return false\n if (\n ap.id !== bp.id ||\n ap.d !== bp.d ||\n ap.variant !== bp.variant ||\n ap.kind !== bp.kind ||\n ap.reversed !== bp.reversed\n ) {\n return false\n }\n }\n return true\n}\n\nexport function PowerNetworkDiagram({\n title,\n loading = false,\n className,\n children,\n}: PowerNetworkDiagramProps): ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const [nodes, setNodes] = useState<Map<string, RegisteredNode>>(\n () => new Map(),\n )\n const [connectorPaths, setConnectorPaths] = useState<ConnectorPath[]>([])\n const connectorPathsRef = useRef<ConnectorPath[]>([])\n const flowPathRefs = useRef<Record<string, SVGPathElement | null>>({})\n const currentFlowVisualsRef = useRef<Record<string, FlowAnimationState>>({})\n const targetFlowVisualsRef = useRef<Record<string, FlowVisual>>({})\n\n const register = useCallback((node: RegisteredNode) => {\n setNodes((prev) => {\n const existing = prev.get(node.id)\n if (\n existing &&\n existing.kind === node.kind &&\n existing.variant === node.variant &&\n existing.watts === node.watts &&\n existing.element === node.element\n ) {\n return prev\n }\n const next = new Map(prev)\n next.set(node.id, node)\n return next\n })\n }, [])\n\n const unregister = useCallback((id: string) => {\n setNodes((prev) => {\n if (!prev.has(id)) return prev\n const next = new Map(prev)\n next.delete(id)\n return next\n })\n }, [])\n\n const totalSourceWatts = useMemo(() => {\n let total = 0\n for (const node of nodes.values()) {\n if (node.kind === 'source') total += node.watts\n }\n return total\n }, [nodes])\n\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n\n let homeNode: RegisteredNode | undefined\n const sources: RegisteredNode[] = []\n const devices: RegisteredNode[] = []\n for (const node of nodes.values()) {\n if (node.kind === 'home') {\n homeNode = node\n } else if (node.element) {\n if (node.kind === 'source') sources.push(node)\n else devices.push(node)\n }\n }\n\n const updatePaths = () => {\n if (!homeNode || !homeNode.element) {\n setConnectorPaths((prev) => (prev.length === 0 ? prev : []))\n return\n }\n const containerRect = container.getBoundingClientRect()\n const homeRect = homeNode.element.getBoundingClientRect()\n const homeTop = anchorTop(homeRect, containerRect)\n const homeBottom = anchorBottom(homeRect, containerRect)\n const next: ConnectorPath[] = []\n\n const sourceCount = sources.length\n const deviceCount = devices.length\n\n sources.forEach((source, index) => {\n if (!source.element) return\n const from = anchorBottom(\n source.element.getBoundingClientRect(),\n containerRect,\n )\n const offsetX =\n SOURCE_HOME_OFFSET_STEP_PX * (index - (sourceCount - 1) / 2)\n const to = { x: homeTop.x + offsetX, y: homeTop.y }\n next.push({\n id: source.id,\n d: buildAdaptivePath(from, to),\n kind: 'source',\n variant: source.variant,\n reversed: source.watts < 0,\n })\n })\n\n devices.forEach((device, index) => {\n if (!device.element) return\n const to = anchorTop(\n device.element.getBoundingClientRect(),\n containerRect,\n )\n const offsetX =\n DEVICE_HOME_OFFSET_STEP_PX * (index - (deviceCount - 1) / 2)\n const from = { x: homeBottom.x + offsetX, y: homeBottom.y }\n next.push({\n id: device.id,\n d: buildAdaptivePath(from, to),\n kind: 'device',\n reversed: false,\n })\n })\n\n setConnectorPaths((prev) =>\n connectorPathsEqual(prev, next) ? prev : next,\n )\n }\n\n updatePaths()\n\n const resizeObserver = new ResizeObserver(updatePaths)\n resizeObserver.observe(container)\n for (const node of nodes.values()) {\n if (node.element) resizeObserver.observe(node.element)\n }\n window.addEventListener('resize', updatePaths)\n\n return () => {\n resizeObserver.disconnect()\n window.removeEventListener('resize', updatePaths)\n }\n }, [nodes])\n\n useEffect(() => {\n connectorPathsRef.current = connectorPaths\n }, [connectorPaths])\n\n useEffect(() => {\n const targets: Record<string, FlowVisual> = {}\n const homeWatts = totalSourceWatts\n for (const node of nodes.values()) {\n if (node.kind === 'home') continue\n const watts = node.watts\n const relative =\n homeWatts > 0\n ? Math.max(0, Math.min(1, Math.abs(watts) / homeWatts))\n : 0\n targets[node.id] = {\n gap: FLOW_GAP_MAX - (FLOW_GAP_MAX - FLOW_GAP_MIN) * relative,\n speed: FLOW_SPEED_MIN + (FLOW_SPEED_MAX - FLOW_SPEED_MIN) * relative,\n opacity: Math.abs(watts) > 0 ? 1 : 0,\n }\n }\n targetFlowVisualsRef.current = targets\n }, [nodes, totalSourceWatts])\n\n useEffect(() => {\n if (loading || reducedMotion) return\n let frameId = 0\n let lastFrameTime = performance.now()\n\n const animate = (now: number) => {\n const deltaSeconds = Math.max(0.001, (now - lastFrameTime) / 1000)\n lastFrameTime = now\n const smoothing = 1 - Math.exp(-FLOW_SMOOTHING * deltaSeconds)\n\n for (const connectorPath of connectorPathsRef.current) {\n const id = connectorPath.id\n const target = targetFlowVisualsRef.current[id]\n const pathElement = flowPathRefs.current[id]\n if (!target || !pathElement) continue\n\n const current = currentFlowVisualsRef.current[id] ?? {\n gap: target.gap,\n offset: 0,\n speed: target.speed,\n opacity: target.opacity,\n }\n\n current.gap += (target.gap - current.gap) * smoothing\n current.speed += (target.speed - current.speed) * smoothing\n current.opacity += (target.opacity - current.opacity) * smoothing\n\n const dashCycle = FLOW_DASH_SOLID + Math.max(0.001, current.gap)\n const direction = connectorPath.reversed ? 1 : -1\n const nextOffset =\n current.offset + direction * current.speed * deltaSeconds\n const wrappedOffset = ((nextOffset % dashCycle) + dashCycle) % dashCycle\n current.offset = wrappedOffset - dashCycle\n\n pathElement.setAttribute(\n 'stroke-dasharray',\n `${FLOW_DASH_SOLID} ${current.gap.toFixed(3)}`,\n )\n pathElement.setAttribute('stroke-dashoffset', current.offset.toFixed(3))\n pathElement.setAttribute('opacity', current.opacity.toFixed(3))\n\n currentFlowVisualsRef.current[id] = current\n }\n\n frameId = window.requestAnimationFrame(animate)\n }\n\n frameId = window.requestAnimationFrame(animate)\n return () => window.cancelAnimationFrame(frameId)\n }, [loading, reducedMotion])\n\n // Reduced motion: freeze the marching dashes but keep the dash gap tracking\n // live magnitude, and let opacity fade in/out via the CSS transition on each\n // flow path (see the flow <path> style below).\n useEffect(() => {\n if (loading || !reducedMotion) return\n for (const connectorPath of connectorPathsRef.current) {\n const target = targetFlowVisualsRef.current[connectorPath.id]\n const pathElement = flowPathRefs.current[connectorPath.id]\n if (!target || !pathElement) continue\n pathElement.setAttribute(\n 'stroke-dasharray',\n `${FLOW_DASH_SOLID} ${target.gap.toFixed(3)}`,\n )\n pathElement.setAttribute('stroke-dashoffset', '0')\n pathElement.setAttribute('opacity', target.opacity.toFixed(3))\n }\n }, [loading, reducedMotion, nodes, totalSourceWatts])\n\n const contextValue = useMemo<PowerNetworkContextValue>(\n () => ({ loading, register, unregister, totalSourceWatts }),\n [loading, register, unregister, totalSourceWatts],\n )\n\n return (\n <PowerNetworkContext.Provider value={contextValue}>\n <div\n ref={containerRef}\n className={cn('@container relative flex flex-col', className)}\n >\n {title && <SectionTitle>{title}</SectionTitle>}\n {connectorPaths.length > 0 && (\n <svg className=\"pointer-events-none absolute inset-0 size-full\">\n {connectorPaths.map((path) => {\n if (loading) {\n return (\n <path\n key={path.id}\n d={path.d}\n fill=\"none\"\n stroke={SKELETON_STROKE}\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n />\n )\n }\n\n const flowStroke =\n path.kind === 'source' && path.variant\n ? SOURCE_VARIANTS[path.variant].flowStroke\n : DEVICE_FLOW_STROKE\n const initialDashGap =\n path.kind === 'source' && path.variant\n ? SOURCE_VARIANTS[path.variant].initialDashGap\n : DEVICE_INITIAL_DASH_GAP\n\n return (\n <g key={path.id}>\n <path\n d={path.d}\n fill=\"none\"\n stroke=\"var(--neutral-a1)\"\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n />\n <path\n d={path.d}\n fill=\"none\"\n stroke={flowStroke}\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n ref={(el) => {\n flowPathRefs.current[path.id] = el\n }}\n strokeDasharray={`${FLOW_DASH_SOLID} ${initialDashGap}`}\n strokeDashoffset={0}\n opacity={0}\n style={\n reducedMotion\n ? { transition: 'opacity 300ms ease' }\n : undefined\n }\n />\n </g>\n )\n })}\n </svg>\n )}\n <div className=\"flex flex-col gap-y-14\">{children}</div>\n </div>\n </PowerNetworkContext.Provider>\n )\n}\n\n// --- Row containers -----------------------------------------------------\n\nexport interface PowerNetworkRowProps {\n className?: string\n children?: ReactNode\n}\n\nexport function PowerNetworkSources({\n className,\n children,\n}: PowerNetworkRowProps): ReactElement {\n return (\n <div\n className={cn(\n 'flex items-center justify-center gap-[clamp(1.5rem,10cqw,4rem)]',\n className,\n )}\n >\n {children}\n </div>\n )\n}\n\nexport function PowerNetworkDevices({\n className,\n children,\n}: PowerNetworkRowProps): ReactElement {\n return (\n <div\n className={cn(\n 'flex items-center justify-center gap-[clamp(1.5rem,10cqw,4rem)]',\n className,\n )}\n >\n {children}\n </div>\n )\n}\n\n// --- PowerSource --------------------------------------------------------\n\nexport interface PowerSourceProps {\n id?: string\n watts: number\n icon: ReactNode\n variant?: PowerSourceVariant\n className?: string\n /**\n * Optional 0–100 progress (e.g. battery SoC). When set, a thin colored arc\n * is drawn around the icon circle as a non-intrusive indicator.\n */\n progress?: number\n}\n\nexport function PowerSource({\n id: idProp,\n watts,\n icon,\n variant = 'neutral',\n className,\n progress,\n}: PowerSourceProps): ReactElement {\n const autoId = useId()\n const id = idProp ?? autoId\n const { register, unregister, loading } = usePowerNetwork()\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!element) return\n register({ id, kind: 'source', variant, watts, element })\n }, [element, register, id, variant, watts])\n\n useEffect(() => {\n return () => unregister(id)\n }, [unregister, id])\n\n if (loading) {\n return <SourceSkeleton anchorRef={setElement} />\n }\n\n const isActive = watts !== 0\n const variantStyles = SOURCE_VARIANTS[variant]\n const clampedProgress =\n progress == null ? null : Math.max(0, Math.min(100, progress))\n const showRing = clampedProgress != null\n const RING_RADIUS = 48\n const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS\n const ringDash =\n clampedProgress != null ? (clampedProgress / 100) * RING_CIRCUMFERENCE : 0\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-1 transition-opacity duration-500',\n !isActive && 'opacity-45',\n className,\n )}\n >\n <NumberFlow\n value={Math.abs(watts)}\n suffix=\"W\"\n className=\"mt-1 text-sm leading-none font-medium\"\n />\n <div\n ref={setElement}\n className={cn(\n 'relative flex aspect-square items-center justify-center rounded-circle border p-4 shadow-sm',\n isActive\n ? variantStyles.activeClassName\n : 'border-gray bg-gray-a3 shadow-gray-a4 saturate-0',\n )}\n >\n {showRing && (\n <svg\n className=\"pointer-events-none absolute inset-0 size-full -rotate-90\"\n viewBox=\"0 0 100 100\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"50\"\n cy=\"50\"\n r={RING_RADIUS}\n fill=\"none\"\n stroke={variantStyles.flowStroke}\n strokeWidth=\"4\"\n strokeLinecap=\"round\"\n strokeDasharray={`${ringDash} ${RING_CIRCUMFERENCE}`}\n opacity={isActive ? 1 : 0.5}\n />\n </svg>\n )}\n {icon}\n </div>\n </div>\n )\n}\n\nfunction SourceSkeleton({\n anchorRef,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n}): ReactElement {\n return (\n <div className=\"flex flex-col items-center justify-center gap-1\">\n <Skeleton className=\"h-3.5 w-10\" />\n <div\n ref={anchorRef}\n className=\"flex aspect-square animate-pulse items-center justify-center rounded-circle border border-gray bg-base-gray-soft p-4 motion-reduce:animate-none\"\n >\n <div className=\"size-7\" />\n </div>\n </div>\n )\n}\n\n// --- PowerDevice --------------------------------------------------------\n\nexport interface PowerDeviceProps {\n id?: string\n watts: number\n icon: ReactNode\n className?: string\n}\n\nexport function PowerDevice({\n id: idProp,\n watts,\n icon,\n className,\n}: PowerDeviceProps): ReactElement {\n const autoId = useId()\n const id = idProp ?? autoId\n const { register, unregister, loading } = usePowerNetwork()\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!element) return\n register({ id, kind: 'device', watts, element })\n }, [element, register, id, watts])\n\n useEffect(() => {\n return () => unregister(id)\n }, [unregister, id])\n\n if (loading) {\n return <DeviceSkeleton anchorRef={setElement} />\n }\n\n return (\n <div\n className={cn(\n 'flex flex-col items-center justify-center gap-1',\n className,\n )}\n >\n <div\n ref={setElement}\n className=\"flex aspect-square items-center justify-center rounded-circle border border-gray bg-gray-a1 p-4\"\n >\n {icon}\n </div>\n <NumberFlow\n value={watts}\n suffix=\"W\"\n className=\"text-sm font-medium tabular-nums\"\n />\n </div>\n )\n}\n\nfunction DeviceSkeleton({\n anchorRef,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n}): ReactElement {\n return (\n <div className=\"flex flex-col items-center justify-center gap-1\">\n <div\n ref={anchorRef}\n className=\"flex aspect-square animate-pulse items-center justify-center rounded-circle border border-gray bg-base-gray-soft p-4 motion-reduce:animate-none\"\n >\n <div className=\"size-5\" />\n </div>\n <Skeleton className=\"h-3.5 w-10\" />\n </div>\n )\n}\n\n// --- PowerNetworkHome ---------------------------------------------------\n\nexport interface PowerNetworkHomeProps {\n /** Override total power. Defaults to the sum of registered source watts. */\n watts?: number\n /** Self-sufficiency ratio in 0..1. Renders a green ring when provided. */\n autarky?: number\n className?: string\n}\n\nexport function PowerNetworkHome({\n watts,\n autarky,\n className,\n}: PowerNetworkHomeProps): ReactElement {\n const autoId = useId()\n const { register, unregister, loading, totalSourceWatts } = usePowerNetwork()\n const [element, setElement] = useState<HTMLDivElement | null>(null)\n const effectiveWatts = watts ?? totalSourceWatts\n\n useEffect(() => {\n if (!element) return\n register({ id: autoId, kind: 'home', watts: effectiveWatts, element })\n }, [element, register, autoId, effectiveWatts])\n\n useEffect(() => {\n return () => unregister(autoId)\n }, [unregister, autoId])\n\n if (loading) {\n return <HomeSkeleton anchorRef={setElement} className={className} />\n }\n\n return (\n <HomeSection\n anchorRef={setElement}\n autarky={autarky ?? null}\n watts={effectiveWatts}\n className={className}\n />\n )\n}\n\nfunction HomeSection({\n anchorRef,\n autarky,\n className,\n watts,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n autarky: number | null\n className?: string\n watts: number\n}): ReactElement {\n const reducedMotion = useReducedMotion()\n const targetAutarky =\n autarky == null ? 0 : Math.max(0, Math.min(100, autarky * 100))\n const [animatedAutarky, setAnimatedAutarky] = useState(targetAutarky)\n const animatedAutarkyRef = useRef(animatedAutarky)\n const nodeSize = 78\n const ringStrokeWidth = 2\n const radius = (nodeSize - ringStrokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n const progressLength = (animatedAutarky / 100) * circumference\n\n useEffect(() => {\n animatedAutarkyRef.current = animatedAutarky\n }, [animatedAutarky])\n\n useEffect(() => {\n if (reducedMotion) {\n animatedAutarkyRef.current = targetAutarky\n setAnimatedAutarky(targetAutarky)\n return\n }\n\n let frameId = 0\n let lastTime = performance.now()\n\n const animate = (now: number) => {\n const deltaSeconds = Math.max(0.001, (now - lastTime) / 1000)\n lastTime = now\n const smoothing = 1 - Math.exp(-7 * deltaSeconds)\n const next =\n animatedAutarkyRef.current +\n (targetAutarky - animatedAutarkyRef.current) * smoothing\n\n animatedAutarkyRef.current = next\n setAnimatedAutarky(next)\n\n if (Math.abs(targetAutarky - next) > 0.05) {\n frameId = window.requestAnimationFrame(animate)\n } else {\n animatedAutarkyRef.current = targetAutarky\n setAnimatedAutarky(targetAutarky)\n }\n }\n\n frameId = window.requestAnimationFrame(animate)\n return () => window.cancelAnimationFrame(frameId)\n }, [targetAutarky, reducedMotion])\n\n return (\n <div\n ref={anchorRef}\n className={cn('relative mx-auto size-[78px]', className)}\n >\n {autarky != null && (\n <svg\n className=\"absolute inset-0 size-full -rotate-90\"\n viewBox={`0 0 ${nodeSize} ${nodeSize}`}\n >\n <circle\n cx={nodeSize / 2}\n cy={nodeSize / 2}\n r={radius}\n fill=\"none\"\n stroke=\"var(--neutral-a5)\"\n strokeWidth={ringStrokeWidth}\n />\n {progressLength > 0 && (\n <circle\n cx={nodeSize / 2}\n cy={nodeSize / 2}\n r={radius}\n fill=\"none\"\n stroke=\"var(--green-a9)\"\n strokeLinecap=\"round\"\n strokeWidth={ringStrokeWidth}\n strokeDasharray={`${progressLength} ${circumference}`}\n />\n )}\n </svg>\n )}\n <div className=\"absolute inset-0 flex flex-col items-center justify-center rounded-circle border border-gray-a5 shadow-sm shadow-gray-a1\">\n <MaterialSymbol className=\"text-[14px]\" name=\"home\" />\n <NumberFlow\n value={watts}\n suffix=\"W\"\n className=\"mt-1 text-sm leading-none font-medium\"\n />\n </div>\n </div>\n )\n}\n\nfunction HomeSkeleton({\n anchorRef,\n className,\n}: {\n anchorRef: (element: HTMLDivElement | null) => void\n className?: string\n}): ReactElement {\n return (\n <div\n ref={anchorRef}\n className={cn(\n 'mx-auto size-[78px] animate-pulse rounded-circle border border-gray motion-reduce:animate-none',\n className,\n )}\n />\n )\n}\n"],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ const Progress = react.forwardRef(({ className, value, max = 100, size = "defaul
|
|
|
17
17
|
"aria-valuemax": max,
|
|
18
18
|
"aria-valuemin": 0,
|
|
19
19
|
"aria-valuenow": value,
|
|
20
|
-
className: (0, cnfast.cn)("w-full overflow-hidden rounded-full bg-
|
|
20
|
+
className: (0, cnfast.cn)("w-full overflow-hidden rounded-full bg-gray-a4", sizeClasses[size], className),
|
|
21
21
|
ref,
|
|
22
22
|
role: "progressbar",
|
|
23
23
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.cjs","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-
|
|
1
|
+
{"version":3,"file":"progress.cjs","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-spark-9 transition-all duration-300\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;CACL;AAED,MAAM,WAAWA,MAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,IAAI,CAAC;AAElE,QACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,OAAA,IACE,kDACA,YAAY,OACZ,UACD;EACI;EACL,MAAK;EACL,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,IAAI;GAClC,CAAA;EACE,CAAA;EAGX;AAED,SAAS,cAAc"}
|
|
@@ -14,7 +14,7 @@ const Progress = React$1.forwardRef(({ className, value, max = 100, size = "defa
|
|
|
14
14
|
"aria-valuemax": max,
|
|
15
15
|
"aria-valuemin": 0,
|
|
16
16
|
"aria-valuenow": value,
|
|
17
|
-
className: cn("w-full overflow-hidden rounded-full bg-
|
|
17
|
+
className: cn("w-full overflow-hidden rounded-full bg-gray-a4", sizeClasses[size], className),
|
|
18
18
|
ref,
|
|
19
19
|
role: "progressbar",
|
|
20
20
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-
|
|
1
|
+
{"version":3,"file":"progress.js","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-spark-9 transition-all duration-300\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;CACL;AAED,MAAM,WAAWA,QAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,IAAI,CAAC;AAElE,QACE,oBAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,WAAW,GACT,kDACA,YAAY,OACZ,UACD;EACI;EACL,MAAK;EACL,GAAI;YAEJ,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,IAAI;GAClC,CAAA;EACE,CAAA;EAGX;AAED,SAAS,cAAc"}
|
|
@@ -7,6 +7,12 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
7
7
|
let cnfast = require("cnfast");
|
|
8
8
|
//#region src/components/radio-card-group.tsx
|
|
9
9
|
const RadioCardGroupContext = react.createContext(void 0);
|
|
10
|
+
const ARROW_STEP = {
|
|
11
|
+
ArrowDown: 1,
|
|
12
|
+
ArrowRight: 1,
|
|
13
|
+
ArrowUp: -1,
|
|
14
|
+
ArrowLeft: -1
|
|
15
|
+
};
|
|
10
16
|
const RadioCardGroup = react.forwardRef(({ value: valueProp, defaultValue, onValueChange, disabled, name, className, children, ...props }, ref) => {
|
|
11
17
|
const generatedName = react.useId();
|
|
12
18
|
const [internalValue, setInternalValue] = react.useState(defaultValue);
|
|
@@ -28,11 +34,29 @@ const RadioCardGroup = react.forwardRef(({ value: valueProp, defaultValue, onVal
|
|
|
28
34
|
name,
|
|
29
35
|
generatedName
|
|
30
36
|
]);
|
|
37
|
+
const handleKeyDown = (event) => {
|
|
38
|
+
const step = ARROW_STEP[event.key];
|
|
39
|
+
if (step === void 0 && event.key !== "Home" && event.key !== "End") return;
|
|
40
|
+
const radios = [...event.currentTarget.querySelectorAll("[role=\"radio\"]:not(:disabled)")];
|
|
41
|
+
if (radios.length === 0) return;
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
let next = 0;
|
|
44
|
+
if (event.key === "End") next = radios.length - 1;
|
|
45
|
+
else if (step !== void 0) {
|
|
46
|
+
const current = radios.indexOf(document.activeElement);
|
|
47
|
+
if (current !== -1) next = (current + step + radios.length) % radios.length;
|
|
48
|
+
}
|
|
49
|
+
const target = radios[next];
|
|
50
|
+
if (!target) return;
|
|
51
|
+
target.focus();
|
|
52
|
+
target.click();
|
|
53
|
+
};
|
|
31
54
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RadioCardGroupContext.Provider, {
|
|
32
55
|
value: ctx,
|
|
33
56
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
34
57
|
ref,
|
|
35
58
|
role: "radiogroup",
|
|
59
|
+
onKeyDown: handleKeyDown,
|
|
36
60
|
className: (0, cnfast.cn)("flex flex-col gap-2", className),
|
|
37
61
|
...props,
|
|
38
62
|
children
|
|
@@ -51,12 +75,13 @@ const RadioCardGroupItem = react.forwardRef(({ value, className, children, disab
|
|
|
51
75
|
role: "radio",
|
|
52
76
|
"aria-checked": checked,
|
|
53
77
|
"data-state": checked ? "checked" : "unchecked",
|
|
78
|
+
tabIndex: checked || ctx.value === void 0 ? 0 : -1,
|
|
54
79
|
disabled: isDisabled,
|
|
55
80
|
onClick: (event) => {
|
|
56
81
|
onClick?.(event);
|
|
57
82
|
if (!event.defaultPrevented && !isDisabled) ctx.onValueChange(value);
|
|
58
83
|
},
|
|
59
|
-
className: (0, cnfast.cn)("h-12 w-full rounded-
|
|
84
|
+
className: (0, cnfast.cn)("h-12 w-full rounded-lg px-4 text-base font-medium transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50", checked ? "border border-accent bg-base-accent-soft text-accent" : "border border-transparent bg-base-gray-soft text-gray hover:bg-gray-a3", className),
|
|
60
85
|
...props,
|
|
61
86
|
children
|
|
62
87
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-card-group.cjs","names":["React"],"sources":["../../src/components/radio-card-group.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\ninterface RadioCardGroupContextValue {\n value: string | undefined\n onValueChange: (value: string) => void\n disabled?: boolean\n name: string\n}\n\nconst RadioCardGroupContext = React.createContext<\n RadioCardGroupContextValue | undefined\n>(undefined)\n\ninterface RadioCardGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n disabled?: boolean\n /** Optional name used for ARIA grouping. Defaults to a generated id. */\n name?: string\n}\n\nconst RadioCardGroup = React.forwardRef<HTMLDivElement, RadioCardGroupProps>(\n (\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n disabled,\n name,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const generatedName = React.useId()\n const [internalValue, setInternalValue] = React.useState<\n string | undefined\n >(defaultValue)\n const isControlled = valueProp !== undefined\n const value = isControlled ? valueProp : internalValue\n\n const handleValueChange = React.useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next)\n onValueChange?.(next)\n },\n [isControlled, onValueChange],\n )\n\n const ctx = React.useMemo<RadioCardGroupContextValue>(\n () => ({\n value,\n onValueChange: handleValueChange,\n disabled,\n name: name ?? generatedName,\n }),\n [value, handleValueChange, disabled, name, generatedName],\n )\n\n return (\n <RadioCardGroupContext.Provider value={ctx}>\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn('flex flex-col gap-2', className)}\n {...props}\n >\n {children}\n </div>\n </RadioCardGroupContext.Provider>\n )\n },\n)\n\nRadioCardGroup.displayName = 'RadioCardGroup'\n\ninterface RadioCardGroupItemProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n value: string\n}\n\nconst RadioCardGroupItem = React.forwardRef<\n HTMLButtonElement,\n RadioCardGroupItemProps\n>(({ value, className, children, disabled, onClick, ...props }, ref) => {\n const ctx = React.useContext(RadioCardGroupContext)\n if (!ctx) {\n throw new Error('RadioCardGroupItem must be used within a RadioCardGroup')\n }\n\n const checked = ctx.value === value\n const isDisabled = disabled ?? ctx.disabled\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n data-state={checked ? 'checked' : 'unchecked'}\n disabled={isDisabled}\n onClick={(event) => {\n onClick?.(event)\n if (!event.defaultPrevented && !isDisabled) {\n ctx.onValueChange(value)\n }\n }}\n className={cn(\n 'h-12 w-full rounded-
|
|
1
|
+
{"version":3,"file":"radio-card-group.cjs","names":["React"],"sources":["../../src/components/radio-card-group.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\ninterface RadioCardGroupContextValue {\n value: string | undefined\n onValueChange: (value: string) => void\n disabled?: boolean\n name: string\n}\n\nconst RadioCardGroupContext = React.createContext<\n RadioCardGroupContextValue | undefined\n>(undefined)\n\nconst ARROW_STEP: Record<string, 1 | -1> = {\n ArrowDown: 1,\n ArrowRight: 1,\n ArrowUp: -1,\n ArrowLeft: -1,\n}\n\ninterface RadioCardGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n disabled?: boolean\n /** Optional name used for ARIA grouping. Defaults to a generated id. */\n name?: string\n}\n\nconst RadioCardGroup = React.forwardRef<HTMLDivElement, RadioCardGroupProps>(\n (\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n disabled,\n name,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const generatedName = React.useId()\n const [internalValue, setInternalValue] = React.useState<\n string | undefined\n >(defaultValue)\n const isControlled = valueProp !== undefined\n const value = isControlled ? valueProp : internalValue\n\n const handleValueChange = React.useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next)\n onValueChange?.(next)\n },\n [isControlled, onValueChange],\n )\n\n const ctx = React.useMemo<RadioCardGroupContextValue>(\n () => ({\n value,\n onValueChange: handleValueChange,\n disabled,\n name: name ?? generatedName,\n }),\n [value, handleValueChange, disabled, name, generatedName],\n )\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const step = ARROW_STEP[event.key]\n if (step === undefined && event.key !== 'Home' && event.key !== 'End')\n return\n const radios = [\n ...event.currentTarget.querySelectorAll<HTMLButtonElement>(\n '[role=\"radio\"]:not(:disabled)',\n ),\n ]\n if (radios.length === 0) return\n event.preventDefault()\n\n let next = 0\n if (event.key === 'End') {\n next = radios.length - 1\n } else if (step !== undefined) {\n const current = radios.indexOf(\n document.activeElement as HTMLButtonElement,\n )\n if (current !== -1) {\n next = (current + step + radios.length) % radios.length\n }\n }\n const target = radios[next]\n if (!target) return\n target.focus()\n target.click()\n }\n\n return (\n <RadioCardGroupContext.Provider value={ctx}>\n <div\n ref={ref}\n role=\"radiogroup\"\n onKeyDown={handleKeyDown}\n className={cn('flex flex-col gap-2', className)}\n {...props}\n >\n {children}\n </div>\n </RadioCardGroupContext.Provider>\n )\n },\n)\n\nRadioCardGroup.displayName = 'RadioCardGroup'\n\ninterface RadioCardGroupItemProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n value: string\n}\n\nconst RadioCardGroupItem = React.forwardRef<\n HTMLButtonElement,\n RadioCardGroupItemProps\n>(({ value, className, children, disabled, onClick, ...props }, ref) => {\n const ctx = React.useContext(RadioCardGroupContext)\n if (!ctx) {\n throw new Error('RadioCardGroupItem must be used within a RadioCardGroup')\n }\n\n const checked = ctx.value === value\n const isDisabled = disabled ?? ctx.disabled\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n data-state={checked ? 'checked' : 'unchecked'}\n tabIndex={checked || ctx.value === undefined ? 0 : -1}\n disabled={isDisabled}\n onClick={(event) => {\n onClick?.(event)\n if (!event.defaultPrevented && !isDisabled) {\n ctx.onValueChange(value)\n }\n }}\n className={cn(\n 'h-12 w-full rounded-lg px-4 text-base font-medium transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',\n checked\n ? 'border border-accent bg-base-accent-soft text-accent'\n : 'border border-transparent bg-base-gray-soft text-gray hover:bg-gray-a3',\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n})\n\nRadioCardGroupItem.displayName = 'RadioCardGroupItem'\n\nexport { RadioCardGroup, RadioCardGroupItem }\nexport type { RadioCardGroupProps, RadioCardGroupItemProps }\n"],"mappings":";;;;;;;;AAUA,MAAM,wBAAwBA,MAAM,cAElC,KAAA,EAAU;AAEZ,MAAM,aAAqC;CACzC,WAAW;CACX,YAAY;CACZ,SAAS;CACT,WAAW;CACZ;AAWD,MAAM,iBAAiBA,MAAM,YAEzB,EACE,OAAO,WACP,cACA,eACA,UACA,MACA,WACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,gBAAgBA,MAAM,OAAO;CACnC,MAAM,CAAC,eAAe,oBAAoBA,MAAM,SAE9C,aAAa;CACf,MAAM,eAAe,cAAc,KAAA;CACnC,MAAM,QAAQ,eAAe,YAAY;CAEzC,MAAM,oBAAoBA,MAAM,aAC7B,SAAiB;AAChB,MAAI,CAAC,aAAc,kBAAiB,KAAK;AACzC,kBAAgB,KAAK;IAEvB,CAAC,cAAc,cAAc,CAC9B;CAED,MAAM,MAAMA,MAAM,eACT;EACL;EACA,eAAe;EACf;EACA,MAAM,QAAQ;EACf,GACD;EAAC;EAAO;EAAmB;EAAU;EAAM;EAAc,CAC1D;CAED,MAAM,iBAAiB,UAA+C;EACpE,MAAM,OAAO,WAAW,MAAM;AAC9B,MAAI,SAAS,KAAA,KAAa,MAAM,QAAQ,UAAU,MAAM,QAAQ,MAC9D;EACF,MAAM,SAAS,CACb,GAAG,MAAM,cAAc,iBACrB,kCACD,CACF;AACD,MAAI,OAAO,WAAW,EAAG;AACzB,QAAM,gBAAgB;EAEtB,IAAI,OAAO;AACX,MAAI,MAAM,QAAQ,MAChB,QAAO,OAAO,SAAS;WACd,SAAS,KAAA,GAAW;GAC7B,MAAM,UAAU,OAAO,QACrB,SAAS,cACV;AACD,OAAI,YAAY,GACd,SAAQ,UAAU,OAAO,OAAO,UAAU,OAAO;;EAGrD,MAAM,SAAS,OAAO;AACtB,MAAI,CAAC,OAAQ;AACb,SAAO,OAAO;AACd,SAAO,OAAO;;AAGhB,QACE,iBAAA,GAAA,kBAAA,KAAC,sBAAsB,UAAvB;EAAgC,OAAO;YACrC,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACO;GACL,MAAK;GACL,WAAW;GACX,YAAA,GAAA,OAAA,IAAc,uBAAuB,UAAU;GAC/C,GAAI;GAEH;GACG,CAAA;EACyB,CAAA;EAGtC;AAED,eAAe,cAAc;AAS7B,MAAM,qBAAqBA,MAAM,YAG9B,EAAE,OAAO,WAAW,UAAU,UAAU,SAAS,GAAG,SAAS,QAAQ;CACtE,MAAM,MAAMA,MAAM,WAAW,sBAAsB;AACnD,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,0DAA0D;CAG5E,MAAM,UAAU,IAAI,UAAU;CAC9B,MAAM,aAAa,YAAY,IAAI;AAEnC,QACE,iBAAA,GAAA,kBAAA,KAAC,UAAD;EACO;EACL,MAAK;EACL,MAAK;EACL,gBAAc;EACd,cAAY,UAAU,YAAY;EAClC,UAAU,WAAW,IAAI,UAAU,KAAA,IAAY,IAAI;EACnD,UAAU;EACV,UAAU,UAAU;AAClB,aAAU,MAAM;AAChB,OAAI,CAAC,MAAM,oBAAoB,CAAC,WAC9B,KAAI,cAAc,MAAM;;EAG5B,YAAA,GAAA,OAAA,IACE,qMACA,UACI,yDACA,0EACJ,UACD;EACD,GAAI;EAEH;EACM,CAAA;EAEX;AAEF,mBAAmB,cAAc"}
|
|
@@ -4,6 +4,12 @@ import * as React$1 from "react";
|
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/radio-card-group.tsx
|
|
6
6
|
const RadioCardGroupContext = React$1.createContext(void 0);
|
|
7
|
+
const ARROW_STEP = {
|
|
8
|
+
ArrowDown: 1,
|
|
9
|
+
ArrowRight: 1,
|
|
10
|
+
ArrowUp: -1,
|
|
11
|
+
ArrowLeft: -1
|
|
12
|
+
};
|
|
7
13
|
const RadioCardGroup = React$1.forwardRef(({ value: valueProp, defaultValue, onValueChange, disabled, name, className, children, ...props }, ref) => {
|
|
8
14
|
const generatedName = React$1.useId();
|
|
9
15
|
const [internalValue, setInternalValue] = React$1.useState(defaultValue);
|
|
@@ -25,11 +31,29 @@ const RadioCardGroup = React$1.forwardRef(({ value: valueProp, defaultValue, onV
|
|
|
25
31
|
name,
|
|
26
32
|
generatedName
|
|
27
33
|
]);
|
|
34
|
+
const handleKeyDown = (event) => {
|
|
35
|
+
const step = ARROW_STEP[event.key];
|
|
36
|
+
if (step === void 0 && event.key !== "Home" && event.key !== "End") return;
|
|
37
|
+
const radios = [...event.currentTarget.querySelectorAll("[role=\"radio\"]:not(:disabled)")];
|
|
38
|
+
if (radios.length === 0) return;
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
let next = 0;
|
|
41
|
+
if (event.key === "End") next = radios.length - 1;
|
|
42
|
+
else if (step !== void 0) {
|
|
43
|
+
const current = radios.indexOf(document.activeElement);
|
|
44
|
+
if (current !== -1) next = (current + step + radios.length) % radios.length;
|
|
45
|
+
}
|
|
46
|
+
const target = radios[next];
|
|
47
|
+
if (!target) return;
|
|
48
|
+
target.focus();
|
|
49
|
+
target.click();
|
|
50
|
+
};
|
|
28
51
|
return /* @__PURE__ */ jsx(RadioCardGroupContext.Provider, {
|
|
29
52
|
value: ctx,
|
|
30
53
|
children: /* @__PURE__ */ jsx("div", {
|
|
31
54
|
ref,
|
|
32
55
|
role: "radiogroup",
|
|
56
|
+
onKeyDown: handleKeyDown,
|
|
33
57
|
className: cn("flex flex-col gap-2", className),
|
|
34
58
|
...props,
|
|
35
59
|
children
|
|
@@ -48,12 +72,13 @@ const RadioCardGroupItem = React$1.forwardRef(({ value, className, children, dis
|
|
|
48
72
|
role: "radio",
|
|
49
73
|
"aria-checked": checked,
|
|
50
74
|
"data-state": checked ? "checked" : "unchecked",
|
|
75
|
+
tabIndex: checked || ctx.value === void 0 ? 0 : -1,
|
|
51
76
|
disabled: isDisabled,
|
|
52
77
|
onClick: (event) => {
|
|
53
78
|
onClick?.(event);
|
|
54
79
|
if (!event.defaultPrevented && !isDisabled) ctx.onValueChange(value);
|
|
55
80
|
},
|
|
56
|
-
className: cn("h-12 w-full rounded-
|
|
81
|
+
className: cn("h-12 w-full rounded-lg px-4 text-base font-medium transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50", checked ? "border border-accent bg-base-accent-soft text-accent" : "border border-transparent bg-base-gray-soft text-gray hover:bg-gray-a3", className),
|
|
57
82
|
...props,
|
|
58
83
|
children
|
|
59
84
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-card-group.js","names":["React"],"sources":["../../src/components/radio-card-group.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\ninterface RadioCardGroupContextValue {\n value: string | undefined\n onValueChange: (value: string) => void\n disabled?: boolean\n name: string\n}\n\nconst RadioCardGroupContext = React.createContext<\n RadioCardGroupContextValue | undefined\n>(undefined)\n\ninterface RadioCardGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n disabled?: boolean\n /** Optional name used for ARIA grouping. Defaults to a generated id. */\n name?: string\n}\n\nconst RadioCardGroup = React.forwardRef<HTMLDivElement, RadioCardGroupProps>(\n (\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n disabled,\n name,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const generatedName = React.useId()\n const [internalValue, setInternalValue] = React.useState<\n string | undefined\n >(defaultValue)\n const isControlled = valueProp !== undefined\n const value = isControlled ? valueProp : internalValue\n\n const handleValueChange = React.useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next)\n onValueChange?.(next)\n },\n [isControlled, onValueChange],\n )\n\n const ctx = React.useMemo<RadioCardGroupContextValue>(\n () => ({\n value,\n onValueChange: handleValueChange,\n disabled,\n name: name ?? generatedName,\n }),\n [value, handleValueChange, disabled, name, generatedName],\n )\n\n return (\n <RadioCardGroupContext.Provider value={ctx}>\n <div\n ref={ref}\n role=\"radiogroup\"\n className={cn('flex flex-col gap-2', className)}\n {...props}\n >\n {children}\n </div>\n </RadioCardGroupContext.Provider>\n )\n },\n)\n\nRadioCardGroup.displayName = 'RadioCardGroup'\n\ninterface RadioCardGroupItemProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n value: string\n}\n\nconst RadioCardGroupItem = React.forwardRef<\n HTMLButtonElement,\n RadioCardGroupItemProps\n>(({ value, className, children, disabled, onClick, ...props }, ref) => {\n const ctx = React.useContext(RadioCardGroupContext)\n if (!ctx) {\n throw new Error('RadioCardGroupItem must be used within a RadioCardGroup')\n }\n\n const checked = ctx.value === value\n const isDisabled = disabled ?? ctx.disabled\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n data-state={checked ? 'checked' : 'unchecked'}\n disabled={isDisabled}\n onClick={(event) => {\n onClick?.(event)\n if (!event.defaultPrevented && !isDisabled) {\n ctx.onValueChange(value)\n }\n }}\n className={cn(\n 'h-12 w-full rounded-
|
|
1
|
+
{"version":3,"file":"radio-card-group.js","names":["React"],"sources":["../../src/components/radio-card-group.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\ninterface RadioCardGroupContextValue {\n value: string | undefined\n onValueChange: (value: string) => void\n disabled?: boolean\n name: string\n}\n\nconst RadioCardGroupContext = React.createContext<\n RadioCardGroupContextValue | undefined\n>(undefined)\n\nconst ARROW_STEP: Record<string, 1 | -1> = {\n ArrowDown: 1,\n ArrowRight: 1,\n ArrowUp: -1,\n ArrowLeft: -1,\n}\n\ninterface RadioCardGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n disabled?: boolean\n /** Optional name used for ARIA grouping. Defaults to a generated id. */\n name?: string\n}\n\nconst RadioCardGroup = React.forwardRef<HTMLDivElement, RadioCardGroupProps>(\n (\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n disabled,\n name,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const generatedName = React.useId()\n const [internalValue, setInternalValue] = React.useState<\n string | undefined\n >(defaultValue)\n const isControlled = valueProp !== undefined\n const value = isControlled ? valueProp : internalValue\n\n const handleValueChange = React.useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next)\n onValueChange?.(next)\n },\n [isControlled, onValueChange],\n )\n\n const ctx = React.useMemo<RadioCardGroupContextValue>(\n () => ({\n value,\n onValueChange: handleValueChange,\n disabled,\n name: name ?? generatedName,\n }),\n [value, handleValueChange, disabled, name, generatedName],\n )\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const step = ARROW_STEP[event.key]\n if (step === undefined && event.key !== 'Home' && event.key !== 'End')\n return\n const radios = [\n ...event.currentTarget.querySelectorAll<HTMLButtonElement>(\n '[role=\"radio\"]:not(:disabled)',\n ),\n ]\n if (radios.length === 0) return\n event.preventDefault()\n\n let next = 0\n if (event.key === 'End') {\n next = radios.length - 1\n } else if (step !== undefined) {\n const current = radios.indexOf(\n document.activeElement as HTMLButtonElement,\n )\n if (current !== -1) {\n next = (current + step + radios.length) % radios.length\n }\n }\n const target = radios[next]\n if (!target) return\n target.focus()\n target.click()\n }\n\n return (\n <RadioCardGroupContext.Provider value={ctx}>\n <div\n ref={ref}\n role=\"radiogroup\"\n onKeyDown={handleKeyDown}\n className={cn('flex flex-col gap-2', className)}\n {...props}\n >\n {children}\n </div>\n </RadioCardGroupContext.Provider>\n )\n },\n)\n\nRadioCardGroup.displayName = 'RadioCardGroup'\n\ninterface RadioCardGroupItemProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'value'\n> {\n value: string\n}\n\nconst RadioCardGroupItem = React.forwardRef<\n HTMLButtonElement,\n RadioCardGroupItemProps\n>(({ value, className, children, disabled, onClick, ...props }, ref) => {\n const ctx = React.useContext(RadioCardGroupContext)\n if (!ctx) {\n throw new Error('RadioCardGroupItem must be used within a RadioCardGroup')\n }\n\n const checked = ctx.value === value\n const isDisabled = disabled ?? ctx.disabled\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n data-state={checked ? 'checked' : 'unchecked'}\n tabIndex={checked || ctx.value === undefined ? 0 : -1}\n disabled={isDisabled}\n onClick={(event) => {\n onClick?.(event)\n if (!event.defaultPrevented && !isDisabled) {\n ctx.onValueChange(value)\n }\n }}\n className={cn(\n 'h-12 w-full rounded-lg px-4 text-base font-medium transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50',\n checked\n ? 'border border-accent bg-base-accent-soft text-accent'\n : 'border border-transparent bg-base-gray-soft text-gray hover:bg-gray-a3',\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n})\n\nRadioCardGroupItem.displayName = 'RadioCardGroupItem'\n\nexport { RadioCardGroup, RadioCardGroupItem }\nexport type { RadioCardGroupProps, RadioCardGroupItemProps }\n"],"mappings":";;;;;AAUA,MAAM,wBAAwBA,QAAM,cAElC,KAAA,EAAU;AAEZ,MAAM,aAAqC;CACzC,WAAW;CACX,YAAY;CACZ,SAAS;CACT,WAAW;CACZ;AAWD,MAAM,iBAAiBA,QAAM,YAEzB,EACE,OAAO,WACP,cACA,eACA,UACA,MACA,WACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,gBAAgBA,QAAM,OAAO;CACnC,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAE9C,aAAa;CACf,MAAM,eAAe,cAAc,KAAA;CACnC,MAAM,QAAQ,eAAe,YAAY;CAEzC,MAAM,oBAAoBA,QAAM,aAC7B,SAAiB;AAChB,MAAI,CAAC,aAAc,kBAAiB,KAAK;AACzC,kBAAgB,KAAK;IAEvB,CAAC,cAAc,cAAc,CAC9B;CAED,MAAM,MAAMA,QAAM,eACT;EACL;EACA,eAAe;EACf;EACA,MAAM,QAAQ;EACf,GACD;EAAC;EAAO;EAAmB;EAAU;EAAM;EAAc,CAC1D;CAED,MAAM,iBAAiB,UAA+C;EACpE,MAAM,OAAO,WAAW,MAAM;AAC9B,MAAI,SAAS,KAAA,KAAa,MAAM,QAAQ,UAAU,MAAM,QAAQ,MAC9D;EACF,MAAM,SAAS,CACb,GAAG,MAAM,cAAc,iBACrB,kCACD,CACF;AACD,MAAI,OAAO,WAAW,EAAG;AACzB,QAAM,gBAAgB;EAEtB,IAAI,OAAO;AACX,MAAI,MAAM,QAAQ,MAChB,QAAO,OAAO,SAAS;WACd,SAAS,KAAA,GAAW;GAC7B,MAAM,UAAU,OAAO,QACrB,SAAS,cACV;AACD,OAAI,YAAY,GACd,SAAQ,UAAU,OAAO,OAAO,UAAU,OAAO;;EAGrD,MAAM,SAAS,OAAO;AACtB,MAAI,CAAC,OAAQ;AACb,SAAO,OAAO;AACd,SAAO,OAAO;;AAGhB,QACE,oBAAC,sBAAsB,UAAvB;EAAgC,OAAO;YACrC,oBAAC,OAAD;GACO;GACL,MAAK;GACL,WAAW;GACX,WAAW,GAAG,uBAAuB,UAAU;GAC/C,GAAI;GAEH;GACG,CAAA;EACyB,CAAA;EAGtC;AAED,eAAe,cAAc;AAS7B,MAAM,qBAAqBA,QAAM,YAG9B,EAAE,OAAO,WAAW,UAAU,UAAU,SAAS,GAAG,SAAS,QAAQ;CACtE,MAAM,MAAMA,QAAM,WAAW,sBAAsB;AACnD,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,0DAA0D;CAG5E,MAAM,UAAU,IAAI,UAAU;CAC9B,MAAM,aAAa,YAAY,IAAI;AAEnC,QACE,oBAAC,UAAD;EACO;EACL,MAAK;EACL,MAAK;EACL,gBAAc;EACd,cAAY,UAAU,YAAY;EAClC,UAAU,WAAW,IAAI,UAAU,KAAA,IAAY,IAAI;EACnD,UAAU;EACV,UAAU,UAAU;AAClB,aAAU,MAAM;AAChB,OAAI,CAAC,MAAM,oBAAoB,CAAC,WAC9B,KAAI,cAAc,MAAM;;EAG5B,WAAW,GACT,qMACA,UACI,yDACA,0EACJ,UACD;EACD,GAAI;EAEH;EACM,CAAA;EAEX;AAEF,mBAAmB,cAAc"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_chunk = require("../chunk-aJGBMT86.cjs");
|
|
4
|
+
const require_accent_colors = require("../accent-colors-CjHCf8fH.cjs");
|
|
4
5
|
let react = require("react");
|
|
5
6
|
react = require_chunk.__toESM(react);
|
|
6
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -8,22 +9,28 @@ let _base_ui_react_radio = require("@base-ui/react/radio");
|
|
|
8
9
|
let _base_ui_react_radio_group = require("@base-ui/react/radio-group");
|
|
9
10
|
let cnfast = require("cnfast");
|
|
10
11
|
//#region src/components/radio.tsx
|
|
11
|
-
function RadioGroup({ className, ...props }) {
|
|
12
|
+
function RadioGroup({ className, styleVariant, color, ...props }) {
|
|
12
13
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_radio_group.RadioGroup, {
|
|
13
14
|
"data-slot": "radio-group",
|
|
15
|
+
"data-style": styleVariant !== void 0 || color !== void 0 ? styleVariant ?? "solid" : void 0,
|
|
16
|
+
"data-accent-color": color ? require_accent_colors.accentByColor[color] : void 0,
|
|
14
17
|
className: (0, cnfast.cn)("grid gap-3", className),
|
|
15
18
|
...props
|
|
16
19
|
});
|
|
17
20
|
}
|
|
18
|
-
function Radio({ className, ...props }) {
|
|
21
|
+
function Radio({ className, styleVariant, color, ...props }) {
|
|
22
|
+
const dataStyle = styleVariant !== void 0 || color !== void 0 ? styleVariant ?? "solid" : void 0;
|
|
23
|
+
const dataAccentColor = color ? require_accent_colors.accentByColor[color] : void 0;
|
|
19
24
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_radio.Radio.Root, {
|
|
20
25
|
"data-slot": "radio",
|
|
21
|
-
|
|
26
|
+
"data-style": dataStyle,
|
|
27
|
+
"data-accent-color": dataAccentColor,
|
|
28
|
+
className: (0, cnfast.cn)("peer relative inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 transition-colors", "before:absolute before:top-1/2 before:left-1/2 before:size-10 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']", "focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden", "data-disabled:cursor-not-allowed data-disabled:opacity-50", "border-(--spark-style-idle-border,var(--spark-a7)) bg-base-accent-soft data-unchecked:border-(length:--spark-style-border-width,2px)", "data-checked:border-(--spark-radio-ring,var(--spark-9))", className),
|
|
22
29
|
...props,
|
|
23
30
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_radio.Radio.Indicator, {
|
|
24
31
|
"data-slot": "radio-indicator",
|
|
25
32
|
className: "flex items-center justify-center",
|
|
26
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "size-4 rounded-full bg-
|
|
33
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "size-4 rounded-full bg-(--spark-radio-dot,var(--spark-9)) motion-safe:animate-dot-pop" })
|
|
27
34
|
})
|
|
28
35
|
});
|
|
29
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio.cjs","names":["RadioGroupPrimitive","RadioPrimitive"],"sources":["../../src/components/radio.tsx"],"sourcesContent":["import { Radio as RadioPrimitive } from '@base-ui/react/radio'\nimport { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\n\ntype RadioGroupProps = React.ComponentProps<typeof RadioGroupPrimitive
|
|
1
|
+
{"version":3,"file":"radio.cjs","names":["RadioGroupPrimitive","accentByColor","RadioPrimitive"],"sources":["../../src/components/radio.tsx"],"sourcesContent":["import { Radio as RadioPrimitive } from '@base-ui/react/radio'\nimport { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\n\nexport type SparkRadioColor = 'accent' | 'neutral' | 'danger'\n\ntype RadioGroupProps = React.ComponentProps<typeof RadioGroupPrimitive> & {\n styleVariant?: SparkStyle\n color?: SparkRadioColor\n}\n\nfunction RadioGroup({\n className,\n styleVariant,\n color,\n ...props\n}: RadioGroupProps) {\n const hasLook = styleVariant !== undefined || color !== undefined\n const dataStyle = hasLook ? (styleVariant ?? 'solid') : undefined\n const dataAccentColor = color ? accentByColor[color] : undefined\n\n return (\n <RadioGroupPrimitive\n data-slot=\"radio-group\"\n data-style={dataStyle}\n data-accent-color={dataAccentColor}\n className={cn('grid gap-3', className)}\n {...props}\n />\n )\n}\n\ntype RadioProps = React.ComponentProps<typeof RadioPrimitive.Root> & {\n styleVariant?: SparkStyle\n color?: SparkRadioColor\n}\n\nfunction Radio({ className, styleVariant, color, ...props }: RadioProps) {\n const hasLook = styleVariant !== undefined || color !== undefined\n const dataStyle = hasLook ? (styleVariant ?? 'solid') : undefined\n const dataAccentColor = color ? accentByColor[color] : undefined\n\n return (\n <RadioPrimitive.Root\n data-slot=\"radio\"\n data-style={dataStyle}\n data-accent-color={dataAccentColor}\n className={cn(\n 'peer relative inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 transition-colors',\n \"before:absolute before:top-1/2 before:left-1/2 before:size-10 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']\",\n 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden',\n 'data-disabled:cursor-not-allowed data-disabled:opacity-50',\n 'border-(--spark-style-idle-border,var(--spark-a7)) bg-base-accent-soft data-unchecked:border-(length:--spark-style-border-width,2px)',\n 'data-checked:border-(--spark-radio-ring,var(--spark-9))',\n className,\n )}\n {...props}\n >\n <RadioPrimitive.Indicator\n data-slot=\"radio-indicator\"\n className=\"flex items-center justify-center\"\n >\n <span className=\"size-4 rounded-full bg-(--spark-radio-dot,var(--spark-9)) motion-safe:animate-dot-pop\" />\n </RadioPrimitive.Indicator>\n </RadioPrimitive.Root>\n )\n}\n\nexport { Radio, RadioGroup }\nexport type { RadioGroupProps, RadioProps }\n"],"mappings":";;;;;;;;;;;AAcA,SAAS,WAAW,EAClB,WACA,cACA,OACA,GAAG,SACe;AAKlB,QACE,iBAAA,GAAA,kBAAA,KAACA,2BAAAA,YAAD;EACE,aAAU;EACV,cAPY,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAC3B,gBAAgB,UAAW,KAAA;EAOpD,qBANoB,QAAQC,sBAAAA,cAAc,SAAS,KAAA;EAOnD,YAAA,GAAA,OAAA,IAAc,cAAc,UAAU;EACtC,GAAI;EACJ,CAAA;;AASN,SAAS,MAAM,EAAE,WAAW,cAAc,OAAO,GAAG,SAAqB;CAEvE,MAAM,YADU,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAC3B,gBAAgB,UAAW,KAAA;CACxD,MAAM,kBAAkB,QAAQA,sBAAAA,cAAc,SAAS,KAAA;AAEvD,QACE,iBAAA,GAAA,kBAAA,KAACC,qBAAAA,MAAe,MAAhB;EACE,aAAU;EACV,cAAY;EACZ,qBAAmB;EACnB,YAAA,GAAA,OAAA,IACE,gIACA,qIACA,iFACA,6DACA,wIACA,2DACA,UACD;EACD,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAACA,qBAAAA,MAAe,WAAhB;GACE,aAAU;GACV,WAAU;aAEV,iBAAA,GAAA,kBAAA,KAAC,QAAD,EAAM,WAAU,yFAA0F,CAAA;GACjF,CAAA;EACP,CAAA"}
|