cortena-ui 1.2.0 → 1.3.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/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);
@@ -956,7 +1008,7 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
956
1008
  switch (type) {
957
1009
  case "heatmap": {
958
1010
  const Component = canvas ? HeatMapCanvas : HeatMapSvg;
959
- const rows = data;
1011
+ const rows = isMatrix(data) ? matrixToSeries(data) : data;
960
1012
  const cells = rows.reduce((n, r) => n + r.data.length, 0);
961
1013
  const colorConfig = scale === "diverging" ? {
962
1014
  type: "diverging",
@@ -1342,9 +1394,11 @@ function seriesKeys(type, data, indexBy, keys) {
1342
1394
  case "pie":
1343
1395
  case "donut":
1344
1396
  case "funnel": return toSlices(data).map((s) => s.id);
1397
+ case "heatmap":
1398
+ if (isMatrix(data)) return data.rows.map(String);
1399
+ return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
1345
1400
  case "scatter":
1346
- case "scatterplot":
1347
- case "heatmap": return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
1401
+ case "scatterplot": return isSeriesArray(data) ? data.map((s) => String(s.id)) : [];
1348
1402
  case "treemap":
1349
1403
  case "sunburst": return (data.children ?? []).map((c) => String(c.id ?? c.name ?? ""));
1350
1404
  case "calendar": return [];
@@ -1368,6 +1422,14 @@ var ChunkBoundary = class extends React.Component {
1368
1422
  * Colours, fonts, axes, grid, tooltip, legend and the empty, loading and
1369
1423
  * error states all come from cortena-design tokens; a consumer passes data
1370
1424
  * and, optionally, a `config` of labels.
1425
+ *
1426
+ * `heatmap` takes either the Nivo series shape or a categorical
1427
+ * {@link ChartMatrix} — `{ rows, columns, values }` — for the correlation and
1428
+ * confusion matrices that are already in that form.
1429
+ *
1430
+ * `testMode` gives the drawing area a 600×300 floor so a chart still draws
1431
+ * where the layout gives it no size; see {@link ChartContainerProps.testMode}
1432
+ * and README.md, "Charts in tests".
1371
1433
  */
1372
1434
  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
1435
  const engine = chartEngine(type);
@@ -1512,15 +1574,22 @@ function Input({ className, type, ...props }) {
1512
1574
  //#endregion
1513
1575
  //#region src/components/spinner.tsx
1514
1576
  /**
1515
- * Spinner — a ring with a primary-coloured leading edge. Announces itself as
1577
+ * Spinner — a ring with a coloured leading edge. Announces itself as
1516
1578
  * `role="status"` with a "Loading" label; pass `aria-label` to override.
1579
+ *
1580
+ * ```tsx
1581
+ * <Button disabled>
1582
+ * <Spinner tone="inverse" /> Saving…
1583
+ * </Button>
1584
+ * ```
1517
1585
  */
1518
- function Spinner({ className, size = 16, style, ...props }) {
1586
+ function Spinner({ className, size = 16, tone = "default", style, ...props }) {
1519
1587
  return /* @__PURE__ */ jsx("span", {
1520
1588
  "data-slot": "spinner",
1589
+ "data-tone": tone,
1521
1590
  role: "status",
1522
1591
  "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),
1592
+ 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
1593
  style: {
1525
1594
  width: size,
1526
1595
  height: size,
@@ -1713,21 +1782,6 @@ function DropdownMenuShortcut({ className, ...props }) {
1713
1782
  }
1714
1783
  //#endregion
1715
1784
  //#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
1785
  function Select(props) {
1732
1786
  return /* @__PURE__ */ jsx(Select$1.Root, { ...props });
1733
1787
  }
@@ -2808,7 +2862,7 @@ function DataTable(props) {
2808
2862
  return /* @__PURE__ */ jsx(DataTableOwned, { ...props });
2809
2863
  }
2810
2864
  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;
2865
+ 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
2866
  const instance = useDataTable({
2813
2867
  ...options,
2814
2868
  enableExpanding: options.enableExpanding ?? Boolean(renderSubComponent)
@@ -2833,7 +2887,9 @@ function DataTableOwned(props) {
2833
2887
  exportFileName,
2834
2888
  loading,
2835
2889
  error,
2836
- onRowClick
2890
+ onRowClick,
2891
+ activeRowId,
2892
+ rowClassName
2837
2893
  });
2838
2894
  }
2839
2895
  function pinAttrs(column, table) {
@@ -2911,7 +2967,7 @@ function EditableCell({ cell, instance, onDone }) {
2911
2967
  className: "h-7 px-2 py-0 text-[length:var(--ds-text-body-sm)]"
2912
2968
  });
2913
2969
  }
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 }) {
2970
+ 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
2971
  const { table, paginationMode } = instance;
2916
2972
  const rows = table.getRowModel().rows;
2917
2973
  const leafColumns = table.getVisibleLeafColumns();
@@ -3101,6 +3157,7 @@ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight =
3101
3157
  const endStart = cells.findIndex((cell) => cell.column.getIsPinned() === "end");
3102
3158
  const splitAt = endStart === -1 ? cells.length : endStart;
3103
3159
  const selected = row.getIsSelected();
3160
+ const active = activeRowId != null && row.id === activeRowId;
3104
3161
  const expanded = renderSubComponent && row.getIsExpanded();
3105
3162
  const renderCell = (cell, c) => {
3106
3163
  const column = cell.column;
@@ -3127,7 +3184,7 @@ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight =
3127
3184
  rowId: row.id,
3128
3185
  columnId: column.id
3129
3186
  }) : 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),
3187
+ 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
3188
  children: isEditing ? /* @__PURE__ */ jsx(EditableCell, {
3132
3189
  cell,
3133
3190
  instance,
@@ -3145,12 +3202,13 @@ function DataTableView({ instance, className, maxHeight, virtualize, rowHeight =
3145
3202
  "data-slot": "data-table-row",
3146
3203
  "data-index": virtualIndex,
3147
3204
  "data-selected": selected ? "" : void 0,
3205
+ "data-active": active ? "" : void 0,
3148
3206
  "data-expanded": expanded ? "" : void 0,
3149
3207
  "aria-rowindex": index + 2,
3150
3208
  "aria-selected": table.options.enableRowSelection ? selected : void 0,
3151
3209
  ref: isVirtual ? virtualizer.measureElement : void 0,
3152
3210
  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"),
3211
+ 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
3212
  children: [
3155
3213
  cells.slice(0, splitAt).map((cell, c) => renderCell(cell, c)),
3156
3214
  /* @__PURE__ */ jsx("td", {
@@ -4205,6 +4263,10 @@ function RadioGroupItem({ className, ...props }) {
4205
4263
  * SectionCard — a Card with a header row (title, description, actions), a
4206
4264
  * body and an optional footer. It composes the Card parts rather than
4207
4265
  * restyling them, so a SectionCard and a hand-built Card look identical.
4266
+ *
4267
+ * `render` is Card's and reaches the outer element, so a section card can be
4268
+ * the landmark it is named after: `render={<section aria-labelledby={id} />}`
4269
+ * with the same `id` on the title, or `render={<aside />}` for a side panel.
4208
4270
  */
4209
4271
  function SectionCard({ title, description, actions, footer, flush, bodyClassName, children, ...props }) {
4210
4272
  return /* @__PURE__ */ jsxs(Card, {
@@ -4263,6 +4325,7 @@ const TONE_CLASS = {
4263
4325
  accent: "bg-[var(--ds-primary)]",
4264
4326
  success: "bg-[var(--ds-success)]",
4265
4327
  warning: "bg-[var(--ds-warning)]",
4328
+ destructive: "bg-[var(--ds-destructive)]",
4266
4329
  danger: "bg-[var(--ds-destructive)]",
4267
4330
  info: "bg-[var(--ds-info)]",
4268
4331
  neutral: "bg-[var(--ds-text-tertiary)]"
@@ -6744,6 +6807,119 @@ function AvatarFallback({ className, ...props }) {
6744
6807
  });
6745
6808
  }
6746
6809
  //#endregion
6810
+ //#region src/components/breadcrumb.tsx
6811
+ /**
6812
+ * Breadcrumb — the trail above a page title.
6813
+ *
6814
+ * A `<nav aria-label="Breadcrumb">` around an ordered list, which is what
6815
+ * assistive technology expects: the list order is the hierarchy, so a screen
6816
+ * reader announces "list, 4 items" and reads the path in order. Separators are
6817
+ * decorative list items, not text inside the links, so a link's accessible
6818
+ * name is the crumb and nothing else.
6819
+ *
6820
+ * The last crumb is `BreadcrumbPage`, not a link: it is the page you are on,
6821
+ * so it carries `aria-current="page"` and is not focusable. Everything before
6822
+ * it is a `BreadcrumbLink`.
6823
+ *
6824
+ * ```tsx
6825
+ * <Breadcrumb>
6826
+ * <BreadcrumbList>
6827
+ * <BreadcrumbItem>
6828
+ * <BreadcrumbLink href="/">Home</BreadcrumbLink>
6829
+ * </BreadcrumbItem>
6830
+ * <BreadcrumbSeparator />
6831
+ * <BreadcrumbItem>
6832
+ * <BreadcrumbEllipsis />
6833
+ * </BreadcrumbItem>
6834
+ * <BreadcrumbSeparator />
6835
+ * <BreadcrumbItem>
6836
+ * <BreadcrumbPage>Invoice 4021</BreadcrumbPage>
6837
+ * </BreadcrumbItem>
6838
+ * </BreadcrumbList>
6839
+ * </Breadcrumb>
6840
+ * ```
6841
+ *
6842
+ * **Router links.** `BreadcrumbLink` renders an `<a>` by default. Pass
6843
+ * `render` with the router's link component and the class name, `href` and
6844
+ * handlers are merged onto it, the same contract as `ButtonLink`:
6845
+ * `render={<NextLink href="/projects" />}` or
6846
+ * `render={<Link to="/projects" />}`. This is deliberately not Base UI's
6847
+ * `render` — a breadcrumb is markup, not a Base UI primitive — but it takes
6848
+ * the same shape so there is one thing to remember.
6849
+ */
6850
+ function Breadcrumb({ className, ...props }) {
6851
+ return /* @__PURE__ */ jsx("nav", {
6852
+ "data-slot": "breadcrumb",
6853
+ "aria-label": "Breadcrumb",
6854
+ className: cn("min-w-0", className),
6855
+ ...props
6856
+ });
6857
+ }
6858
+ function BreadcrumbList({ className, ...props }) {
6859
+ return /* @__PURE__ */ jsx("ol", {
6860
+ "data-slot": "breadcrumb-list",
6861
+ 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),
6862
+ ...props
6863
+ });
6864
+ }
6865
+ function BreadcrumbItem({ className, ...props }) {
6866
+ return /* @__PURE__ */ jsx("li", {
6867
+ "data-slot": "breadcrumb-item",
6868
+ className: cn("inline-flex min-w-0 items-center gap-1.5", className),
6869
+ ...props
6870
+ });
6871
+ }
6872
+ function BreadcrumbLink({ className, render, children, ...props }) {
6873
+ return renderWith(render, {
6874
+ ...props,
6875
+ "data-slot": "breadcrumb-link",
6876
+ 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)
6877
+ }, children, "a");
6878
+ }
6879
+ /**
6880
+ * The current page: the last crumb, which is not a link.
6881
+ *
6882
+ * A plain `<span aria-current="page">`, deliberately not `role="link"` with
6883
+ * `aria-disabled` — a disabled link that cannot be focused is a fiction, and
6884
+ * it makes `getByRole("link")` return a crumb that navigates nowhere. With a
6885
+ * span, the roles in the trail are exactly the crumbs you can travel to.
6886
+ */
6887
+ function BreadcrumbPage({ className, ...props }) {
6888
+ return /* @__PURE__ */ jsx("span", {
6889
+ "data-slot": "breadcrumb-page",
6890
+ "aria-current": "page",
6891
+ className: cn("truncate font-medium text-[color:var(--ds-foreground)]", className),
6892
+ ...props
6893
+ });
6894
+ }
6895
+ /** Decorative divider between two crumbs; a chevron unless children replace it. */
6896
+ function BreadcrumbSeparator({ className, children, ...props }) {
6897
+ return /* @__PURE__ */ jsx("li", {
6898
+ "data-slot": "breadcrumb-separator",
6899
+ role: "presentation",
6900
+ "aria-hidden": "true",
6901
+ className: cn("inline-flex shrink-0 items-center text-[color:var(--ds-text-tertiary)] [&_svg]:size-3.5", className),
6902
+ ...props,
6903
+ children: children ?? /* @__PURE__ */ jsx(ChevronRight, {})
6904
+ });
6905
+ }
6906
+ /**
6907
+ * Stands in for the crumbs a long trail collapses; put it inside a
6908
+ * BreadcrumbItem. The glyph is hidden and the label is not: an ellipsis that
6909
+ * announces nothing leaves a screen-reader user with a gap in the path.
6910
+ */
6911
+ function BreadcrumbEllipsis({ className, children, ...props }) {
6912
+ return /* @__PURE__ */ jsxs("span", {
6913
+ "data-slot": "breadcrumb-ellipsis",
6914
+ className: cn("inline-flex size-5 items-center justify-center text-[color:var(--ds-text-tertiary)]", "[&_svg]:size-3.5", className),
6915
+ ...props,
6916
+ children: [/* @__PURE__ */ jsx(MoreHorizontal, { "aria-hidden": true }), /* @__PURE__ */ jsx("span", {
6917
+ className: "sr-only",
6918
+ children: children ?? "More levels"
6919
+ })]
6920
+ });
6921
+ }
6922
+ //#endregion
6747
6923
  //#region src/components/chip.tsx
6748
6924
  /**
6749
6925
  * Chip — a pill for filters, tags and selected values. Unlike Badge it keeps
@@ -7299,6 +7475,30 @@ function DateRangePicker(props) {
7299
7475
  * `onFiles` receives only the accepted files; rejections go to `onReject`.
7300
7476
  * `FileList` and `formatFileSize` are separate parts for listing what was
7301
7477
  * picked, so a form can keep the zone and the list in different places.
7478
+ *
7479
+ * ## Wrapping a whole page as a drop target
7480
+ *
7481
+ * `noClick` and `noKeyboard` turn off the two activators that only make sense
7482
+ * on a dashed box: clicking anywhere inside opens the file dialog, and Enter /
7483
+ * Space on the focused root does the same. A page-sized zone wraps content
7484
+ * that has its own buttons and inputs, so both have to go — otherwise every
7485
+ * click in the page opens a file picker.
7486
+ *
7487
+ * ```tsx
7488
+ * <Dropzone noClick noKeyboard onFiles={upload} className="min-h-dvh border-0 bg-transparent p-0">
7489
+ * {({ isDragActive, open }) => (
7490
+ * <>
7491
+ * <PageContent />
7492
+ * <Button onClick={open}>Browse…</Button>
7493
+ * {isDragActive ? <DropHint /> : null}
7494
+ * </>
7495
+ * )}
7496
+ * </Dropzone>
7497
+ * ```
7498
+ *
7499
+ * The render-prop form gets react-dropzone's `open()`, which is the way back
7500
+ * to the file dialog from a deliberate control. With `noClick` the root also
7501
+ * drops its `cursor-pointer`, since it is no longer clickable.
7302
7502
  */
7303
7503
  const UNITS = [
7304
7504
  "B",
@@ -7332,13 +7532,15 @@ function describeAccept(accept) {
7332
7532
  }
7333
7533
  return exts.size > 0 ? [...exts].join(", ") : void 0;
7334
7534
  }
7335
- function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFiles, disabled = false, label, hint, children, className, ref, ...props }) {
7535
+ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFiles, disabled = false, noClick = false, noKeyboard = false, label, hint, children, className, ref, ...props }) {
7336
7536
  const state = useDropzone({
7337
7537
  accept,
7338
7538
  multiple,
7339
7539
  maxSize,
7340
7540
  maxFiles,
7341
7541
  disabled,
7542
+ noClick,
7543
+ noKeyboard,
7342
7544
  onDropAccepted: (files) => onFiles(files),
7343
7545
  onDropRejected: (rejections) => onReject?.(rejections)
7344
7546
  });
@@ -7373,7 +7575,7 @@ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFile
7373
7575
  ...getRootProps({
7374
7576
  ...props,
7375
7577
  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)
7578
+ 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
7579
  }),
7378
7580
  "data-slot": "dropzone",
7379
7581
  "data-active": isDragActive || void 0,
@@ -7381,6 +7583,8 @@ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFile
7381
7583
  "data-reject": isDragReject || void 0,
7382
7584
  "data-focused": isFocused || void 0,
7383
7585
  "data-disabled": disabled || void 0,
7586
+ "data-no-click": noClick || void 0,
7587
+ "data-no-keyboard": noKeyboard || void 0,
7384
7588
  children: [/* @__PURE__ */ jsx("input", {
7385
7589
  "data-slot": "dropzone-input",
7386
7590
  ...getInputProps()
@@ -7760,9 +7964,35 @@ function Segmented({ className, value, onValueChange, options, "aria-label": ari
7760
7964
  * Description, Close); only the Popup's placement differs, chosen by `side`.
7761
7965
  * `SheetContent` composes Portal + Backdrop + Popup so consumers keep writing
7762
7966
  * `<SheetContent side="right">…</SheetContent>`.
7967
+ *
7968
+ * ## Non-modal side panels
7969
+ *
7970
+ * `modal={false}` is the help-panel / inspector case: a sheet read *alongside*
7971
+ * the app rather than instead of it. No backdrop is rendered, focus is not
7972
+ * trapped, page scroll is not locked, and the rest of the page stays clickable,
7973
+ * so a user can keep working with the panel open. `modal="trap-focus"` is the
7974
+ * middle setting Base UI offers: focus stays inside, the page still scrolls.
7975
+ *
7976
+ * ```tsx
7977
+ * <Sheet modal={false} open={helpOpen} onOpenChange={setHelpOpen}>
7978
+ * <SheetContent side="right" disablePointerDismissal>…</SheetContent>
7979
+ * </Sheet>
7980
+ * ```
7981
+ *
7982
+ * A non-modal sheet closes when focus or a press leaves it; pass
7983
+ * `disablePointerDismissal` (a Base UI Root prop, forwarded) to keep it open
7984
+ * until the user closes it. `modal` is on the Root because that is where Base
7985
+ * UI reads it; `SheetContent` picks the backdrop up from there through context.
7763
7986
  */
7764
- function Sheet(props) {
7765
- return /* @__PURE__ */ jsx(Dialog$1.Root, { ...props });
7987
+ const SheetModalContext = React.createContext(true);
7988
+ function Sheet({ modal = true, ...props }) {
7989
+ return /* @__PURE__ */ jsx(SheetModalContext.Provider, {
7990
+ value: modal,
7991
+ children: /* @__PURE__ */ jsx(Dialog$1.Root, {
7992
+ modal,
7993
+ ...props
7994
+ })
7995
+ });
7766
7996
  }
7767
7997
  function SheetTrigger(props) {
7768
7998
  return /* @__PURE__ */ jsx(Dialog$1.Trigger, {
@@ -7804,12 +8034,14 @@ const sheetVariants = cva([
7804
8034
  defaultVariants: { side: "right" }
7805
8035
  });
7806
8036
  function SheetContent({ side = "right", className, children, keepMounted, container, ...props }) {
8037
+ const modal = React.useContext(SheetModalContext);
7807
8038
  return /* @__PURE__ */ jsxs(SheetPortal, {
7808
8039
  keepMounted,
7809
8040
  container,
7810
- children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsx(Dialog$1.Popup, {
8041
+ children: [modal === false ? null : /* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsx(Dialog$1.Popup, {
7811
8042
  "data-slot": "sheet-content",
7812
8043
  "data-side": side,
8044
+ "data-modal": modal === false ? void 0 : modal === true ? "" : modal,
7813
8045
  className: cn(sheetVariants({ side }), className),
7814
8046
  ...props,
7815
8047
  children
@@ -7846,6 +8078,40 @@ function SheetDescription({ className, ...props }) {
7846
8078
  }
7847
8079
  //#endregion
7848
8080
  //#region src/components/sortable-list.tsx
8081
+ /**
8082
+ * SortableList.
8083
+ *
8084
+ * A reorderable list over dnd-kit: `DndContext` + `SortableContext` around one
8085
+ * `useSortable` item per entry. The list owns nothing — it calls `onReorder`
8086
+ * with the next array and the consumer stores it. Pointer and keyboard sensors
8087
+ * are wired (Space picks up, arrows move, Space drops, Escape cancels) and a
8088
+ * `DragOverlay` paints the lifted copy with `--ds-shadow-lg` while the item
8089
+ * left behind dims and takes the ring colour.
8090
+ *
8091
+ * By default the whole row is the drag activator. Pass `handle` to restrict
8092
+ * dragging to a `<SortableHandle>` (or any element given `handleProps`) so
8093
+ * buttons and inputs inside a row stay clickable.
8094
+ *
8095
+ * ## Touch
8096
+ *
8097
+ * A pointer drag and a touch scroll start the same way, so something has to
8098
+ * say which one a finger means. In handle mode only the small handle is
8099
+ * `touch-none`, so the rest of the row scrolls: nothing to decide. In
8100
+ * whole-row mode the whole row is `touch-none`, which is why a touch list
8101
+ * whose rows are the activators cannot be scrolled by dragging over them.
8102
+ *
8103
+ * `touchScroll` is the way out, and it defaults to whichever of those is
8104
+ * already true — `true` in handle mode, `false` in whole-row mode, so existing
8105
+ * lists behave exactly as before. Setting it `true` on a whole-row list drops
8106
+ * `touch-none` and swaps the PointerSensor for a MouseSensor plus a
8107
+ * TouchSensor with delay activation: a quick swipe scrolls, a press held for
8108
+ * {@link TOUCH_DELAY_MS} picks the row up. `false` in handle mode is the
8109
+ * opposite trade — the row never scrolls under a finger.
8110
+ */
8111
+ /** How long a finger must rest on a row before it becomes a drag. */
8112
+ const TOUCH_DELAY_MS = 250;
8113
+ /** How far it may slip in that time and still count as a press, not a scroll. */
8114
+ const TOUCH_TOLERANCE_PX = 8;
7849
8115
  /** dnd-kit types its listeners as a bag of `Function`s; narrow them to the handlers they are. */
7850
8116
  function asHandlers(listeners) {
7851
8117
  return listeners ?? {};
@@ -7858,7 +8124,7 @@ const SortableItemContext = React.createContext({
7858
8124
  disabled: false
7859
8125
  });
7860
8126
  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 }) {
8127
+ function SortableItem({ id, item, handle, disabled, touchScroll, renderItem, className }) {
7862
8128
  const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
7863
8129
  id,
7864
8130
  disabled
@@ -7896,7 +8162,7 @@ function SortableItem({ id, item, handle, disabled, renderItem, className }) {
7896
8162
  transform: CSS.Translate.toString(transform),
7897
8163
  transition
7898
8164
  },
7899
- className: cn(itemClassName, !handle && !disabled && "cursor-grab touch-none data-[dragging]:cursor-grabbing", className),
8165
+ className: cn(itemClassName, !handle && !disabled && "cursor-grab data-[dragging]:cursor-grabbing", !touchScroll && !disabled && "touch-none", className),
7900
8166
  ...rowActivator,
7901
8167
  children: renderItem(item, {
7902
8168
  handleProps,
@@ -7923,9 +8189,19 @@ function SortableHandle({ className, children, ...props }) {
7923
8189
  children: children ?? /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": true })
7924
8190
  });
7925
8191
  }
7926
- function SortableList({ items, getId, onReorder, renderItem, orientation = "vertical", handle = false, disabled = false, itemClassName: itemClass, className, ...props }) {
8192
+ function SortableList({ items, getId, onReorder, renderItem, orientation = "vertical", handle = false, disabled = false, touchScroll = handle, itemClassName: itemClass, className, ...props }) {
7927
8193
  const [activeId, setActiveId] = React.useState(null);
7928
- const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 4 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
8194
+ const delayTouch = touchScroll && !handle;
8195
+ const pointerSensor = useSensor(PointerSensor, { activationConstraint: { distance: 4 } });
8196
+ const mouseSensor = useSensor(MouseSensor, { activationConstraint: { distance: 4 } });
8197
+ const touchSensor = useSensor(TouchSensor, { activationConstraint: {
8198
+ delay: TOUCH_DELAY_MS,
8199
+ tolerance: TOUCH_TOLERANCE_PX
8200
+ } });
8201
+ const keyboardSensor = useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates });
8202
+ const pointerSensors = useSensors(pointerSensor, keyboardSensor);
8203
+ const splitSensors = useSensors(mouseSensor, touchSensor, keyboardSensor);
8204
+ const sensors = delayTouch ? splitSensors : pointerSensors;
7929
8205
  const ids = React.useMemo(() => items.map(getId), [items, getId]);
7930
8206
  const activeItem = activeId === null ? void 0 : items[ids.indexOf(activeId)];
7931
8207
  const onDragStart = React.useCallback((event) => {
@@ -7938,7 +8214,12 @@ function SortableList({ items, getId, onReorder, renderItem, orientation = "vert
7938
8214
  const from = ids.indexOf(active.id);
7939
8215
  const to = ids.indexOf(over.id);
7940
8216
  if (from < 0 || to < 0) return;
7941
- onReorder(arrayMove([...items], from, to));
8217
+ onReorder(arrayMove([...items], from, to), {
8218
+ from,
8219
+ to,
8220
+ activeId: active.id,
8221
+ overId: over.id
8222
+ });
7942
8223
  }, [
7943
8224
  ids,
7944
8225
  items,
@@ -7959,6 +8240,7 @@ function SortableList({ items, getId, onReorder, renderItem, orientation = "vert
7959
8240
  "data-slot": "sortable-list",
7960
8241
  "data-orientation": orientation,
7961
8242
  "data-disabled": disabled || void 0,
8243
+ "data-touch-scroll": touchScroll ? "" : void 0,
7962
8244
  className: cn("flex list-none gap-2 p-0", orientation === "horizontal" ? "flex-row flex-wrap" : "flex-col", className),
7963
8245
  ...props,
7964
8246
  children: items.map((item, index) => /* @__PURE__ */ jsx(SortableItem, {
@@ -7966,6 +8248,7 @@ function SortableList({ items, getId, onReorder, renderItem, orientation = "vert
7966
8248
  item,
7967
8249
  handle,
7968
8250
  disabled,
8251
+ touchScroll,
7969
8252
  renderItem,
7970
8253
  className: itemClass
7971
8254
  }, ids[index] ?? index))
@@ -8312,6 +8595,6 @@ function useCortenaTheme(options = {}) {
8312
8595
  };
8313
8596
  }
8314
8597
  //#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 };
8598
+ 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
8599
 
8317
8600
  //# sourceMappingURL=index.js.map