@marktiderman/genesis-ui 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -167,11 +167,34 @@ interface DataGridProps<T> {
|
|
|
167
167
|
}
|
|
168
168
|
declare function DataGrid<T>({ items, viewMode, batchSize, gridCols, renderCard, renderTable, renderListItem, staggerAnimation, getKey, focusedIndex, keyboardContainerRef, }: DataGridProps<T>): React.JSX.Element;
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* One button in the bulk action bar.
|
|
172
|
+
*
|
|
173
|
+
* @stability Stable
|
|
174
|
+
*/
|
|
170
175
|
interface BulkAction {
|
|
171
176
|
label: string;
|
|
172
177
|
icon: LucideIcon;
|
|
173
178
|
onClick: () => void;
|
|
174
179
|
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
|
|
180
|
+
/**
|
|
181
|
+
* Makes this action inert: it renders with the Button primitive's native
|
|
182
|
+
* `disabled` attribute, so it is neither clickable nor focusable, and it
|
|
183
|
+
* picks up the disabled styling.
|
|
184
|
+
*
|
|
185
|
+
* The case this exists for is an in-flight bulk mutation. A consumer that
|
|
186
|
+
* fires a move/duplicate/delete over the selected ids wants every bulk
|
|
187
|
+
* action dead until it settles, so a user cannot double-fire a destructive
|
|
188
|
+
* operation on the same selection. Without this field the only guard
|
|
189
|
+
* available is an early return inside `onClick` — which does stop the
|
|
190
|
+
* second call, but leaves the button looking live, so the affordance lies
|
|
191
|
+
* about what pressing it will do. Concretely, that is what blocks a real
|
|
192
|
+
* consumer's hand-rolled bulk bar from migrating to `DataBulkBar`: it
|
|
193
|
+
* would have to give up its double-submit guard to do so.
|
|
194
|
+
*
|
|
195
|
+
* Optional and defaulted off — omitting it behaves exactly as before.
|
|
196
|
+
*/
|
|
197
|
+
disabled?: boolean;
|
|
175
198
|
}
|
|
176
199
|
interface DataBulkBarProps {
|
|
177
200
|
selected: Set<string>;
|
|
@@ -405,6 +428,26 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
405
428
|
searchPlaceholder?: string;
|
|
406
429
|
initialFilters?: FilterParam[];
|
|
407
430
|
onFiltersChange?: (filters: FilterParam[]) => void;
|
|
431
|
+
/**
|
|
432
|
+
* Extra multi-select filter comboboxes, handed straight to `DataFilters`
|
|
433
|
+
* and rendered beside the status chips. Each config owns its own
|
|
434
|
+
* `selected` array and `onChange` — ResourcePage never reads or writes
|
|
435
|
+
* them, because it cannot know what a caller's "Grade" or "Subject"
|
|
436
|
+
* combobox means against the row shape. That is deliberate rather than a
|
|
437
|
+
* gap: the built-in machinery (`searchFields`, `statusFilter`) covers the
|
|
438
|
+
* two shapes it *can* interpret, and this prop covers everything else by
|
|
439
|
+
* getting out of the way.
|
|
440
|
+
*
|
|
441
|
+
* Two consequences of that hands-off stance, stated rather than hidden:
|
|
442
|
+
* - These selections are invisible to `page.hasActiveFilters`, so the
|
|
443
|
+
* header's "Clear filters" affordance does not appear for them alone,
|
|
444
|
+
* and `onClearFilters` does not reset them. DataFilters' own "Clear all"
|
|
445
|
+
* does call each config's `onChange([])`, so wire real clearing there.
|
|
446
|
+
* - In client-side mode the rows are NOT re-filtered by these selections.
|
|
447
|
+
* A caller using them is expected to filter its own `data` (or ask the
|
|
448
|
+
* server to), exactly as it already does in server-controlled mode.
|
|
449
|
+
*/
|
|
450
|
+
filters?: DataFilterConfig[];
|
|
408
451
|
/** Current page, 1-based. Server-controlled mode only. */
|
|
409
452
|
page?: number;
|
|
410
453
|
/**
|
|
@@ -448,6 +491,35 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
448
491
|
onSortChange?: (sort: SortParam | null) => void;
|
|
449
492
|
/** Called when the user changes the search text (including clearing it). */
|
|
450
493
|
onSearchChange?: (search: string) => void;
|
|
494
|
+
/**
|
|
495
|
+
* Server-data mode: the caller's fetch is in flight, so render the shell's
|
|
496
|
+
* loading skeleton instead of the (as yet meaningless) `data`. Ignored in
|
|
497
|
+
* client-side mode, where `useResource`'s own loading state is used.
|
|
498
|
+
*/
|
|
499
|
+
loading?: boolean;
|
|
500
|
+
/**
|
|
501
|
+
* Server-data mode: the caller's fetch failed, so render the shell's error
|
|
502
|
+
* panel instead of an empty table. Takes effect even when `data` is
|
|
503
|
+
* non-empty — stale rows beside no indication of failure are exactly the
|
|
504
|
+
* silent-failure this prop exists to prevent. Ignored in client-side mode,
|
|
505
|
+
* where `useResource`'s own error state is used.
|
|
506
|
+
*/
|
|
507
|
+
error?: boolean;
|
|
508
|
+
/**
|
|
509
|
+
* Message shown in the error panel, in place of the shell's generic
|
|
510
|
+
* "Failed to load …". Forwarded in both modes — it is inert unless an
|
|
511
|
+
* error state is actually showing — so a client-side page can also give
|
|
512
|
+
* its failure a human sentence.
|
|
513
|
+
*/
|
|
514
|
+
errorMessage?: string;
|
|
515
|
+
/**
|
|
516
|
+
* Server-data mode: invoked by the error panel's Retry button. The button
|
|
517
|
+
* only appears when this is supplied, so a caller with no way to retry
|
|
518
|
+
* simply omits it and gets an error panel without a dead control. In
|
|
519
|
+
* client-side mode the button is already wired to `useResource`'s own
|
|
520
|
+
* `refetch` and this prop is ignored.
|
|
521
|
+
*/
|
|
522
|
+
onRetry?: () => void;
|
|
451
523
|
/** Saved filter views to offer. Pair with `useSavedViews` for per-user storage. */
|
|
452
524
|
savedViews?: SavedViewItem[];
|
|
453
525
|
onLoadView?: (filters: Record<string, unknown>) => void;
|
|
@@ -496,6 +568,13 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
496
568
|
icon?: LucideIcon;
|
|
497
569
|
variant?: "default" | "destructive";
|
|
498
570
|
onAction: (ids: string[]) => void | Promise<void>;
|
|
571
|
+
/**
|
|
572
|
+
* Forwarded straight to the rendered bulk button (see `BulkAction`).
|
|
573
|
+
* Exists so a consumer can kill every bulk action while one of them is
|
|
574
|
+
* in flight — an early return inside `onAction` blocks the second call
|
|
575
|
+
* but leaves the button looking live, which misstates what it will do.
|
|
576
|
+
*/
|
|
577
|
+
disabled?: boolean;
|
|
499
578
|
}>;
|
|
500
579
|
/**
|
|
501
580
|
* Override the default card renderer used for grid view (and, absent a
|
|
@@ -512,6 +591,23 @@ interface ResourcePageProps<T extends Record<string, unknown> = Record<string, u
|
|
|
512
591
|
* call `actions.open()` from wherever your card should activate.
|
|
513
592
|
*/
|
|
514
593
|
renderCard?: (item: T, index: number, actions: ResourceCardActions<T>) => ReactNode;
|
|
594
|
+
/**
|
|
595
|
+
* Replace the built-in `DataTable` that table view renders, keeping every
|
|
596
|
+
* other piece of the page — header, counts, view settings, filters,
|
|
597
|
+
* bulk bar, detail panel, form, pager — exactly as it is. The counterpart
|
|
598
|
+
* to `renderCard`, which does the same for grid and list view.
|
|
599
|
+
*
|
|
600
|
+
* Receives the rows for the current view, which in server-controlled mode
|
|
601
|
+
* is precisely the page the server returned. Ownership is total: row
|
|
602
|
+
* click-through, selection, density, column visibility and resizing are
|
|
603
|
+
* all things the built-in table wires for you and a custom table must
|
|
604
|
+
* wire for itself. That is the trade this prop makes, and it is why
|
|
605
|
+
* `renderCard` remains the lighter option when only the *cells* need to
|
|
606
|
+
* change — a `ResourceColumnDef.render` is lighter still.
|
|
607
|
+
*
|
|
608
|
+
* Omitted, the default `DataTable` render is used unchanged.
|
|
609
|
+
*/
|
|
610
|
+
renderTable?: (items: T[]) => ReactNode;
|
|
515
611
|
/**
|
|
516
612
|
* Tailwind grid-column classes for the card grid, passed through to
|
|
517
613
|
* `DataGrid`'s `gridCols` — e.g. `"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"`.
|
|
@@ -15,17 +15,17 @@ import '../../chunk-B2GIBUQD.js';
|
|
|
15
15
|
import { Badge } from '../../chunk-NPNADZHM.js';
|
|
16
16
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../chunk-FK42ZGGV.js';
|
|
17
17
|
import { Popover, PopoverTrigger, PopoverContent } from '../../chunk-22XB3Q6H.js';
|
|
18
|
+
import { PageLoading } from '../../chunk-YRYQXU3G.js';
|
|
19
|
+
import { Spinner } from '../../chunk-OVU3Z2MT.js';
|
|
18
20
|
import { Switch } from '../../chunk-27ELYTGE.js';
|
|
21
|
+
import { DetailPage } from '../../chunk-AFZ76Q26.js';
|
|
22
|
+
import '../../chunk-D2LIXA2Y.js';
|
|
23
|
+
import '../../chunk-AVFZ4P4B.js';
|
|
19
24
|
import { EmptyState } from '../../chunk-65SPT7EA.js';
|
|
20
25
|
export { EmptyState } from '../../chunk-65SPT7EA.js';
|
|
21
26
|
import { Button } from '../../chunk-NWSCYLPT.js';
|
|
22
|
-
import { PageLoading } from '../../chunk-YRYQXU3G.js';
|
|
23
|
-
import { Spinner } from '../../chunk-OVU3Z2MT.js';
|
|
24
27
|
import { FormField, FormLabel, FormError, FormDescription } from '../../chunk-R7QFP6EL.js';
|
|
25
28
|
import '../../chunk-F47U5KNW.js';
|
|
26
|
-
import { DetailPage } from '../../chunk-AFZ76Q26.js';
|
|
27
|
-
import '../../chunk-D2LIXA2Y.js';
|
|
28
|
-
import '../../chunk-AVFZ4P4B.js';
|
|
29
29
|
import { titleCase, useResourcePage } from '../../chunk-GDFNAHI4.js';
|
|
30
30
|
export { useSavedViews } from '../../chunk-GCQFVMPI.js';
|
|
31
31
|
import '../../chunk-FTTNGGLA.js';
|
|
@@ -1146,6 +1146,7 @@ function DataBulkBar({
|
|
|
1146
1146
|
size: "sm",
|
|
1147
1147
|
variant: action.variant || "outline",
|
|
1148
1148
|
onClick: action.onClick,
|
|
1149
|
+
disabled: action.disabled,
|
|
1149
1150
|
children: [
|
|
1150
1151
|
/* @__PURE__ */ jsx(ActionIcon, { className: "h-3.5 w-3.5 mr-1" }),
|
|
1151
1152
|
action.label
|
|
@@ -2424,12 +2425,17 @@ function ResourcePage(props) {
|
|
|
2424
2425
|
searchPlaceholder,
|
|
2425
2426
|
initialFilters,
|
|
2426
2427
|
onFiltersChange,
|
|
2428
|
+
filters: filtersProp,
|
|
2427
2429
|
page: pageProp,
|
|
2428
2430
|
perPage: perPageProp,
|
|
2429
2431
|
onPageChange,
|
|
2430
2432
|
onPerPageChange,
|
|
2431
2433
|
onSortChange,
|
|
2432
2434
|
onSearchChange,
|
|
2435
|
+
loading: loadingProp,
|
|
2436
|
+
error: errorProp,
|
|
2437
|
+
errorMessage,
|
|
2438
|
+
onRetry: onRetryProp,
|
|
2433
2439
|
savedViews,
|
|
2434
2440
|
onLoadView,
|
|
2435
2441
|
onSaveView,
|
|
@@ -2455,6 +2461,7 @@ function ResourcePage(props) {
|
|
|
2455
2461
|
formLayout: formLayoutProp,
|
|
2456
2462
|
bulkActions,
|
|
2457
2463
|
renderCard: renderCardProp,
|
|
2464
|
+
renderTable: renderTableProp,
|
|
2458
2465
|
gridCols: gridColsProp,
|
|
2459
2466
|
keyboardNavigation = true,
|
|
2460
2467
|
toolbar,
|
|
@@ -2475,8 +2482,8 @@ function ResourcePage(props) {
|
|
|
2475
2482
|
);
|
|
2476
2483
|
const displayData = isClientMode ? resourceHook.list.data?.data ?? [] : dataProp ?? [];
|
|
2477
2484
|
const displayTotal = isClientMode ? resourceHook.list.data?.total ?? 0 : totalProp ?? 0;
|
|
2478
|
-
const isLoading = isClientMode ? resourceHook.list.isLoading : false;
|
|
2479
|
-
const hasError = isClientMode ? resourceHook.list.isError : false;
|
|
2485
|
+
const isLoading = isClientMode ? resourceHook.list.isLoading : loadingProp ?? false;
|
|
2486
|
+
const hasError = isClientMode ? resourceHook.list.isError : errorProp ?? false;
|
|
2480
2487
|
const page = useResourcePage({
|
|
2481
2488
|
resource: resourceName,
|
|
2482
2489
|
data: displayData,
|
|
@@ -2807,7 +2814,8 @@ function ResourcePage(props) {
|
|
|
2807
2814
|
isLoading,
|
|
2808
2815
|
hasError,
|
|
2809
2816
|
isEmpty,
|
|
2810
|
-
|
|
2817
|
+
errorMessage,
|
|
2818
|
+
onRetry: isClientMode ? () => resourceHook.list.refetch() : onRetryProp,
|
|
2811
2819
|
viewMode: page.viewMode,
|
|
2812
2820
|
onViewModeChange: page.setViewMode,
|
|
2813
2821
|
pageSize: effectivePageSize,
|
|
@@ -2827,6 +2835,7 @@ function ResourcePage(props) {
|
|
|
2827
2835
|
sort: page.filterConfig.sort,
|
|
2828
2836
|
onSortChange: handleSortSelect,
|
|
2829
2837
|
sortOptions: page.filterConfig.sortOptions,
|
|
2838
|
+
filters: filtersProp,
|
|
2830
2839
|
statusChips: statusChipsConfig,
|
|
2831
2840
|
savedViews,
|
|
2832
2841
|
onLoadView,
|
|
@@ -2848,6 +2857,7 @@ function ResourcePage(props) {
|
|
|
2848
2857
|
label: a.label,
|
|
2849
2858
|
icon: a.icon ?? Trash2,
|
|
2850
2859
|
variant: a.variant,
|
|
2860
|
+
disabled: a.disabled,
|
|
2851
2861
|
onClick: () => {
|
|
2852
2862
|
void a.onAction(Array.from(selectedIds));
|
|
2853
2863
|
}
|
|
@@ -2863,7 +2873,7 @@ function ResourcePage(props) {
|
|
|
2863
2873
|
getKey: (item) => String(item.id ?? ""),
|
|
2864
2874
|
focusedIndex: keyboardNav.focusedIndex,
|
|
2865
2875
|
keyboardContainerRef: keyboardNav.containerRef,
|
|
2866
|
-
renderTable: (items) => /* @__PURE__ */ jsx(
|
|
2876
|
+
renderTable: (items) => renderTableProp ? renderTableProp(items) : /* @__PURE__ */ jsx(
|
|
2867
2877
|
DataTable,
|
|
2868
2878
|
{
|
|
2869
2879
|
items,
|