@epilot/spark-ui 0.20.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion.cjs +5 -5
- package/dist/components/accordion.cjs.map +1 -1
- package/dist/components/alert-dialog.cjs +9 -9
- package/dist/components/alert-dialog.cjs.map +1 -1
- package/dist/components/attachment.cjs +10 -10
- package/dist/components/attachment.cjs.map +1 -1
- package/dist/components/attachment.d.cts +1 -1
- package/dist/components/attachment.d.ts +1 -1
- package/dist/components/autocomplete.cjs +12 -12
- package/dist/components/autocomplete.cjs.map +1 -1
- package/dist/components/avatar.cjs +2 -2
- package/dist/components/avatar.cjs.map +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/badge.cjs +2 -2
- package/dist/components/badge.cjs.map +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/bar-chart.cjs +3 -3
- package/dist/components/bar-chart.cjs.map +1 -1
- package/dist/components/box.cjs +2 -2
- package/dist/components/box.cjs.map +1 -1
- package/dist/components/box.js +1 -1
- package/dist/components/breadcrumbs.cjs +7 -7
- package/dist/components/breadcrumbs.cjs.map +1 -1
- package/dist/components/bubble.cjs +5 -5
- package/dist/components/bubble.cjs.map +1 -1
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button-group.cjs +5 -5
- package/dist/components/button-group.cjs.map +1 -1
- package/dist/components/button-group.js +1 -1
- package/dist/components/button.cjs +2 -2
- package/dist/components/button.cjs.map +1 -1
- package/dist/components/button.d.cts +2 -2
- package/dist/components/button.d.ts +2 -2
- package/dist/components/button.js +1 -1
- package/dist/components/calendar.cjs +8 -8
- package/dist/components/calendar.cjs.map +1 -1
- package/dist/components/callout.cjs +7 -7
- package/dist/components/callout.cjs.map +1 -1
- package/dist/components/callout.d.cts +1 -1
- package/dist/components/callout.d.ts +1 -1
- package/dist/components/card.cjs +16 -16
- package/dist/components/card.cjs.map +1 -1
- package/dist/components/card.js +2 -2
- package/dist/components/card.js.map +1 -1
- package/dist/components/chart.cjs +2 -2
- package/dist/components/chart.cjs.map +1 -1
- package/dist/components/checkbox.cjs +2 -2
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/circular-gauge.cjs +3 -3
- package/dist/components/circular-gauge.cjs.map +1 -1
- package/dist/components/day-of-week-picker.cjs +4 -4
- package/dist/components/day-of-week-picker.cjs.map +1 -1
- package/dist/components/dialog.cjs +8 -8
- package/dist/components/dialog.cjs.map +1 -1
- package/dist/components/drawer.cjs +8 -8
- package/dist/components/drawer.cjs.map +1 -1
- package/dist/components/dropdown-menu.cjs +10 -10
- package/dist/components/dropdown-menu.cjs.map +1 -1
- package/dist/components/entity-card.cjs +11 -11
- package/dist/components/entity-card.cjs.map +1 -1
- package/dist/components/field.cjs +15 -15
- package/dist/components/field.cjs.map +1 -1
- package/dist/components/hero-metric.cjs +4 -4
- package/dist/components/hero-metric.cjs.map +1 -1
- package/dist/components/icon.cjs +2 -2
- package/dist/components/icon.cjs.map +1 -1
- package/dist/components/icon.js +1 -1
- package/dist/components/info-block.cjs +9 -9
- package/dist/components/info-block.cjs.map +1 -1
- package/dist/components/info-block.d.cts +1 -1
- package/dist/components/info-block.d.ts +1 -1
- package/dist/components/input-date.cjs +2 -2
- package/dist/components/input-date.cjs.map +1 -1
- package/dist/components/input-group.cjs +5 -5
- package/dist/components/input-group.cjs.map +1 -1
- package/dist/components/input-otp.cjs +3 -3
- package/dist/components/input-otp.cjs.map +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/input.cjs +3 -3
- package/dist/components/input.cjs.map +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/label.cjs +2 -2
- package/dist/components/label.cjs.map +1 -1
- package/dist/components/layout.cjs +2 -2
- package/dist/components/layout.cjs.map +1 -1
- package/dist/components/line-chart.cjs +4 -4
- package/dist/components/line-chart.cjs.map +1 -1
- package/dist/components/list.cjs +11 -11
- package/dist/components/list.cjs.map +1 -1
- package/dist/components/list.d.cts +7 -2
- package/dist/components/list.d.ts +7 -2
- package/dist/components/list.js +2 -2
- package/dist/components/list.js.map +1 -1
- package/dist/components/marker.cjs +4 -4
- package/dist/components/marker.cjs.map +1 -1
- package/dist/components/marker.d.cts +1 -1
- package/dist/components/marker.d.ts +1 -1
- package/dist/components/message-scroller.cjs +7 -7
- package/dist/components/message-scroller.cjs.map +1 -1
- package/dist/components/message-scroller.js +2 -2
- package/dist/components/message-scroller.js.map +1 -1
- package/dist/components/message.cjs +7 -7
- package/dist/components/message.cjs.map +1 -1
- package/dist/components/page-header.cjs +5 -5
- package/dist/components/page-header.cjs.map +1 -1
- package/dist/components/period-tabs.cjs +6 -6
- package/dist/components/period-tabs.cjs.map +1 -1
- package/dist/components/pill.cjs +2 -2
- package/dist/components/pill.cjs.map +1 -1
- package/dist/components/pill.js +1 -1
- package/dist/components/popover.cjs +2 -2
- package/dist/components/popover.cjs.map +1 -1
- package/dist/components/power-history-chart.cjs +2 -2
- package/dist/components/power-history-chart.cjs.map +1 -1
- package/dist/components/power-network.cjs +9 -9
- package/dist/components/power-network.cjs.map +1 -1
- package/dist/components/progress.cjs +2 -2
- package/dist/components/progress.cjs.map +1 -1
- package/dist/components/radio-card-group.cjs +3 -3
- package/dist/components/radio-card-group.cjs.map +1 -1
- package/dist/components/radio.cjs +3 -3
- package/dist/components/radio.cjs.map +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/sankey-chart.cjs +2 -2
- package/dist/components/sankey-chart.cjs.map +1 -1
- package/dist/components/schedule-editor.cjs +9 -9
- package/dist/components/schedule-editor.cjs.map +1 -1
- package/dist/components/schedule-list.cjs +10 -10
- package/dist/components/schedule-list.cjs.map +1 -1
- package/dist/components/scroll-area.cjs +3 -3
- package/dist/components/scroll-area.cjs.map +1 -1
- package/dist/components/segmented-control.cjs +4 -4
- package/dist/components/segmented-control.cjs.map +1 -1
- package/dist/components/select.cjs +12 -12
- package/dist/components/select.cjs.map +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/separator.cjs +3 -3
- package/dist/components/separator.cjs.map +1 -1
- package/dist/components/series-legend.cjs +4 -4
- package/dist/components/series-legend.cjs.map +1 -1
- package/dist/components/sheet.cjs +8 -8
- package/dist/components/sheet.cjs.map +1 -1
- package/dist/components/skeleton.cjs +3 -3
- package/dist/components/skeleton.cjs.map +1 -1
- package/dist/components/slider.cjs +11 -11
- package/dist/components/slider.cjs.map +1 -1
- package/dist/components/solar-forecast-chart.cjs +2 -2
- package/dist/components/solar-forecast-chart.cjs.map +1 -1
- package/dist/components/sortable-list.cjs +3 -3
- package/dist/components/sortable-list.cjs.map +1 -1
- package/dist/components/spinner.cjs +2 -2
- package/dist/components/spinner.cjs.map +1 -1
- package/dist/components/stat-card.cjs +8 -8
- package/dist/components/stat-card.cjs.map +1 -1
- package/dist/components/stepper.cjs +2 -2
- package/dist/components/stepper.cjs.map +1 -1
- package/dist/components/switch.cjs +3 -3
- package/dist/components/switch.cjs.map +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/table.cjs +8 -8
- package/dist/components/table.cjs.map +1 -1
- package/dist/components/tabs.cjs +7 -7
- package/dist/components/tabs.cjs.map +1 -1
- package/dist/components/textarea.cjs +3 -3
- package/dist/components/textarea.cjs.map +1 -1
- package/dist/components/textarea.js +1 -1
- package/dist/components/theme.cjs +2 -2
- package/dist/components/theme.cjs.map +1 -1
- package/dist/components/theme.js +1 -1
- package/dist/components/thumbnail.cjs +3 -3
- package/dist/components/thumbnail.cjs.map +1 -1
- package/dist/components/time-picker.cjs +3 -3
- package/dist/components/time-picker.cjs.map +1 -1
- package/dist/components/toast.cjs +7 -7
- package/dist/components/toast.cjs.map +1 -1
- package/dist/components/tooltip.cjs +3 -3
- package/dist/components/tooltip.cjs.map +1 -1
- package/dist/components/typography.cjs +6 -6
- package/dist/components/typography.cjs.map +1 -1
- package/dist/components/wizard-layout.cjs +8 -8
- package/dist/components/wizard-layout.cjs.map +1 -1
- package/dist/lib/input-box-variants.cjs +9 -9
- package/dist/lib/input-box-variants.cjs.map +1 -1
- package/dist/styles/spark-default.css +11 -0
- package/dist/utils/cn.cjs +2 -2
- package/dist/utils/cn.d.cts +1 -1
- package/dist/utils/cn.d.ts +1 -1
- package/dist/utils/cn.js +1 -1
- package/package.json +2 -2
|
@@ -7,7 +7,7 @@ const require_components_number_flow = require("./number-flow.cjs");
|
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let _epilot_spark_icons = require("@epilot/spark-icons");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
-
let
|
|
10
|
+
let cn = require("cn");
|
|
11
11
|
//#region src/components/power-network.tsx
|
|
12
12
|
const SOURCE_VARIANTS = {
|
|
13
13
|
grid: {
|
|
@@ -261,7 +261,7 @@ function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
|
261
261
|
value: contextValue,
|
|
262
262
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
263
263
|
ref: containerRef,
|
|
264
|
-
className: (0,
|
|
264
|
+
className: (0, cn.cn)("@container relative flex flex-col", className),
|
|
265
265
|
children: [
|
|
266
266
|
title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_typography.SectionTitle, { children: title }),
|
|
267
267
|
connectorPaths.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
@@ -313,13 +313,13 @@ function PowerNetworkDiagram({ title, loading = false, className, children }) {
|
|
|
313
313
|
}
|
|
314
314
|
function PowerNetworkSources({ className, children }) {
|
|
315
315
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
316
|
-
className: (0,
|
|
316
|
+
className: (0, cn.cn)("flex items-center justify-center gap-[clamp(1.5rem,10cqw,4rem)]", className),
|
|
317
317
|
children
|
|
318
318
|
});
|
|
319
319
|
}
|
|
320
320
|
function PowerNetworkDevices({ className, children }) {
|
|
321
321
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
322
|
-
className: (0,
|
|
322
|
+
className: (0, cn.cn)("flex items-center justify-center gap-[clamp(1.5rem,10cqw,4rem)]", className),
|
|
323
323
|
children
|
|
324
324
|
});
|
|
325
325
|
}
|
|
@@ -356,14 +356,14 @@ function PowerSource({ id: idProp, watts, icon, variant = "neutral", className,
|
|
|
356
356
|
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
|
|
357
357
|
const ringDash = clampedProgress != null ? clampedProgress / 100 * RING_CIRCUMFERENCE : 0;
|
|
358
358
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
359
|
-
className: (0,
|
|
359
|
+
className: (0, cn.cn)("flex flex-col items-center justify-center gap-1 transition-opacity duration-500", !isColored && "opacity-45", className),
|
|
360
360
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_number_flow, {
|
|
361
361
|
value: Math.abs(watts),
|
|
362
362
|
suffix: "W",
|
|
363
363
|
className: "mt-1 text-sm leading-none font-medium"
|
|
364
364
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
365
365
|
ref: setElement,
|
|
366
|
-
className: (0,
|
|
366
|
+
className: (0, cn.cn)("relative flex aspect-square items-center justify-center rounded-full border p-4 shadow-sm", isColored ? variantStyles.activeClassName : "border-gray bg-gray-a3 shadow-gray-a4 saturate-0"),
|
|
367
367
|
children: [showRing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
368
368
|
className: "pointer-events-none absolute inset-0 size-full -rotate-90",
|
|
369
369
|
viewBox: "0 0 100 100",
|
|
@@ -417,7 +417,7 @@ function PowerDevice({ id: idProp, watts, icon, className }) {
|
|
|
417
417
|
}, [unregister, id]);
|
|
418
418
|
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DeviceSkeleton, { anchorRef: setElement });
|
|
419
419
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
420
|
-
className: (0,
|
|
420
|
+
className: (0, cn.cn)("flex flex-col items-center justify-center gap-1", className),
|
|
421
421
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
422
422
|
ref: setElement,
|
|
423
423
|
className: "flex aspect-square items-center justify-center rounded-full border border-gray bg-gray-a1 p-4",
|
|
@@ -511,7 +511,7 @@ function HomeSection({ anchorRef, autarky, className, watts }) {
|
|
|
511
511
|
}, [targetAutarky, reducedMotion]);
|
|
512
512
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
513
513
|
ref: anchorRef,
|
|
514
|
-
className: (0,
|
|
514
|
+
className: (0, cn.cn)("relative mx-auto size-[78px]", className),
|
|
515
515
|
children: [autarky != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
516
516
|
className: "absolute inset-0 size-full -rotate-90",
|
|
517
517
|
viewBox: `0 0 ${nodeSize} ${nodeSize}`,
|
|
@@ -545,7 +545,7 @@ function HomeSection({ anchorRef, autarky, className, watts }) {
|
|
|
545
545
|
function HomeSkeleton({ anchorRef, className }) {
|
|
546
546
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
547
547
|
ref: anchorRef,
|
|
548
|
-
className: (0,
|
|
548
|
+
className: (0, cn.cn)("mx-auto size-[78px] animate-pulse rounded-full border border-gray motion-reduce:animate-none", className)
|
|
549
549
|
});
|
|
550
550
|
}
|
|
551
551
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"power-network.cjs","names":["useReducedMotion","SectionTitle","NumberFlow","Skeleton","Home"],"sources":["../../src/components/power-network.tsx"],"sourcesContent":["import { Home } from '@epilot/spark-icons'\nimport {\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 { 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\n// Drawn under every connector and revealed when nothing flows, so a 0 W node\n// still reads as wired up and online rather than disconnected.\nconst IDLE_STROKE = 'var(--neutral-a6)'\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 idlePathRefs = 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 idlePathRefs.current[id]?.setAttribute(\n 'opacity',\n (1 - current.opacity).toFixed(3),\n )\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 idlePathRefs.current[connectorPath.id]?.setAttribute(\n 'opacity',\n (1 - target.opacity).toFixed(3),\n )\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={IDLE_STROKE}\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n ref={(el) => {\n idlePathRefs.current[path.id] = el\n }}\n opacity={1}\n style={\n reducedMotion\n ? { transition: 'opacity 300ms ease' }\n : undefined\n }\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 /** Keeps the variant colors at 0 W, for a source that is online but idle. */\n standby?: boolean\n}\n\nexport function PowerSource({\n id: idProp,\n watts,\n icon,\n variant = 'neutral',\n className,\n progress,\n standby = false,\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 isColored = isActive || standby\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 !isColored && '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-full border p-4 shadow-sm',\n isColored\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={isColored ? 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-full 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-full 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-full 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-full border border-gray-a5 shadow-sm shadow-gray-a1\">\n <Home className=\"[--spark-icon-size:14px]\" />\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-full 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.cjs","names":["useReducedMotion","SectionTitle","NumberFlow","Skeleton","Home"],"sources":["../../src/components/power-network.tsx"],"sourcesContent":["import { Home } from '@epilot/spark-icons'\nimport {\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 { 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\n// Drawn under every connector and revealed when nothing flows, so a 0 W node\n// still reads as wired up and online rather than disconnected.\nconst IDLE_STROKE = 'var(--neutral-a6)'\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 idlePathRefs = 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 idlePathRefs.current[id]?.setAttribute(\n 'opacity',\n (1 - current.opacity).toFixed(3),\n )\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 idlePathRefs.current[connectorPath.id]?.setAttribute(\n 'opacity',\n (1 - target.opacity).toFixed(3),\n )\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={IDLE_STROKE}\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n ref={(el) => {\n idlePathRefs.current[path.id] = el\n }}\n opacity={1}\n style={\n reducedMotion\n ? { transition: 'opacity 300ms ease' }\n : undefined\n }\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 /** Keeps the variant colors at 0 W, for a source that is online but idle. */\n standby?: boolean\n}\n\nexport function PowerSource({\n id: idProp,\n watts,\n icon,\n variant = 'neutral',\n className,\n progress,\n standby = false,\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 isColored = isActive || standby\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 !isColored && '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-full border p-4 shadow-sm',\n isColored\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={isColored ? 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-full 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-full 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-full 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-full border border-gray-a5 shadow-sm shadow-gray-a1\">\n <Home className=\"[--spark-icon-size:14px]\" />\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-full border border-gray motion-reduce:animate-none',\n className,\n )}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;AA6BA,MAAM,kBAAmE;CACvE,MAAM;EACJ,iBAAiB;EACjB,YAAY;EACZ,gBAAgB;CAClB;CACA,OAAO;EACL,iBAAiB;EACjB,YAAY;EACZ,gBAAgB;CAClB;CACA,SAAS;EACP,iBAAiB;EACjB,YAAY;EACZ,gBAAgB;CAClB;CACA,SAAS;EACP,iBAAiB;EACjB,YAAY;EACZ,gBAAgB;CAClB;AACF;AAEA,MAAM,qBAAqB;AAC3B,MAAM,0BAA0B;AAIhC,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,eAAe;AACrB,MAAM,wBAAwB;AAC9B,MAAM,6BAA6B;AACnC,MAAM,6BAA6B;AAInC,MAAM,kBAAkB;AACxB,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AAgCvB,MAAM,uBAAA,GAAA,MAAA,eAAqE,IAAI;AAE/E,SAAS,kBAA4C;CACnD,MAAM,OAAA,GAAA,MAAA,YAAiB,mBAAmB;CAC1C,IAAI,CAAC,KACH,MAAM,IAAI,MACR,yEACF;CAEF,OAAO;AACT;AASA,SAAS,UAAU,MAAe,eAA+B;CAC/D,OAAO;EACL,GAAG,KAAK,OAAO,KAAK,QAAQ,IAAI,cAAc;EAC9C,GAAG,KAAK,MAAM,cAAc;CAC9B;AACF;AAEA,SAAS,aAAa,MAAe,eAA+B;CAClE,OAAO;EACL,GAAG,KAAK,OAAO,KAAK,QAAQ,IAAI,cAAc;EAC9C,GAAG,KAAK,SAAS,cAAc;CACjC;AACF;AAEA,SAAS,kBAAkB,MAAa,IAAmB;CACzD,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,CAAC,IAAI,uBAC5B,OAAO,KAAK,KAAK,EAAE,GAAG,KAAK,EAAE,KAAK,GAAG,EAAE,GAAG,GAAG;CAE/C,MAAM,KAAK,GAAG,IAAI,KAAK;CACvB,MAAM,MAAM,KAAK,IAAI,KAAK;CAC1B,MAAM,MAAM,GAAG,IAAI,KAAK;CACxB,OAAO,KAAK,KAAK,EAAE,GAAG,KAAK,EAAE,KAAK,KAAK,EAAE,GAAG,IAAI,GAAG,GAAG,EAAE,GAAG,IAAI,GAAG,GAAG,EAAE,GAAG,GAAG;AAC/E;AAmBA,SAAS,oBAAoB,GAAoB,GAA6B;CAC5E,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;CAClC,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;EACjC,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,EAAE;EACb,IAAI,CAAC,MAAM,CAAC,IAAI,OAAO;EACvB,IACE,GAAG,OAAO,GAAG,MACb,GAAG,MAAM,GAAG,KACZ,GAAG,YAAY,GAAG,WAClB,GAAG,SAAS,GAAG,QACf,GAAG,aAAa,GAAG,UAEnB,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAgB,oBAAoB,EAClC,OACA,UAAU,OACV,WACA,YACyC;CACzC,MAAM,gBAAA,GAAA,MAAA,QAAsC,IAAI;CAChD,MAAM,gBAAgBA,iCAAAA,iBAAiB;CACvC,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,gCACN,IAAI,IAAI,CAChB;CACA,MAAM,CAAC,gBAAgB,sBAAA,GAAA,MAAA,UAA+C,CAAC,CAAC;CACxE,MAAM,qBAAA,GAAA,MAAA,QAA4C,CAAC,CAAC;CACpD,MAAM,gBAAA,GAAA,MAAA,QAA6D,CAAC,CAAC;CACrE,MAAM,gBAAA,GAAA,MAAA,QAA6D,CAAC,CAAC;CACrE,MAAM,yBAAA,GAAA,MAAA,QAAmE,CAAC,CAAC;CAC3E,MAAM,wBAAA,GAAA,MAAA,QAA0D,CAAC,CAAC;CAElE,MAAM,YAAA,GAAA,MAAA,cAAwB,SAAyB;EACrD,UAAU,SAAS;GACjB,MAAM,WAAW,KAAK,IAAI,KAAK,EAAE;GACjC,IACE,YACA,SAAS,SAAS,KAAK,QACvB,SAAS,YAAY,KAAK,WAC1B,SAAS,UAAU,KAAK,SACxB,SAAS,YAAY,KAAK,SAE1B,OAAO;GAET,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,KAAK,IAAI,KAAK,IAAI,IAAI;GACtB,OAAO;EACT,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,MAAM,cAAA,GAAA,MAAA,cAA0B,OAAe;EAC7C,UAAU,SAAS;GACjB,IAAI,CAAC,KAAK,IAAI,EAAE,GAAG,OAAO;GAC1B,MAAM,OAAO,IAAI,IAAI,IAAI;GACzB,KAAK,OAAO,EAAE;GACd,OAAO;EACT,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,MAAM,oBAAA,GAAA,MAAA,eAAiC;EACrC,IAAI,QAAQ;EACZ,KAAK,MAAM,QAAQ,MAAM,OAAO,GAC9B,IAAI,KAAK,SAAS,UAAU,SAAS,KAAK;EAE5C,OAAO;CACT,GAAG,CAAC,KAAK,CAAC;CAEV,CAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,YAAY,aAAa;EAC/B,IAAI,CAAC,WAAW;EAEhB,IAAI;EACJ,MAAM,UAA4B,CAAC;EACnC,MAAM,UAA4B,CAAC;EACnC,KAAK,MAAM,QAAQ,MAAM,OAAO,GAC9B,IAAI,KAAK,SAAS,QAChB,WAAW;OACN,IAAI,KAAK,SACd,IAAI,KAAK,SAAS,UAAU,QAAQ,KAAK,IAAI;OACxC,QAAQ,KAAK,IAAI;EAI1B,MAAM,oBAAoB;GACxB,IAAI,CAAC,YAAY,CAAC,SAAS,SAAS;IAClC,mBAAmB,SAAU,KAAK,WAAW,IAAI,OAAO,CAAC,CAAE;IAC3D;GACF;GACA,MAAM,gBAAgB,UAAU,sBAAsB;GACtD,MAAM,WAAW,SAAS,QAAQ,sBAAsB;GACxD,MAAM,UAAU,UAAU,UAAU,aAAa;GACjD,MAAM,aAAa,aAAa,UAAU,aAAa;GACvD,MAAM,OAAwB,CAAC;GAE/B,MAAM,cAAc,QAAQ;GAC5B,MAAM,cAAc,QAAQ;GAE5B,QAAQ,SAAS,QAAQ,UAAU;IACjC,IAAI,CAAC,OAAO,SAAS;IACrB,MAAM,OAAO,aACX,OAAO,QAAQ,sBAAsB,GACrC,aACF;IACA,MAAM,UACJ,8BAA8B,SAAS,cAAc,KAAK;IAC5D,MAAM,KAAK;KAAE,GAAG,QAAQ,IAAI;KAAS,GAAG,QAAQ;IAAE;IAClD,KAAK,KAAK;KACR,IAAI,OAAO;KACX,GAAG,kBAAkB,MAAM,EAAE;KAC7B,MAAM;KACN,SAAS,OAAO;KAChB,UAAU,OAAO,QAAQ;IAC3B,CAAC;GACH,CAAC;GAED,QAAQ,SAAS,QAAQ,UAAU;IACjC,IAAI,CAAC,OAAO,SAAS;IACrB,MAAM,KAAK,UACT,OAAO,QAAQ,sBAAsB,GACrC,aACF;IACA,MAAM,UACJ,8BAA8B,SAAS,cAAc,KAAK;IAC5D,MAAM,OAAO;KAAE,GAAG,WAAW,IAAI;KAAS,GAAG,WAAW;IAAE;IAC1D,KAAK,KAAK;KACR,IAAI,OAAO;KACX,GAAG,kBAAkB,MAAM,EAAE;KAC7B,MAAM;KACN,UAAU;IACZ,CAAC;GACH,CAAC;GAED,mBAAmB,SACjB,oBAAoB,MAAM,IAAI,IAAI,OAAO,IAC3C;EACF;EAEA,YAAY;EAEZ,MAAM,iBAAiB,IAAI,eAAe,WAAW;EACrD,eAAe,QAAQ,SAAS;EAChC,KAAK,MAAM,QAAQ,MAAM,OAAO,GAC9B,IAAI,KAAK,SAAS,eAAe,QAAQ,KAAK,OAAO;EAEvD,OAAO,iBAAiB,UAAU,WAAW;EAE7C,aAAa;GACX,eAAe,WAAW;GAC1B,OAAO,oBAAoB,UAAU,WAAW;EAClD;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,CAAA,GAAA,MAAA,iBAAgB;EACd,kBAAkB,UAAU;CAC9B,GAAG,CAAC,cAAc,CAAC;CAEnB,CAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,UAAsC,CAAC;EAC7C,MAAM,YAAY;EAClB,KAAK,MAAM,QAAQ,MAAM,OAAO,GAAG;GACjC,IAAI,KAAK,SAAS,QAAQ;GAC1B,MAAM,QAAQ,KAAK;GACnB,MAAM,WACJ,YAAY,IACR,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC,IACpD;GACN,QAAQ,KAAK,MAAM;IACjB,KAAK,gBAAgB,eAAe,gBAAgB;IACpD,OAAO,kBAAkB,iBAAiB,kBAAkB;IAC5D,SAAS,KAAK,IAAI,KAAK,IAAI,IAAI,IAAI;GACrC;EACF;EACA,qBAAqB,UAAU;CACjC,GAAG,CAAC,OAAO,gBAAgB,CAAC;CAE5B,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,WAAW,eAAe;EAC9B,IAAI,UAAU;EACd,IAAI,gBAAgB,YAAY,IAAI;EAEpC,MAAM,WAAW,QAAgB;GAC/B,MAAM,eAAe,KAAK,IAAI,OAAQ,MAAM,iBAAiB,GAAI;GACjE,gBAAgB;GAChB,MAAM,YAAY,IAAI,KAAK,IAAI,OAAkB,YAAY;GAE7D,KAAK,MAAM,iBAAiB,kBAAkB,SAAS;IACrD,MAAM,KAAK,cAAc;IACzB,MAAM,SAAS,qBAAqB,QAAQ;IAC5C,MAAM,cAAc,aAAa,QAAQ;IACzC,IAAI,CAAC,UAAU,CAAC,aAAa;IAE7B,MAAM,UAAU,sBAAsB,QAAQ,OAAO;KACnD,KAAK,OAAO;KACZ,QAAQ;KACR,OAAO,OAAO;KACd,SAAS,OAAO;IAClB;IAEA,QAAQ,QAAQ,OAAO,MAAM,QAAQ,OAAO;IAC5C,QAAQ,UAAU,OAAO,QAAQ,QAAQ,SAAS;IAClD,QAAQ,YAAY,OAAO,UAAU,QAAQ,WAAW;IAExD,MAAM,YAAY,kBAAkB,KAAK,IAAI,MAAO,QAAQ,GAAG;IAC/D,MAAM,YAAY,cAAc,WAAW,IAAI;IAI/C,QAAQ,WAFN,QAAQ,SAAS,YAAY,QAAQ,QAAQ,gBACV,YAAa,aAAa,YAC9B;IAEjC,YAAY,aACV,oBACA,GAAG,gBAAgB,GAAG,QAAQ,IAAI,QAAQ,CAAC,GAC7C;IACA,YAAY,aAAa,qBAAqB,QAAQ,OAAO,QAAQ,CAAC,CAAC;IACvE,YAAY,aAAa,WAAW,QAAQ,QAAQ,QAAQ,CAAC,CAAC;IAC9D,aAAa,QAAQ,KAAK,aACxB,YACC,IAAI,QAAQ,SAAS,QAAQ,CAAC,CACjC;IAEA,sBAAsB,QAAQ,MAAM;GACtC;GAEA,UAAU,OAAO,sBAAsB,OAAO;EAChD;EAEA,UAAU,OAAO,sBAAsB,OAAO;EAC9C,aAAa,OAAO,qBAAqB,OAAO;CAClD,GAAG,CAAC,SAAS,aAAa,CAAC;CAK3B,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,WAAW,CAAC,eAAe;EAC/B,KAAK,MAAM,iBAAiB,kBAAkB,SAAS;GACrD,MAAM,SAAS,qBAAqB,QAAQ,cAAc;GAC1D,MAAM,cAAc,aAAa,QAAQ,cAAc;GACvD,IAAI,CAAC,UAAU,CAAC,aAAa;GAC7B,YAAY,aACV,oBACA,GAAG,gBAAgB,GAAG,OAAO,IAAI,QAAQ,CAAC,GAC5C;GACA,YAAY,aAAa,qBAAqB,GAAG;GACjD,YAAY,aAAa,WAAW,OAAO,QAAQ,QAAQ,CAAC,CAAC;GAC7D,aAAa,QAAQ,cAAc,KAAK,aACtC,YACC,IAAI,OAAO,SAAS,QAAQ,CAAC,CAChC;EACF;CACF,GAAG;EAAC;EAAS;EAAe;EAAO;CAAgB,CAAC;CAEpD,MAAM,gBAAA,GAAA,MAAA,gBACG;EAAE;EAAS;EAAU;EAAY;CAAiB,IACzD;EAAC;EAAS;EAAU;EAAY;CAAgB,CAClD;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAC,oBAAoB,UAArB;EAA8B,OAAO;YACnC,iBAAA,GAAA,kBAAA,MAAC,OAAD;GACE,KAAK;GACL,YAAA,GAAA,GAAA,IAAc,qCAAqC,SAAS;aAF9D;IAIG,SAAS,iBAAA,GAAA,kBAAA,KAACC,8BAAAA,cAAD,EAAA,UAAe,MAAoB,CAAA;IAC5C,eAAe,SAAS,KACvB,iBAAA,GAAA,kBAAA,KAAC,OAAD;KAAK,WAAU;eACZ,eAAe,KAAK,SAAS;MAC5B,IAAI,SACF,OACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;OAEE,GAAG,KAAK;OACR,MAAK;OACL,QAAQ;OACR,aAAa;OACb,eAAc;MACf,GANM,KAAK,EAMX;MAIL,MAAM,aACJ,KAAK,SAAS,YAAY,KAAK,UAC3B,gBAAgB,KAAK,SAAS,aAC9B;MACN,MAAM,iBACJ,KAAK,SAAS,YAAY,KAAK,UAC3B,gBAAgB,KAAK,SAAS,iBAC9B;MAEN,OACE,iBAAA,GAAA,kBAAA,MAAC,KAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,GAAG,KAAK;OACR,MAAK;OACL,QAAQ;OACR,aAAa;OACb,eAAc;OACd,MAAM,OAAO;QACX,aAAa,QAAQ,KAAK,MAAM;OAClC;OACA,SAAS;OACT,OACE,gBACI,EAAE,YAAY,qBAAqB,IACnC,KAAA;MAEP,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAC,QAAD;OACE,GAAG,KAAK;OACR,MAAK;OACL,QAAQ;OACR,aAAa;OACb,eAAc;OACd,MAAM,OAAO;QACX,aAAa,QAAQ,KAAK,MAAM;OAClC;OACA,iBAAiB,GAAG,gBAAgB,GAAG;OACvC,kBAAkB;OAClB,SAAS;OACT,OACE,gBACI,EAAE,YAAY,qBAAqB,IACnC,KAAA;MAEP,CAAA,CACA,EAAA,GAnCK,KAAK,EAmCV;KAEP,CAAC;IACE,CAAA;IAEP,iBAAA,GAAA,kBAAA,KAAC,OAAD;KAAK,WAAU;KAA0B;IAAc,CAAA;GACpD;;CACuB,CAAA;AAElC;AASA,SAAgB,oBAAoB,EAClC,WACA,YACqC;CACrC,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,YAAA,GAAA,GAAA,IACE,mEACA,SACF;EAEC;CACE,CAAA;AAET;AAEA,SAAgB,oBAAoB,EAClC,WACA,YACqC;CACrC,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,YAAA,GAAA,GAAA,IACE,mEACA,SACF;EAEC;CACE,CAAA;AAET;AAmBA,SAAgB,YAAY,EAC1B,IAAI,QACJ,OACA,MACA,UAAU,WACV,WACA,UACA,UAAU,SACuB;CACjC,MAAM,UAAA,GAAA,MAAA,OAAe;CACrB,MAAM,KAAK,UAAU;CACrB,MAAM,EAAE,UAAU,YAAY,YAAY,gBAAgB;CAC1D,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,UAA8C,IAAI;CAElE,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,CAAC,SAAS;EACd,SAAS;GAAE;GAAI,MAAM;GAAU;GAAS;GAAO;EAAQ,CAAC;CAC1D,GAAG;EAAC;EAAS;EAAU;EAAI;EAAS;CAAK,CAAC;CAE1C,CAAA,GAAA,MAAA,iBAAgB;EACd,aAAa,WAAW,EAAE;CAC5B,GAAG,CAAC,YAAY,EAAE,CAAC;CAEnB,IAAI,SACF,OAAO,iBAAA,GAAA,kBAAA,KAAC,gBAAD,EAAgB,WAAW,WAAa,CAAA;CAIjD,MAAM,YADW,UAAU,KACG;CAC9B,MAAM,gBAAgB,gBAAgB;CACtC,MAAM,kBACJ,YAAY,OAAO,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,QAAQ,CAAC;CAC/D,MAAM,WAAW,mBAAmB;CACpC,MAAM,cAAc;CACpB,MAAM,qBAAqB,IAAI,KAAK,KAAK;CACzC,MAAM,WACJ,mBAAmB,OAAQ,kBAAkB,MAAO,qBAAqB;CAE3E,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EACE,YAAA,GAAA,GAAA,IACE,mFACA,CAAC,aAAa,cACd,SACF;YALF,CAOE,iBAAA,GAAA,kBAAA,KAACC,gCAAD;GACE,OAAO,KAAK,IAAI,KAAK;GACrB,QAAO;GACP,WAAU;EACX,CAAA,GACD,iBAAA,GAAA,kBAAA,MAAC,OAAD;GACE,KAAK;GACL,YAAA,GAAA,GAAA,IACE,6FACA,YACI,cAAc,kBACd,kDACN;aAPF,CASG,YACC,iBAAA,GAAA,kBAAA,KAAC,OAAD;IACE,WAAU;IACV,SAAQ;IACR,eAAY;cAEZ,iBAAA,GAAA,kBAAA,KAAC,UAAD;KACE,IAAG;KACH,IAAG;KACH,GAAG;KACH,MAAK;KACL,QAAQ,cAAc;KACtB,aAAY;KACZ,eAAc;KACd,iBAAiB,GAAG,SAAS,GAAG;KAChC,SAAS,YAAY,IAAI;IAC1B,CAAA;GACE,CAAA,GAEN,IACE;IACF;;AAET;AAEA,SAAS,eAAe,EACtB,aAGe;CACf,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,WAAU;YAAf,CACE,iBAAA,GAAA,kBAAA,KAACC,4BAAAA,UAAD,EAAU,WAAU,aAAc,CAAA,GAClC,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,KAAK;GACL,WAAU;aAEV,iBAAA,GAAA,kBAAA,KAAC,OAAD,EAAK,WAAU,SAAU,CAAA;EACtB,CAAA,CACF;;AAET;AAWA,SAAgB,YAAY,EAC1B,IAAI,QACJ,OACA,MACA,aACiC;CACjC,MAAM,UAAA,GAAA,MAAA,OAAe;CACrB,MAAM,KAAK,UAAU;CACrB,MAAM,EAAE,UAAU,YAAY,YAAY,gBAAgB;CAC1D,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,UAA8C,IAAI;CAElE,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,CAAC,SAAS;EACd,SAAS;GAAE;GAAI,MAAM;GAAU;GAAO;EAAQ,CAAC;CACjD,GAAG;EAAC;EAAS;EAAU;EAAI;CAAK,CAAC;CAEjC,CAAA,GAAA,MAAA,iBAAgB;EACd,aAAa,WAAW,EAAE;CAC5B,GAAG,CAAC,YAAY,EAAE,CAAC;CAEnB,IAAI,SACF,OAAO,iBAAA,GAAA,kBAAA,KAAC,gBAAD,EAAgB,WAAW,WAAa,CAAA;CAGjD,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EACE,YAAA,GAAA,GAAA,IACE,mDACA,SACF;YAJF,CAME,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,KAAK;GACL,WAAU;aAET;EACE,CAAA,GACL,iBAAA,GAAA,kBAAA,KAACD,gCAAD;GACE,OAAO;GACP,QAAO;GACP,WAAU;EACX,CAAA,CACE;;AAET;AAEA,SAAS,eAAe,EACtB,aAGe;CACf,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,WAAU;YAAf,CACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,KAAK;GACL,WAAU;aAEV,iBAAA,GAAA,kBAAA,KAAC,OAAD,EAAK,WAAU,SAAU,CAAA;EACtB,CAAA,GACL,iBAAA,GAAA,kBAAA,KAACC,4BAAAA,UAAD,EAAU,WAAU,aAAc,CAAA,CAC/B;;AAET;AAYA,SAAgB,iBAAiB,EAC/B,OACA,SACA,aACsC;CACtC,MAAM,UAAA,GAAA,MAAA,OAAe;CACrB,MAAM,EAAE,UAAU,YAAY,SAAS,qBAAqB,gBAAgB;CAC5E,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,UAA8C,IAAI;CAClE,MAAM,iBAAiB,SAAS;CAEhC,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,CAAC,SAAS;EACd,SAAS;GAAE,IAAI;GAAQ,MAAM;GAAQ,OAAO;GAAgB;EAAQ,CAAC;CACvE,GAAG;EAAC;EAAS;EAAU;EAAQ;CAAc,CAAC;CAE9C,CAAA,GAAA,MAAA,iBAAgB;EACd,aAAa,WAAW,MAAM;CAChC,GAAG,CAAC,YAAY,MAAM,CAAC;CAEvB,IAAI,SACF,OAAO,iBAAA,GAAA,kBAAA,KAAC,cAAD;EAAc,WAAW;EAAuB;CAAY,CAAA;CAGrE,OACE,iBAAA,GAAA,kBAAA,KAAC,aAAD;EACE,WAAW;EACX,SAAS,WAAW;EACpB,OAAO;EACI;CACZ,CAAA;AAEL;AAEA,SAAS,YAAY,EACnB,WACA,SACA,WACA,SAMe;CACf,MAAM,gBAAgBH,iCAAAA,iBAAiB;CACvC,MAAM,gBACJ,WAAW,OAAO,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,UAAU,GAAG,CAAC;CAChE,MAAM,CAAC,iBAAiB,uBAAA,GAAA,MAAA,UAA+B,aAAa;CACpE,MAAM,sBAAA,GAAA,MAAA,QAA4B,eAAe;CACjD,MAAM,WAAW;CACjB,MAAM,kBAAkB;CACxB,MAAM,UAAU,WAAW,mBAAmB;CAC9C,MAAM,gBAAgB,IAAI,KAAK,KAAK;CACpC,MAAM,iBAAkB,kBAAkB,MAAO;CAEjD,CAAA,GAAA,MAAA,iBAAgB;EACd,mBAAmB,UAAU;CAC/B,GAAG,CAAC,eAAe,CAAC;CAEpB,CAAA,GAAA,MAAA,iBAAgB;EACd,IAAI,eAAe;GACjB,mBAAmB,UAAU;GAC7B,mBAAmB,aAAa;GAChC;EACF;EAEA,IAAI,UAAU;EACd,IAAI,WAAW,YAAY,IAAI;EAE/B,MAAM,WAAW,QAAgB;GAC/B,MAAM,eAAe,KAAK,IAAI,OAAQ,MAAM,YAAY,GAAI;GAC5D,WAAW;GACX,MAAM,YAAY,IAAI,KAAK,IAAI,KAAK,YAAY;GAChD,MAAM,OACJ,mBAAmB,WAClB,gBAAgB,mBAAmB,WAAW;GAEjD,mBAAmB,UAAU;GAC7B,mBAAmB,IAAI;GAEvB,IAAI,KAAK,IAAI,gBAAgB,IAAI,IAAI,KACnC,UAAU,OAAO,sBAAsB,OAAO;QACzC;IACL,mBAAmB,UAAU;IAC7B,mBAAmB,aAAa;GAClC;EACF;EAEA,UAAU,OAAO,sBAAsB,OAAO;EAC9C,aAAa,OAAO,qBAAqB,OAAO;CAClD,GAAG,CAAC,eAAe,aAAa,CAAC;CAEjC,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EACE,KAAK;EACL,YAAA,GAAA,GAAA,IAAc,gCAAgC,SAAS;YAFzD,CAIG,WAAW,QACV,iBAAA,GAAA,kBAAA,MAAC,OAAD;GACE,WAAU;GACV,SAAS,OAAO,SAAS,GAAG;aAF9B,CAIE,iBAAA,GAAA,kBAAA,KAAC,UAAD;IACE,IAAI,WAAW;IACf,IAAI,WAAW;IACf,GAAG;IACH,MAAK;IACL,QAAO;IACP,aAAa;GACd,CAAA,GACA,iBAAiB,KAChB,iBAAA,GAAA,kBAAA,KAAC,UAAD;IACE,IAAI,WAAW;IACf,IAAI,WAAW;IACf,GAAG;IACH,MAAK;IACL,QAAO;IACP,eAAc;IACd,aAAa;IACb,iBAAiB,GAAG,eAAe,GAAG;GACvC,CAAA,CAEA;MAEP,iBAAA,GAAA,kBAAA,MAAC,OAAD;GAAK,WAAU;aAAf,CACE,iBAAA,GAAA,kBAAA,KAACI,oBAAAA,MAAD,EAAM,WAAU,2BAA4B,CAAA,GAC5C,iBAAA,GAAA,kBAAA,KAACF,gCAAD;IACE,OAAO;IACP,QAAO;IACP,WAAU;GACX,CAAA,CACE;IACF;;AAET;AAEA,SAAS,aAAa,EACpB,WACA,aAIe;CACf,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,KAAK;EACL,YAAA,GAAA,GAAA,IACE,gGACA,SACF;CACD,CAAA;AAEL"}
|
|
@@ -4,7 +4,7 @@ const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
|
4
4
|
let react = require("react");
|
|
5
5
|
react = require_chunk.__toESM(react);
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
-
let
|
|
7
|
+
let cn = require("cn");
|
|
8
8
|
//#region src/components/progress.tsx
|
|
9
9
|
const sizeClasses = {
|
|
10
10
|
sm: "h-1",
|
|
@@ -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,
|
|
20
|
+
className: (0, cn.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-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-style-bg,var(--color-primary)) 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;AACN;AAEA,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,GAAG,CAAC;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,
|
|
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-style-bg,var(--color-primary)) 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;AACN;AAEA,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,GAAG,CAAC;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,GAAA,IACE,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
|
|
@@ -4,7 +4,7 @@ const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
|
4
4
|
let react = require("react");
|
|
5
5
|
react = require_chunk.__toESM(react);
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
-
let
|
|
7
|
+
let cn = require("cn");
|
|
8
8
|
//#region src/components/radio-card-group.tsx
|
|
9
9
|
const RadioCardGroupContext = react.createContext(void 0);
|
|
10
10
|
const ARROW_STEP = {
|
|
@@ -57,7 +57,7 @@ const RadioCardGroup = react.forwardRef(({ value: valueProp, defaultValue, onVal
|
|
|
57
57
|
ref,
|
|
58
58
|
role: "radiogroup",
|
|
59
59
|
onKeyDown: handleKeyDown,
|
|
60
|
-
className: (0,
|
|
60
|
+
className: (0, cn.cn)("flex flex-col gap-[calc(var(--spark-layout-gap)*var(--radio-card-group-gap-steps,2))]", className),
|
|
61
61
|
style: {
|
|
62
62
|
"--radio-card-group-gap-steps": gap,
|
|
63
63
|
...style
|
|
@@ -85,7 +85,7 @@ const RadioCardGroupItem = react.forwardRef(({ value, className, children, disab
|
|
|
85
85
|
onClick?.(event);
|
|
86
86
|
if (!event.defaultPrevented && !isDisabled) ctx.onValueChange(value);
|
|
87
87
|
},
|
|
88
|
-
className: (0,
|
|
88
|
+
className: (0, cn.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),
|
|
89
89
|
...props,
|
|
90
90
|
children
|
|
91
91
|
});
|
|
@@ -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\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 /** How many `--spark-layout-gap` units apart each card sits. */\n gap?: number\n}\n\nconst RadioCardGroup = React.forwardRef<HTMLDivElement, RadioCardGroupProps>(\n (\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n disabled,\n name,\n gap = 2,\n className,\n style,\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 {/* oxlint-disable-next-line jsx-a11y/interactive-supports-focus -- radiogroup is a composite: the tab stop belongs on the roving radios, not the container */}\n <div\n ref={ref}\n role=\"radiogroup\"\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex flex-col gap-[calc(var(--spark-layout-gap)*var(--radio-card-group-gap-steps,2))]',\n className,\n )}\n style={\n {\n '--radio-card-group-gap-steps': gap,\n ...style,\n } as React.CSSProperties\n }\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,CAAS;AAEX,MAAM,aAAqC;CACzC,WAAW;CACX,YAAY;CACZ,SAAS;CACT,WAAW;AACb;AAaA,MAAM,iBAAiBA,MAAM,YAEzB,EACE,OAAO,WACP,cACA,eACA,UACA,MACA,MAAM,GACN,WACA,OACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,gBAAgBA,MAAM,MAAM;CAClC,MAAM,CAAC,eAAe,oBAAoBA,MAAM,SAE9C,YAAY;CACd,MAAM,eAAe,cAAc,KAAA;CACnC,MAAM,QAAQ,eAAe,YAAY;CAEzC,MAAM,oBAAoBA,MAAM,aAC7B,SAAiB;EAChB,IAAI,CAAC,cAAc,iBAAiB,IAAI;EACxC,gBAAgB,IAAI;CACtB,GACA,CAAC,cAAc,aAAa,CAC9B;CAEA,MAAM,MAAMA,MAAM,eACT;EACL;EACA,eAAe;EACf;EACA,MAAM,QAAQ;CAChB,IACA;EAAC;EAAO;EAAmB;EAAU;EAAM;CAAa,CAC1D;CAEA,MAAM,iBAAiB,UAA+C;EACpE,MAAM,OAAO,WAAW,MAAM;EAC9B,IAAI,SAAS,KAAA,KAAa,MAAM,QAAQ,UAAU,MAAM,QAAQ,OAC9D;EACF,MAAM,SAAS,CACb,GAAG,MAAM,cAAc,iBACrB,iCACF,CACF;EACA,IAAI,OAAO,WAAW,GAAG;EACzB,MAAM,eAAe;EAErB,IAAI,OAAO;EACX,IAAI,MAAM,QAAQ,OAChB,OAAO,OAAO,SAAS;OAClB,IAAI,SAAS,KAAA,GAAW;GAC7B,MAAM,UAAU,OAAO,QACrB,SAAS,aACX;GACA,IAAI,YAAY,IACd,QAAQ,UAAU,OAAO,OAAO,UAAU,OAAO;EAErD;EACA,MAAM,SAAS,OAAO;EACtB,IAAI,CAAC,QAAQ;EACb,OAAO,MAAM;EACb,OAAO,MAAM;CACf;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAC,sBAAsB,UAAvB;EAAgC,OAAO;YAErC,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACO;GACL,MAAK;GACL,WAAW;GACX,YAAA,GAAA,
|
|
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 /** How many `--spark-layout-gap` units apart each card sits. */\n gap?: number\n}\n\nconst RadioCardGroup = React.forwardRef<HTMLDivElement, RadioCardGroupProps>(\n (\n {\n value: valueProp,\n defaultValue,\n onValueChange,\n disabled,\n name,\n gap = 2,\n className,\n style,\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 {/* oxlint-disable-next-line jsx-a11y/interactive-supports-focus -- radiogroup is a composite: the tab stop belongs on the roving radios, not the container */}\n <div\n ref={ref}\n role=\"radiogroup\"\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex flex-col gap-[calc(var(--spark-layout-gap)*var(--radio-card-group-gap-steps,2))]',\n className,\n )}\n style={\n {\n '--radio-card-group-gap-steps': gap,\n ...style,\n } as React.CSSProperties\n }\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,CAAS;AAEX,MAAM,aAAqC;CACzC,WAAW;CACX,YAAY;CACZ,SAAS;CACT,WAAW;AACb;AAaA,MAAM,iBAAiBA,MAAM,YAEzB,EACE,OAAO,WACP,cACA,eACA,UACA,MACA,MAAM,GACN,WACA,OACA,UACA,GAAG,SAEL,QACG;CACH,MAAM,gBAAgBA,MAAM,MAAM;CAClC,MAAM,CAAC,eAAe,oBAAoBA,MAAM,SAE9C,YAAY;CACd,MAAM,eAAe,cAAc,KAAA;CACnC,MAAM,QAAQ,eAAe,YAAY;CAEzC,MAAM,oBAAoBA,MAAM,aAC7B,SAAiB;EAChB,IAAI,CAAC,cAAc,iBAAiB,IAAI;EACxC,gBAAgB,IAAI;CACtB,GACA,CAAC,cAAc,aAAa,CAC9B;CAEA,MAAM,MAAMA,MAAM,eACT;EACL;EACA,eAAe;EACf;EACA,MAAM,QAAQ;CAChB,IACA;EAAC;EAAO;EAAmB;EAAU;EAAM;CAAa,CAC1D;CAEA,MAAM,iBAAiB,UAA+C;EACpE,MAAM,OAAO,WAAW,MAAM;EAC9B,IAAI,SAAS,KAAA,KAAa,MAAM,QAAQ,UAAU,MAAM,QAAQ,OAC9D;EACF,MAAM,SAAS,CACb,GAAG,MAAM,cAAc,iBACrB,iCACF,CACF;EACA,IAAI,OAAO,WAAW,GAAG;EACzB,MAAM,eAAe;EAErB,IAAI,OAAO;EACX,IAAI,MAAM,QAAQ,OAChB,OAAO,OAAO,SAAS;OAClB,IAAI,SAAS,KAAA,GAAW;GAC7B,MAAM,UAAU,OAAO,QACrB,SAAS,aACX;GACA,IAAI,YAAY,IACd,QAAQ,UAAU,OAAO,OAAO,UAAU,OAAO;EAErD;EACA,MAAM,SAAS,OAAO;EACtB,IAAI,CAAC,QAAQ;EACb,OAAO,MAAM;EACb,OAAO,MAAM;CACf;CAEA,OACE,iBAAA,GAAA,kBAAA,KAAC,sBAAsB,UAAvB;EAAgC,OAAO;YAErC,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACO;GACL,MAAK;GACL,WAAW;GACX,YAAA,GAAA,GAAA,IACE,yFACA,SACF;GACA,OACE;IACE,gCAAgC;IAChC,GAAG;GACL;GAEF,GAAI;GAEH;EACE,CAAA;CACyB,CAAA;AAEpC,CACF;AAEA,eAAe,cAAc;AAS7B,MAAM,qBAAqBA,MAAM,YAG9B,EAAE,OAAO,WAAW,UAAU,UAAU,SAAS,GAAG,SAAS,QAAQ;CACtE,MAAM,MAAMA,MAAM,WAAW,qBAAqB;CAClD,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,yDAAyD;CAG3E,MAAM,UAAU,IAAI,UAAU;CAC9B,MAAM,aAAa,YAAY,IAAI;CAEnC,OACE,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;GAClB,UAAU,KAAK;GACf,IAAI,CAAC,MAAM,oBAAoB,CAAC,YAC9B,IAAI,cAAc,KAAK;EAE3B;EACA,YAAA,GAAA,GAAA,IACE,qMACA,UACI,yDACA,0EACJ,SACF;EACA,GAAI;EAEH;CACK,CAAA;AAEZ,CAAC;AAED,mBAAmB,cAAc"}
|
|
@@ -7,14 +7,14 @@ react = require_chunk.__toESM(react);
|
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let _base_ui_react_radio = require("@base-ui/react/radio");
|
|
9
9
|
let _base_ui_react_radio_group = require("@base-ui/react/radio-group");
|
|
10
|
-
let
|
|
10
|
+
let cn = require("cn");
|
|
11
11
|
//#region src/components/radio.tsx
|
|
12
12
|
function RadioGroup({ className, styleVariant, color, gap = 3, style, ...props }) {
|
|
13
13
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_radio_group.RadioGroup, {
|
|
14
14
|
"data-slot": "radio-group",
|
|
15
15
|
"data-style": styleVariant !== void 0 || color !== void 0 ? styleVariant ?? "solid" : void 0,
|
|
16
16
|
"data-accent-color": color ? require_lib_accent_colors.accentByColor[color] : void 0,
|
|
17
|
-
className: (0,
|
|
17
|
+
className: (0, cn.cn)("grid gap-[calc(var(--spark-layout-gap)*var(--radio-group-gap-steps,3))]", className),
|
|
18
18
|
style: {
|
|
19
19
|
"--radio-group-gap-steps": gap,
|
|
20
20
|
...style
|
|
@@ -31,7 +31,7 @@ function Radio({ className, styleVariant, color, render, nativeButton, ...props
|
|
|
31
31
|
"data-accent-color": dataAccentColor,
|
|
32
32
|
nativeButton: nativeButton ?? render === void 0,
|
|
33
33
|
render: render ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {}),
|
|
34
|
-
className: (0,
|
|
34
|
+
className: (0, cn.cn)("peer touch-target relative my-2 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 p-0 transition-colors any-pointer-coarse:my-[calc((2.75rem-var(--spacing)*6)/2)]", "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(--color-accent-ui)) bg-base-accent-soft data-unchecked:border-(length:--spark-style-border-width,2px)", "data-checked:border-(--spark-radio-ring,var(--spark-9))", className),
|
|
35
35
|
...props,
|
|
36
36
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_radio.Radio.Indicator, {
|
|
37
37
|
"data-slot": "radio-indicator",
|
|
@@ -1 +1 @@
|
|
|
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 = Omit<\n React.ComponentProps<typeof RadioGroupPrimitive>,\n 'style'\n> & {\n styleVariant?: SparkStyle\n color?: SparkRadioColor\n /** How many `--spark-layout-gap` units apart each radio sits. */\n gap?: number\n // Base UI's own `style` also accepts a state-callback form; the gap-steps var below\n // needs a plain object to merge into, so narrow it to that here.\n style?: React.CSSProperties\n}\n\nfunction RadioGroup({\n className,\n styleVariant,\n color,\n gap = 3,\n style,\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(\n 'grid gap-[calc(var(--spark-layout-gap)*var(--radio-group-gap-steps,3))]',\n className,\n )}\n style={\n { '--radio-group-gap-steps': gap, ...style } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\ntype RadioProps = React.ComponentProps<typeof RadioPrimitive.Root> & {\n styleVariant?: SparkStyle\n color?: SparkRadioColor\n}\n\nfunction Radio({\n className,\n styleVariant,\n color,\n render,\n nativeButton,\n ...props\n}: 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 // a native button owns the caller's `id`; on Base UI's default <span> the\n // `id` lands on the hidden input and the control gets a generated one\n nativeButton={nativeButton ?? render === undefined}\n render={render ?? <button />}\n className={cn(\n // p-0 defends the 24px box against the UA button padding when the\n // consumer's CSS has no Tailwind preflight\n 'peer touch-target relative my-2 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 p-0 transition-colors any-pointer-coarse:my-[calc((2.75rem-var(--spacing)*6)/2)]',\n // both overlays are reserved as layout, so neither can bleed into a scroller;\n // the coarse margin tracks --spacing so the 44px reserve holds at any [data-scaling]\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(--color-accent-ui)) 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":";;;;;;;;;;;AAsBA,SAAS,WAAW,EAClB,WACA,cACA,OACA,MAAM,GACN,OACA,GAAG,SACe;CAKlB,OACE,iBAAA,GAAA,kBAAA,KAACA,2BAAAA,YAAD;EACE,aAAU;EACV,cAPY,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAC3B,gBAAgB,UAAW,KAAA;EAOpD,qBANoB,QAAQC,0BAAAA,cAAc,SAAS,KAAA;EAOnD,YAAA,GAAA,
|
|
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 = Omit<\n React.ComponentProps<typeof RadioGroupPrimitive>,\n 'style'\n> & {\n styleVariant?: SparkStyle\n color?: SparkRadioColor\n /** How many `--spark-layout-gap` units apart each radio sits. */\n gap?: number\n // Base UI's own `style` also accepts a state-callback form; the gap-steps var below\n // needs a plain object to merge into, so narrow it to that here.\n style?: React.CSSProperties\n}\n\nfunction RadioGroup({\n className,\n styleVariant,\n color,\n gap = 3,\n style,\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(\n 'grid gap-[calc(var(--spark-layout-gap)*var(--radio-group-gap-steps,3))]',\n className,\n )}\n style={\n { '--radio-group-gap-steps': gap, ...style } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\ntype RadioProps = React.ComponentProps<typeof RadioPrimitive.Root> & {\n styleVariant?: SparkStyle\n color?: SparkRadioColor\n}\n\nfunction Radio({\n className,\n styleVariant,\n color,\n render,\n nativeButton,\n ...props\n}: 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 // a native button owns the caller's `id`; on Base UI's default <span> the\n // `id` lands on the hidden input and the control gets a generated one\n nativeButton={nativeButton ?? render === undefined}\n render={render ?? <button />}\n className={cn(\n // p-0 defends the 24px box against the UA button padding when the\n // consumer's CSS has no Tailwind preflight\n 'peer touch-target relative my-2 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 p-0 transition-colors any-pointer-coarse:my-[calc((2.75rem-var(--spacing)*6)/2)]',\n // both overlays are reserved as layout, so neither can bleed into a scroller;\n // the coarse margin tracks --spacing so the 44px reserve holds at any [data-scaling]\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(--color-accent-ui)) 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":";;;;;;;;;;;AAsBA,SAAS,WAAW,EAClB,WACA,cACA,OACA,MAAM,GACN,OACA,GAAG,SACe;CAKlB,OACE,iBAAA,GAAA,kBAAA,KAACA,2BAAAA,YAAD;EACE,aAAU;EACV,cAPY,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAC3B,gBAAgB,UAAW,KAAA;EAOpD,qBANoB,QAAQC,0BAAAA,cAAc,SAAS,KAAA;EAOnD,YAAA,GAAA,GAAA,IACE,2EACA,SACF;EACA,OACE;GAAE,2BAA2B;GAAK,GAAG;EAAM;EAE7C,GAAI;CACL,CAAA;AAEL;AAOA,SAAS,MAAM,EACb,WACA,cACA,OACA,QACA,cACA,GAAG,SACU;CAEb,MAAM,YADU,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAC3B,gBAAgB,UAAW,KAAA;CACxD,MAAM,kBAAkB,QAAQA,0BAAAA,cAAc,SAAS,KAAA;CAEvD,OACE,iBAAA,GAAA,kBAAA,KAACC,qBAAAA,MAAe,MAAhB;EACE,aAAU;EACV,cAAY;EACZ,qBAAmB;EAGnB,cAAc,gBAAgB,WAAW,KAAA;EACzC,QAAQ,UAAU,iBAAA,GAAA,kBAAA,KAAC,UAAD,CAAS,CAAA;EAC3B,YAAA,GAAA,GAAA,IAGE,iNAGA,qIACA,iFACA,6DACA,+IACA,2DACA,SACF;EACA,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAACA,qBAAAA,MAAe,WAAhB;GACE,aAAU;GACV,WAAU;aAEV,iBAAA,GAAA,kBAAA,KAAC,QAAD,EAAM,WAAU,wFAAyF,CAAA;EACjF,CAAA;CACP,CAAA;AAEzB"}
|
package/dist/components/radio.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../utils/cn.js";
|
|
3
2
|
import { accentByColor } from "../lib/accent-colors.js";
|
|
3
|
+
import { cn } from "../utils/cn.js";
|
|
4
4
|
import "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import { Radio as Radio$1 } from "@base-ui/react/radio";
|
|
@@ -6,7 +6,7 @@ let react = require("react");
|
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
7
|
let _visx_group = require("@visx/group");
|
|
8
8
|
let _visx_sankey = require("@visx/sankey");
|
|
9
|
-
let
|
|
9
|
+
let cn = require("cn");
|
|
10
10
|
//#region src/components/sankey-chart.tsx
|
|
11
11
|
const SANKEY_CATEGORY_COLORS = {
|
|
12
12
|
solar: "var(--orange-a9)",
|
|
@@ -83,7 +83,7 @@ function SankeyChart({ nodes, links, categoryColors = SANKEY_CATEGORY_COLORS, de
|
|
|
83
83
|
const halfLabelStack = LABEL_STACK_HEIGHT / 2;
|
|
84
84
|
const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4);
|
|
85
85
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
86
|
-
className: (0,
|
|
86
|
+
className: (0, cn.cn)("flex w-full flex-col gap-2 py-2", className),
|
|
87
87
|
children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_typography.SectionTitle, { children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
88
88
|
ref: containerRef,
|
|
89
89
|
className: "relative w-full",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sankey-chart.cjs","names":["SectionTitle","Sankey","Group","NumberFlow"],"sources":["../../src/components/sankey-chart.tsx"],"sourcesContent":["import { Group } from '@visx/group'\nimport {\n Sankey,\n sankeyJustify,\n type SankeyLink,\n type SankeyNode,\n} from '@visx/sankey'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../utils/cn'\nimport NumberFlow, { type Format } from './number-flow'\nimport { SectionTitle } from './typography'\n\nconst SANKEY_CATEGORY_COLORS: Record<string, string> = {\n solar: 'var(--orange-a9)',\n battery: 'var(--green-a9)',\n grid: 'var(--blue-9)',\n gridFeedIn: 'var(--blue-7)',\n home: 'var(--spark-9)',\n floor: 'var(--neutral-a9)',\n device: 'var(--neutral-a8)',\n}\n\nexport interface SankeyChartNode {\n id: string\n label: string\n /** Optional category — used to colour the node and any outgoing links. */\n category?: string\n /** Override the colour resolved from `category`. */\n color?: string\n}\n\nexport interface SankeyChartLink {\n source: string\n target: string\n value: number\n /** Override the colour of the link. Defaults to the source node colour. */\n color?: string\n}\n\nexport interface SankeyChartProps {\n nodes: SankeyChartNode[]\n links: SankeyChartLink[]\n /** Resolves a category to a colour. */\n categoryColors?: Record<string, string>\n /** Fallback colour for nodes/links without a category. */\n defaultColor?: string\n /** Suffix appended to each animated value label (e.g. \" W\"). */\n valueSuffix?: string\n /** NumberFlow `format` for value labels. */\n valueFormat?: Format\n height?: number\n nodeWidth?: number\n nodePadding?: number\n /** Reserved horizontal space on the right for labels. */\n labelPadding?: number\n /**\n * Node ids kept at their natural (computed) column instead of being pushed to\n * the last column. By default `sankeyJustify` sends every node with no\n * outgoing link to the right edge; use this to hold a terminal node (e.g. grid\n * feed-in) in a middle column alongside its siblings.\n */\n keepAtDepthIds?: readonly string[]\n title?: string\n className?: string\n}\n\ntype SankeyDatumNode = SankeyChartNode & {\n /** Computed by d3-sankey; present after layout. */\n}\n\ntype SankeyDatumLink = SankeyChartLink\n\nconst DEFAULT_HEIGHT = 260\nconst DEFAULT_NODE_WIDTH = 12\nconst DEFAULT_NODE_PADDING = 10\nconst DEFAULT_LABEL_PADDING = 96\nconst LEFT_INSET = 4\nconst TOP_INSET = 8\nconst LABEL_GAP = 8\nconst LABEL_STACK_HEIGHT = 26\nconst DEFAULT_COLOR = 'var(--neutral-a9)'\n\nfunction resolveNodeColor(\n node: SankeyChartNode,\n categoryColors: Record<string, string> | undefined,\n defaultColor: string,\n) {\n if (node.color) return node.color\n if (node.category && categoryColors?.[node.category]) {\n return categoryColors[node.category] as string\n }\n return defaultColor\n}\n\nfunction SankeyChart({\n nodes,\n links,\n categoryColors = SANKEY_CATEGORY_COLORS,\n defaultColor = DEFAULT_COLOR,\n valueSuffix,\n valueFormat,\n height = DEFAULT_HEIGHT,\n nodeWidth = DEFAULT_NODE_WIDTH,\n nodePadding = DEFAULT_NODE_PADDING,\n labelPadding = DEFAULT_LABEL_PADDING,\n keepAtDepthIds,\n title,\n className,\n}: SankeyChartProps): React.ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const nodeIds = useMemo(() => new Set(nodes.map((node) => node.id)), [nodes])\n\n // Drop zero-value links and any whose endpoints aren't in the current node\n // set (a stale link referencing a removed node makes d3-sankey throw\n // `missing: <id>`).\n const visibleLinks = useMemo(\n () =>\n links.filter(\n (link) =>\n link.value > 0 &&\n nodeIds.has(link.source) &&\n nodeIds.has(link.target),\n ),\n [links, nodeIds],\n )\n\n const visibleNodes = useMemo(() => {\n const referencedIds = new Set<string>()\n for (const link of visibleLinks) {\n referencedIds.add(link.source)\n referencedIds.add(link.target)\n }\n return nodes.filter((node) => referencedIds.has(node.id))\n }, [nodes, visibleLinks])\n\n const colorById = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of nodes) {\n map.set(node.id, resolveNodeColor(node, categoryColors, defaultColor))\n }\n return map\n }, [categoryColors, defaultColor, nodes])\n\n const orderById = useMemo(() => {\n const map = new Map<string, number>()\n visibleNodes.forEach((node, index) => map.set(node.id, index))\n return map\n }, [visibleNodes])\n\n const nodeSort = useMemo(\n () =>\n (\n a: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n b: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n ) =>\n (orderById.get(a.id) ?? 0) - (orderById.get(b.id) ?? 0),\n [orderById],\n )\n\n const graph = useMemo(\n () => ({\n nodes: visibleNodes.map((node) => ({ ...node })),\n links: visibleLinks.map((link) => ({ ...link })),\n }),\n [visibleNodes, visibleLinks],\n )\n\n const nodeAlign = useMemo(() => {\n const keep = new Set(keepAtDepthIds)\n return (node: SankeyNode<SankeyDatumNode, SankeyDatumLink>, n: number) =>\n keep.has(node.id) ? (node.depth ?? 0) : sankeyJustify(node, n)\n }, [keepAtDepthIds])\n\n const innerWidth = Math.max(0, width - labelPadding - LEFT_INSET)\n const innerHeight = Math.max(0, height - TOP_INSET * 2)\n const halfLabelStack = LABEL_STACK_HEIGHT / 2\n const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4)\n\n return (\n <div className={cn('flex w-full flex-col gap-2 py-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && innerWidth > 0 && (\n <Sankey<SankeyDatumNode, SankeyDatumLink>\n root={graph}\n size={[innerWidth, innerHeight]}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n nodeAlign={nodeAlign}\n nodeId={(node) => node.id}\n nodeSort={nodeSort}\n >\n {({ graph: laidOut, createPath }) => (\n <>\n <svg\n aria-hidden=\"true\"\n className=\"block\"\n height={height}\n width={width}\n >\n <Group left={LEFT_INSET} top={TOP_INSET}>\n <Group className=\"visx-sankey-links\">\n {laidOut.links.map((link, index) => {\n const sourceId =\n typeof link.source === 'object'\n ? (\n link.source as SankeyNode<\n SankeyDatumNode,\n SankeyDatumLink\n >\n ).id\n : String(link.source)\n const stroke =\n link.color ?? colorById.get(sourceId) ?? defaultColor\n\n return (\n <path\n key={`sankey-link-${index}`}\n d={createPath(link) ?? ''}\n fill=\"none\"\n stroke={stroke}\n strokeOpacity={0.32}\n strokeWidth={Math.max(1, link.width ?? 0)}\n />\n )\n })}\n </Group>\n\n <Group className=\"visx-sankey-nodes\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const w = node.x1 - node.x0\n const h = node.y1 - node.y0\n const fill = colorById.get(node.id) ?? defaultColor\n\n return (\n <rect\n key={`sankey-node-${index}`}\n x={node.x0}\n y={node.y0}\n width={w}\n height={Math.max(1, h)}\n fill={fill}\n rx={2}\n />\n )\n })}\n </Group>\n </Group>\n </svg>\n\n <div className=\"pointer-events-none absolute inset-0 [-webkit-text-size-adjust:100%] [text-size-adjust:100%]\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const h = node.y1 - node.y0\n const nodeCenterY = TOP_INSET + node.y0 + h / 2\n const labelCenterY = Math.min(\n Math.max(nodeCenterY, halfLabelStack),\n height - halfLabelStack,\n )\n\n return (\n <div\n key={`sankey-label-${node.id ?? index}`}\n className=\"absolute flex -translate-y-1/2 flex-col items-start gap-1\"\n style={{\n left: LEFT_INSET + node.x1 + LABEL_GAP,\n top: labelCenterY,\n width: labelWidth,\n }}\n >\n <span className=\"text-[11px] leading-none font-semibold whitespace-nowrap text-gray\">\n {node.label}\n </span>\n <NumberFlow\n value={node.value ?? 0}\n {...(valueSuffix !== undefined && {\n suffix: valueSuffix,\n })}\n {...(valueFormat !== undefined && {\n format: valueFormat,\n })}\n className=\"block min-h-[10px] text-[10px] leading-none text-gray-light tabular-nums\"\n />\n </div>\n )\n })}\n </div>\n </>\n )}\n </Sankey>\n )}\n </div>\n </div>\n )\n}\n\nexport type { SankeyLink, SankeyNode }\nexport { SankeyChart, SANKEY_CATEGORY_COLORS }\n"],"mappings":";;;;;;;;;;AAYA,MAAM,yBAAiD;CACrD,OAAO;CACP,SAAS;CACT,MAAM;CACN,YAAY;CACZ,MAAM;CACN,OAAO;CACP,QAAQ;AACV;AAoDA,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAC3B,MAAM,uBAAuB;AAC7B,MAAM,wBAAwB;AAC9B,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,qBAAqB;AAC3B,MAAM,gBAAgB;AAEtB,SAAS,iBACP,MACA,gBACA,cACA;CACA,IAAI,KAAK,OAAO,OAAO,KAAK;CAC5B,IAAI,KAAK,YAAY,iBAAiB,KAAK,WACzC,OAAO,eAAe,KAAK;CAE7B,OAAO;AACT;AAEA,SAAS,YAAY,EACnB,OACA,OACA,iBAAiB,wBACjB,eAAe,eACf,aACA,aACA,SAAS,gBACT,YAAY,oBACZ,cAAc,sBACd,eAAe,uBACf,gBACA,OACA,aACuC;CACvC,MAAM,gBAAA,GAAA,MAAA,QAAsC,IAAI;CAChD,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,UAAqB,CAAC;CAEpC,CAAA,GAAA,MAAA,iBAAgB;EACd,MAAM,UAAU,aAAa;EAC7B,IAAI,CAAC,SAAS;EAEd,MAAM,iBAAiB,IAAI,gBAAgB,YAAY;GACrD,MAAM,QAAQ,QAAQ;GACtB,IAAI,CAAC,OAAO;GACZ,SAAS,MAAM,YAAY,KAAK;EAClC,CAAC;EAED,eAAe,QAAQ,OAAO;EAC9B,aAAa,eAAe,WAAW;CACzC,GAAG,CAAC,CAAC;CAEL,MAAM,WAAA,GAAA,MAAA,eAAwB,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;CAK5E,MAAM,gBAAA,GAAA,MAAA,eAEF,MAAM,QACH,SACC,KAAK,QAAQ,KACb,QAAQ,IAAI,KAAK,MAAM,KACvB,QAAQ,IAAI,KAAK,MAAM,CAC3B,GACF,CAAC,OAAO,OAAO,CACjB;CAEA,MAAM,gBAAA,GAAA,MAAA,eAA6B;EACjC,MAAM,gCAAgB,IAAI,IAAY;EACtC,KAAK,MAAM,QAAQ,cAAc;GAC/B,cAAc,IAAI,KAAK,MAAM;GAC7B,cAAc,IAAI,KAAK,MAAM;EAC/B;EACA,OAAO,MAAM,QAAQ,SAAS,cAAc,IAAI,KAAK,EAAE,CAAC;CAC1D,GAAG,CAAC,OAAO,YAAY,CAAC;CAExB,MAAM,aAAA,GAAA,MAAA,eAA0B;EAC9B,MAAM,sBAAM,IAAI,IAAoB;EACpC,KAAK,MAAM,QAAQ,OACjB,IAAI,IAAI,KAAK,IAAI,iBAAiB,MAAM,gBAAgB,YAAY,CAAC;EAEvE,OAAO;CACT,GAAG;EAAC;EAAgB;EAAc;CAAK,CAAC;CAExC,MAAM,aAAA,GAAA,MAAA,eAA0B;EAC9B,MAAM,sBAAM,IAAI,IAAoB;EACpC,aAAa,SAAS,MAAM,UAAU,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC;EAC7D,OAAO;CACT,GAAG,CAAC,YAAY,CAAC;CAEjB,MAAM,YAAA,GAAA,MAAA,gBAGA,GACA,OAEC,UAAU,IAAI,EAAE,EAAE,KAAK,MAAM,UAAU,IAAI,EAAE,EAAE,KAAK,IACzD,CAAC,SAAS,CACZ;CAEA,MAAM,SAAA,GAAA,MAAA,gBACG;EACL,OAAO,aAAa,KAAK,UAAU,EAAE,GAAG,KAAK,EAAE;EAC/C,OAAO,aAAa,KAAK,UAAU,EAAE,GAAG,KAAK,EAAE;CACjD,IACA,CAAC,cAAc,YAAY,CAC7B;CAEA,MAAM,aAAA,GAAA,MAAA,eAA0B;EAC9B,MAAM,OAAO,IAAI,IAAI,cAAc;EACnC,QAAQ,MAAoD,MAC1D,KAAK,IAAI,KAAK,EAAE,IAAK,KAAK,SAAS,KAAA,GAAA,aAAA,eAAmB,MAAM,CAAC;CACjE,GAAG,CAAC,cAAc,CAAC;CAEnB,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,eAAe,UAAU;CAChE,MAAM,cAAc,KAAK,IAAI,GAAG,SAAS,YAAY,CAAC;CACtD,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,aAAa,KAAK,IAAI,IAAI,eAAe,YAAY,CAAC;CAE5D,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,YAAA,GAAA,OAAA,IAAc,mCAAmC,SAAS;YAA/D,CACG,SAAS,iBAAA,GAAA,kBAAA,KAACA,8BAAAA,cAAD,EAAA,UAAe,MAAoB,CAAA,GAE7C,iBAAA,GAAA,kBAAA,KAAC,OAAD;GAAK,KAAK;GAAc,WAAU;GAAkB,OAAO,EAAE,OAAO;aACjE,QAAQ,KAAK,aAAa,KACzB,iBAAA,GAAA,kBAAA,KAACC,aAAAA,QAAD;IACE,MAAM;IACN,MAAM,CAAC,YAAY,WAAW;IACnB;IACE;IACF;IACX,SAAS,SAAS,KAAK;IACb;eAER,EAAE,OAAO,SAAS,iBAClB,iBAAA,GAAA,kBAAA,MAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;KACE,eAAY;KACZ,WAAU;KACF;KACD;eAEP,iBAAA,GAAA,kBAAA,MAACC,YAAAA,OAAD;MAAO,MAAM;MAAY,KAAK;gBAA9B,CACE,iBAAA,GAAA,kBAAA,KAACA,YAAAA,OAAD;OAAO,WAAU;iBACd,QAAQ,MAAM,KAAK,MAAM,UAAU;QAClC,MAAM,WACJ,OAAO,KAAK,WAAW,WAEjB,KAAK,OAIL,KACF,OAAO,KAAK,MAAM;QACxB,MAAM,SACJ,KAAK,SAAS,UAAU,IAAI,QAAQ,KAAK;QAE3C,OACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;SAEE,GAAG,WAAW,IAAI,KAAK;SACvB,MAAK;SACG;SACR,eAAe;SACf,aAAa,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC;QACzC,GANM,eAAe,OAMrB;OAEL,CAAC;MACI,CAAA,GAEP,iBAAA,GAAA,kBAAA,KAACA,YAAAA,OAAD;OAAO,WAAU;iBACd,QAAQ,MAAM,KAAK,MAAM,UAAU;QAClC,IACE,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,GAEZ,OAAO;QAGT,MAAM,IAAI,KAAK,KAAK,KAAK;QACzB,MAAM,IAAI,KAAK,KAAK,KAAK;QACzB,MAAM,OAAO,UAAU,IAAI,KAAK,EAAE,KAAK;QAEvC,OACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;SAEE,GAAG,KAAK;SACR,GAAG,KAAK;SACR,OAAO;SACP,QAAQ,KAAK,IAAI,GAAG,CAAC;SACf;SACN,IAAI;QACL,GAPM,eAAe,OAOrB;OAEL,CAAC;MACI,CAAA,CACF;;IACJ,CAAA,GAEL,iBAAA,GAAA,kBAAA,KAAC,OAAD;KAAK,WAAU;eACZ,QAAQ,MAAM,KAAK,MAAM,UAAU;MAClC,IACE,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,GAEZ,OAAO;MAGT,MAAM,IAAI,KAAK,KAAK,KAAK;MACzB,MAAM,cAAc,YAAY,KAAK,KAAK,IAAI;MAC9C,MAAM,eAAe,KAAK,IACxB,KAAK,IAAI,aAAa,cAAc,GACpC,SAAS,cACX;MAEA,OACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;OAEE,WAAU;OACV,OAAO;QACL,MAAM,aAAa,KAAK,KAAK;QAC7B,KAAK;QACL,OAAO;OACT;iBAPF,CASE,iBAAA,GAAA,kBAAA,KAAC,QAAD;QAAM,WAAU;kBACb,KAAK;OACF,CAAA,GACN,iBAAA,GAAA,kBAAA,KAACC,gCAAD;QACE,OAAO,KAAK,SAAS;QACrB,GAAK,gBAAgB,KAAA,KAAa,EAChC,QAAQ,YACV;QACA,GAAK,gBAAgB,KAAA,KAAa,EAChC,QAAQ,YACV;QACA,WAAU;OACX,CAAA,CACE;SArBE,gBAAgB,KAAK,MAAM,OAqB7B;KAET,CAAC;IACE,CAAA,CACL,EAAA,CAAA;GAEE,CAAA;EAEP,CAAA,CACF;;AAET"}
|
|
1
|
+
{"version":3,"file":"sankey-chart.cjs","names":["SectionTitle","Sankey","Group","NumberFlow"],"sources":["../../src/components/sankey-chart.tsx"],"sourcesContent":["import { Group } from '@visx/group'\nimport {\n Sankey,\n sankeyJustify,\n type SankeyLink,\n type SankeyNode,\n} from '@visx/sankey'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../utils/cn'\nimport NumberFlow, { type Format } from './number-flow'\nimport { SectionTitle } from './typography'\n\nconst SANKEY_CATEGORY_COLORS: Record<string, string> = {\n solar: 'var(--orange-a9)',\n battery: 'var(--green-a9)',\n grid: 'var(--blue-9)',\n gridFeedIn: 'var(--blue-7)',\n home: 'var(--spark-9)',\n floor: 'var(--neutral-a9)',\n device: 'var(--neutral-a8)',\n}\n\nexport interface SankeyChartNode {\n id: string\n label: string\n /** Optional category — used to colour the node and any outgoing links. */\n category?: string\n /** Override the colour resolved from `category`. */\n color?: string\n}\n\nexport interface SankeyChartLink {\n source: string\n target: string\n value: number\n /** Override the colour of the link. Defaults to the source node colour. */\n color?: string\n}\n\nexport interface SankeyChartProps {\n nodes: SankeyChartNode[]\n links: SankeyChartLink[]\n /** Resolves a category to a colour. */\n categoryColors?: Record<string, string>\n /** Fallback colour for nodes/links without a category. */\n defaultColor?: string\n /** Suffix appended to each animated value label (e.g. \" W\"). */\n valueSuffix?: string\n /** NumberFlow `format` for value labels. */\n valueFormat?: Format\n height?: number\n nodeWidth?: number\n nodePadding?: number\n /** Reserved horizontal space on the right for labels. */\n labelPadding?: number\n /**\n * Node ids kept at their natural (computed) column instead of being pushed to\n * the last column. By default `sankeyJustify` sends every node with no\n * outgoing link to the right edge; use this to hold a terminal node (e.g. grid\n * feed-in) in a middle column alongside its siblings.\n */\n keepAtDepthIds?: readonly string[]\n title?: string\n className?: string\n}\n\ntype SankeyDatumNode = SankeyChartNode & {\n /** Computed by d3-sankey; present after layout. */\n}\n\ntype SankeyDatumLink = SankeyChartLink\n\nconst DEFAULT_HEIGHT = 260\nconst DEFAULT_NODE_WIDTH = 12\nconst DEFAULT_NODE_PADDING = 10\nconst DEFAULT_LABEL_PADDING = 96\nconst LEFT_INSET = 4\nconst TOP_INSET = 8\nconst LABEL_GAP = 8\nconst LABEL_STACK_HEIGHT = 26\nconst DEFAULT_COLOR = 'var(--neutral-a9)'\n\nfunction resolveNodeColor(\n node: SankeyChartNode,\n categoryColors: Record<string, string> | undefined,\n defaultColor: string,\n) {\n if (node.color) return node.color\n if (node.category && categoryColors?.[node.category]) {\n return categoryColors[node.category] as string\n }\n return defaultColor\n}\n\nfunction SankeyChart({\n nodes,\n links,\n categoryColors = SANKEY_CATEGORY_COLORS,\n defaultColor = DEFAULT_COLOR,\n valueSuffix,\n valueFormat,\n height = DEFAULT_HEIGHT,\n nodeWidth = DEFAULT_NODE_WIDTH,\n nodePadding = DEFAULT_NODE_PADDING,\n labelPadding = DEFAULT_LABEL_PADDING,\n keepAtDepthIds,\n title,\n className,\n}: SankeyChartProps): React.ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const nodeIds = useMemo(() => new Set(nodes.map((node) => node.id)), [nodes])\n\n // Drop zero-value links and any whose endpoints aren't in the current node\n // set (a stale link referencing a removed node makes d3-sankey throw\n // `missing: <id>`).\n const visibleLinks = useMemo(\n () =>\n links.filter(\n (link) =>\n link.value > 0 &&\n nodeIds.has(link.source) &&\n nodeIds.has(link.target),\n ),\n [links, nodeIds],\n )\n\n const visibleNodes = useMemo(() => {\n const referencedIds = new Set<string>()\n for (const link of visibleLinks) {\n referencedIds.add(link.source)\n referencedIds.add(link.target)\n }\n return nodes.filter((node) => referencedIds.has(node.id))\n }, [nodes, visibleLinks])\n\n const colorById = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of nodes) {\n map.set(node.id, resolveNodeColor(node, categoryColors, defaultColor))\n }\n return map\n }, [categoryColors, defaultColor, nodes])\n\n const orderById = useMemo(() => {\n const map = new Map<string, number>()\n visibleNodes.forEach((node, index) => map.set(node.id, index))\n return map\n }, [visibleNodes])\n\n const nodeSort = useMemo(\n () =>\n (\n a: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n b: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n ) =>\n (orderById.get(a.id) ?? 0) - (orderById.get(b.id) ?? 0),\n [orderById],\n )\n\n const graph = useMemo(\n () => ({\n nodes: visibleNodes.map((node) => ({ ...node })),\n links: visibleLinks.map((link) => ({ ...link })),\n }),\n [visibleNodes, visibleLinks],\n )\n\n const nodeAlign = useMemo(() => {\n const keep = new Set(keepAtDepthIds)\n return (node: SankeyNode<SankeyDatumNode, SankeyDatumLink>, n: number) =>\n keep.has(node.id) ? (node.depth ?? 0) : sankeyJustify(node, n)\n }, [keepAtDepthIds])\n\n const innerWidth = Math.max(0, width - labelPadding - LEFT_INSET)\n const innerHeight = Math.max(0, height - TOP_INSET * 2)\n const halfLabelStack = LABEL_STACK_HEIGHT / 2\n const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4)\n\n return (\n <div className={cn('flex w-full flex-col gap-2 py-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && innerWidth > 0 && (\n <Sankey<SankeyDatumNode, SankeyDatumLink>\n root={graph}\n size={[innerWidth, innerHeight]}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n nodeAlign={nodeAlign}\n nodeId={(node) => node.id}\n nodeSort={nodeSort}\n >\n {({ graph: laidOut, createPath }) => (\n <>\n <svg\n aria-hidden=\"true\"\n className=\"block\"\n height={height}\n width={width}\n >\n <Group left={LEFT_INSET} top={TOP_INSET}>\n <Group className=\"visx-sankey-links\">\n {laidOut.links.map((link, index) => {\n const sourceId =\n typeof link.source === 'object'\n ? (\n link.source as SankeyNode<\n SankeyDatumNode,\n SankeyDatumLink\n >\n ).id\n : String(link.source)\n const stroke =\n link.color ?? colorById.get(sourceId) ?? defaultColor\n\n return (\n <path\n key={`sankey-link-${index}`}\n d={createPath(link) ?? ''}\n fill=\"none\"\n stroke={stroke}\n strokeOpacity={0.32}\n strokeWidth={Math.max(1, link.width ?? 0)}\n />\n )\n })}\n </Group>\n\n <Group className=\"visx-sankey-nodes\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const w = node.x1 - node.x0\n const h = node.y1 - node.y0\n const fill = colorById.get(node.id) ?? defaultColor\n\n return (\n <rect\n key={`sankey-node-${index}`}\n x={node.x0}\n y={node.y0}\n width={w}\n height={Math.max(1, h)}\n fill={fill}\n rx={2}\n />\n )\n })}\n </Group>\n </Group>\n </svg>\n\n <div className=\"pointer-events-none absolute inset-0 [-webkit-text-size-adjust:100%] [text-size-adjust:100%]\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const h = node.y1 - node.y0\n const nodeCenterY = TOP_INSET + node.y0 + h / 2\n const labelCenterY = Math.min(\n Math.max(nodeCenterY, halfLabelStack),\n height - halfLabelStack,\n )\n\n return (\n <div\n key={`sankey-label-${node.id ?? index}`}\n className=\"absolute flex -translate-y-1/2 flex-col items-start gap-1\"\n style={{\n left: LEFT_INSET + node.x1 + LABEL_GAP,\n top: labelCenterY,\n width: labelWidth,\n }}\n >\n <span className=\"text-[11px] leading-none font-semibold whitespace-nowrap text-gray\">\n {node.label}\n </span>\n <NumberFlow\n value={node.value ?? 0}\n {...(valueSuffix !== undefined && {\n suffix: valueSuffix,\n })}\n {...(valueFormat !== undefined && {\n format: valueFormat,\n })}\n className=\"block min-h-[10px] text-[10px] leading-none text-gray-light tabular-nums\"\n />\n </div>\n )\n })}\n </div>\n </>\n )}\n </Sankey>\n )}\n </div>\n </div>\n )\n}\n\nexport type { SankeyLink, SankeyNode }\nexport { SankeyChart, SANKEY_CATEGORY_COLORS }\n"],"mappings":"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"}
|
|
@@ -4,35 +4,35 @@ const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
|
4
4
|
let react = require("react");
|
|
5
5
|
react = require_chunk.__toESM(react);
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
-
let
|
|
7
|
+
let cn = require("cn");
|
|
8
8
|
//#region src/components/schedule-editor.tsx
|
|
9
9
|
const ScheduleEditor = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
10
10
|
ref,
|
|
11
|
-
className: (0,
|
|
11
|
+
className: (0, cn.cn)("flex flex-col gap-4 px-2", className),
|
|
12
12
|
...props
|
|
13
13
|
}));
|
|
14
14
|
ScheduleEditor.displayName = "ScheduleEditor";
|
|
15
15
|
const ScheduleEditorHeader = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
16
16
|
ref,
|
|
17
|
-
className: (0,
|
|
17
|
+
className: (0, cn.cn)("flex flex-col gap-1", className),
|
|
18
18
|
...props
|
|
19
19
|
}));
|
|
20
20
|
ScheduleEditorHeader.displayName = "ScheduleEditorHeader";
|
|
21
21
|
const ScheduleEditorTitle = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
22
22
|
ref,
|
|
23
|
-
className: (0,
|
|
23
|
+
className: (0, cn.cn)("text-base font-bold text-gray", className),
|
|
24
24
|
...props
|
|
25
25
|
}));
|
|
26
26
|
ScheduleEditorTitle.displayName = "ScheduleEditorTitle";
|
|
27
27
|
const ScheduleEditorDescription = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
28
28
|
ref,
|
|
29
|
-
className: (0,
|
|
29
|
+
className: (0, cn.cn)("text-sm leading-5 text-gray-light", className),
|
|
30
30
|
...props
|
|
31
31
|
}));
|
|
32
32
|
ScheduleEditorDescription.displayName = "ScheduleEditorDescription";
|
|
33
33
|
const ScheduleEditorBody = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
34
34
|
ref,
|
|
35
|
-
className: (0,
|
|
35
|
+
className: (0, cn.cn)("flex flex-col gap-[calc(var(--spark-layout-gap)*4)]", className),
|
|
36
36
|
...props
|
|
37
37
|
}));
|
|
38
38
|
ScheduleEditorBody.displayName = "ScheduleEditorBody";
|
|
@@ -41,20 +41,20 @@ ScheduleEditorBody.displayName = "ScheduleEditorBody";
|
|
|
41
41
|
* pattern. Override with `className` if you need a different arrangement. */
|
|
42
42
|
const ScheduleEditorFooter = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
|
|
43
43
|
ref,
|
|
44
|
-
className: (0,
|
|
44
|
+
className: (0, cn.cn)("flex items-center justify-between gap-2 pt-2", className),
|
|
45
45
|
...props
|
|
46
46
|
}));
|
|
47
47
|
ScheduleEditorFooter.displayName = "ScheduleEditorFooter";
|
|
48
48
|
/** Right-aligned cluster of footer actions (e.g. Cancel + Save). */
|
|
49
49
|
const ScheduleEditorActions = react.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
50
50
|
ref,
|
|
51
|
-
className: (0,
|
|
51
|
+
className: (0, cn.cn)("ml-auto flex items-center gap-2", className),
|
|
52
52
|
...props
|
|
53
53
|
}));
|
|
54
54
|
ScheduleEditorActions.displayName = "ScheduleEditorActions";
|
|
55
55
|
const ScheduleEditorField = react.forwardRef(({ children, className, hint, label, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
56
56
|
ref,
|
|
57
|
-
className: (0,
|
|
57
|
+
className: (0, cn.cn)("flex flex-col gap-1.5", className),
|
|
58
58
|
...props,
|
|
59
59
|
children: [
|
|
60
60
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|