@marktiderman/genesis-ui 2.2.0 → 2.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/EXTENDING.md +104 -0
- package/dist/{AppShell-sQT0UbXg.d.ts → AppShell-D_E_Hk4g.d.ts} +27 -2
- package/dist/chunk-7S7LKQBD.js +12 -0
- package/dist/chunk-7S7LKQBD.js.map +1 -0
- package/dist/{chunk-FEJAZFP3.js → chunk-UBCYXOXT.js} +36 -4
- package/dist/chunk-UBCYXOXT.js.map +1 -0
- package/dist/components/data/index.d.ts +106 -34
- package/dist/components/data/index.js +101 -60
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/layout/index.d.ts +1 -1
- package/dist/components/layout/index.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/slots-tR0wf89f.d.ts +60 -0
- package/package.json +13 -12
- package/dist/chunk-FEJAZFP3.js.map +0 -1
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ReactNode, RefObject } from 'react';
|
|
3
|
-
import { c as PageHeaderOptions,
|
|
4
|
-
export { a as AppShellOptions, b as AppShellProps, N as NavItem, d as PageHeaderProps } from '../../AppShell-
|
|
2
|
+
import { ReactNode, ComponentType, ComponentProps, RefObject } from 'react';
|
|
3
|
+
import { e as PageHeaderAction, c as PageHeaderOptions, P as PageHeader$1, A as AppShell$1 } from '../../AppShell-D_E_Hk4g.js';
|
|
4
|
+
export { a as AppShellOptions, b as AppShellProps, N as NavItem, d as PageHeaderProps } from '../../AppShell-D_E_Hk4g.js';
|
|
5
5
|
import { V as ViewMode } from '../../view-toggle-CahQ87PW.js';
|
|
6
6
|
export { a as ViewToggle } from '../../view-toggle-CahQ87PW.js';
|
|
7
7
|
import { LucideIcon } from 'lucide-react';
|
|
8
8
|
import { Density } from '../../hooks/use-view-settings.js';
|
|
9
|
-
import { R as ResourceActions, a as ResourceColumnDef } from '../../use-resource-page-DTQS011Q.js';
|
|
10
|
-
export {
|
|
9
|
+
import { R as ResourceActions, a as ResourceColumnDef, D as DataTable } from '../../use-resource-page-DTQS011Q.js';
|
|
10
|
+
export { c as DataTableColumn, d as DataTableProps, L as LinkCell, T as ThumbnailCell } from '../../use-resource-page-DTQS011Q.js';
|
|
11
11
|
export { EmptyState } from '../patterns/empty-state.js';
|
|
12
12
|
export { V as ViewSettings } from '../../view-settings-kHKCHMjj.js';
|
|
13
13
|
import { L as LinkComponent, N as NavigateFn } from '../../navigation-CpOEyUSb.js';
|
|
14
14
|
export { D as DefaultLink, G as GenesisLinkProps, d as defaultNavigate, i as isPathActive } from '../../navigation-CpOEyUSb.js';
|
|
15
15
|
import { UseFormReturn, Control, FieldErrors } from 'react-hook-form';
|
|
16
|
-
import { WizardStep, UseResourceOptions } from '@marktiderman/genesis-core/hooks';
|
|
17
|
-
import { FilterParam, SortParam, BaseRecord, Identifier } from '@marktiderman/genesis-core/provider';
|
|
18
16
|
import { ResourceFormFieldDef } from '@marktiderman/genesis-core';
|
|
19
17
|
export { FieldType, ResourceFormFieldDef, SelectOption } from '@marktiderman/genesis-core';
|
|
18
|
+
import { WizardStep, UseResourceOptions } from '@marktiderman/genesis-core/hooks';
|
|
19
|
+
import { b as Slots, a as SlotPropsFor } from '../../slots-tR0wf89f.js';
|
|
20
|
+
import { FilterParam, SortParam, BaseRecord, Identifier } from '@marktiderman/genesis-core/provider';
|
|
20
21
|
export { CommandPalette, CommandPaletteItem, CommandPaletteProps, useCommandPaletteHotkey } from '../ui/command-palette.js';
|
|
21
22
|
export { UseSavedViewsReturn, useSavedViews } from '../../hooks/use-saved-views.js';
|
|
22
23
|
|
|
@@ -29,6 +30,8 @@ interface DataPageShellProps {
|
|
|
29
30
|
/** Subtitle text */
|
|
30
31
|
subtitle?: string;
|
|
31
32
|
createAction?: ReactNode;
|
|
33
|
+
/** Extra actions, rendered in the header's one overflow menu. */
|
|
34
|
+
secondaryActions?: PageHeaderAction[];
|
|
32
35
|
isLoading?: boolean;
|
|
33
36
|
isEmpty?: boolean;
|
|
34
37
|
emptyIcon?: LucideIcon;
|
|
@@ -72,8 +75,19 @@ interface DataPageShellProps {
|
|
|
72
75
|
/** Persist user header-option overrides (localStorage) */
|
|
73
76
|
headerOptionsStorageKey?: string;
|
|
74
77
|
children: ReactNode;
|
|
78
|
+
/** Merged onto the root, not replacing its layout. */
|
|
79
|
+
className?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Replace the header outright. It receives exactly the props
|
|
82
|
+
* `DataPageShell` would have given `PageHeader`, so the common case is a
|
|
83
|
+
* wrapper that renders `PageHeader` with one thing added — an overflow
|
|
84
|
+
* menu, a status strip, a second row — rather than a reimplementation.
|
|
85
|
+
*
|
|
86
|
+
* `null` ejects it: shell layout, states and filter row, no header.
|
|
87
|
+
*/
|
|
88
|
+
header?: ComponentType<ComponentProps<typeof PageHeader$1>> | null;
|
|
75
89
|
}
|
|
76
|
-
declare function DataPageShell({ title, icon, entityName, subtitle, createAction, isLoading, isEmpty, emptyIcon, emptyMessage, emptyAction, hasError, errorMessage, onRetry, count, totalCount, viewMode, onViewModeChange, pageSize, onPageSizeChange, density, onDensityChange, filters, hasActiveFilters, onClearFilters, filtersCollapsible, defaultFiltersOpen, filtersOpen, onFiltersOpenChange, filtersLabel, filtersHint, headerOptions, headerOptionsStorageKey, children, }: DataPageShellProps): React.JSX.Element;
|
|
90
|
+
declare function DataPageShell({ title, icon, entityName, subtitle, createAction, secondaryActions, isLoading, isEmpty, emptyIcon, emptyMessage, emptyAction, hasError, errorMessage, onRetry, count, totalCount, viewMode, onViewModeChange, pageSize, onPageSizeChange, density, onDensityChange, filters, hasActiveFilters, onClearFilters, filtersCollapsible, defaultFiltersOpen, filtersOpen, onFiltersOpenChange, filtersLabel, filtersHint, headerOptions, headerOptionsStorageKey, children, className, header, }: DataPageShellProps): React.JSX.Element;
|
|
77
91
|
|
|
78
92
|
interface FilterOption {
|
|
79
93
|
value: string;
|
|
@@ -195,6 +209,15 @@ interface BulkAction {
|
|
|
195
209
|
* Optional and defaulted off — omitting it behaves exactly as before.
|
|
196
210
|
*/
|
|
197
211
|
disabled?: boolean;
|
|
212
|
+
/**
|
|
213
|
+
* `data-testid` on the rendered button.
|
|
214
|
+
*
|
|
215
|
+
* Without it, a consumer migrating a hand-rolled bulk bar to `DataBulkBar`
|
|
216
|
+
* loses every selector its e2e suite uses to drive one — which is a real
|
|
217
|
+
* reason not to migrate, and the kind of small missing affordance that
|
|
218
|
+
* keeps a parallel implementation alive.
|
|
219
|
+
*/
|
|
220
|
+
testId?: string;
|
|
198
221
|
}
|
|
199
222
|
interface DataBulkBarProps {
|
|
200
223
|
selected: Set<string>;
|
|
@@ -202,8 +225,10 @@ interface DataBulkBarProps {
|
|
|
202
225
|
onToggleAll: () => void;
|
|
203
226
|
onClearSelection: () => void;
|
|
204
227
|
actions: BulkAction[];
|
|
228
|
+
/** Merged onto the bar, not replacing its positioning. */
|
|
229
|
+
className?: string;
|
|
205
230
|
}
|
|
206
|
-
declare function DataBulkBar({ selected, totalCount, onToggleAll, onClearSelection, actions, }: DataBulkBarProps): React.JSX.Element | null;
|
|
231
|
+
declare function DataBulkBar({ selected, totalCount, onToggleAll, onClearSelection, actions, className, }: DataBulkBarProps): React.JSX.Element | null;
|
|
207
232
|
|
|
208
233
|
/**
|
|
209
234
|
* DateCell -- shows relative date by default, formatted date otherwise.
|
|
@@ -329,6 +354,30 @@ interface DetailPanelProps<T> {
|
|
|
329
354
|
}
|
|
330
355
|
declare function DetailPanel<T extends Record<string, unknown>>({ item, open, onClose, title, subtitle, onEdit, onDelete, children, fields, width, layout, }: DetailPanelProps<T>): React.JSX.Element | null;
|
|
331
356
|
|
|
357
|
+
interface ResourceFormProps {
|
|
358
|
+
resource: string;
|
|
359
|
+
action: "create" | "edit";
|
|
360
|
+
item?: Record<string, unknown>;
|
|
361
|
+
open: boolean;
|
|
362
|
+
onOpenChange: (open: boolean) => void;
|
|
363
|
+
title?: string;
|
|
364
|
+
fields?: ResourceFormFieldDef[];
|
|
365
|
+
/** Full dataset used to auto-detect select fields from unique values. */
|
|
366
|
+
listData?: Record<string, unknown>[];
|
|
367
|
+
renderForm?: (form: UseFormReturn<Record<string, unknown>>, fields: ResourceFormFieldDef[]) => ReactNode;
|
|
368
|
+
onSuccess?: (data: Record<string, unknown>) => void;
|
|
369
|
+
onError?: (error: Error) => void;
|
|
370
|
+
/** Layout mode for the form container. Default "dialog". */
|
|
371
|
+
layout?: "dialog" | "sheet" | "page";
|
|
372
|
+
/** Form presentation mode. Default "standard". */
|
|
373
|
+
mode?: "standard" | "wizard";
|
|
374
|
+
/** Wizard step definitions. Required when mode is "wizard". */
|
|
375
|
+
steps?: WizardStep[];
|
|
376
|
+
/** Number of columns for the form layout. Default 1. */
|
|
377
|
+
columns?: 1 | 2;
|
|
378
|
+
}
|
|
379
|
+
declare function ResourceForm(props: ResourceFormProps): React.JSX.Element | null;
|
|
380
|
+
|
|
332
381
|
/**
|
|
333
382
|
* Row-bound actions handed to `renderCard`, on top of the resource-wide
|
|
334
383
|
* `ResourceActions`. `open` is per-row, which is why it lives here rather
|
|
@@ -377,6 +426,32 @@ interface ResourceCardActions<T> extends ResourceActions<T> {
|
|
|
377
426
|
*
|
|
378
427
|
* @stability Beta
|
|
379
428
|
*/
|
|
429
|
+
/**
|
|
430
|
+
* Every interior part `ResourcePage` composes, with the props it composes it
|
|
431
|
+
* with. Each key is a slot: replace it via `slots`, feed it more props via
|
|
432
|
+
* `slotProps`, or eject it with `null`.
|
|
433
|
+
*
|
|
434
|
+
* The map is the contract. A part that is not named here cannot be reached,
|
|
435
|
+
* so adding a part to the render tree means adding it here in the same
|
|
436
|
+
* change — otherwise the next surface that needs it forks the whole page.
|
|
437
|
+
*/
|
|
438
|
+
type ResourcePageSlotMap<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
439
|
+
/** The page shell: header, count pill, loading / empty / error states. */
|
|
440
|
+
shell: ComponentProps<typeof DataPageShell>;
|
|
441
|
+
/** The filter row — search, sort, facets, saved views. */
|
|
442
|
+
filters: ComponentProps<typeof DataFilters>;
|
|
443
|
+
/** The selection bar. Ejecting it leaves selection working, just unshown. */
|
|
444
|
+
bulkBar: ComponentProps<typeof DataBulkBar>;
|
|
445
|
+
/** The card/table switch. */
|
|
446
|
+
grid: ComponentProps<typeof DataGrid<T>>;
|
|
447
|
+
/** The table inside the grid. `renderTable` stays the terser way to swap. */
|
|
448
|
+
table: ComponentProps<typeof DataTable<T>>;
|
|
449
|
+
/** The detail peek. */
|
|
450
|
+
detail: ComponentProps<typeof DetailPanel<T>>;
|
|
451
|
+
/** The create / edit form. */
|
|
452
|
+
form: ComponentProps<typeof ResourceForm>;
|
|
453
|
+
};
|
|
454
|
+
type ResourcePageSlots<T extends Record<string, unknown> = Record<string, unknown>> = Slots<ResourcePageSlotMap<T>>;
|
|
380
455
|
interface ResourcePageProps<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
381
456
|
/** Client-side mode: resource name passed to useResource / DataProvider. */
|
|
382
457
|
resource?: string;
|
|
@@ -549,6 +624,11 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
549
624
|
onEdit?: (item: T) => void;
|
|
550
625
|
onDelete?: (item: T) => void;
|
|
551
626
|
createLabel?: string;
|
|
627
|
+
/**
|
|
628
|
+
* Extra header actions beyond the create button, in one overflow menu.
|
|
629
|
+
* See {@link PageHeaderAction} — the budget is one primary, the rest here.
|
|
630
|
+
*/
|
|
631
|
+
secondaryActions?: PageHeaderAction[];
|
|
552
632
|
/** Enable built-in create form (ignored if onCreate callback is provided). */
|
|
553
633
|
allowCreate?: boolean;
|
|
554
634
|
/** Enable built-in edit form (ignored if onEdit callback is provided). */
|
|
@@ -575,6 +655,8 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
575
655
|
* but leaves the button looking live, which misstates what it will do.
|
|
576
656
|
*/
|
|
577
657
|
disabled?: boolean;
|
|
658
|
+
/** `data-testid` on the rendered bulk button, so e2e can drive it. */
|
|
659
|
+
testId?: string;
|
|
578
660
|
}>;
|
|
579
661
|
/**
|
|
580
662
|
* Override the default card renderer used for grid view (and, absent a
|
|
@@ -649,6 +731,20 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
649
731
|
* Defaults to `resource` or a slug of `title`.
|
|
650
732
|
*/
|
|
651
733
|
viewSettingsKey?: string;
|
|
734
|
+
/**
|
|
735
|
+
* The interior parts `ResourcePage` composes, by name. Replace one with your
|
|
736
|
+
* own component, or pass `null` to eject it entirely — see
|
|
737
|
+
* {@link ResourcePageSlots}.
|
|
738
|
+
*
|
|
739
|
+
* This is the reason a surface that needs one part to differ does not have
|
|
740
|
+
* to drop to the primitives and re-derive the pagination, selection and
|
|
741
|
+
* detail wiring that were already correct.
|
|
742
|
+
*/
|
|
743
|
+
slots?: ResourcePageSlots<T>;
|
|
744
|
+
/** Extra props merged into a part you are KEEPING. Wins over ours. */
|
|
745
|
+
slotProps?: SlotPropsFor<ResourcePageSlotMap<T>>;
|
|
746
|
+
/** Merged onto the page shell's root. */
|
|
747
|
+
className?: string;
|
|
652
748
|
}
|
|
653
749
|
declare function ResourcePage<T extends Record<string, unknown> = Record<string, unknown>>(props: ResourcePageProps<T>): React.JSX.Element;
|
|
654
750
|
|
|
@@ -731,30 +827,6 @@ interface ResourceDetailPageProps<T extends BaseRecord = BaseRecord> {
|
|
|
731
827
|
*/
|
|
732
828
|
declare function ResourceDetailPage<T extends BaseRecord = BaseRecord>({ resource, id, titleField, subtitleField, select, breadcrumb, actions, sidebar, children, className, testID, }: ResourceDetailPageProps<T>): React.JSX.Element;
|
|
733
829
|
|
|
734
|
-
interface ResourceFormProps {
|
|
735
|
-
resource: string;
|
|
736
|
-
action: "create" | "edit";
|
|
737
|
-
item?: Record<string, unknown>;
|
|
738
|
-
open: boolean;
|
|
739
|
-
onOpenChange: (open: boolean) => void;
|
|
740
|
-
title?: string;
|
|
741
|
-
fields?: ResourceFormFieldDef[];
|
|
742
|
-
/** Full dataset used to auto-detect select fields from unique values. */
|
|
743
|
-
listData?: Record<string, unknown>[];
|
|
744
|
-
renderForm?: (form: UseFormReturn<Record<string, unknown>>, fields: ResourceFormFieldDef[]) => ReactNode;
|
|
745
|
-
onSuccess?: (data: Record<string, unknown>) => void;
|
|
746
|
-
onError?: (error: Error) => void;
|
|
747
|
-
/** Layout mode for the form container. Default "dialog". */
|
|
748
|
-
layout?: "dialog" | "sheet" | "page";
|
|
749
|
-
/** Form presentation mode. Default "standard". */
|
|
750
|
-
mode?: "standard" | "wizard";
|
|
751
|
-
/** Wizard step definitions. Required when mode is "wizard". */
|
|
752
|
-
steps?: WizardStep[];
|
|
753
|
-
/** Number of columns for the form layout. Default 1. */
|
|
754
|
-
columns?: 1 | 2;
|
|
755
|
-
}
|
|
756
|
-
declare function ResourceForm(props: ResourceFormProps): React.JSX.Element | null;
|
|
757
|
-
|
|
758
830
|
interface RelationFieldProps {
|
|
759
831
|
field: ResourceFormFieldDef;
|
|
760
832
|
value: string;
|
|
@@ -805,4 +877,4 @@ declare const AppShell: typeof AppShell$1;
|
|
|
805
877
|
*/
|
|
806
878
|
declare const PageHeader: typeof PageHeader$1;
|
|
807
879
|
|
|
808
|
-
export { AppShell, BadgeCell, type BulkAction, type ColumnStatus, CurrencyCell, DataBulkBar, type DataFilterConfig, DataFilters, DataGrid, DataList, type DataListProps, DataPageShell, type DataPageShellProps, DateCell, Density, DetailPanel, type DetailPanelProps, FieldArrayRenderer, FileUploadField, FilterCombobox, type FilterOption, KanbanBoard, type KanbanBoardProps, type KanbanColumn, LinkComponent, NavigateFn, NumberCell, PageHeader, PageHeaderOptions, RelationField, ResourceActions, type ResourceCardActions, ResourceColumnDef, ResourceDetailPage, type ResourceDetailPageProps, ResourceForm, type ResourceFormProps, ResourcePage, type ResourcePageProps, type SavedViewItem, StatCard, type StatCardColor, type StatCardProps, ViewMode, WizardForm };
|
|
880
|
+
export { AppShell, BadgeCell, type BulkAction, type ColumnStatus, CurrencyCell, DataBulkBar, type DataFilterConfig, DataFilters, DataGrid, DataList, type DataListProps, DataPageShell, type DataPageShellProps, DataTable, DateCell, Density, DetailPanel, type DetailPanelProps, FieldArrayRenderer, FileUploadField, FilterCombobox, type FilterOption, KanbanBoard, type KanbanBoardProps, type KanbanColumn, LinkComponent, NavigateFn, NumberCell, PageHeader, PageHeaderOptions, RelationField, ResourceActions, type ResourceCardActions, ResourceColumnDef, ResourceDetailPage, type ResourceDetailPageProps, ResourceForm, type ResourceFormProps, ResourcePage, type ResourcePageProps, type SavedViewItem, StatCard, type StatCardColor, type StatCardProps, ViewMode, WizardForm };
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { AppShell, PageHeader } from '../../chunk-
|
|
2
|
+
import { AppShell, PageHeader } from '../../chunk-UBCYXOXT.js';
|
|
3
3
|
export { ViewSettings, ViewToggle } from '../../chunk-46TZGMFW.js';
|
|
4
|
+
import { renderSlot } from '../../chunk-7S7LKQBD.js';
|
|
4
5
|
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '../../chunk-N2ZFNOOT.js';
|
|
5
6
|
import { Textarea } from '../../chunk-YETMISCE.js';
|
|
6
7
|
import { Separator } from '../../chunk-SXSCHWZU.js';
|
|
7
8
|
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '../../chunk-XCGZPFT7.js';
|
|
8
9
|
import { Skeleton } from '../../chunk-QFM4Z2SS.js';
|
|
9
10
|
import { Progress } from '../../chunk-W5Y3LG5K.js';
|
|
10
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-QJ6P2DW6.js';
|
|
11
11
|
import { Input } from '../../chunk-QSXVL5UB.js';
|
|
12
12
|
import { Checkbox } from '../../chunk-SEBU6JCK.js';
|
|
13
13
|
import { Card, CardContent, CardHeader, CardTitle } from '../../chunk-FN3FZVUR.js';
|
|
14
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-QJ6P2DW6.js';
|
|
14
15
|
import '../../chunk-B2GIBUQD.js';
|
|
15
16
|
import { Badge } from '../../chunk-NPNADZHM.js';
|
|
16
17
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../chunk-FK42ZGGV.js';
|
|
@@ -53,6 +54,7 @@ function DataPageShell({
|
|
|
53
54
|
entityName,
|
|
54
55
|
subtitle,
|
|
55
56
|
createAction,
|
|
57
|
+
secondaryActions,
|
|
56
58
|
isLoading = false,
|
|
57
59
|
isEmpty = false,
|
|
58
60
|
emptyIcon,
|
|
@@ -80,7 +82,9 @@ function DataPageShell({
|
|
|
80
82
|
filtersHint,
|
|
81
83
|
headerOptions,
|
|
82
84
|
headerOptionsStorageKey,
|
|
83
|
-
children
|
|
85
|
+
children,
|
|
86
|
+
className,
|
|
87
|
+
header
|
|
84
88
|
}) {
|
|
85
89
|
const entity = entityName || title.toLowerCase();
|
|
86
90
|
const collapseFilters = filtersCollapsible && filters != null;
|
|
@@ -105,11 +109,13 @@ function DataPageShell({
|
|
|
105
109
|
density,
|
|
106
110
|
onDensityChange,
|
|
107
111
|
createAction,
|
|
112
|
+
secondaryActions,
|
|
108
113
|
optionsStorageKey: headerOptionsStorageKey
|
|
109
114
|
};
|
|
110
|
-
|
|
115
|
+
const Header = header === null ? (() => null) : header ?? PageHeader;
|
|
116
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
|
|
111
117
|
collapseFilters ? /* @__PURE__ */ jsx(
|
|
112
|
-
|
|
118
|
+
Header,
|
|
113
119
|
{
|
|
114
120
|
...sharedHeaderProps,
|
|
115
121
|
details: filters,
|
|
@@ -120,7 +126,7 @@ function DataPageShell({
|
|
|
120
126
|
options: mergedOptions
|
|
121
127
|
}
|
|
122
128
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
|
-
/* @__PURE__ */ jsx(
|
|
129
|
+
/* @__PURE__ */ jsx(Header, { ...sharedHeaderProps, options: headerOptions }),
|
|
124
130
|
filters
|
|
125
131
|
] }),
|
|
126
132
|
isLoading ? /* @__PURE__ */ jsx(
|
|
@@ -1123,49 +1129,60 @@ function DataBulkBar({
|
|
|
1123
1129
|
totalCount,
|
|
1124
1130
|
onToggleAll,
|
|
1125
1131
|
onClearSelection,
|
|
1126
|
-
actions
|
|
1132
|
+
actions,
|
|
1133
|
+
className
|
|
1127
1134
|
}) {
|
|
1128
1135
|
if (selected.size === 0) return null;
|
|
1129
|
-
return /* @__PURE__ */ jsxs(
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1136
|
+
return /* @__PURE__ */ jsxs(
|
|
1137
|
+
"div",
|
|
1138
|
+
{
|
|
1139
|
+
className: cn(
|
|
1140
|
+
"flex items-center gap-2 p-3 rounded-lg border bg-muted/50 animate-fade-in",
|
|
1141
|
+
className
|
|
1142
|
+
),
|
|
1143
|
+
children: [
|
|
1144
|
+
/* @__PURE__ */ jsx(
|
|
1145
|
+
Checkbox,
|
|
1146
|
+
{
|
|
1147
|
+
checked: selected.size === totalCount,
|
|
1148
|
+
onCheckedChange: onToggleAll
|
|
1149
|
+
}
|
|
1150
|
+
),
|
|
1151
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "secondary", children: [
|
|
1152
|
+
selected.size,
|
|
1153
|
+
" selected"
|
|
1154
|
+
] }),
|
|
1155
|
+
actions.map((action) => {
|
|
1156
|
+
const ActionIcon = action.icon;
|
|
1157
|
+
return /* @__PURE__ */ jsxs(
|
|
1158
|
+
Button,
|
|
1159
|
+
{
|
|
1160
|
+
size: "sm",
|
|
1161
|
+
variant: action.variant || "outline",
|
|
1162
|
+
onClick: action.onClick,
|
|
1163
|
+
disabled: action.disabled,
|
|
1164
|
+
"data-testid": action.testId,
|
|
1165
|
+
children: [
|
|
1166
|
+
/* @__PURE__ */ jsx(ActionIcon, { className: "h-3.5 w-3.5 mr-1" }),
|
|
1167
|
+
action.label
|
|
1168
|
+
]
|
|
1169
|
+
},
|
|
1152
1170
|
action.label
|
|
1153
|
-
|
|
1154
|
-
},
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
] });
|
|
1171
|
+
);
|
|
1172
|
+
}),
|
|
1173
|
+
/* @__PURE__ */ jsx(
|
|
1174
|
+
Button,
|
|
1175
|
+
{
|
|
1176
|
+
size: "sm",
|
|
1177
|
+
variant: "ghost",
|
|
1178
|
+
onClick: onClearSelection,
|
|
1179
|
+
className: "ml-auto text-xs text-muted-foreground",
|
|
1180
|
+
children: "Clear"
|
|
1181
|
+
}
|
|
1182
|
+
)
|
|
1183
|
+
]
|
|
1184
|
+
}
|
|
1185
|
+
);
|
|
1169
1186
|
}
|
|
1170
1187
|
function DateCell(options) {
|
|
1171
1188
|
return (_item, value) => {
|
|
@@ -2452,6 +2469,7 @@ function ResourcePage(props) {
|
|
|
2452
2469
|
onEdit,
|
|
2453
2470
|
onDelete,
|
|
2454
2471
|
createLabel = "New",
|
|
2472
|
+
secondaryActions,
|
|
2455
2473
|
allowCreate,
|
|
2456
2474
|
allowEdit,
|
|
2457
2475
|
formFields,
|
|
@@ -2469,7 +2487,10 @@ function ResourcePage(props) {
|
|
|
2469
2487
|
resourceOptions,
|
|
2470
2488
|
defaultView = "table",
|
|
2471
2489
|
onNavigate = defaultNavigate,
|
|
2472
|
-
viewSettingsKey: viewSettingsKeyProp
|
|
2490
|
+
viewSettingsKey: viewSettingsKeyProp,
|
|
2491
|
+
slots,
|
|
2492
|
+
slotProps,
|
|
2493
|
+
className
|
|
2473
2494
|
} = props;
|
|
2474
2495
|
const isClientMode = !!resourceName;
|
|
2475
2496
|
const isServerControlled = pageProp !== void 0 || perPageProp !== void 0 || onPageChange !== void 0 || onPerPageChange !== void 0 || onSortChange !== void 0 || onSearchChange !== void 0;
|
|
@@ -2803,8 +2824,10 @@ function ResourcePage(props) {
|
|
|
2803
2824
|
}
|
|
2804
2825
|
);
|
|
2805
2826
|
};
|
|
2827
|
+
const Shell = slots?.shell ?? DataPageShell;
|
|
2828
|
+
const shellSlotProps = slotProps?.shell;
|
|
2806
2829
|
return /* @__PURE__ */ jsxs(
|
|
2807
|
-
|
|
2830
|
+
Shell,
|
|
2808
2831
|
{
|
|
2809
2832
|
title,
|
|
2810
2833
|
subtitle,
|
|
@@ -2823,11 +2846,13 @@ function ResourcePage(props) {
|
|
|
2823
2846
|
density: settings.density,
|
|
2824
2847
|
onDensityChange: (d) => updateSetting("density", d),
|
|
2825
2848
|
createAction,
|
|
2849
|
+
secondaryActions,
|
|
2826
2850
|
hasActiveFilters: page.hasActiveFilters,
|
|
2827
2851
|
onClearFilters: handleClearFilters,
|
|
2828
2852
|
emptyAction: emptyState,
|
|
2829
|
-
filters:
|
|
2853
|
+
filters: renderSlot(
|
|
2830
2854
|
DataFilters,
|
|
2855
|
+
slots?.filters,
|
|
2831
2856
|
{
|
|
2832
2857
|
search: page.filterConfig.search,
|
|
2833
2858
|
onSearchChange: handleSearchChange,
|
|
@@ -2843,11 +2868,15 @@ function ResourcePage(props) {
|
|
|
2843
2868
|
onDeleteView,
|
|
2844
2869
|
canSaveViews,
|
|
2845
2870
|
onClearAll: page.hasActiveFilters ? handleClearFilters : void 0
|
|
2846
|
-
}
|
|
2871
|
+
},
|
|
2872
|
+
slotProps?.filters
|
|
2847
2873
|
),
|
|
2874
|
+
...shellSlotProps,
|
|
2875
|
+
className: cn(className, shellSlotProps?.className),
|
|
2848
2876
|
children: [
|
|
2849
|
-
bulkActions && bulkActions.length > 0 &&
|
|
2877
|
+
bulkActions && bulkActions.length > 0 && renderSlot(
|
|
2850
2878
|
DataBulkBar,
|
|
2879
|
+
slots?.bulkBar,
|
|
2851
2880
|
{
|
|
2852
2881
|
selected: selectedIds,
|
|
2853
2882
|
totalCount: page.data.length,
|
|
@@ -2858,14 +2887,17 @@ function ResourcePage(props) {
|
|
|
2858
2887
|
icon: a.icon ?? Trash2,
|
|
2859
2888
|
variant: a.variant,
|
|
2860
2889
|
disabled: a.disabled,
|
|
2890
|
+
testId: a.testId,
|
|
2861
2891
|
onClick: () => {
|
|
2862
2892
|
void a.onAction(Array.from(selectedIds));
|
|
2863
2893
|
}
|
|
2864
2894
|
}))
|
|
2865
|
-
}
|
|
2895
|
+
},
|
|
2896
|
+
slotProps?.bulkBar
|
|
2866
2897
|
),
|
|
2867
|
-
|
|
2898
|
+
renderSlot(
|
|
2868
2899
|
DataGrid,
|
|
2900
|
+
slots?.grid,
|
|
2869
2901
|
{
|
|
2870
2902
|
items: page.data,
|
|
2871
2903
|
viewMode: page.viewMode,
|
|
@@ -2873,8 +2905,9 @@ function ResourcePage(props) {
|
|
|
2873
2905
|
getKey: (item) => String(item.id ?? ""),
|
|
2874
2906
|
focusedIndex: keyboardNav.focusedIndex,
|
|
2875
2907
|
keyboardContainerRef: keyboardNav.containerRef,
|
|
2876
|
-
renderTable: (items) => renderTableProp ? renderTableProp(items) :
|
|
2908
|
+
renderTable: (items) => renderTableProp ? renderTableProp(items) : renderSlot(
|
|
2877
2909
|
DataTable,
|
|
2910
|
+
slots?.table,
|
|
2878
2911
|
{
|
|
2879
2912
|
items,
|
|
2880
2913
|
columns: tableColumns,
|
|
@@ -2893,12 +2926,16 @@ function ResourcePage(props) {
|
|
|
2893
2926
|
resizeKey,
|
|
2894
2927
|
stickyHeader,
|
|
2895
2928
|
stickyFirstColumn,
|
|
2929
|
+
// The server already cut the page window; DataTable's own
|
|
2930
|
+
// pagination would slice the slice.
|
|
2896
2931
|
pagination: isServerControlled ? false : void 0
|
|
2897
|
-
}
|
|
2932
|
+
},
|
|
2933
|
+
slotProps?.table
|
|
2898
2934
|
),
|
|
2899
2935
|
renderCard: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultCard(item),
|
|
2900
2936
|
renderListItem: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultListItem(item)
|
|
2901
|
-
}
|
|
2937
|
+
},
|
|
2938
|
+
slotProps?.grid
|
|
2902
2939
|
),
|
|
2903
2940
|
isServerControlled && onPageChange && pagerMisconfigured && /* @__PURE__ */ jsxs(
|
|
2904
2941
|
"div",
|
|
@@ -2987,8 +3024,9 @@ function ResourcePage(props) {
|
|
|
2987
3024
|
]
|
|
2988
3025
|
}
|
|
2989
3026
|
),
|
|
2990
|
-
(detail === "panel" || detail === "modal") &&
|
|
3027
|
+
(detail === "panel" || detail === "modal") && renderSlot(
|
|
2991
3028
|
DetailPanel,
|
|
3029
|
+
slots?.detail,
|
|
2992
3030
|
{
|
|
2993
3031
|
item: page.selectedItem,
|
|
2994
3032
|
open: page.detailOpen,
|
|
@@ -3004,10 +3042,12 @@ function ResourcePage(props) {
|
|
|
3004
3042
|
width: detailWidth,
|
|
3005
3043
|
layout: detail === "modal" ? "dialog" : "sheet",
|
|
3006
3044
|
children: renderDetailProp ? (item) => renderDetailProp(item) : void 0
|
|
3007
|
-
}
|
|
3045
|
+
},
|
|
3046
|
+
slotProps?.detail
|
|
3008
3047
|
),
|
|
3009
|
-
(useBuiltInCreate || useBuiltInEdit) && resourceName &&
|
|
3048
|
+
(useBuiltInCreate || useBuiltInEdit) && resourceName && renderSlot(
|
|
3010
3049
|
ResourceForm,
|
|
3050
|
+
slots?.form,
|
|
3011
3051
|
{
|
|
3012
3052
|
resource: resourceName,
|
|
3013
3053
|
action: formAction,
|
|
@@ -3026,7 +3066,8 @@ function ResourcePage(props) {
|
|
|
3026
3066
|
page.setDetailOpen(false);
|
|
3027
3067
|
page.setSelectedItem(null);
|
|
3028
3068
|
}
|
|
3029
|
-
}
|
|
3069
|
+
},
|
|
3070
|
+
slotProps?.form
|
|
3030
3071
|
)
|
|
3031
3072
|
]
|
|
3032
3073
|
}
|