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/README.md +34 -0
- package/dist/index.d.ts +249 -54
- package/dist/index.js +347 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/alert.tsx +9 -2
- package/src/components/badge.tsx +9 -4
- package/src/components/breadcrumb.tsx +171 -0
- package/src/components/button-link.tsx +3 -10
- package/src/components/card.tsx +27 -9
- package/src/components/chart/chart.tsx +13 -2
- package/src/components/chart/container.tsx +33 -2
- package/src/components/chart/data.ts +25 -1
- package/src/components/chart/index.tsx +1 -0
- package/src/components/chart/nivo-charts.tsx +4 -3
- package/src/components/chart/types.ts +19 -2
- package/src/components/data-table/data-table.tsx +23 -1
- package/src/components/dropzone.tsx +35 -1
- package/src/components/section-card.tsx +4 -0
- package/src/components/select.tsx +30 -1
- package/src/components/sheet.tsx +40 -4
- package/src/components/sortable-list.tsx +77 -9
- package/src/components/spinner.tsx +21 -4
- package/src/components/status-dot.tsx +14 -1
- package/src/index.ts +5 -3
- package/src/lib/render.tsx +39 -0
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
|
-
|
|
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
|
|
110
|
-
*
|
|
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
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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
|
-
|
|
230
|
-
|
|
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
|
|
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-
|
|
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
|
-
|
|
7765
|
-
|
|
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
|
|
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
|
|
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
|