@nexub/react 7.5.0 → 7.7.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/accessibility.json +1 -1
- package/dist/components/Badge/Badge.d.ts +9 -0
- package/dist/components/Badge/Badge.js +40 -2
- package/dist/components/Button/Button.d.ts +1 -1
- package/dist/components/CommandPalette/CommandPalette.d.ts +34 -13
- package/dist/components/CommandPalette/CommandPalette.js +174 -40
- package/dist/components/CommandPalette/index.d.ts +1 -1
- package/dist/components/DataTable/DataTable.d.ts +16 -2
- package/dist/components/DataTable/DataTable.js +131 -76
- package/dist/components/DataTable/ViewOptions.d.ts +15 -0
- package/dist/components/DataTable/ViewOptions.js +33 -0
- package/dist/components/DataTableVirtual/DataTableVirtual.d.ts +1 -1
- package/dist/components/Dialog/Dialog.d.ts +7 -0
- package/dist/components/Dialog/Dialog.js +35 -8
- package/dist/components/EmptyState/EmptyState.d.ts +7 -1
- package/dist/components/EmptyState/EmptyState.js +21 -5
- package/dist/components/EmptyState/EmptyStatePreset.d.ts +2 -1
- package/dist/components/FilterBuilder/FilterBuilder.js +33 -15
- package/dist/components/Heading/Heading.d.ts +1 -1
- package/dist/components/IconButton/IconButton.d.ts +5 -1
- package/dist/components/IconButton/IconButton.js +14 -3
- package/dist/components/Kanban/Kanban.d.ts +1 -1
- package/dist/components/Sortable/Sortable.d.ts +2 -2
- package/dist/components/Sortable/reorder.d.ts +10 -11
- package/dist/components/Sortable/reorder.js +185 -27
- package/dist/components/Tabs/Tabs.d.ts +5 -1
- package/dist/components/Tabs/Tabs.js +98 -35
- package/dist/components/Text/Text.d.ts +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +10 -3
- package/dist/components/Tooltip/Tooltip.js +16 -4
- package/dist/index.d.ts +2 -0
- package/dist/styles.css +1 -1
- package/package.json +8 -7
|
@@ -20,7 +20,6 @@ import { useLocale, useNexubMessages } from "../NexubProvider/NexubProvider.js";
|
|
|
20
20
|
import { Table } from "../Table/Table.js";
|
|
21
21
|
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
22
22
|
import { Button } from "../Button/Button.js";
|
|
23
|
-
import { DropdownMenu } from "../DropdownMenu/DropdownMenu.js";
|
|
24
23
|
import { createGridNavigation } from "./gridNavigation.js";
|
|
25
24
|
import { useReorder } from "../Sortable/reorder.js";
|
|
26
25
|
import { Skeleton } from "../Skeleton/Skeleton.js";
|
|
@@ -134,6 +133,67 @@ function TreeCell({
|
|
|
134
133
|
] });
|
|
135
134
|
}
|
|
136
135
|
const px = (n) => typeof n === "number" ? `${n}px` : n;
|
|
136
|
+
const ViewOptions = React.lazy(() => import("./ViewOptions.js").then((m) => ({ default: m.ViewOptions })));
|
|
137
|
+
const placeCell = (column, resizable) => (el) => {
|
|
138
|
+
if (!el) return;
|
|
139
|
+
el.style.width = resizable ? px(column.getSize()) : "";
|
|
140
|
+
const side = column.getIsPinned();
|
|
141
|
+
el.style.insetInlineStart = side === "left" ? px(column.getStart("left")) : "";
|
|
142
|
+
el.style.insetInlineEnd = side === "right" ? px(column.getAfter("right")) : "";
|
|
143
|
+
};
|
|
144
|
+
const pinClass = (column) => {
|
|
145
|
+
const side = column.getIsPinned();
|
|
146
|
+
if (!side) return void 0;
|
|
147
|
+
return cn(
|
|
148
|
+
"sticky z-[1] bg-[var(--color-surface)]",
|
|
149
|
+
"[tr:hover>&]:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,var(--color-surface))]",
|
|
150
|
+
"[tr[data-state=selected]>&]:bg-[color-mix(in_srgb,var(--color-primary)_10%,var(--color-surface))]",
|
|
151
|
+
side === "left" && column.getIsLastColumn("left") && "border-e border-[var(--color-outline-variant)]",
|
|
152
|
+
side === "right" && column.getIsFirstColumn("right") && "border-s border-[var(--color-outline-variant)]"
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
function RowViewImpl({
|
|
156
|
+
row,
|
|
157
|
+
index,
|
|
158
|
+
selected,
|
|
159
|
+
compact,
|
|
160
|
+
pinned,
|
|
161
|
+
resizable,
|
|
162
|
+
selectable,
|
|
163
|
+
tree,
|
|
164
|
+
virtual,
|
|
165
|
+
measure,
|
|
166
|
+
expandLabel,
|
|
167
|
+
collapseLabel,
|
|
168
|
+
dragProps
|
|
169
|
+
}) {
|
|
170
|
+
return /* @__PURE__ */ jsx(
|
|
171
|
+
TableRow,
|
|
172
|
+
{
|
|
173
|
+
ref: measure,
|
|
174
|
+
"data-index": index,
|
|
175
|
+
"aria-rowindex": virtual ? index + 2 : void 0,
|
|
176
|
+
"data-state": selectable && selected ? "selected" : void 0,
|
|
177
|
+
...dragProps,
|
|
178
|
+
children: row.getVisibleCells().map((cell, _i, cells) => /* @__PURE__ */ jsx(
|
|
179
|
+
TableCell,
|
|
180
|
+
{
|
|
181
|
+
ref: pinned || resizable ? placeCell(cell.column, resizable) : void 0,
|
|
182
|
+
className: cn(
|
|
183
|
+
alignText[cell.column.columnDef.meta?.align ?? "start"],
|
|
184
|
+
compact && "py-1.5",
|
|
185
|
+
pinClass(cell.column),
|
|
186
|
+
resizable && "truncate"
|
|
187
|
+
),
|
|
188
|
+
children: tree && cell === cells.find((c) => c.column.id !== "__select") ? /* @__PURE__ */ jsx(TreeCell, { row, expandLabel, collapseLabel, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }) : flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
189
|
+
},
|
|
190
|
+
cell.id
|
|
191
|
+
))
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const sameRow = (a, b) => Object.keys(a).every((k) => k === "row" || a[k] === b[k]) && a.row.id === b.row.id && a.row.original === b.row.original && a.row.index === b.row.index && a.row.depth === b.row.depth;
|
|
196
|
+
const RowView = React.memo(RowViewImpl, sameRow);
|
|
137
197
|
const apply = (u, prev) => typeof u === "function" ? u(prev) : u;
|
|
138
198
|
const columnLabel = (c) => typeof c.columnDef.header === "string" ? c.columnDef.header : c.id;
|
|
139
199
|
function DataTable({
|
|
@@ -164,7 +224,15 @@ function DataTable({
|
|
|
164
224
|
maxHeight,
|
|
165
225
|
grid = false,
|
|
166
226
|
rowWindow,
|
|
167
|
-
pagination,
|
|
227
|
+
pagination: paginationProp,
|
|
228
|
+
sort,
|
|
229
|
+
onSortChange,
|
|
230
|
+
manualSort = false,
|
|
231
|
+
page,
|
|
232
|
+
pageSize: pageSizeProp,
|
|
233
|
+
onPageChange,
|
|
234
|
+
manualPagination = false,
|
|
235
|
+
rowCount,
|
|
168
236
|
getSubRows,
|
|
169
237
|
expanded: expandedProp,
|
|
170
238
|
defaultExpanded,
|
|
@@ -182,7 +250,11 @@ function DataTable({
|
|
|
182
250
|
}) {
|
|
183
251
|
const messages = useNexubMessages();
|
|
184
252
|
const { locale, dir } = useLocale();
|
|
185
|
-
const [sorting, setSorting] =
|
|
253
|
+
const [sorting = [], setSorting] = useControllableState({
|
|
254
|
+
value: sort,
|
|
255
|
+
defaultValue: [],
|
|
256
|
+
onChange: onSortChange
|
|
257
|
+
});
|
|
186
258
|
const [rowSelection = {}, setRowSelection] = useControllableState({
|
|
187
259
|
value: rowSelectionProp,
|
|
188
260
|
defaultValue: {},
|
|
@@ -222,10 +294,14 @@ function DataTable({
|
|
|
222
294
|
const tableRef = React.useRef(null);
|
|
223
295
|
const gridNav = React.useMemo(() => createGridNavigation(dir), [dir]);
|
|
224
296
|
const dragged = React.useRef(null);
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
297
|
+
const pagination = !!paginationProp || page !== void 0 || pageSizeProp !== void 0 || manualPagination;
|
|
298
|
+
const [pageIndex = 0, setPageIndex] = useControllableState({
|
|
299
|
+
value: page,
|
|
300
|
+
defaultValue: 0,
|
|
301
|
+
onChange: onPageChange
|
|
228
302
|
});
|
|
303
|
+
const pageSize = pageSizeProp ?? paginationProp?.pageSize ?? 20;
|
|
304
|
+
const paginationState = React.useMemo(() => ({ pageIndex, pageSize }), [pageIndex, pageSize]);
|
|
229
305
|
const withSelectColumn = enableRowSelection && selectionColumn;
|
|
230
306
|
const reorderRows = Boolean(onRowOrderChange) && sorting.length === 0;
|
|
231
307
|
const rowReorder = useReorder({
|
|
@@ -312,7 +388,8 @@ function DataTable({
|
|
|
312
388
|
columns: allColumns,
|
|
313
389
|
state: tableState,
|
|
314
390
|
getRowId,
|
|
315
|
-
onSortingChange: enableSorting ? setSorting : void 0,
|
|
391
|
+
onSortingChange: enableSorting ? (u) => setSorting(apply(u, sorting)) : void 0,
|
|
392
|
+
manualSorting: manualSort,
|
|
316
393
|
onRowSelectionChange: enableRowSelection ? (u) => setRowSelection(apply(u, rowSelection)) : void 0,
|
|
317
394
|
onColumnVisibilityChange: (u) => setColumnVisibility(apply(u, columnVisibility)),
|
|
318
395
|
onColumnSizingChange: (u) => setColumnSizing(apply(u, columnSizing)),
|
|
@@ -321,11 +398,15 @@ function DataTable({
|
|
|
321
398
|
enableColumnResizing,
|
|
322
399
|
columnResizeMode: "onChange",
|
|
323
400
|
columnResizeDirection: dir,
|
|
324
|
-
onPaginationChange: pagination ?
|
|
401
|
+
onPaginationChange: pagination ? (u) => {
|
|
402
|
+
const next = apply(u, paginationState).pageIndex;
|
|
403
|
+
if (next !== pageIndex) setPageIndex(next);
|
|
404
|
+
} : void 0,
|
|
325
405
|
getCoreRowModel: getCoreRowModel(),
|
|
326
|
-
getSortedRowModel: enableSorting ? getSortedRowModel() : void 0,
|
|
406
|
+
getSortedRowModel: enableSorting && !manualSort ? getSortedRowModel() : void 0,
|
|
327
407
|
getFilteredRowModel: getFilteredRowModel(),
|
|
328
|
-
getPaginationRowModel: pagination ? getPaginationRowModel() : void 0,
|
|
408
|
+
getPaginationRowModel: pagination && !manualPagination ? getPaginationRowModel() : void 0,
|
|
409
|
+
rowCount,
|
|
329
410
|
getSubRows,
|
|
330
411
|
getExpandedRowModel: getSubRows ? getExpandedRowModel() : void 0,
|
|
331
412
|
onExpandedChange: getSubRows ? (u) => setExpanded(apply(u, expanded)) : void 0,
|
|
@@ -333,7 +414,7 @@ function DataTable({
|
|
|
333
414
|
// (TanStack then expands inside the page model; without pages it must not).
|
|
334
415
|
paginateExpandedRows: !pagination,
|
|
335
416
|
enableRowSelection,
|
|
336
|
-
manualPagination
|
|
417
|
+
manualPagination
|
|
337
418
|
});
|
|
338
419
|
const compact = density === "compact";
|
|
339
420
|
const selectedRows = enableRowSelection ? table.getSelectedRowModel().rows : [];
|
|
@@ -354,24 +435,11 @@ function DataTable({
|
|
|
354
435
|
if (scroller) scroller.style.maxHeight = maxHeight == null ? "" : px(maxHeight);
|
|
355
436
|
if (grid) gridNav.sync(t);
|
|
356
437
|
});
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
el.style.insetInlineEnd = side === "right" ? px(column.getAfter("right")) : "";
|
|
363
|
-
};
|
|
364
|
-
const pinClass = (column) => {
|
|
365
|
-
const side = column.getIsPinned();
|
|
366
|
-
if (!side) return void 0;
|
|
367
|
-
return cn(
|
|
368
|
-
"sticky z-[1] bg-[var(--color-surface)]",
|
|
369
|
-
"[tr:hover>&]:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,var(--color-surface))]",
|
|
370
|
-
"[tr[data-state=selected]>&]:bg-[color-mix(in_srgb,var(--color-primary)_10%,var(--color-surface))]",
|
|
371
|
-
side === "left" && column.getIsLastColumn("left") && "border-e border-[var(--color-outline-variant)]",
|
|
372
|
-
side === "right" && column.getIsFirstColumn("right") && "border-s border-[var(--color-outline-variant)]"
|
|
373
|
-
);
|
|
374
|
-
};
|
|
438
|
+
const visibleColumns = table.getVisibleLeafColumns();
|
|
439
|
+
const viewButton = /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "ms-auto", children: [
|
|
440
|
+
/* @__PURE__ */ jsx(SlidersHorizontal, { size: "sm", "aria-hidden": "true" }),
|
|
441
|
+
messages.view
|
|
442
|
+
] });
|
|
375
443
|
const moveColumn = (id, to) => {
|
|
376
444
|
const ids = table.getAllLeafColumns().map((c) => c.id);
|
|
377
445
|
const order = (columnOrder.length ? columnOrder : ids).filter((c) => c !== "__select" && ids.includes(c));
|
|
@@ -390,31 +458,21 @@ function DataTable({
|
|
|
390
458
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), "data-density": density, ...props, children: [
|
|
391
459
|
(toolbar != null || viewOptions) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
392
460
|
toolbar,
|
|
393
|
-
viewOptions &&
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
{
|
|
409
|
-
checked: c.getIsVisible(),
|
|
410
|
-
onCheckedChange: (v) => c.toggleVisibility(v === true),
|
|
411
|
-
onSelect: (e) => e.preventDefault(),
|
|
412
|
-
children: columnLabel(c)
|
|
413
|
-
},
|
|
414
|
-
c.id
|
|
415
|
-
))
|
|
416
|
-
] })
|
|
417
|
-
] })
|
|
461
|
+
viewOptions && // The plain button stands in (same size, no layout shift) until the menu loads.
|
|
462
|
+
/* @__PURE__ */ jsx(React.Suspense, { fallback: viewButton, children: /* @__PURE__ */ jsx(
|
|
463
|
+
ViewOptions,
|
|
464
|
+
{
|
|
465
|
+
trigger: viewButton,
|
|
466
|
+
density,
|
|
467
|
+
onDensityChange: setDensity,
|
|
468
|
+
columns: table.getAllLeafColumns().filter((c) => c.getCanHide()).map((c) => ({
|
|
469
|
+
id: c.id,
|
|
470
|
+
label: columnLabel(c),
|
|
471
|
+
visible: c.getIsVisible(),
|
|
472
|
+
toggle: (v) => c.toggleVisibility(v)
|
|
473
|
+
}))
|
|
474
|
+
}
|
|
475
|
+
) })
|
|
418
476
|
] }),
|
|
419
477
|
offerAll && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-shape-container bg-[var(--color-surface-container-low)] px-4 py-2 text-body-sm", children: table.getIsAllRowsSelected() ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
420
478
|
/* @__PURE__ */ jsx("span", { children: messages.selectedCount(fmt(totalRows)) }),
|
|
@@ -447,7 +505,7 @@ function DataTable({
|
|
|
447
505
|
return /* @__PURE__ */ jsxs(
|
|
448
506
|
TableHead,
|
|
449
507
|
{
|
|
450
|
-
ref: placeCell(col),
|
|
508
|
+
ref: placeCell(col, enableColumnResizing),
|
|
451
509
|
className: cn(
|
|
452
510
|
alignText[align],
|
|
453
511
|
compact && "h-8",
|
|
@@ -532,32 +590,29 @@ function DataTable({
|
|
|
532
590
|
header.id
|
|
533
591
|
);
|
|
534
592
|
}) }, headerGroup.id)) }),
|
|
535
|
-
/* @__PURE__ */ jsx(TableBody, { children: loading ? Array.from({ length: pagination
|
|
593
|
+
/* @__PURE__ */ jsx(TableBody, { children: loading ? Array.from({ length: pagination ? pageSize : 5 }, (_, i) => /* @__PURE__ */ jsx(TableRow, { "data-loading": "", className: "hover:bg-transparent", children: table.getVisibleLeafColumns().map((c) => /* @__PURE__ */ jsx(TableCell, { className: cn(compact && "py-1.5"), children: /* @__PURE__ */ jsx(Skeleton, {}) }, c.id)) }, i)) : rows.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
536
594
|
rowWin.before > 0 && /* @__PURE__ */ jsx(Spacer, { height: rowWin.before }),
|
|
537
595
|
rowWin.indexes.map((i) => {
|
|
538
596
|
const row = rows[i];
|
|
539
597
|
return /* @__PURE__ */ jsx(
|
|
540
|
-
|
|
598
|
+
RowView,
|
|
541
599
|
{
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
},
|
|
559
|
-
cell.id
|
|
560
|
-
))
|
|
600
|
+
row,
|
|
601
|
+
index: i,
|
|
602
|
+
selected: enableRowSelection && row.getIsSelected(),
|
|
603
|
+
expanded: !!getSubRows && row.getIsExpanded(),
|
|
604
|
+
columns: visibleColumns,
|
|
605
|
+
columnSizing,
|
|
606
|
+
compact,
|
|
607
|
+
pinned,
|
|
608
|
+
resizable: enableColumnResizing,
|
|
609
|
+
selectable: enableRowSelection,
|
|
610
|
+
tree: !!getSubRows,
|
|
611
|
+
virtual: rowWin.virtual,
|
|
612
|
+
measure: rowWin.measure,
|
|
613
|
+
expandLabel: messages.expandRow,
|
|
614
|
+
collapseLabel: messages.collapseRow,
|
|
615
|
+
dragProps: reorderRows && row.depth === 0 ? rowReorder.itemProps([0, row.index]) : void 0
|
|
561
616
|
},
|
|
562
617
|
row.id
|
|
563
618
|
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { DataTableDensity } from './DataTable.js';
|
|
3
|
+
export interface ViewOptionsProps {
|
|
4
|
+
trigger: React.ReactElement;
|
|
5
|
+
density: DataTableDensity;
|
|
6
|
+
onDensityChange: (density: DataTableDensity) => void;
|
|
7
|
+
/** Hideable columns. */
|
|
8
|
+
columns: {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
visible: boolean;
|
|
12
|
+
toggle: (visible: boolean) => void;
|
|
13
|
+
}[];
|
|
14
|
+
}
|
|
15
|
+
export declare function ViewOptions({ trigger, density, onDensityChange, columns }: ViewOptionsProps): React.JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useNexubMessages } from "../NexubProvider/NexubProvider.js";
|
|
5
|
+
import { DropdownMenu } from "../DropdownMenu/DropdownMenu.js";
|
|
6
|
+
function ViewOptions({ trigger, density, onDensityChange, columns }) {
|
|
7
|
+
const messages = useNexubMessages();
|
|
8
|
+
return /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
|
|
9
|
+
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: trigger }),
|
|
10
|
+
/* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", children: [
|
|
11
|
+
/* @__PURE__ */ jsx(DropdownMenu.Label, { children: messages.density }),
|
|
12
|
+
/* @__PURE__ */ jsxs(DropdownMenu.RadioGroup, { value: density, onValueChange: (v) => onDensityChange(v), children: [
|
|
13
|
+
/* @__PURE__ */ jsx(DropdownMenu.RadioItem, { value: "comfortable", children: messages.comfortable }),
|
|
14
|
+
/* @__PURE__ */ jsx(DropdownMenu.RadioItem, { value: "compact", children: messages.compact })
|
|
15
|
+
] }),
|
|
16
|
+
/* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
|
|
17
|
+
/* @__PURE__ */ jsx(DropdownMenu.Label, { children: messages.columns }),
|
|
18
|
+
columns.map((c) => /* @__PURE__ */ jsx(
|
|
19
|
+
DropdownMenu.CheckboxItem,
|
|
20
|
+
{
|
|
21
|
+
checked: c.visible,
|
|
22
|
+
onCheckedChange: (v) => c.toggle(v === true),
|
|
23
|
+
onSelect: (e) => e.preventDefault(),
|
|
24
|
+
children: c.label
|
|
25
|
+
},
|
|
26
|
+
c.id
|
|
27
|
+
))
|
|
28
|
+
] })
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
ViewOptions
|
|
33
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { type DataTableProps } from '../DataTable/DataTable.js';
|
|
3
|
-
export interface DataTableVirtualProps<TData> extends Omit<DataTableProps<TData>, 'pagination' | 'rowWindow'> {
|
|
3
|
+
export interface DataTableVirtualProps<TData> extends Omit<DataTableProps<TData>, 'pagination' | 'rowWindow' | 'page' | 'pageSize' | 'onPageChange' | 'manualPagination' | 'rowCount'> {
|
|
4
4
|
/** Height of the scrolling area. Required: rows are windowed within it. */
|
|
5
5
|
maxHeight: number | string;
|
|
6
6
|
/** Row height before measuring, in px. Default 49, or 37 when compact. */
|
|
@@ -15,6 +15,12 @@ declare function Root({ scrollLock, modal, ...props }: DialogRootProps): React.J
|
|
|
15
15
|
interface ContentProps extends RadixDialog.DialogContentProps {
|
|
16
16
|
size?: Size | 'xl' | 'full';
|
|
17
17
|
surface?: SurfaceValue;
|
|
18
|
+
/** Adds a close button in the top-end corner. @default false */
|
|
19
|
+
showClose?: boolean;
|
|
20
|
+
/** Accessible name of the close button. @default messages.close */
|
|
21
|
+
closeLabel?: string;
|
|
22
|
+
/** `true`: bottom sheet. `'auto'`: bottom sheet below the `md` breakpoint, centred above. @default false */
|
|
23
|
+
sheet?: boolean | 'auto';
|
|
18
24
|
}
|
|
19
25
|
export declare const Dialog: {
|
|
20
26
|
Root: typeof Root;
|
|
@@ -26,6 +32,7 @@ export declare const Dialog: {
|
|
|
26
32
|
Title: React.ForwardRefExoticComponent<RadixDialog.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>>;
|
|
27
33
|
Description: React.ForwardRefExoticComponent<RadixDialog.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>>;
|
|
28
34
|
Header: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
35
|
+
Body: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
29
36
|
Footer: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
30
37
|
};
|
|
31
38
|
export {};
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import * as RadixDialog from "@radix-ui/react-dialog";
|
|
5
5
|
import { cn } from "../../utils/cn.js";
|
|
6
6
|
import { overlaySurface, scrim } from "../../utils/surface.js";
|
|
7
7
|
import { glassSurfaceClass } from "../../utils/glass.js";
|
|
8
8
|
import { ClassLockOverlay, ScrollLockContext } from "../../hooks/useClassScrollLock.js";
|
|
9
|
+
import { useMediaQuery } from "../../hooks/useMediaQuery.js";
|
|
10
|
+
import { X } from "@nexub/icons";
|
|
11
|
+
import { IconButton } from "../IconButton/IconButton.js";
|
|
12
|
+
import { useNexubMessages } from "../NexubProvider/NexubProvider.js";
|
|
9
13
|
function Root({ scrollLock = "style", modal = true, ...props }) {
|
|
10
14
|
const lock = React.useMemo(() => ({ scrollLock, modal }), [scrollLock, modal]);
|
|
11
15
|
return /* @__PURE__ */ jsx(ScrollLockContext.Provider, { value: lock, children: /* @__PURE__ */ jsx(RadixDialog.Root, { modal, ...props }) });
|
|
@@ -33,8 +37,19 @@ const Overlay = React.forwardRef(
|
|
|
33
37
|
}
|
|
34
38
|
);
|
|
35
39
|
Overlay.displayName = "Dialog.Overlay";
|
|
40
|
+
const StackContext = React.createContext(null);
|
|
36
41
|
const Content = React.forwardRef(
|
|
37
|
-
function DialogContent({ className, size = "md", surface = "solid", children, ...props }, ref) {
|
|
42
|
+
function DialogContent({ className, size = "md", surface = "solid", showClose = false, closeLabel, sheet = false, children, ...props }, ref) {
|
|
43
|
+
const messages = useNexubMessages();
|
|
44
|
+
const narrow = useMediaQuery("(max-width: 47.99rem)");
|
|
45
|
+
const asSheet = sheet === true || sheet === "auto" && narrow;
|
|
46
|
+
const parent = React.useContext(StackContext);
|
|
47
|
+
const [openChildren, setOpenChildren] = React.useState(0);
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
if (!parent) return;
|
|
50
|
+
parent((n) => n + 1);
|
|
51
|
+
return () => parent((n) => n - 1);
|
|
52
|
+
}, [parent]);
|
|
38
53
|
const sizeMap = {
|
|
39
54
|
sm: "max-w-sm",
|
|
40
55
|
md: "max-w-lg",
|
|
@@ -42,23 +57,28 @@ const Content = React.forwardRef(
|
|
|
42
57
|
xl: "max-w-4xl",
|
|
43
58
|
full: "max-w-[calc(100vw-2rem)]"
|
|
44
59
|
};
|
|
45
|
-
return /* @__PURE__ */
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
46
61
|
RadixDialog.Content,
|
|
47
62
|
{
|
|
48
63
|
ref,
|
|
49
64
|
className: cn(
|
|
50
|
-
"fixed left-1/2 top-1/2 z-[var(--z-modal)] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2",
|
|
65
|
+
asSheet ? "fixed inset-x-0 bottom-0 z-[var(--z-modal)] w-full" : "fixed left-1/2 top-1/2 z-[var(--z-modal)] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2",
|
|
51
66
|
"nx-motion nx-dialog",
|
|
52
67
|
overlaySurface,
|
|
53
68
|
"max-h-[90vh] overflow-y-auto p-6 focus:outline-none",
|
|
54
|
-
sizeMap[size],
|
|
69
|
+
asSheet ? "rounded-b-none" : sizeMap[size],
|
|
55
70
|
surface === "glass" && glassSurfaceClass,
|
|
56
71
|
className
|
|
57
72
|
),
|
|
58
73
|
"data-material": surface === "glass" ? "glass" : void 0,
|
|
59
74
|
"data-glass-thickness": surface === "glass" ? "thick" : void 0,
|
|
75
|
+
"data-sheet": asSheet ? "" : void 0,
|
|
76
|
+
"data-nested": openChildren > 0 ? "" : void 0,
|
|
60
77
|
...props,
|
|
61
|
-
children
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsx(StackContext.Provider, { value: setOpenChildren, children }),
|
|
80
|
+
showClose && /* @__PURE__ */ jsx(RadixDialog.Close, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { label: closeLabel ?? messages.close, variant: "ghost", size: "sm", className: "absolute end-3 top-3", children: /* @__PURE__ */ jsx(X, { size: "sm", "aria-hidden": "true" }) }) })
|
|
81
|
+
]
|
|
62
82
|
}
|
|
63
83
|
);
|
|
64
84
|
}
|
|
@@ -92,16 +112,22 @@ const Description = React.forwardRef(
|
|
|
92
112
|
Description.displayName = "Dialog.Description";
|
|
93
113
|
const Header = React.forwardRef(
|
|
94
114
|
function DialogHeader({ className, ...props }, ref) {
|
|
95
|
-
return /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4 flex flex-col gap-1", className), ...props });
|
|
115
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn("nx-dialog-header mb-4 flex flex-col gap-1", className), ...props });
|
|
96
116
|
}
|
|
97
117
|
);
|
|
98
118
|
Header.displayName = "Dialog.Header";
|
|
99
119
|
const Footer = React.forwardRef(
|
|
100
120
|
function DialogFooter({ className, ...props }, ref) {
|
|
101
|
-
return /* @__PURE__ */ jsx("div", { ref, className: cn("mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props });
|
|
121
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn("nx-dialog-footer mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props });
|
|
102
122
|
}
|
|
103
123
|
);
|
|
104
124
|
Footer.displayName = "Dialog.Footer";
|
|
125
|
+
const Body = React.forwardRef(
|
|
126
|
+
function DialogBody({ className, ...props }, ref) {
|
|
127
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn("nx-dialog-body", className), ...props });
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
Body.displayName = "Dialog.Body";
|
|
105
131
|
const Dialog = {
|
|
106
132
|
Root,
|
|
107
133
|
Trigger,
|
|
@@ -112,6 +138,7 @@ const Dialog = {
|
|
|
112
138
|
Title,
|
|
113
139
|
Description,
|
|
114
140
|
Header,
|
|
141
|
+
Body,
|
|
115
142
|
Footer
|
|
116
143
|
};
|
|
117
144
|
export {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
export interface EmptyStateRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
/** Scales spacing, icon and text. @default 'md' */
|
|
4
|
+
size?: 'sm' | 'md' | 'lg';
|
|
5
|
+
/** Fades in on mount (nx-reveal; reduced motion keeps the crossfade). */
|
|
6
|
+
animate?: boolean;
|
|
7
|
+
}
|
|
2
8
|
export declare const EmptyState: {
|
|
3
|
-
Root: React.ForwardRefExoticComponent<
|
|
9
|
+
Root: React.ForwardRefExoticComponent<EmptyStateRootProps & React.RefAttributes<HTMLDivElement>>;
|
|
4
10
|
Illustration: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
5
11
|
Title: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLHeadingElement>>;
|
|
6
12
|
Description: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "../../utils/cn.js";
|
|
4
|
+
const rootSizes = { sm: "gap-3 py-8", md: "gap-4 py-16", lg: "gap-6 py-24" };
|
|
4
5
|
const Root = React.forwardRef(
|
|
5
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
6
|
+
({ className, size = "md", animate, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
6
7
|
"div",
|
|
7
8
|
{
|
|
8
9
|
ref,
|
|
10
|
+
"data-size": size,
|
|
9
11
|
className: cn(
|
|
10
|
-
"flex flex-col items-center justify-center
|
|
12
|
+
"group/empty-state flex flex-col items-center justify-center text-center",
|
|
13
|
+
rootSizes[size],
|
|
14
|
+
animate && "nx-reveal",
|
|
11
15
|
className
|
|
12
16
|
),
|
|
13
17
|
...props
|
|
@@ -20,7 +24,11 @@ const Illustration = React.forwardRef(
|
|
|
20
24
|
"div",
|
|
21
25
|
{
|
|
22
26
|
ref,
|
|
23
|
-
className: cn(
|
|
27
|
+
className: cn(
|
|
28
|
+
"text-[var(--color-on-surface-muted)]",
|
|
29
|
+
"group-data-[size=sm]/empty-state:[&_svg]:size-8 group-data-[size=lg]/empty-state:[&_svg]:size-16",
|
|
30
|
+
className
|
|
31
|
+
),
|
|
24
32
|
...props
|
|
25
33
|
}
|
|
26
34
|
)
|
|
@@ -31,7 +39,11 @@ const Title = React.forwardRef(
|
|
|
31
39
|
"h3",
|
|
32
40
|
{
|
|
33
41
|
ref,
|
|
34
|
-
className: cn(
|
|
42
|
+
className: cn(
|
|
43
|
+
"text-lg font-semibold text-[var(--color-on-surface)]",
|
|
44
|
+
"group-data-[size=sm]/empty-state:text-base group-data-[size=lg]/empty-state:text-xl",
|
|
45
|
+
className
|
|
46
|
+
),
|
|
35
47
|
...props
|
|
36
48
|
}
|
|
37
49
|
)
|
|
@@ -41,7 +53,11 @@ const Description = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
41
53
|
"p",
|
|
42
54
|
{
|
|
43
55
|
ref,
|
|
44
|
-
className: cn(
|
|
56
|
+
className: cn(
|
|
57
|
+
"max-w-sm text-sm text-[var(--color-on-surface-muted)]",
|
|
58
|
+
"group-data-[size=sm]/empty-state:text-xs group-data-[size=lg]/empty-state:text-base",
|
|
59
|
+
className
|
|
60
|
+
),
|
|
45
61
|
...props
|
|
46
62
|
}
|
|
47
63
|
));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { NexubMessages } from '@nexub/core';
|
|
3
|
+
import { type EmptyStateRootProps } from './EmptyState.js';
|
|
3
4
|
export type EmptyStateKind = keyof NexubMessages['emptyStates'];
|
|
4
|
-
export interface EmptyStatePresetProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
5
|
+
export interface EmptyStatePresetProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>, Pick<EmptyStateRootProps, 'size' | 'animate'> {
|
|
5
6
|
/** noData, noResults, noAccess (403), serverError (500) or offline. */
|
|
6
7
|
kind: EmptyStateKind;
|
|
7
8
|
/** Overrides the preset's title; the description too. Both default to localised text. */
|