cortena-ui 1.2.0 → 1.3.1

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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { clsx } from "clsx";
3
3
  import { twMerge } from "tailwind-merge";
4
4
  import { z } from "zod";
5
- import { AlertTriangle, ArrowDown, ArrowUp, CalendarIcon, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, Circle, Copy, Download, EyeOff, FileIcon, GripVertical, Inbox, Info, ListFilter, Minus, OctagonAlert, PinOff, RefreshCw, Search, Settings2, Terminal, Upload, X } from "lucide-react";
5
+ import { AlertTriangle, ArrowDown, ArrowUp, CalendarIcon, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsLeft, ChevronsRight, ChevronsUpDown, Circle, Copy, Download, EyeOff, FileIcon, GripVertical, Inbox, Info, ListFilter, Minus, MoreHorizontal, OctagonAlert, PinOff, RefreshCw, Search, Settings2, Terminal, Upload, X } from "lucide-react";
6
6
  import * as React from "react";
7
7
  import { createContext, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
8
8
  import { cva } from "class-variance-authority";
@@ -39,7 +39,7 @@ import { useRender } from "@base-ui/react/use-render";
39
39
  import { Controller, FormProvider, useForm, useFormContext, useFormState } from "react-hook-form";
40
40
  import { zodResolver } from "@hookform/resolvers/zod";
41
41
  import { ScrollArea as ScrollArea$1 } from "@base-ui/react/scroll-area";
42
- import { DndContext, DragOverlay, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
42
+ import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, PointerSensor, TouchSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
43
43
  import { SortableContext, arrayMove, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
44
44
  import { CSS } from "@dnd-kit/utilities";
45
45
  import { Toast as Toast$1 } from "@base-ui/react/toast";
@@ -58,7 +58,11 @@ function cn(...inputs) {
58
58
  * Alert — an inline callout. Tinted variants use the `-soft` fill for the
59
59
  * background and the tone colour for the border and icon; the body text stays
60
60
  * on the foreground token so it reads in both themes.
61
+ *
62
+ * The error tone is `destructive`, the same spelling Button uses; `danger` is
63
+ * a deprecated alias of it and paints identically.
61
64
  */
65
+ const DESTRUCTIVE_ALERT = "bg-[var(--ds-destructive-soft)] border-[var(--ds-destructive)]/35 [&_[data-slot=alert-icon]]:text-[color:var(--ds-destructive)]";
62
66
  const alertVariants = cva([
63
67
  "flex gap-3 rounded-[var(--ds-radius-md)] border px-3.5 py-3",
64
68
  "text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)]",
@@ -68,7 +72,9 @@ const alertVariants = cva([
68
72
  default: "bg-[var(--ds-card)] border-[var(--ds-border-subtle)] [&_[data-slot=alert-icon]]:text-[color:var(--ds-muted-foreground)]",
69
73
  success: "bg-[var(--ds-success-soft)] border-[var(--ds-success)]/35 [&_[data-slot=alert-icon]]:text-[color:var(--ds-success)]",
70
74
  warn: "bg-[var(--ds-warning-soft)] border-[var(--ds-warning)]/35 [&_[data-slot=alert-icon]]:text-[color:var(--ds-warning)]",
71
- danger: "bg-[var(--ds-destructive-soft)] border-[var(--ds-destructive)]/35 [&_[data-slot=alert-icon]]:text-[color:var(--ds-destructive)]",
75
+ destructive: DESTRUCTIVE_ALERT,
76
+ /** @deprecated Use `destructive`, the name Button and Badge use; this alias paints the same. */
77
+ danger: DESTRUCTIVE_ALERT,
72
78
  info: "bg-[var(--ds-info-soft)] border-[var(--ds-info)]/35 [&_[data-slot=alert-icon]]:text-[color:var(--ds-info)]"
73
79
  } },
74
80
  defaultVariants: { variant: "default" }
@@ -106,9 +112,11 @@ function AlertDescription({ className, ...props }) {
106
112
  //#region src/components/badge.tsx
107
113
  /**
108
114
  * Badge — the mono, uppercase status label. Each tone pairs a `-soft` fill
109
- * with its foreground token; `warn`, `danger` and `secondary` are aliases kept
110
- * for cortenaweb call sites.
115
+ * with its foreground token. The error tone is `destructive`, the same
116
+ * spelling Button and Alert use; `warn`, `danger` and `secondary` are
117
+ * deprecated aliases kept for cortenaweb call sites and paint identically.
111
118
  */
119
+ const DESTRUCTIVE_BADGE = "bg-[var(--ds-destructive-soft)] text-[color:var(--ds-destructive)]";
112
120
  const badgeVariants = cva([
113
121
  "inline-flex items-center gap-1.5 px-1.5 py-0.5 whitespace-nowrap",
114
122
  "rounded-[var(--ds-radius-sm)]",
@@ -120,9 +128,11 @@ const badgeVariants = cva([
120
128
  default: "bg-[var(--ds-primary-soft)] text-[color:var(--ds-primary-soft-foreground)]",
121
129
  success: "bg-[var(--ds-success-soft)] text-[color:var(--ds-success)]",
122
130
  warning: "bg-[var(--ds-warning-soft)] text-[color:var(--ds-warning)]",
131
+ /** @deprecated Use `warning`; this alias paints the same. */
123
132
  warn: "bg-[var(--ds-warning-soft)] text-[color:var(--ds-warning)]",
124
- danger: "bg-[var(--ds-destructive-soft)] text-[color:var(--ds-destructive)]",
125
- destructive: "bg-[var(--ds-destructive-soft)] text-[color:var(--ds-destructive)]",
133
+ destructive: DESTRUCTIVE_BADGE,
134
+ /** @deprecated Use `destructive`, the name Button and Alert use; this alias paints the same. */
135
+ danger: DESTRUCTIVE_BADGE,
126
136
  info: "bg-[var(--ds-info-soft)] text-[color:var(--ds-info)]",
127
137
  neutral: "bg-[var(--ds-hover)] text-[color:var(--ds-muted-foreground)]",
128
138
  secondary: "bg-[var(--ds-hover)] text-[color:var(--ds-muted-foreground)]",
@@ -194,41 +204,48 @@ function Button({ className, variant, size, ...props }) {
194
204
  });
195
205
  }
196
206
  //#endregion
207
+ //#region src/lib/render.tsx
208
+ function renderWith(render, props, children, Fallback) {
209
+ if (!render) return /* @__PURE__ */ jsx(Fallback, {
210
+ ...props,
211
+ children
212
+ });
213
+ const Element = render.type;
214
+ const given = render.props;
215
+ return /* @__PURE__ */ jsx(Element, {
216
+ ...render.props,
217
+ ...props,
218
+ children: children ?? given.children
219
+ });
220
+ }
221
+ //#endregion
197
222
  //#region src/components/button-link.tsx
198
223
  function ButtonLink({ className, variant, size, render, disabled, children, ...props }) {
199
224
  const classes = cn(buttonVariants({
200
225
  variant,
201
226
  size
202
227
  }), disabled && "pointer-events-none opacity-50", className);
203
- const merged = {
228
+ return renderWith(render, {
204
229
  ...props,
205
230
  "data-slot": "button-link",
206
231
  className: classes,
207
232
  "aria-disabled": disabled || void 0,
208
233
  tabIndex: disabled ? -1 : props.tabIndex
209
- };
210
- if (render) {
211
- const Element = render.type;
212
- return /* @__PURE__ */ jsx(Element, {
213
- ...render.props,
214
- ...merged,
215
- children: children ?? render.props.children
216
- });
217
- }
218
- return /* @__PURE__ */ jsx("a", {
219
- ...merged,
220
- children
221
- });
234
+ }, children, "a");
222
235
  }
223
236
  //#endregion
224
237
  //#region src/components/card.tsx
225
- function Card({ className, hover, accent, ...props }) {
226
- return /* @__PURE__ */ jsx("div", {
238
+ /**
239
+ * Card the bordered surface everything else sits on. `render` changes the
240
+ * element without changing the look, which is how a card becomes a landmark.
241
+ */
242
+ function Card({ className, hover, accent, render, children, ...props }) {
243
+ return renderWith(render, {
227
244
  "data-slot": "card",
228
245
  "data-accent": accent ? "" : void 0,
229
- className: cn("flex flex-col rounded-[var(--ds-radius-lg)] border bg-[var(--ds-card)] text-[color:var(--ds-foreground)]", "transition-[border-color,background-color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", accent ? "border-[var(--ds-ring)]" : "border-[var(--ds-border-subtle)]", hover && "hover:border-[var(--ds-border)] hover:bg-[var(--ds-popover)]", className),
230
- ...props
231
- });
246
+ ...props,
247
+ className: cn("flex flex-col rounded-[var(--ds-radius-lg)] border bg-[var(--ds-card)] text-[color:var(--ds-foreground)]", "transition-[border-color,background-color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", accent ? "border-[var(--ds-ring)]" : "border-[var(--ds-border-subtle)]", hover && "hover:border-[var(--ds-border)] hover:bg-[var(--ds-popover)]", className)
248
+ }, children, "div");
232
249
  }
233
250
  function CardHeader({ className, ...props }) {
234
251
  return /* @__PURE__ */ jsx("div", {
@@ -484,6 +501,9 @@ function seriesColor(config, key, index) {
484
501
  const position = (config ? Object.keys(config) : []).indexOf(key);
485
502
  return vizVar(position >= 0 ? position : index ?? 0);
486
503
  }
504
+ /** The box `testMode` falls back to when the layout gives the chart none. */
505
+ const TEST_WIDTH = 600;
506
+ const TEST_HEIGHT = 300;
487
507
  /**
488
508
  * ChartContainer — the sized, token-styled surface every chart draws in.
489
509
  *
@@ -492,7 +512,7 @@ function seriesColor(config, key, index) {
492
512
  * cursor classes from tokens with a caption-sm floor for text, so a custom
493
513
  * chart composed inside it needs no local colours or fonts.
494
514
  */
495
- function ChartContainer({ config = {}, height = 300, responsive = true, className, style, children, ...props }) {
515
+ function ChartContainer({ config = {}, height = 300, responsive = true, testMode = false, className, style, children, ...props }) {
496
516
  const [element, setElement] = React.useState(null);
497
517
  const vars = React.useMemo(() => {
498
518
  const out = {};
@@ -508,9 +528,14 @@ function ChartContainer({ config = {}, height = 300, responsive = true, classNam
508
528
  children: /* @__PURE__ */ jsx("div", {
509
529
  ref: setElement,
510
530
  "data-slot": "chart",
531
+ "data-test-mode": testMode ? "" : void 0,
511
532
  className: cn("relative w-full min-w-0 overflow-hidden text-[length:var(--ds-text-caption-sm)] text-[color:var(--ds-foreground)]", responsive ? "flex justify-center" : "block [&>div]:h-full [&>div]:w-full", "[&_.recharts-cartesian-axis-tick-value]:fill-[var(--ds-muted-foreground)]", "[&_.recharts-polar-angle-axis-tick-value]:fill-[var(--ds-muted-foreground)]", "[&_.recharts-polar-radius-axis-tick-value]:fill-[var(--ds-muted-foreground)]", "[&_.recharts-cartesian-grid-horizontal_line]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-cartesian-grid-vertical_line]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-polar-grid-angle_line]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-polar-grid-concentric-polygon]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-polar-grid-concentric-circle]:stroke-[var(--ds-border-subtle)]", "[&_.recharts-cartesian-axis-line]:stroke-[var(--ds-border)]", "[&_.recharts-cartesian-axis-tick-line]:stroke-[var(--ds-border)]", "[&_.recharts-radial-bar-background-sector]:fill-[var(--ds-muted)]", "[&_.recharts-rectangle.recharts-tooltip-cursor]:fill-[var(--ds-hover)]", "[&_.recharts-curve.recharts-tooltip-cursor]:stroke-[var(--ds-border-strong)]", "[&_.recharts-reference-line_line]:stroke-[var(--ds-border-strong)]", "[&_.recharts-pie-sector_.recharts-sector]:stroke-[var(--ds-card)]", "[&_.recharts-funnel-trapezoid_.recharts-trapezoid]:stroke-[var(--ds-card)]", "[&_.recharts-layer]:outline-hidden [&_.recharts-surface]:outline-hidden [&_.recharts-sector]:outline-hidden", "[&_text]:font-[family-name:var(--ds-font-body)]", className),
512
533
  style: {
513
534
  height,
535
+ ...testMode ? {
536
+ minWidth: TEST_WIDTH,
537
+ minHeight: TEST_HEIGHT
538
+ } : null,
514
539
  ...vars,
515
540
  ...style
516
541
  },
@@ -518,6 +543,8 @@ function ChartContainer({ config = {}, height = 300, responsive = true, classNam
518
543
  children: responsive ? /* @__PURE__ */ jsx(ResponsiveContainer$1, {
519
544
  width: "100%",
520
545
  height: "100%",
546
+ minWidth: testMode ? TEST_WIDTH : void 0,
547
+ minHeight: testMode ? TEST_HEIGHT : void 0,
521
548
  children
522
549
  }) : children
523
550
  })
@@ -650,6 +677,30 @@ function ChartLegend(props) {
650
677
  function isSeriesArray(data) {
651
678
  return Array.isArray(data) && data.length > 0 && data.every((d) => d && typeof d === "object" && Array.isArray(d.data));
652
679
  }
680
+ /** True for the `{ rows, columns, values }` categorical matrix shape. */
681
+ function isMatrix(data) {
682
+ if (!data || typeof data !== "object" || Array.isArray(data)) return false;
683
+ const m = data;
684
+ return Array.isArray(m.rows) && Array.isArray(m.columns) && Array.isArray(m.values);
685
+ }
686
+ /**
687
+ * Pivot a categorical matrix onto the Nivo heatmap shape. Every row gets a
688
+ * point for every column so the grid stays rectangular; a missing or
689
+ * non-finite cell becomes `null`, which Nivo paints with `emptyColor` rather
690
+ * than as a zero.
691
+ */
692
+ function matrixToSeries(matrix) {
693
+ return matrix.rows.map((row, r) => ({
694
+ id: row,
695
+ data: matrix.columns.map((column, c) => {
696
+ const value = matrix.values[r]?.[c];
697
+ return {
698
+ x: column,
699
+ y: typeof value === "number" && Number.isFinite(value) ? value : null
700
+ };
701
+ })
702
+ }));
703
+ }
653
704
  /**
654
705
  * Infer the category key and the series keys of a row array the way the
655
706
  * webchat renderer did: the first string field is the index, every numeric
@@ -732,6 +783,7 @@ function treeIdentity(tree) {
732
783
  /** True when there is nothing to draw. */
733
784
  function isEmptyData(data) {
734
785
  if (data == null) return true;
786
+ if (isMatrix(data)) return data.rows.length === 0 || data.columns.length === 0;
735
787
  if (Array.isArray(data)) {
736
788
  if (data.length === 0) return true;
737
789
  if (isSeriesArray(data)) return data.every((s) => s.data.length === 0);
@@ -898,14 +950,28 @@ function useNivoTheme() {
898
950
  }
899
951
  //#endregion
900
952
  //#region src/components/chart/nivo-charts.tsx
901
- const lazy = (load) => React.lazy(async () => ({ default: await load() }));
902
- const HeatMapSvg = lazy(async () => (await import("@nivo/heatmap")).ResponsiveHeatMap);
903
- const HeatMapCanvas = lazy(async () => (await import("@nivo/heatmap")).ResponsiveHeatMapCanvas);
904
- const CalendarSvg = lazy(async () => (await import("@nivo/calendar")).ResponsiveCalendar);
905
- const CalendarCanvas = lazy(async () => (await import("@nivo/calendar")).ResponsiveCalendarCanvas);
906
- const TreeMapSvg = lazy(async () => (await import("@nivo/treemap")).ResponsiveTreeMap);
907
- const TreeMapCanvas = lazy(async () => (await import("@nivo/treemap")).ResponsiveTreeMapCanvas);
908
- const SunburstSvg = lazy(async () => (await import("@nivo/sunburst")).ResponsiveSunburst);
953
+ const loaders = {
954
+ "heatmap-svg": async () => (await import("@nivo/heatmap")).ResponsiveHeatMap,
955
+ "heatmap-canvas": async () => (await import("@nivo/heatmap")).ResponsiveHeatMapCanvas,
956
+ "calendar-svg": async () => (await import("@nivo/calendar")).ResponsiveCalendar,
957
+ "calendar-canvas": async () => (await import("@nivo/calendar")).ResponsiveCalendarCanvas,
958
+ "treemap-svg": async () => (await import("@nivo/treemap")).ResponsiveTreeMap,
959
+ "treemap-canvas": async () => (await import("@nivo/treemap")).ResponsiveTreeMapCanvas,
960
+ "sunburst-svg": async () => (await import("@nivo/sunburst")).ResponsiveSunburst
961
+ };
962
+ let chunks = /* @__PURE__ */ new Map();
963
+ function chunk(name) {
964
+ let component = chunks.get(name);
965
+ if (!component) {
966
+ component = React.lazy(async () => ({ default: await loaders[name]() }));
967
+ chunks.set(name, component);
968
+ }
969
+ return component;
970
+ }
971
+ /** Forget every loaded-or-failed Nivo chunk, so the next render loads it again. */
972
+ function resetNivoChunks() {
973
+ chunks = /* @__PURE__ */ new Map();
974
+ }
909
975
  /**
910
976
  * Resolved series colours for Nivo: config colours are `var()` strings, which
911
977
  * Nivo cannot use, so a configured colour is resolved through the container's
@@ -955,8 +1021,8 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
955
1021
  const labelOn = (d) => onFill(d.color);
956
1022
  switch (type) {
957
1023
  case "heatmap": {
958
- const Component = canvas ? HeatMapCanvas : HeatMapSvg;
959
- const rows = data;
1024
+ const Component = chunk(canvas ? "heatmap-canvas" : "heatmap-svg");
1025
+ const rows = isMatrix(data) ? matrixToSeries(data) : data;
960
1026
  const cells = rows.reduce((n, r) => n + r.data.length, 0);
961
1027
  const colorConfig = scale === "diverging" ? {
962
1028
  type: "diverging",
@@ -1004,7 +1070,7 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
1004
1070
  });
1005
1071
  }
1006
1072
  case "calendar": {
1007
- const Component = canvas ? CalendarCanvas : CalendarSvg;
1073
+ const Component = chunk(canvas ? "calendar-canvas" : "calendar-svg");
1008
1074
  const days = data;
1009
1075
  const range = calendarRange(days, from, to);
1010
1076
  return /* @__PURE__ */ jsx(Component, {
@@ -1038,7 +1104,7 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
1038
1104
  });
1039
1105
  }
1040
1106
  case "treemap": {
1041
- const Component = canvas ? TreeMapCanvas : TreeMapSvg;
1107
+ const Component = chunk(canvas ? "treemap-canvas" : "treemap-svg");
1042
1108
  const tree = data;
1043
1109
  return /* @__PURE__ */ jsx(Component, {
1044
1110
  data: tree,
@@ -1065,8 +1131,9 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
1065
1131
  });
1066
1132
  }
1067
1133
  case "sunburst": {
1134
+ const Component = chunk("sunburst-svg");
1068
1135
  const tree = data;
1069
- return /* @__PURE__ */ jsx(SunburstSvg, {
1136
+ return /* @__PURE__ */ jsx(Component, {
1070
1137
  data: tree,
1071
1138
  id: treeIdentity(tree),
1072
1139
  value: "value",
@@ -1342,21 +1409,43 @@ function seriesKeys(type, data, indexBy, keys) {
1342
1409
  case "pie":
1343
1410
  case "donut":
1344
1411
  case "funnel": return toSlices(data).map((s) => s.id);
1412
+ case "heatmap":
1413
+ if (isMatrix(data)) return data.rows.map(String);
1414
+ return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
1345
1415
  case "scatter":
1346
- case "scatterplot":
1347
- case "heatmap": return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
1416
+ case "scatterplot": return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
1348
1417
  case "treemap":
1349
1418
  case "sunburst": return (data.children ?? []).map((c) => String(c.id ?? c.name ?? ""));
1350
1419
  case "calendar": return [];
1351
1420
  }
1352
1421
  }
1422
+ /**
1423
+ * Catches a Nivo chunk that failed to load. Recovering takes two things,
1424
+ * because `React.lazy` memoises a rejection for the life of the page: fresh
1425
+ * lazy components ({@link resetNivoChunks}) and a boundary that leaves its
1426
+ * failed state. `resetKey` does the second on its own when the chart changes,
1427
+ * and the fallback gets a `retry` that does both.
1428
+ */
1353
1429
  var ChunkBoundary = class extends React.Component {
1354
- state = { failed: false };
1430
+ state = {
1431
+ failed: false,
1432
+ key: this.props.resetKey
1433
+ };
1355
1434
  static getDerivedStateFromError() {
1356
1435
  return { failed: true };
1357
1436
  }
1437
+ static getDerivedStateFromProps(props, state) {
1438
+ return props.resetKey === state.key ? null : {
1439
+ failed: false,
1440
+ key: props.resetKey
1441
+ };
1442
+ }
1443
+ retry = () => {
1444
+ resetNivoChunks();
1445
+ this.setState({ failed: false });
1446
+ };
1358
1447
  render() {
1359
- return this.state.failed ? this.props.fallback : this.props.children;
1448
+ return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
1360
1449
  }
1361
1450
  };
1362
1451
  /**
@@ -1368,6 +1457,14 @@ var ChunkBoundary = class extends React.Component {
1368
1457
  * Colours, fonts, axes, grid, tooltip, legend and the empty, loading and
1369
1458
  * error states all come from cortena-design tokens; a consumer passes data
1370
1459
  * and, optionally, a `config` of labels.
1460
+ *
1461
+ * `heatmap` takes either the Nivo series shape or a categorical
1462
+ * {@link ChartMatrix} — `{ rows, columns, values }` — for the correlation and
1463
+ * confusion matrices that are already in that form.
1464
+ *
1465
+ * `testMode` gives the drawing area a 600×300 floor so a chart still draws
1466
+ * where the layout gives it no size; see {@link ChartContainerProps.testMode}
1467
+ * and README.md, "Charts in tests".
1371
1468
  */
1372
1469
  function Chart({ type, data, config, renderer = "svg", indexBy, keys, stacked, scale = "sequential", legend, grid = true, tooltip = true, valueFormatter, from, to, loading, error, emptyMessage, className, height, ...props }) {
1373
1470
  const engine = chartEngine(type);
@@ -1388,7 +1485,18 @@ function Chart({ type, data, config, renderer = "svg", indexBy, keys, stacked, s
1388
1485
  else if (errorText) body = /* @__PURE__ */ jsx(ChartError, { children: errorText });
1389
1486
  else if (empty) body = /* @__PURE__ */ jsx(ChartEmpty, { children: emptyMessage });
1390
1487
  else if (engine === "nivo") body = /* @__PURE__ */ jsx(ChunkBoundary, {
1391
- fallback: /* @__PURE__ */ jsx(ChartError, { children: "Chart could not be loaded" }),
1488
+ resetKey: `${type}|${renderer}`,
1489
+ fallback: (retry) => /* @__PURE__ */ jsxs(ChartError, { children: [
1490
+ "Chart could not be loaded.",
1491
+ " ",
1492
+ /* @__PURE__ */ jsx("button", {
1493
+ type: "button",
1494
+ "data-slot": "chart-retry",
1495
+ onClick: retry,
1496
+ className: "underline underline-offset-2",
1497
+ children: "Retry"
1498
+ })
1499
+ ] }),
1392
1500
  children: /* @__PURE__ */ jsx(React.Suspense, {
1393
1501
  fallback: /* @__PURE__ */ jsx(ChartLoading, {}),
1394
1502
  children: /* @__PURE__ */ jsx(NivoChart, {
@@ -1512,15 +1620,22 @@ function Input({ className, type, ...props }) {
1512
1620
  //#endregion
1513
1621
  //#region src/components/spinner.tsx
1514
1622
  /**
1515
- * Spinner — a ring with a primary-coloured leading edge. Announces itself as
1623
+ * Spinner — a ring with a coloured leading edge. Announces itself as
1516
1624
  * `role="status"` with a "Loading" label; pass `aria-label` to override.
1625
+ *
1626
+ * ```tsx
1627
+ * <Button disabled>
1628
+ * <Spinner tone="inverse" /> Saving…
1629
+ * </Button>
1630
+ * ```
1517
1631
  */
1518
- function Spinner({ className, size = 16, style, ...props }) {
1632
+ function Spinner({ className, size = 16, tone = "default", style, ...props }) {
1519
1633
  return /* @__PURE__ */ jsx("span", {
1520
1634
  "data-slot": "spinner",
1635
+ "data-tone": tone,
1521
1636
  role: "status",
1522
1637
  "aria-label": "Loading",
1523
- className: cn("inline-block shrink-0 animate-spin rounded-[var(--ds-radius-full)]", "border-2 border-[var(--ds-border)] border-t-[var(--ds-primary)]", className),
1638
+ className: cn("inline-block shrink-0 animate-spin rounded-[var(--ds-radius-full)] border-2", tone === "inverse" ? "border-[var(--ds-primary-foreground)]/35 border-t-[var(--ds-primary-foreground)]" : "border-[var(--ds-border)] border-t-[var(--ds-primary)]", className),
1524
1639
  style: {
1525
1640
  width: size,
1526
1641
  height: size,
@@ -1713,21 +1828,6 @@ function DropdownMenuShortcut({ className, ...props }) {
1713
1828
  }
1714
1829
  //#endregion
1715
1830
  //#region src/components/select.tsx
1716
- /**
1717
- * Select.
1718
- *
1719
- * `SelectContent` composes Base UI's Portal + Positioner + Popup so consumers
1720
- * keep writing `<SelectContent side="bottom" align="start">`; positioning
1721
- * props are forwarded to the Positioner, which is where Base UI reads them.
1722
- *
1723
- * Radix users:
1724
- * - `SelectValue` shows the raw value unless the Root is given `items`
1725
- * (`{ [value]: label }` or `[{ value, label }]`) or `itemToStringLabel`;
1726
- * Base UI does not read the label out of an unmounted `SelectItem`.
1727
- * - `onValueChange` receives `(value, eventDetails)`.
1728
- * - `SelectContent` defaults to the dropdown layout; pass
1729
- * `alignItemWithTrigger` for Base UI's native-like overlay of the selected item.
1730
- */
1731
1831
  function Select(props) {
1732
1832
  return /* @__PURE__ */ jsx(Select$1.Root, { ...props });
1733
1833
  }
@@ -1850,12 +1950,25 @@ function toSheet(table, rows = exportRows(table)) {
1850
1950
  }))
1851
1951
  };
1852
1952
  }
1953
+ /**
1954
+ * Text a spreadsheet would evaluate rather than display. Excel, Sheets and
1955
+ * LibreOffice all treat a leading `=`, `+`, `-` or `@` as the start of a
1956
+ * formula, and a leading tab or CR as whitespace they strip before looking
1957
+ * again — so `\t=cmd|'/c calc'!A1` is a formula too. Only strings are checked:
1958
+ * a number, boolean or Date cell cannot carry one, and prefixing `-5` would
1959
+ * turn a number column into text.
1960
+ */
1961
+ const FORMULA_START = /^[=+\-@\t\r]/;
1853
1962
  function csvField(value) {
1854
1963
  if (value == null) return "";
1855
- const text = value instanceof Date ? value.toISOString() : String(value);
1964
+ let text = value instanceof Date ? value.toISOString() : String(value);
1965
+ if (typeof value === "string" && FORMULA_START.test(text)) text = `'${text}`;
1856
1966
  return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, "\"\"")}"` : text;
1857
1967
  }
1858
- /** RFC 4180 text: comma separated, CRLF lines, quotes doubled. */
1968
+ /**
1969
+ * RFC 4180 text: comma separated, CRLF lines, quotes doubled. A cell that a
1970
+ * spreadsheet would read as a formula is prefixed with `'` so it stays text.
1971
+ */
1859
1972
  function toCsv(table, rows) {
1860
1973
  const { headers, records } = toSheet(table, rows);
1861
1974
  return [headers, ...records].map((line) => line.map(csvField).join(",")).join("\r\n");
@@ -2542,14 +2655,20 @@ function useLocalStrategy(source, query, mode) {
2542
2655
  const targetPage = infinite ? loadedForScope.length : query.page;
2543
2656
  const needs = infinite ? loadedForScope.length < wantedPages : !(state.scopeKey === scopeKey && state.page === query.page && state.pages.length > 0);
2544
2657
  const requestKey = `${scopeKey}|${targetPage}|${tick}`;
2658
+ const fetchRef = React.useRef(fetch);
2659
+ React.useEffect(() => {
2660
+ fetchRef.current = fetch;
2661
+ });
2662
+ const hasFetch = Boolean(fetch);
2545
2663
  React.useEffect(() => {
2546
- if (!fetch || !needs) return;
2664
+ const run = fetchRef.current;
2665
+ if (!run || !needs) return;
2547
2666
  const controller = new AbortController();
2548
2667
  setState((s) => ({
2549
2668
  ...s,
2550
2669
  isFetching: true
2551
2670
  }));
2552
- fetch({
2671
+ run({
2553
2672
  ...scope,
2554
2673
  page: targetPage
2555
2674
  }, controller.signal).then((result) => {
@@ -2571,7 +2690,7 @@ function useLocalStrategy(source, query, mode) {
2571
2690
  });
2572
2691
  return () => controller.abort();
2573
2692
  }, [
2574
- fetch,
2693
+ hasFetch,
2575
2694
  requestKey,
2576
2695
  needs
2577
2696
  ]);
@@ -2808,7 +2927,7 @@ function DataTable(props) {
2808
2927
  return /* @__PURE__ */ jsx(DataTableOwned, { ...props });
2809
2928
  }
2810
2929
  function DataTableOwned(props) {
2811
- const { className, maxHeight, virtualize, rowHeight, stickyHeader, density, renderSubComponent, toolbar, bulkActions, toolbarActions, searchPlaceholder, emptyState, emptyMessage, pageSizeOptions, enableExport, exportFileName, loading, error, onRowClick, ...options } = props;
2930
+ const { className, maxHeight, virtualize, rowHeight, stickyHeader, density, renderSubComponent, toolbar, bulkActions, toolbarActions, searchPlaceholder, emptyState, emptyMessage, pageSizeOptions, enableExport, exportFileName, loading, error, onRowClick, activeRowId, rowClassName, ...options } = props;
2812
2931
  const instance = useDataTable({
2813
2932
  ...options,
2814
2933
  enableExpanding: options.enableExpanding ?? Boolean(renderSubComponent)
@@ -2833,7 +2952,9 @@ function DataTableOwned(props) {
2833
2952
  exportFileName,
2834
2953
  loading,
2835
2954
  error,
2836
- onRowClick
2955
+ onRowClick,
2956
+ activeRowId,
2957
+ rowClassName
2837
2958
  });
2838
2959
  }
2839
2960
  function pinAttrs(column, table) {
@@ -2911,7 +3032,7 @@ function EditableCell({ cell, instance, onDone }) {
2911
3032
  className: "h-7 px-2 py-0 text-[length:var(--ds-text-body-sm)]"
2912
3033
  });
2913
3034
  }
2914
- function DataTableView({ instance, className, maxHeight, virtualize, rowHeight = 40, stickyHeader = true, density = "default", renderSubComponent, toolbar = true, bulkActions, toolbarActions, searchPlaceholder, emptyState, emptyMessage = "No results", pageSizeOptions, enableExport = true, exportFileName = "export", loading, error, onRowClick }) {
3035
+ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight = 40, stickyHeader = true, density = "default", renderSubComponent, toolbar = true, bulkActions, toolbarActions, searchPlaceholder, emptyState, emptyMessage = "No results", pageSizeOptions, enableExport = true, exportFileName = "export", loading, error, onRowClick, activeRowId, rowClassName }) {
2915
3036
  const { table, paginationMode } = instance;
2916
3037
  const rows = table.getRowModel().rows;
2917
3038
  const leafColumns = table.getVisibleLeafColumns();
@@ -3101,6 +3222,7 @@ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight =
3101
3222
  const endStart = cells.findIndex((cell) => cell.column.getIsPinned() === "end");
3102
3223
  const splitAt = endStart === -1 ? cells.length : endStart;
3103
3224
  const selected = row.getIsSelected();
3225
+ const active = activeRowId != null && row.id === activeRowId;
3104
3226
  const expanded = renderSubComponent && row.getIsExpanded();
3105
3227
  const renderCell = (cell, c) => {
3106
3228
  const column = cell.column;
@@ -3127,7 +3249,7 @@ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight =
3127
3249
  rowId: row.id,
3128
3250
  columnId: column.id
3129
3251
  }) : void 0,
3130
- className: cn("relative align-middle outline-none", isEditing ? "px-1.5 py-1" : cellPadding, alignClass[meta?.align ?? "start"], "focus-visible:ring-[2px] focus-visible:ring-inset focus-visible:ring-[var(--ds-ring)]", pin.pinned && "z-[1] bg-[var(--ds-card)] group-hover/row:bg-[linear-gradient(var(--ds-hover),var(--ds-hover))] group-data-[selected]/row:bg-[linear-gradient(var(--ds-primary-soft),var(--ds-primary-soft))]", pin.pinned === "start" && pin.edge && "shadow-[inset_-1px_0_0_var(--ds-border)]", pin.pinned === "end" && pin.edge && "shadow-[inset_1px_0_0_var(--ds-border)]", !isEditing && "truncate", meta?.cellClassName),
3252
+ className: cn("relative align-middle outline-none", isEditing ? "px-1.5 py-1" : cellPadding, alignClass[meta?.align ?? "start"], "focus-visible:ring-[2px] focus-visible:ring-inset focus-visible:ring-[var(--ds-ring)]", pin.pinned && "z-[1] bg-[var(--ds-card)] group-hover/row:bg-[linear-gradient(var(--ds-hover),var(--ds-hover))] group-data-[selected]/row:bg-[linear-gradient(var(--ds-primary-soft),var(--ds-primary-soft))] group-data-[active]/row:bg-[linear-gradient(var(--ds-primary-soft),var(--ds-primary-soft))]", pin.pinned === "start" && pin.edge && "shadow-[inset_-1px_0_0_var(--ds-border)]", pin.pinned === "end" && pin.edge && "shadow-[inset_1px_0_0_var(--ds-border)]", !isEditing && "truncate", meta?.cellClassName),
3131
3253
  children: isEditing ? /* @__PURE__ */ jsx(EditableCell, {
3132
3254
  cell,
3133
3255
  instance,
@@ -3145,12 +3267,13 @@ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight =
3145
3267
  "data-slot": "data-table-row",
3146
3268
  "data-index": virtualIndex,
3147
3269
  "data-selected": selected ? "" : void 0,
3270
+ "data-active": active ? "" : void 0,
3148
3271
  "data-expanded": expanded ? "" : void 0,
3149
3272
  "aria-rowindex": index + 2,
3150
3273
  "aria-selected": table.options.enableRowSelection ? selected : void 0,
3151
3274
  ref: isVirtual ? virtualizer.measureElement : void 0,
3152
3275
  onClick: onRowClick ? (e) => onRowClick(row.original, e) : void 0,
3153
- className: cn("group/row border-b border-[var(--ds-border-subtle)] transition-colors duration-[var(--ds-duration-fast)]", "hover:bg-[var(--ds-hover)] data-[selected]:bg-[var(--ds-primary-soft)]", onRowClick && "cursor-pointer"),
3276
+ className: cn("group/row border-b border-[var(--ds-border-subtle)] transition-colors duration-[var(--ds-duration-fast)]", "hover:bg-[var(--ds-hover)] data-[selected]:bg-[var(--ds-primary-soft)]", "data-[active]:bg-[var(--ds-primary-soft)]", onRowClick && "cursor-pointer", rowClassName?.(row.original)),
3154
3277
  children: [
3155
3278
  cells.slice(0, splitAt).map((cell, c) => renderCell(cell, c)),
3156
3279
  /* @__PURE__ */ jsx("td", {
@@ -4205,6 +4328,10 @@ function RadioGroupItem({ className, ...props }) {
4205
4328
  * SectionCard — a Card with a header row (title, description, actions), a
4206
4329
  * body and an optional footer. It composes the Card parts rather than
4207
4330
  * restyling them, so a SectionCard and a hand-built Card look identical.
4331
+ *
4332
+ * `render` is Card's and reaches the outer element, so a section card can be
4333
+ * the landmark it is named after: `render={<section aria-labelledby={id} />}`
4334
+ * with the same `id` on the title, or `render={<aside />}` for a side panel.
4208
4335
  */
4209
4336
  function SectionCard({ title, description, actions, footer, flush, bodyClassName, children, ...props }) {
4210
4337
  return /* @__PURE__ */ jsxs(Card, {
@@ -4263,6 +4390,7 @@ const TONE_CLASS = {
4263
4390
  accent: "bg-[var(--ds-primary)]",
4264
4391
  success: "bg-[var(--ds-success)]",
4265
4392
  warning: "bg-[var(--ds-warning)]",
4393
+ destructive: "bg-[var(--ds-destructive)]",
4266
4394
  danger: "bg-[var(--ds-destructive)]",
4267
4395
  info: "bg-[var(--ds-info)]",
4268
4396
  neutral: "bg-[var(--ds-text-tertiary)]"
@@ -5064,14 +5192,21 @@ const buttonSize = z.enum([
5064
5192
  "lg"
5065
5193
  ]).describe("Control size.");
5066
5194
  /**
5195
+ * Root-relative means one slash and then a path. `//evil.example/x` is
5196
+ * protocol-relative — the browser resolves it against the page's scheme and
5197
+ * goes off-origin — and `/\evil.example` is the same trick, because browsers
5198
+ * treat a backslash after the leading slash as a second slash.
5199
+ */
5200
+ const rootRelative = (value) => value.startsWith("/") && value[1] !== "/" && value[1] !== "\\";
5201
+ /**
5067
5202
  * A URL an agent may point at.
5068
5203
  *
5069
5204
  * Restricted by scheme rather than sanitised after the fact: `javascript:` and
5070
5205
  * friends never reach the DOM because they never pass validation, so the
5071
5206
  * component gets an error card instead.
5072
5207
  */
5073
- const httpUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || value.startsWith("/") || value.startsWith("#"), "Must be an http(s), root-relative or fragment URL.").describe("Destination URL (http, https, root-relative or fragment).");
5074
- const imageUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || /^data:image\//i.test(value) || value.startsWith("/"), "Must be an http(s), root-relative or data:image URL.").describe("Image URL (http, https, root-relative or data:image).");
5208
+ const httpUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || rootRelative(value) || value.startsWith("#"), "Must be an http(s), root-relative or fragment URL.").describe("Destination URL (http, https, root-relative or fragment).");
5209
+ const imageUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || /^data:image\//i.test(value) || rootRelative(value), "Must be an http(s), root-relative or data:image URL.").describe("Image URL (http, https, root-relative or data:image).");
5075
5210
  /** Every chart type the Chart composite draws, Recharts and Nivo alike. */
5076
5211
  const chartType = z.enum([
5077
5212
  "bar",
@@ -6744,6 +6879,119 @@ function AvatarFallback({ className, ...props }) {
6744
6879
  });
6745
6880
  }
6746
6881
  //#endregion
6882
+ //#region src/components/breadcrumb.tsx
6883
+ /**
6884
+ * Breadcrumb — the trail above a page title.
6885
+ *
6886
+ * A `<nav aria-label="Breadcrumb">` around an ordered list, which is what
6887
+ * assistive technology expects: the list order is the hierarchy, so a screen
6888
+ * reader announces "list, 4 items" and reads the path in order. Separators are
6889
+ * decorative list items, not text inside the links, so a link's accessible
6890
+ * name is the crumb and nothing else.
6891
+ *
6892
+ * The last crumb is `BreadcrumbPage`, not a link: it is the page you are on,
6893
+ * so it carries `aria-current="page"` and is not focusable. Everything before
6894
+ * it is a `BreadcrumbLink`.
6895
+ *
6896
+ * ```tsx
6897
+ * <Breadcrumb>
6898
+ * <BreadcrumbList>
6899
+ * <BreadcrumbItem>
6900
+ * <BreadcrumbLink href="/">Home</BreadcrumbLink>
6901
+ * </BreadcrumbItem>
6902
+ * <BreadcrumbSeparator />
6903
+ * <BreadcrumbItem>
6904
+ * <BreadcrumbEllipsis />
6905
+ * </BreadcrumbItem>
6906
+ * <BreadcrumbSeparator />
6907
+ * <BreadcrumbItem>
6908
+ * <BreadcrumbPage>Invoice 4021</BreadcrumbPage>
6909
+ * </BreadcrumbItem>
6910
+ * </BreadcrumbList>
6911
+ * </Breadcrumb>
6912
+ * ```
6913
+ *
6914
+ * **Router links.** `BreadcrumbLink` renders an `<a>` by default. Pass
6915
+ * `render` with the router's link component and the class name, `href` and
6916
+ * handlers are merged onto it, the same contract as `ButtonLink`:
6917
+ * `render={<NextLink href="/projects" />}` or
6918
+ * `render={<Link to="/projects" />}`. This is deliberately not Base UI's
6919
+ * `render` — a breadcrumb is markup, not a Base UI primitive — but it takes
6920
+ * the same shape so there is one thing to remember.
6921
+ */
6922
+ function Breadcrumb({ className, ...props }) {
6923
+ return /* @__PURE__ */ jsx("nav", {
6924
+ "data-slot": "breadcrumb",
6925
+ "aria-label": "Breadcrumb",
6926
+ className: cn("min-w-0", className),
6927
+ ...props
6928
+ });
6929
+ }
6930
+ function BreadcrumbList({ className, ...props }) {
6931
+ return /* @__PURE__ */ jsx("ol", {
6932
+ "data-slot": "breadcrumb-list",
6933
+ className: cn("flex flex-wrap items-center gap-1.5 break-words p-0 sm:gap-2", "list-none text-[length:var(--ds-text-caption-lg)]", "leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-muted-foreground)]", className),
6934
+ ...props
6935
+ });
6936
+ }
6937
+ function BreadcrumbItem({ className, ...props }) {
6938
+ return /* @__PURE__ */ jsx("li", {
6939
+ "data-slot": "breadcrumb-item",
6940
+ className: cn("inline-flex min-w-0 items-center gap-1.5", className),
6941
+ ...props
6942
+ });
6943
+ }
6944
+ function BreadcrumbLink({ className, render, children, ...props }) {
6945
+ return renderWith(render, {
6946
+ ...props,
6947
+ "data-slot": "breadcrumb-link",
6948
+ className: cn("truncate rounded-[var(--ds-radius-sm)] text-[color:var(--ds-muted-foreground)] no-underline", "transition-colors duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "hover:text-[color:var(--ds-foreground)] hover:underline underline-offset-4", "outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "[&_svg]:size-3.5 [&_svg]:shrink-0", className)
6949
+ }, children, "a");
6950
+ }
6951
+ /**
6952
+ * The current page: the last crumb, which is not a link.
6953
+ *
6954
+ * A plain `<span aria-current="page">`, deliberately not `role="link"` with
6955
+ * `aria-disabled` — a disabled link that cannot be focused is a fiction, and
6956
+ * it makes `getByRole("link")` return a crumb that navigates nowhere. With a
6957
+ * span, the roles in the trail are exactly the crumbs you can travel to.
6958
+ */
6959
+ function BreadcrumbPage({ className, ...props }) {
6960
+ return /* @__PURE__ */ jsx("span", {
6961
+ "data-slot": "breadcrumb-page",
6962
+ "aria-current": "page",
6963
+ className: cn("truncate font-medium text-[color:var(--ds-foreground)]", className),
6964
+ ...props
6965
+ });
6966
+ }
6967
+ /** Decorative divider between two crumbs; a chevron unless children replace it. */
6968
+ function BreadcrumbSeparator({ className, children, ...props }) {
6969
+ return /* @__PURE__ */ jsx("li", {
6970
+ "data-slot": "breadcrumb-separator",
6971
+ role: "presentation",
6972
+ "aria-hidden": "true",
6973
+ className: cn("inline-flex shrink-0 items-center text-[color:var(--ds-text-tertiary)] [&_svg]:size-3.5", className),
6974
+ ...props,
6975
+ children: children ?? /* @__PURE__ */ jsx(ChevronRight, {})
6976
+ });
6977
+ }
6978
+ /**
6979
+ * Stands in for the crumbs a long trail collapses; put it inside a
6980
+ * BreadcrumbItem. The glyph is hidden and the label is not: an ellipsis that
6981
+ * announces nothing leaves a screen-reader user with a gap in the path.
6982
+ */
6983
+ function BreadcrumbEllipsis({ className, children, ...props }) {
6984
+ return /* @__PURE__ */ jsxs("span", {
6985
+ "data-slot": "breadcrumb-ellipsis",
6986
+ className: cn("inline-flex size-5 items-center justify-center text-[color:var(--ds-text-tertiary)]", "[&_svg]:size-3.5", className),
6987
+ ...props,
6988
+ children: [/* @__PURE__ */ jsx(MoreHorizontal, { "aria-hidden": true }), /* @__PURE__ */ jsx("span", {
6989
+ className: "sr-only",
6990
+ children: children ?? "More levels"
6991
+ })]
6992
+ });
6993
+ }
6994
+ //#endregion
6747
6995
  //#region src/components/chip.tsx
6748
6996
  /**
6749
6997
  * Chip — a pill for filters, tags and selected values. Unlike Badge it keeps
@@ -7299,6 +7547,30 @@ function DateRangePicker(props) {
7299
7547
  * `onFiles` receives only the accepted files; rejections go to `onReject`.
7300
7548
  * `FileList` and `formatFileSize` are separate parts for listing what was
7301
7549
  * picked, so a form can keep the zone and the list in different places.
7550
+ *
7551
+ * ## Wrapping a whole page as a drop target
7552
+ *
7553
+ * `noClick` and `noKeyboard` turn off the two activators that only make sense
7554
+ * on a dashed box: clicking anywhere inside opens the file dialog, and Enter /
7555
+ * Space on the focused root does the same. A page-sized zone wraps content
7556
+ * that has its own buttons and inputs, so both have to go — otherwise every
7557
+ * click in the page opens a file picker.
7558
+ *
7559
+ * ```tsx
7560
+ * <Dropzone noClick noKeyboard onFiles={upload} className="min-h-dvh border-0 bg-transparent p-0">
7561
+ * {({ isDragActive, open }) => (
7562
+ * <>
7563
+ * <PageContent />
7564
+ * <Button onClick={open}>Browse…</Button>
7565
+ * {isDragActive ? <DropHint /> : null}
7566
+ * </>
7567
+ * )}
7568
+ * </Dropzone>
7569
+ * ```
7570
+ *
7571
+ * The render-prop form gets react-dropzone's `open()`, which is the way back
7572
+ * to the file dialog from a deliberate control. With `noClick` the root also
7573
+ * drops its `cursor-pointer`, since it is no longer clickable.
7302
7574
  */
7303
7575
  const UNITS = [
7304
7576
  "B",
@@ -7332,13 +7604,15 @@ function describeAccept(accept) {
7332
7604
  }
7333
7605
  return exts.size > 0 ? [...exts].join(", ") : void 0;
7334
7606
  }
7335
- function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFiles, disabled = false, label, hint, children, className, ref, ...props }) {
7607
+ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFiles, disabled = false, noClick = false, noKeyboard = false, label, hint, children, className, ref, ...props }) {
7336
7608
  const state = useDropzone({
7337
7609
  accept,
7338
7610
  multiple,
7339
7611
  maxSize,
7340
7612
  maxFiles,
7341
7613
  disabled,
7614
+ noClick,
7615
+ noKeyboard,
7342
7616
  onDropAccepted: (files) => onFiles(files),
7343
7617
  onDropRejected: (rejections) => onReject?.(rejections)
7344
7618
  });
@@ -7373,7 +7647,7 @@ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFile
7373
7647
  ...getRootProps({
7374
7648
  ...props,
7375
7649
  ref: setRoot,
7376
- className: cn("group/dropzone flex flex-col items-center justify-center gap-2 p-6 text-center", "rounded-[var(--ds-radius-lg)] border border-dashed border-[var(--ds-border)] bg-[var(--ds-card)]", "cursor-pointer select-none outline-none", "transition-[border-color,background-color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]", "focus-visible:border-[var(--ds-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[active]:border-[var(--ds-primary)] data-[active]:bg-[var(--ds-primary-soft)]", "data-[reject]:border-[var(--ds-destructive)] data-[reject]:bg-[var(--ds-destructive-soft)]", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className)
7650
+ className: cn("group/dropzone flex flex-col items-center justify-center gap-2 p-6 text-center", "rounded-[var(--ds-radius-lg)] border border-dashed border-[var(--ds-border)] bg-[var(--ds-card)]", noClick ? "select-none outline-none" : "cursor-pointer select-none outline-none", "transition-[border-color,background-color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]", "focus-visible:border-[var(--ds-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[active]:border-[var(--ds-primary)] data-[active]:bg-[var(--ds-primary-soft)]", "data-[reject]:border-[var(--ds-destructive)] data-[reject]:bg-[var(--ds-destructive-soft)]", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className)
7377
7651
  }),
7378
7652
  "data-slot": "dropzone",
7379
7653
  "data-active": isDragActive || void 0,
@@ -7381,6 +7655,8 @@ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFile
7381
7655
  "data-reject": isDragReject || void 0,
7382
7656
  "data-focused": isFocused || void 0,
7383
7657
  "data-disabled": disabled || void 0,
7658
+ "data-no-click": noClick || void 0,
7659
+ "data-no-keyboard": noKeyboard || void 0,
7384
7660
  children: [/* @__PURE__ */ jsx("input", {
7385
7661
  "data-slot": "dropzone-input",
7386
7662
  ...getInputProps()
@@ -7760,9 +8036,35 @@ function Segmented({ className, value, onValueChange, options, "aria-label": ari
7760
8036
  * Description, Close); only the Popup's placement differs, chosen by `side`.
7761
8037
  * `SheetContent` composes Portal + Backdrop + Popup so consumers keep writing
7762
8038
  * `<SheetContent side="right">…</SheetContent>`.
8039
+ *
8040
+ * ## Non-modal side panels
8041
+ *
8042
+ * `modal={false}` is the help-panel / inspector case: a sheet read *alongside*
8043
+ * the app rather than instead of it. No backdrop is rendered, focus is not
8044
+ * trapped, page scroll is not locked, and the rest of the page stays clickable,
8045
+ * so a user can keep working with the panel open. `modal="trap-focus"` is the
8046
+ * middle setting Base UI offers: focus stays inside, the page still scrolls.
8047
+ *
8048
+ * ```tsx
8049
+ * <Sheet modal={false} open={helpOpen} onOpenChange={setHelpOpen}>
8050
+ * <SheetContent side="right" disablePointerDismissal>…</SheetContent>
8051
+ * </Sheet>
8052
+ * ```
8053
+ *
8054
+ * A non-modal sheet closes when focus or a press leaves it; pass
8055
+ * `disablePointerDismissal` (a Base UI Root prop, forwarded) to keep it open
8056
+ * until the user closes it. `modal` is on the Root because that is where Base
8057
+ * UI reads it; `SheetContent` picks the backdrop up from there through context.
7763
8058
  */
7764
- function Sheet(props) {
7765
- return /* @__PURE__ */ jsx(Dialog$1.Root, { ...props });
8059
+ const SheetModalContext = React.createContext(true);
8060
+ function Sheet({ modal = true, ...props }) {
8061
+ return /* @__PURE__ */ jsx(SheetModalContext.Provider, {
8062
+ value: modal,
8063
+ children: /* @__PURE__ */ jsx(Dialog$1.Root, {
8064
+ modal,
8065
+ ...props
8066
+ })
8067
+ });
7766
8068
  }
7767
8069
  function SheetTrigger(props) {
7768
8070
  return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
@@ -7804,12 +8106,14 @@ const sheetVariants = cva([
7804
8106
  defaultVariants: { side: "right" }
7805
8107
  });
7806
8108
  function SheetContent({ side = "right", className, children, keepMounted, container, ...props }) {
8109
+ const modal = React.useContext(SheetModalContext);
7807
8110
  return /* @__PURE__ */ jsxs(SheetPortal, {
7808
8111
  keepMounted,
7809
8112
  container,
7810
- children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsx(Dialog$1.Popup, {
8113
+ children: [modal === false ? null : /* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsx(Dialog$1.Popup, {
7811
8114
  "data-slot": "sheet-content",
7812
8115
  "data-side": side,
8116
+ "data-modal": modal === false ? void 0 : modal === true ? "" : modal,
7813
8117
  className: cn(sheetVariants({ side }), className),
7814
8118
  ...props,
7815
8119
  children
@@ -7846,6 +8150,40 @@ function SheetDescription({ className, ...props }) {
7846
8150
  }
7847
8151
  //#endregion
7848
8152
  //#region src/components/sortable-list.tsx
8153
+ /**
8154
+ * SortableList.
8155
+ *
8156
+ * A reorderable list over dnd-kit: `DndContext` + `SortableContext` around one
8157
+ * `useSortable` item per entry. The list owns nothing — it calls `onReorder`
8158
+ * with the next array and the consumer stores it. Pointer and keyboard sensors
8159
+ * are wired (Space picks up, arrows move, Space drops, Escape cancels) and a
8160
+ * `DragOverlay` paints the lifted copy with `--ds-shadow-lg` while the item
8161
+ * left behind dims and takes the ring colour.
8162
+ *
8163
+ * By default the whole row is the drag activator. Pass `handle` to restrict
8164
+ * dragging to a `<SortableHandle>` (or any element given `handleProps`) so
8165
+ * buttons and inputs inside a row stay clickable.
8166
+ *
8167
+ * ## Touch
8168
+ *
8169
+ * A pointer drag and a touch scroll start the same way, so something has to
8170
+ * say which one a finger means. In handle mode only the small handle is
8171
+ * `touch-none`, so the rest of the row scrolls: nothing to decide. In
8172
+ * whole-row mode the whole row is `touch-none`, which is why a touch list
8173
+ * whose rows are the activators cannot be scrolled by dragging over them.
8174
+ *
8175
+ * `touchScroll` is the way out, and it defaults to whichever of those is
8176
+ * already true — `true` in handle mode, `false` in whole-row mode, so existing
8177
+ * lists behave exactly as before. Setting it `true` on a whole-row list drops
8178
+ * `touch-none` and swaps the PointerSensor for a MouseSensor plus a
8179
+ * TouchSensor with delay activation: a quick swipe scrolls, a press held for
8180
+ * {@link TOUCH_DELAY_MS} picks the row up. `false` in handle mode is the
8181
+ * opposite trade — the row never scrolls under a finger.
8182
+ */
8183
+ /** How long a finger must rest on a row before it becomes a drag. */
8184
+ const TOUCH_DELAY_MS = 250;
8185
+ /** How far it may slip in that time and still count as a press, not a scroll. */
8186
+ const TOUCH_TOLERANCE_PX = 8;
7849
8187
  /** dnd-kit types its listeners as a bag of `Function`s; narrow them to the handlers they are. */
7850
8188
  function asHandlers(listeners) {
7851
8189
  return listeners ?? {};
@@ -7858,7 +8196,7 @@ const SortableItemContext = React.createContext({
7858
8196
  disabled: false
7859
8197
  });
7860
8198
  const itemClassName = cn("relative rounded-[var(--ds-radius-md)] outline-none", "focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[dragging]:opacity-50 data-[dragging]:ring-2 data-[dragging]:ring-[var(--ds-ring)]");
7861
- function SortableItem({ id, item, handle, disabled, renderItem, className }) {
8199
+ function SortableItem({ id, item, handle, disabled, touchScroll, renderItem, className }) {
7862
8200
  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
7863
8201
  id,
7864
8202
  disabled
@@ -7896,7 +8234,7 @@ function SortableItem({ id, item, handle, disabled, renderItem, className }) {
7896
8234
  transform: CSS.Translate.toString(transform),
7897
8235
  transition
7898
8236
  },
7899
- className: cn(itemClassName, !handle && !disabled && "cursor-grab touch-none data-[dragging]:cursor-grabbing", className),
8237
+ className: cn(itemClassName, !handle && !disabled && "cursor-grab data-[dragging]:cursor-grabbing", !touchScroll && !disabled && "touch-none", className),
7900
8238
  ...rowActivator,
7901
8239
  children: renderItem(item, {
7902
8240
  handleProps,
@@ -7923,9 +8261,19 @@ function SortableHandle({ className, children, ...props }) {
7923
8261
  children: children ?? /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": true })
7924
8262
  });
7925
8263
  }
7926
- function SortableList({ items, getId, onReorder, renderItem, orientation = "vertical", handle = false, disabled = false, itemClassName: itemClass, className, ...props }) {
8264
+ function SortableList({ items, getId, onReorder, renderItem, orientation = "vertical", handle = false, disabled = false, touchScroll = handle, itemClassName: itemClass, className, ...props }) {
7927
8265
  const [activeId, setActiveId] = React.useState(null);
7928
- const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 4 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
8266
+ const delayTouch = touchScroll && !handle;
8267
+ const pointerSensor = useSensor(PointerSensor, { activationConstraint: { distance: 4 } });
8268
+ const mouseSensor = useSensor(MouseSensor, { activationConstraint: { distance: 4 } });
8269
+ const touchSensor = useSensor(TouchSensor, { activationConstraint: {
8270
+ delay: TOUCH_DELAY_MS,
8271
+ tolerance: TOUCH_TOLERANCE_PX
8272
+ } });
8273
+ const keyboardSensor = useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates });
8274
+ const pointerSensors = useSensors(pointerSensor, keyboardSensor);
8275
+ const splitSensors = useSensors(mouseSensor, touchSensor, keyboardSensor);
8276
+ const sensors = delayTouch ? splitSensors : pointerSensors;
7929
8277
  const ids = React.useMemo(() => items.map(getId), [items, getId]);
7930
8278
  const activeItem = activeId === null ? void 0 : items[ids.indexOf(activeId)];
7931
8279
  const onDragStart = React.useCallback((event) => {
@@ -7938,7 +8286,12 @@ function SortableList({ items, getId, onReorder, renderItem, orientation = "vert
7938
8286
  const from = ids.indexOf(active.id);
7939
8287
  const to = ids.indexOf(over.id);
7940
8288
  if (from < 0 || to < 0) return;
7941
- onReorder(arrayMove([...items], from, to));
8289
+ onReorder(arrayMove([...items], from, to), {
8290
+ from,
8291
+ to,
8292
+ activeId: active.id,
8293
+ overId: over.id
8294
+ });
7942
8295
  }, [
7943
8296
  ids,
7944
8297
  items,
@@ -7959,6 +8312,7 @@ function SortableList({ items, getId, onReorder, renderItem, orientation = "vert
7959
8312
  "data-slot": "sortable-list",
7960
8313
  "data-orientation": orientation,
7961
8314
  "data-disabled": disabled || void 0,
8315
+ "data-touch-scroll": touchScroll ? "" : void 0,
7962
8316
  className: cn("flex list-none gap-2 p-0", orientation === "horizontal" ? "flex-row flex-wrap" : "flex-col", className),
7963
8317
  ...props,
7964
8318
  children: items.map((item, index) => /* @__PURE__ */ jsx(SortableItem, {
@@ -7966,6 +8320,7 @@ function SortableList({ items, getId, onReorder, renderItem, orientation = "vert
7966
8320
  item,
7967
8321
  handle,
7968
8322
  disabled,
8323
+ touchScroll,
7969
8324
  renderItem,
7970
8325
  className: itemClass
7971
8326
  }, ids[index] ?? index))
@@ -8312,6 +8667,6 @@ function useCortenaTheme(options = {}) {
8312
8667
  };
8313
8668
  }
8314
8669
  //#endregion
8315
- export { A2UIErrorCard, A2UIRenderer, A2UI_CATALOGUE_ID, A2UI_DEFAULT_SURFACE_ID, A2UI_VERSION, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertIcon, AlertTitle, Area, AreaChart, Avatar, AvatarFallback, AvatarImage, Badge, Bar, BarChart, Button, ButtonLink, Calendar, CalendarDayButton, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CartesianGrid, Cell, Chart, ChartContainer, ChartEmpty, ChartError, ChartLegend, ChartLegendContent, ChartLoading, ChartTooltip, ChartTooltipContent, Checkbox, Chip, Combobox, ComboboxChip, ComboboxChips, ComboboxClear, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxSeparator, ComboboxStatus, ComboboxTrigger, ComboboxValue, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposedChart, DEFAULT_THEME_STORAGE_KEY, DataTable, DataTableColumnHeader, DataTableExportMenu, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableView, DataTableViewOptions, DateField, DatePicker, DateRangePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, EmptyState, ErrorBanner, Field, FieldContent, FieldControl, FieldDescription, FieldError, FieldGroup, FieldLabel, Fieldset, FieldsetLegend, FileList, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Funnel, FunnelChart, Input, Kbd, Label, LabelList, Line, LineChart, LoadingSkeleton, Markdown, NIVO_TYPES, PageHeader, Pie, PieChart, PolarAngleAxis, PolarGrid, PolarRadiusAxis, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RECHARTS_TYPES, Radar, RadarChart, RadioGroup, RadioGroupItem, RechartsLegend, RechartsTooltip, ReferenceLine, ResponsiveContainer, Scatter, ScatterChart, ScrollArea, ScrollBar, SectionCard, Segmented, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, SortableHandle, SortableList, Spinner, StatusDot, Switch, THEME_MESSAGE_TYPE, Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger, Textarea, Toast, Toaster, Toolbar, ToolbarEnd, ToolbarSeparator, ToolbarStart, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, VIZ_SERIES_COUNT, XAxis, YAxis, alertVariants, applyStoredTheme, applyTheme, arrayMove, badgeVariants, buildCatalogueDoc, buttonVariants, catalogueMarkdown, catalogueNames, chartEngine, chartScales, chipVariants, cn, createColumnHelper, dataTableFeatures, defaultCatalogue, downloadCsv, downloadExcel, exampleMessages, fieldVariants, foldMessages, formatDateDefault, formatDateISO, formatDateLong, formatDateShort, formatFileSize, formatRelative, getAtPointer, getThemeInitScript, inputClassName, isDataBinding, labelVariants, markdownSanitizeSchema, nivoTheme, parseA2UIMessages, parseDateDefault, parseDateISO, pointerSegments, readAction, readChildren, readComponent, readStoredTheme, resolveColor, resolvePointer, resolveValue, resolveVizSeries, seriesColor, setAtPointer, sheetVariants, textareaClassName, themeFromBackground, themeFromMessage, themeInitScript, toCsv, toHex, toSheet, toastVariants, useChart, useComboboxFilter, useCortenaTheme, useDataTable, useForm, useFormField, useNivoTheme, useToast, vizSeriesVars, vizVar, zodResolver };
8670
+ export { A2UIErrorCard, A2UIRenderer, A2UI_CATALOGUE_ID, A2UI_DEFAULT_SURFACE_ID, A2UI_VERSION, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertIcon, AlertTitle, Area, AreaChart, Avatar, AvatarFallback, AvatarImage, Badge, Bar, BarChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonLink, Calendar, CalendarDayButton, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CartesianGrid, Cell, Chart, ChartContainer, ChartEmpty, ChartError, ChartLegend, ChartLegendContent, ChartLoading, ChartTooltip, ChartTooltipContent, Checkbox, Chip, Combobox, ComboboxChip, ComboboxChips, ComboboxClear, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxSeparator, ComboboxStatus, ComboboxTrigger, ComboboxValue, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposedChart, DEFAULT_THEME_STORAGE_KEY, DataTable, DataTableColumnHeader, DataTableExportMenu, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableView, DataTableViewOptions, DateField, DatePicker, DateRangePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, EmptyState, ErrorBanner, Field, FieldContent, FieldControl, FieldDescription, FieldError, FieldGroup, FieldLabel, Fieldset, FieldsetLegend, FileList, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Funnel, FunnelChart, Input, Kbd, Label, LabelList, Line, LineChart, LoadingSkeleton, Markdown, NIVO_TYPES, PageHeader, Pie, PieChart, PolarAngleAxis, PolarGrid, PolarRadiusAxis, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, RECHARTS_TYPES, Radar, RadarChart, RadioGroup, RadioGroupItem, RechartsLegend, RechartsTooltip, ReferenceLine, ResponsiveContainer, Scatter, ScatterChart, ScrollArea, ScrollBar, SectionCard, Segmented, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, SortableHandle, SortableList, Spinner, StatusDot, Switch, THEME_MESSAGE_TYPE, Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger, Textarea, Toast, Toaster, Toolbar, ToolbarEnd, ToolbarSeparator, ToolbarStart, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, VIZ_SERIES_COUNT, XAxis, YAxis, alertVariants, applyStoredTheme, applyTheme, arrayMove, badgeVariants, buildCatalogueDoc, buttonVariants, catalogueMarkdown, catalogueNames, chartEngine, chartScales, chipVariants, cn, createColumnHelper, dataTableFeatures, defaultCatalogue, downloadCsv, downloadExcel, exampleMessages, fieldVariants, foldMessages, formatDateDefault, formatDateISO, formatDateLong, formatDateShort, formatFileSize, formatRelative, getAtPointer, getThemeInitScript, inputClassName, isDataBinding, labelVariants, markdownSanitizeSchema, nivoTheme, parseA2UIMessages, parseDateDefault, parseDateISO, pointerSegments, readAction, readChildren, readComponent, readStoredTheme, resolveColor, resolvePointer, resolveValue, resolveVizSeries, seriesColor, setAtPointer, sheetVariants, textareaClassName, themeFromBackground, themeFromMessage, themeInitScript, toCsv, toHex, toSheet, toastVariants, useChart, useComboboxFilter, useCortenaTheme, useDataTable, useForm, useFormField, useNivoTheme, useToast, vizSeriesVars, vizVar, zodResolver };
8316
8671
 
8317
8672
  //# sourceMappingURL=index.js.map