twico-ui 1.43.0 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2624,6 +2624,20 @@ interface DatatableBatchAction<T = any> {
2624
2624
  /** #322: hide the action entirely when the predicate returns true (e.g. `(_, rows) => rows.every(r => r.isGroup)`
2625
2625
  * drops "Mark as new thread" when only group rows are selected). A static `true` always hides it. */
2626
2626
  hidden?: boolean | ((keys: Array<string | number>, rows: T[]) => boolean);
2627
+ /** #479: why the action is disabled, shown as the button's tooltip — the batch mirror of the row-action
2628
+ * `disabledReason` (#399/#419). It may be a **predicate over the selection**, because that is where the
2629
+ * answer usually lives: a batch is refused for selection-dependent reasons ("that selection is 5.1 GB; the
2630
+ * limit is 4 GB", "at most 50 items per batch") that the label cannot carry and the user cannot infer from
2631
+ * a greyed-out button.
2632
+ *
2633
+ * When a reason is present the button renders `aria-disabled` rather than native `disabled`, so it stays in
2634
+ * the tab order and the reason is reachable by hover AND keyboard; click and Enter/Space stay blocked. With
2635
+ * no reason the tooltip would only repeat the label, so the button stays natively disabled and out of the
2636
+ * tab order — the same rule as the row-action path. There is deliberately no `hiddenReason`: a hidden
2637
+ * action explains nothing. */
2638
+ disabledReason?:
2639
+ | React.ReactNode
2640
+ | ((keys: Array<string | number>, rows: T[]) => React.ReactNode);
2627
2641
  }
2628
2642
 
2629
2643
  declare function Datatable<T = any>(props: DatatableProps<T>): React.JSX.Element;
@@ -2688,8 +2702,19 @@ interface CardGridSortOption {
2688
2702
  interface CardGridProps<Row = any> extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
2689
2703
  /** The rows: the full set in client mode, or the current page in `serverMode`. */
2690
2704
  rows?: Row[];
2691
- /** Render one card for a row. Required. */
2692
- renderCard: (row: Row, index: number) => React.ReactNode;
2705
+ /**
2706
+ * Render one card for a row. Required.
2707
+ *
2708
+ * #478: with `checkboxSelection`, the third argument carries that card's selection state so a
2709
+ * consumer can put the control inside their own layout (set `selectionPlacement="none"` to drop the
2710
+ * grid's overlay) without re-implementing any selection state. It is `undefined` when
2711
+ * `checkboxSelection` is off.
2712
+ */
2713
+ renderCard: (
2714
+ row: Row,
2715
+ index: number,
2716
+ selection?: { selected: boolean; toggle: () => void },
2717
+ ) => React.ReactNode;
2693
2718
  /** Stable key per row: a field name or an accessor. @default "id" */
2694
2719
  rowKey?: string | ((row: Row) => React.Key);
2695
2720
  /** Min card width for the responsive auto-fill grid (Grid `minChildWidth`). @default "18rem" */
@@ -2752,6 +2777,86 @@ interface CardGridProps<Row = any> extends Omit<React.HTMLAttributes<HTMLDivElem
2752
2777
  emptyState?: React.ReactNode;
2753
2778
  /** Optional heading above the grid. */
2754
2779
  label?: React.ReactNode;
2780
+
2781
+ /**
2782
+ * #478: show a selection checkbox on each card and a selection toolbar when anything is picked —
2783
+ * the same prop name, and the same behaviour, as `Datatable`. A card grid is what you reach for
2784
+ * when the rows are visual (assets, previews, renders), where "pick several and do one thing to
2785
+ * them" is most of the point.
2786
+ * @default false
2787
+ */
2788
+ checkboxSelection?: boolean;
2789
+ /**
2790
+ * Actions shown in the selection toolbar, with the same shape as `DatatableBatchAction` — including
2791
+ * `disabled` / `hidden` / `disabledReason` as predicates over the current selection (#479), and
2792
+ * `ctx.anchorEl` so an action can anchor a popover to its own button.
2793
+ */
2794
+ batchActions?: CardGridBatchAction<Row>[];
2795
+ /** Fired when the selection changes — `(keys, rows)`, matching `Datatable.onRowSelectionChange`. */
2796
+ onRowSelectionChange?: (keys: Array<string | number>, rows: Row[]) => void;
2797
+ /**
2798
+ * Where the grid draws its checkbox over each card. `"none"` suppresses the overlay entirely for
2799
+ * consumers who render the control themselves from `renderCard`'s third argument.
2800
+ * @default "top-start"
2801
+ */
2802
+ selectionPlacement?: "top-start" | "top-end" | "bottom-start" | "bottom-end" | "none";
2803
+ /**
2804
+ * Per-card `aria-label` for the checkbox. Worth setting: unlike a table row, a card's own content is
2805
+ * not necessarily announced with the control, so the default `"Select item"` is the same for every
2806
+ * card. @default (row, index) => "Select item"
2807
+ */
2808
+ selectionLabel?: (row: Row, index: number) => string;
2809
+ /** Show the page-scoped "Select all on this page" control. @default true */
2810
+ showSelectAll?: boolean;
2811
+ /**
2812
+ * Retain the row OBJECTS of off-page selections so `batchActions` and `onRowSelectionChange` receive
2813
+ * them for a selection spanning several pages. `keys` is always the authoritative cross-page
2814
+ * selection (as in `Datatable`); this exists because the grid only renders one page, which is the
2815
+ * `Map<id, row>` consumers were otherwise hand-rolling.
2816
+ *
2817
+ * Opt-in because it holds references the host may expect to be released, and **bounded**: `true`
2818
+ * retains up to 1000 rows, or pass a number for your own cap. Past the cap it stops retaining and
2819
+ * those keys simply resolve to nothing in `rows`, so re-fetch by key.
2820
+ * @default false
2821
+ */
2822
+ retainSelectedRows?: boolean | number;
2823
+ }
2824
+
2825
+ /** Extra context handed to a CardGrid batch action's `onClick`. */
2826
+ interface CardGridBatchActionContext {
2827
+ /** The toolbar button that was clicked — anchor a popover or panel to it. */
2828
+ anchorEl: HTMLElement;
2829
+ }
2830
+
2831
+ /** A batch action shown in CardGrid's selection toolbar. Mirrors `DatatableBatchAction`. */
2832
+ interface CardGridBatchAction<Row = any> {
2833
+ /** Button label. */
2834
+ label: string;
2835
+ /** Leading icon node. */
2836
+ icon?: React.ReactNode;
2837
+ /** Handler: `(selectedKeys, selectedRows, clearSelection, ctx)`. */
2838
+ onClick?: (
2839
+ keys: Array<string | number>,
2840
+ rows: Row[],
2841
+ clearSelection: () => void,
2842
+ ctx: CardGridBatchActionContext,
2843
+ ) => void;
2844
+ /** Render in danger color. */
2845
+ danger?: boolean;
2846
+ /** Disable the action — a static boolean, or a predicate over the current selection. */
2847
+ disabled?: boolean | ((keys: Array<string | number>, rows: Row[]) => boolean);
2848
+ /** Hide the action entirely — a static boolean, or a predicate over the current selection. */
2849
+ hidden?: boolean | ((keys: Array<string | number>, rows: Row[]) => boolean);
2850
+ /**
2851
+ * Why the action is disabled, shown as the button's tooltip (#479). May be a predicate over the
2852
+ * selection, because that is usually where the answer lives ("that selection is 5.1 GB; the limit is
2853
+ * 4 GB"). With a reason present the button renders `aria-disabled` rather than native `disabled`, so
2854
+ * it stays focusable and the reason is keyboard-reachable; without one it stays natively disabled and
2855
+ * out of the tab order.
2856
+ */
2857
+ disabledReason?:
2858
+ | React.ReactNode
2859
+ | ((keys: Array<string | number>, rows: Row[]) => React.ReactNode);
2755
2860
  }
2756
2861
 
2757
2862
  declare function CardGrid<Row = any>(props: CardGridProps<Row>): React.JSX.Element;
@@ -4961,4 +5066,4 @@ declare function useFilePicker(options?: UseFilePickerOptions): {
4961
5066
  getInputProps: () => React.InputHTMLAttributes<HTMLInputElement> & { ref: React.RefObject<HTMLInputElement | null> };
4962
5067
  };
4963
5068
 
4964
- export { Accordion, type AccordionItem, type AccordionProps, type ActionTone, Alert, type AlertProps, Anchor, type AnchorProps, AppShell, type AppShellProps, Avatar, AvatarMenu, type AvatarMenuProps, type AvatarProps, Badge, type BadgeProps, type BarTone, Box, type BoxOwnProps, type BoxProps, Boxplot, type BoxplotDatum, type BoxplotProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, BubbleChart, type BubbleChartProps, Button, type ButtonProps, Candlestick, type CandlestickDatum, type CandlestickProps, Card, CardGrid, type CardGridFilter, type CardGridProps, type CardGridQuery, type CardGridSort, type CardGridSortOption, type CardProps, Carousel, type CarouselProps, Chart, type ChartDatum, type ChartProps, Checkbox, type CheckboxProps, Code, type CodeProps, ColorPicker, type ColorPickerProps, ColorSchemeScript, type ColorSchemeScriptOptions, type ColorSchemeScriptProps, Combobox, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type Command, CommandPalette, type CommandPaletteProps, Container, type ContainerOwnProps, type ContainerProps, Currency, CurrencyField, type CurrencyFieldProps, type CurrencyOption, type CurrencyProps, Datatable, type DatatableBatchAction, type DatatableBatchActionContext, type DatatableBatchUpdateDetail, type DatatableColumn, type DatatableFilter, type DatatableFilterOp, type DatatableNumberOp, type DatatableProps, type DatatableQuery, type DatatableRowAction, type DatatableState, type DatatableStringOp, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, DateTimePicker, type DateTimePickerProps, Dialog, type DialogProps, type DiffOp, DiffTable, type DiffTableColumn, type DiffTableProps, Divider, type DividerProps, DonutChart, type DonutChartDatum, type DonutChartProps, Drawer, type DrawerProps, EmptyState, type EmptyStateProps, Field, type FieldProps, FileUpload, type FileUploadProps, FilterBar, type FilterBarProps, type FilterClause, type FilterField, type FilterFieldType, type FilterValues, Form, type FormProps, FunnelChart, type FunnelChartProps, type FunnelDatum, Gauge, type GaugeProps, type GaugeSeries, Grid, type GridOwnProps, type GridProps, Heading, type HeadingOwnProps, type HeadingProps, type HeadingSize, Heatmap, type HeatmapDatum, type HeatmapProps, IconButton, type IconButtonProps, Image, type ImageProps, ImageViewer, type ImageViewerControlsApi, type ImageViewerProps, type ImageViewerState, Input, type InputProps, Kanban, type KanbanCard, type KanbanColumn, type KanbanProps, Kbd, type KbdProps, Label, type LabelProps, List, type ListItemData, type ListProps, Menu, type MenuItemDef, type MenuProps, MultiSelect, type MultiSelectGroup, type MultiSelectOption, type MultiSelectProps, Navbar, type NavbarLink, type NavbarProps, type Option, type OptionGroup, Pagination, type PaginationProps, PieChart, type PieChartDatum, type PieChartProps, PolarAreaChart, type PolarAreaChartProps, type PolarAreaDatum, type PolymorphicAs, type PolymorphicComponent, type PolymorphicProps, type PolymorphicPropsWithRef, type PolymorphicRef, Popover, type PopoverProps, Portal, type PortalProps, Pre, type PreProps, Progress, type ProgressProps, RadarChart, type RadarChartProps, type RadarDatum, Radio, RadioGroup, type RadioGroupOption, type RadioGroupProps, type RadioProps, RangeChart, type RangeChartProps, type RangeDatum, Rating, type RatingProps, type ResponsiveColumns, ScatterChart, type ScatterChartProps, type ScatterPoint, type ScatterSeries, Select, type SelectGroup, type SelectOption, type SelectProps, Sidebar, type SidebarItem, type SidebarProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Sparkline, type SparklineDatum, type SparklineProps, Spinner, type SpinnerProps, Stack, type StackOwnProps, type StackProps, Stat, type StatProps, type Step, Stepper, type StepperProps, Switch, type SwitchProps, type Sx, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Tag, type TagProps, Text, type TextOwnProps, type TextProps, type TextTone, Textarea, type TextareaProps, type Theme, ThemeProvider, type ThemeProviderProps, type ThemeTokens, TimePicker, type TimePickerProps, Timeline, type TimelineItem, type TimelineProps, Toast, type ToastApi, type ToastOptions, type ToastProps, ToastProvider, type ToastProviderProps, type ToastTone, ToastViewport, type ToastViewportProps, ToggleGroup, type ToggleGroupItem, type ToggleGroupProps, type Tone, Tooltip, type TooltipProps, type TreeNode, TreeView, type TreeViewProps, Treemap, type TreemapDatum, type TreemapProps, TwicoProvider, type TwicoProviderProps, type UseColorSchemeOptions, type UseFilePickerOptions, type UseFocusTrapOptions, type UseKeyPressOptions, type UseLocalStorageOptions, type UseMediaQueryOptions, type UseWindowSizeOptions, VisuallyHidden, type VisuallyHiddenProps, type WithSx, createTheme, getColorSchemeScript, runDatatableQuery, useClickOutside, useColorScheme, useControllableState, useCopyToClipboard, useDebouncedCallback, useDebouncedValue, useDisclosure, useEventListener, useFilePicker, useFocusTrap, useHover, useId, useIntersectionObserver, useInterval, useIsomorphicLayoutEffect, useKeyPress, useLocalStorage, useMediaQuery, useMounted, usePortal, usePrefersReducedMotion, usePrevious, useScrollLock, useTimeout, useToast, useToggle, useWindowSize };
5069
+ export { Accordion, type AccordionItem, type AccordionProps, type ActionTone, Alert, type AlertProps, Anchor, type AnchorProps, AppShell, type AppShellProps, Avatar, AvatarMenu, type AvatarMenuProps, type AvatarProps, Badge, type BadgeProps, type BarTone, Box, type BoxOwnProps, type BoxProps, Boxplot, type BoxplotDatum, type BoxplotProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, BubbleChart, type BubbleChartProps, Button, type ButtonProps, Candlestick, type CandlestickDatum, type CandlestickProps, Card, CardGrid, type CardGridBatchAction, type CardGridBatchActionContext, type CardGridFilter, type CardGridProps, type CardGridQuery, type CardGridSort, type CardGridSortOption, type CardProps, Carousel, type CarouselProps, Chart, type ChartDatum, type ChartProps, Checkbox, type CheckboxProps, Code, type CodeProps, ColorPicker, type ColorPickerProps, ColorSchemeScript, type ColorSchemeScriptOptions, type ColorSchemeScriptProps, Combobox, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type Command, CommandPalette, type CommandPaletteProps, Container, type ContainerOwnProps, type ContainerProps, Currency, CurrencyField, type CurrencyFieldProps, type CurrencyOption, type CurrencyProps, Datatable, type DatatableBatchAction, type DatatableBatchActionContext, type DatatableBatchUpdateDetail, type DatatableColumn, type DatatableFilter, type DatatableFilterOp, type DatatableNumberOp, type DatatableProps, type DatatableQuery, type DatatableRowAction, type DatatableState, type DatatableStringOp, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, DateTimePicker, type DateTimePickerProps, Dialog, type DialogProps, type DiffOp, DiffTable, type DiffTableColumn, type DiffTableProps, Divider, type DividerProps, DonutChart, type DonutChartDatum, type DonutChartProps, Drawer, type DrawerProps, EmptyState, type EmptyStateProps, Field, type FieldProps, FileUpload, type FileUploadProps, FilterBar, type FilterBarProps, type FilterClause, type FilterField, type FilterFieldType, type FilterValues, Form, type FormProps, FunnelChart, type FunnelChartProps, type FunnelDatum, Gauge, type GaugeProps, type GaugeSeries, Grid, type GridOwnProps, type GridProps, Heading, type HeadingOwnProps, type HeadingProps, type HeadingSize, Heatmap, type HeatmapDatum, type HeatmapProps, IconButton, type IconButtonProps, Image, type ImageProps, ImageViewer, type ImageViewerControlsApi, type ImageViewerProps, type ImageViewerState, Input, type InputProps, Kanban, type KanbanCard, type KanbanColumn, type KanbanProps, Kbd, type KbdProps, Label, type LabelProps, List, type ListItemData, type ListProps, Menu, type MenuItemDef, type MenuProps, MultiSelect, type MultiSelectGroup, type MultiSelectOption, type MultiSelectProps, Navbar, type NavbarLink, type NavbarProps, type Option, type OptionGroup, Pagination, type PaginationProps, PieChart, type PieChartDatum, type PieChartProps, PolarAreaChart, type PolarAreaChartProps, type PolarAreaDatum, type PolymorphicAs, type PolymorphicComponent, type PolymorphicProps, type PolymorphicPropsWithRef, type PolymorphicRef, Popover, type PopoverProps, Portal, type PortalProps, Pre, type PreProps, Progress, type ProgressProps, RadarChart, type RadarChartProps, type RadarDatum, Radio, RadioGroup, type RadioGroupOption, type RadioGroupProps, type RadioProps, RangeChart, type RangeChartProps, type RangeDatum, Rating, type RatingProps, type ResponsiveColumns, ScatterChart, type ScatterChartProps, type ScatterPoint, type ScatterSeries, Select, type SelectGroup, type SelectOption, type SelectProps, Sidebar, type SidebarItem, type SidebarProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Sparkline, type SparklineDatum, type SparklineProps, Spinner, type SpinnerProps, Stack, type StackOwnProps, type StackProps, Stat, type StatProps, type Step, Stepper, type StepperProps, Switch, type SwitchProps, type Sx, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Tag, type TagProps, Text, type TextOwnProps, type TextProps, type TextTone, Textarea, type TextareaProps, type Theme, ThemeProvider, type ThemeProviderProps, type ThemeTokens, TimePicker, type TimePickerProps, Timeline, type TimelineItem, type TimelineProps, Toast, type ToastApi, type ToastOptions, type ToastProps, ToastProvider, type ToastProviderProps, type ToastTone, ToastViewport, type ToastViewportProps, ToggleGroup, type ToggleGroupItem, type ToggleGroupProps, type Tone, Tooltip, type TooltipProps, type TreeNode, TreeView, type TreeViewProps, Treemap, type TreemapDatum, type TreemapProps, TwicoProvider, type TwicoProviderProps, type UseColorSchemeOptions, type UseFilePickerOptions, type UseFocusTrapOptions, type UseKeyPressOptions, type UseLocalStorageOptions, type UseMediaQueryOptions, type UseWindowSizeOptions, VisuallyHidden, type VisuallyHiddenProps, type WithSx, createTheme, getColorSchemeScript, runDatatableQuery, useClickOutside, useColorScheme, useControllableState, useCopyToClipboard, useDebouncedCallback, useDebouncedValue, useDisclosure, useEventListener, useFilePicker, useFocusTrap, useHover, useId, useIntersectionObserver, useInterval, useIsomorphicLayoutEffect, useKeyPress, useLocalStorage, useMediaQuery, useMounted, usePortal, usePrefersReducedMotion, usePrevious, useScrollLock, useTimeout, useToast, useToggle, useWindowSize };
package/dist/index.d.ts CHANGED
@@ -2624,6 +2624,20 @@ interface DatatableBatchAction<T = any> {
2624
2624
  /** #322: hide the action entirely when the predicate returns true (e.g. `(_, rows) => rows.every(r => r.isGroup)`
2625
2625
  * drops "Mark as new thread" when only group rows are selected). A static `true` always hides it. */
2626
2626
  hidden?: boolean | ((keys: Array<string | number>, rows: T[]) => boolean);
2627
+ /** #479: why the action is disabled, shown as the button's tooltip — the batch mirror of the row-action
2628
+ * `disabledReason` (#399/#419). It may be a **predicate over the selection**, because that is where the
2629
+ * answer usually lives: a batch is refused for selection-dependent reasons ("that selection is 5.1 GB; the
2630
+ * limit is 4 GB", "at most 50 items per batch") that the label cannot carry and the user cannot infer from
2631
+ * a greyed-out button.
2632
+ *
2633
+ * When a reason is present the button renders `aria-disabled` rather than native `disabled`, so it stays in
2634
+ * the tab order and the reason is reachable by hover AND keyboard; click and Enter/Space stay blocked. With
2635
+ * no reason the tooltip would only repeat the label, so the button stays natively disabled and out of the
2636
+ * tab order — the same rule as the row-action path. There is deliberately no `hiddenReason`: a hidden
2637
+ * action explains nothing. */
2638
+ disabledReason?:
2639
+ | React.ReactNode
2640
+ | ((keys: Array<string | number>, rows: T[]) => React.ReactNode);
2627
2641
  }
2628
2642
 
2629
2643
  declare function Datatable<T = any>(props: DatatableProps<T>): React.JSX.Element;
@@ -2688,8 +2702,19 @@ interface CardGridSortOption {
2688
2702
  interface CardGridProps<Row = any> extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
2689
2703
  /** The rows: the full set in client mode, or the current page in `serverMode`. */
2690
2704
  rows?: Row[];
2691
- /** Render one card for a row. Required. */
2692
- renderCard: (row: Row, index: number) => React.ReactNode;
2705
+ /**
2706
+ * Render one card for a row. Required.
2707
+ *
2708
+ * #478: with `checkboxSelection`, the third argument carries that card's selection state so a
2709
+ * consumer can put the control inside their own layout (set `selectionPlacement="none"` to drop the
2710
+ * grid's overlay) without re-implementing any selection state. It is `undefined` when
2711
+ * `checkboxSelection` is off.
2712
+ */
2713
+ renderCard: (
2714
+ row: Row,
2715
+ index: number,
2716
+ selection?: { selected: boolean; toggle: () => void },
2717
+ ) => React.ReactNode;
2693
2718
  /** Stable key per row: a field name or an accessor. @default "id" */
2694
2719
  rowKey?: string | ((row: Row) => React.Key);
2695
2720
  /** Min card width for the responsive auto-fill grid (Grid `minChildWidth`). @default "18rem" */
@@ -2752,6 +2777,86 @@ interface CardGridProps<Row = any> extends Omit<React.HTMLAttributes<HTMLDivElem
2752
2777
  emptyState?: React.ReactNode;
2753
2778
  /** Optional heading above the grid. */
2754
2779
  label?: React.ReactNode;
2780
+
2781
+ /**
2782
+ * #478: show a selection checkbox on each card and a selection toolbar when anything is picked —
2783
+ * the same prop name, and the same behaviour, as `Datatable`. A card grid is what you reach for
2784
+ * when the rows are visual (assets, previews, renders), where "pick several and do one thing to
2785
+ * them" is most of the point.
2786
+ * @default false
2787
+ */
2788
+ checkboxSelection?: boolean;
2789
+ /**
2790
+ * Actions shown in the selection toolbar, with the same shape as `DatatableBatchAction` — including
2791
+ * `disabled` / `hidden` / `disabledReason` as predicates over the current selection (#479), and
2792
+ * `ctx.anchorEl` so an action can anchor a popover to its own button.
2793
+ */
2794
+ batchActions?: CardGridBatchAction<Row>[];
2795
+ /** Fired when the selection changes — `(keys, rows)`, matching `Datatable.onRowSelectionChange`. */
2796
+ onRowSelectionChange?: (keys: Array<string | number>, rows: Row[]) => void;
2797
+ /**
2798
+ * Where the grid draws its checkbox over each card. `"none"` suppresses the overlay entirely for
2799
+ * consumers who render the control themselves from `renderCard`'s third argument.
2800
+ * @default "top-start"
2801
+ */
2802
+ selectionPlacement?: "top-start" | "top-end" | "bottom-start" | "bottom-end" | "none";
2803
+ /**
2804
+ * Per-card `aria-label` for the checkbox. Worth setting: unlike a table row, a card's own content is
2805
+ * not necessarily announced with the control, so the default `"Select item"` is the same for every
2806
+ * card. @default (row, index) => "Select item"
2807
+ */
2808
+ selectionLabel?: (row: Row, index: number) => string;
2809
+ /** Show the page-scoped "Select all on this page" control. @default true */
2810
+ showSelectAll?: boolean;
2811
+ /**
2812
+ * Retain the row OBJECTS of off-page selections so `batchActions` and `onRowSelectionChange` receive
2813
+ * them for a selection spanning several pages. `keys` is always the authoritative cross-page
2814
+ * selection (as in `Datatable`); this exists because the grid only renders one page, which is the
2815
+ * `Map<id, row>` consumers were otherwise hand-rolling.
2816
+ *
2817
+ * Opt-in because it holds references the host may expect to be released, and **bounded**: `true`
2818
+ * retains up to 1000 rows, or pass a number for your own cap. Past the cap it stops retaining and
2819
+ * those keys simply resolve to nothing in `rows`, so re-fetch by key.
2820
+ * @default false
2821
+ */
2822
+ retainSelectedRows?: boolean | number;
2823
+ }
2824
+
2825
+ /** Extra context handed to a CardGrid batch action's `onClick`. */
2826
+ interface CardGridBatchActionContext {
2827
+ /** The toolbar button that was clicked — anchor a popover or panel to it. */
2828
+ anchorEl: HTMLElement;
2829
+ }
2830
+
2831
+ /** A batch action shown in CardGrid's selection toolbar. Mirrors `DatatableBatchAction`. */
2832
+ interface CardGridBatchAction<Row = any> {
2833
+ /** Button label. */
2834
+ label: string;
2835
+ /** Leading icon node. */
2836
+ icon?: React.ReactNode;
2837
+ /** Handler: `(selectedKeys, selectedRows, clearSelection, ctx)`. */
2838
+ onClick?: (
2839
+ keys: Array<string | number>,
2840
+ rows: Row[],
2841
+ clearSelection: () => void,
2842
+ ctx: CardGridBatchActionContext,
2843
+ ) => void;
2844
+ /** Render in danger color. */
2845
+ danger?: boolean;
2846
+ /** Disable the action — a static boolean, or a predicate over the current selection. */
2847
+ disabled?: boolean | ((keys: Array<string | number>, rows: Row[]) => boolean);
2848
+ /** Hide the action entirely — a static boolean, or a predicate over the current selection. */
2849
+ hidden?: boolean | ((keys: Array<string | number>, rows: Row[]) => boolean);
2850
+ /**
2851
+ * Why the action is disabled, shown as the button's tooltip (#479). May be a predicate over the
2852
+ * selection, because that is usually where the answer lives ("that selection is 5.1 GB; the limit is
2853
+ * 4 GB"). With a reason present the button renders `aria-disabled` rather than native `disabled`, so
2854
+ * it stays focusable and the reason is keyboard-reachable; without one it stays natively disabled and
2855
+ * out of the tab order.
2856
+ */
2857
+ disabledReason?:
2858
+ | React.ReactNode
2859
+ | ((keys: Array<string | number>, rows: Row[]) => React.ReactNode);
2755
2860
  }
2756
2861
 
2757
2862
  declare function CardGrid<Row = any>(props: CardGridProps<Row>): React.JSX.Element;
@@ -4961,4 +5066,4 @@ declare function useFilePicker(options?: UseFilePickerOptions): {
4961
5066
  getInputProps: () => React.InputHTMLAttributes<HTMLInputElement> & { ref: React.RefObject<HTMLInputElement | null> };
4962
5067
  };
4963
5068
 
4964
- export { Accordion, type AccordionItem, type AccordionProps, type ActionTone, Alert, type AlertProps, Anchor, type AnchorProps, AppShell, type AppShellProps, Avatar, AvatarMenu, type AvatarMenuProps, type AvatarProps, Badge, type BadgeProps, type BarTone, Box, type BoxOwnProps, type BoxProps, Boxplot, type BoxplotDatum, type BoxplotProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, BubbleChart, type BubbleChartProps, Button, type ButtonProps, Candlestick, type CandlestickDatum, type CandlestickProps, Card, CardGrid, type CardGridFilter, type CardGridProps, type CardGridQuery, type CardGridSort, type CardGridSortOption, type CardProps, Carousel, type CarouselProps, Chart, type ChartDatum, type ChartProps, Checkbox, type CheckboxProps, Code, type CodeProps, ColorPicker, type ColorPickerProps, ColorSchemeScript, type ColorSchemeScriptOptions, type ColorSchemeScriptProps, Combobox, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type Command, CommandPalette, type CommandPaletteProps, Container, type ContainerOwnProps, type ContainerProps, Currency, CurrencyField, type CurrencyFieldProps, type CurrencyOption, type CurrencyProps, Datatable, type DatatableBatchAction, type DatatableBatchActionContext, type DatatableBatchUpdateDetail, type DatatableColumn, type DatatableFilter, type DatatableFilterOp, type DatatableNumberOp, type DatatableProps, type DatatableQuery, type DatatableRowAction, type DatatableState, type DatatableStringOp, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, DateTimePicker, type DateTimePickerProps, Dialog, type DialogProps, type DiffOp, DiffTable, type DiffTableColumn, type DiffTableProps, Divider, type DividerProps, DonutChart, type DonutChartDatum, type DonutChartProps, Drawer, type DrawerProps, EmptyState, type EmptyStateProps, Field, type FieldProps, FileUpload, type FileUploadProps, FilterBar, type FilterBarProps, type FilterClause, type FilterField, type FilterFieldType, type FilterValues, Form, type FormProps, FunnelChart, type FunnelChartProps, type FunnelDatum, Gauge, type GaugeProps, type GaugeSeries, Grid, type GridOwnProps, type GridProps, Heading, type HeadingOwnProps, type HeadingProps, type HeadingSize, Heatmap, type HeatmapDatum, type HeatmapProps, IconButton, type IconButtonProps, Image, type ImageProps, ImageViewer, type ImageViewerControlsApi, type ImageViewerProps, type ImageViewerState, Input, type InputProps, Kanban, type KanbanCard, type KanbanColumn, type KanbanProps, Kbd, type KbdProps, Label, type LabelProps, List, type ListItemData, type ListProps, Menu, type MenuItemDef, type MenuProps, MultiSelect, type MultiSelectGroup, type MultiSelectOption, type MultiSelectProps, Navbar, type NavbarLink, type NavbarProps, type Option, type OptionGroup, Pagination, type PaginationProps, PieChart, type PieChartDatum, type PieChartProps, PolarAreaChart, type PolarAreaChartProps, type PolarAreaDatum, type PolymorphicAs, type PolymorphicComponent, type PolymorphicProps, type PolymorphicPropsWithRef, type PolymorphicRef, Popover, type PopoverProps, Portal, type PortalProps, Pre, type PreProps, Progress, type ProgressProps, RadarChart, type RadarChartProps, type RadarDatum, Radio, RadioGroup, type RadioGroupOption, type RadioGroupProps, type RadioProps, RangeChart, type RangeChartProps, type RangeDatum, Rating, type RatingProps, type ResponsiveColumns, ScatterChart, type ScatterChartProps, type ScatterPoint, type ScatterSeries, Select, type SelectGroup, type SelectOption, type SelectProps, Sidebar, type SidebarItem, type SidebarProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Sparkline, type SparklineDatum, type SparklineProps, Spinner, type SpinnerProps, Stack, type StackOwnProps, type StackProps, Stat, type StatProps, type Step, Stepper, type StepperProps, Switch, type SwitchProps, type Sx, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Tag, type TagProps, Text, type TextOwnProps, type TextProps, type TextTone, Textarea, type TextareaProps, type Theme, ThemeProvider, type ThemeProviderProps, type ThemeTokens, TimePicker, type TimePickerProps, Timeline, type TimelineItem, type TimelineProps, Toast, type ToastApi, type ToastOptions, type ToastProps, ToastProvider, type ToastProviderProps, type ToastTone, ToastViewport, type ToastViewportProps, ToggleGroup, type ToggleGroupItem, type ToggleGroupProps, type Tone, Tooltip, type TooltipProps, type TreeNode, TreeView, type TreeViewProps, Treemap, type TreemapDatum, type TreemapProps, TwicoProvider, type TwicoProviderProps, type UseColorSchemeOptions, type UseFilePickerOptions, type UseFocusTrapOptions, type UseKeyPressOptions, type UseLocalStorageOptions, type UseMediaQueryOptions, type UseWindowSizeOptions, VisuallyHidden, type VisuallyHiddenProps, type WithSx, createTheme, getColorSchemeScript, runDatatableQuery, useClickOutside, useColorScheme, useControllableState, useCopyToClipboard, useDebouncedCallback, useDebouncedValue, useDisclosure, useEventListener, useFilePicker, useFocusTrap, useHover, useId, useIntersectionObserver, useInterval, useIsomorphicLayoutEffect, useKeyPress, useLocalStorage, useMediaQuery, useMounted, usePortal, usePrefersReducedMotion, usePrevious, useScrollLock, useTimeout, useToast, useToggle, useWindowSize };
5069
+ export { Accordion, type AccordionItem, type AccordionProps, type ActionTone, Alert, type AlertProps, Anchor, type AnchorProps, AppShell, type AppShellProps, Avatar, AvatarMenu, type AvatarMenuProps, type AvatarProps, Badge, type BadgeProps, type BarTone, Box, type BoxOwnProps, type BoxProps, Boxplot, type BoxplotDatum, type BoxplotProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, BubbleChart, type BubbleChartProps, Button, type ButtonProps, Candlestick, type CandlestickDatum, type CandlestickProps, Card, CardGrid, type CardGridBatchAction, type CardGridBatchActionContext, type CardGridFilter, type CardGridProps, type CardGridQuery, type CardGridSort, type CardGridSortOption, type CardProps, Carousel, type CarouselProps, Chart, type ChartDatum, type ChartProps, Checkbox, type CheckboxProps, Code, type CodeProps, ColorPicker, type ColorPickerProps, ColorSchemeScript, type ColorSchemeScriptOptions, type ColorSchemeScriptProps, Combobox, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type Command, CommandPalette, type CommandPaletteProps, Container, type ContainerOwnProps, type ContainerProps, Currency, CurrencyField, type CurrencyFieldProps, type CurrencyOption, type CurrencyProps, Datatable, type DatatableBatchAction, type DatatableBatchActionContext, type DatatableBatchUpdateDetail, type DatatableColumn, type DatatableFilter, type DatatableFilterOp, type DatatableNumberOp, type DatatableProps, type DatatableQuery, type DatatableRowAction, type DatatableState, type DatatableStringOp, DatePicker, type DatePickerProps, type DateRange, DateRangePicker, type DateRangePickerProps, DateTimePicker, type DateTimePickerProps, Dialog, type DialogProps, type DiffOp, DiffTable, type DiffTableColumn, type DiffTableProps, Divider, type DividerProps, DonutChart, type DonutChartDatum, type DonutChartProps, Drawer, type DrawerProps, EmptyState, type EmptyStateProps, Field, type FieldProps, FileUpload, type FileUploadProps, FilterBar, type FilterBarProps, type FilterClause, type FilterField, type FilterFieldType, type FilterValues, Form, type FormProps, FunnelChart, type FunnelChartProps, type FunnelDatum, Gauge, type GaugeProps, type GaugeSeries, Grid, type GridOwnProps, type GridProps, Heading, type HeadingOwnProps, type HeadingProps, type HeadingSize, Heatmap, type HeatmapDatum, type HeatmapProps, IconButton, type IconButtonProps, Image, type ImageProps, ImageViewer, type ImageViewerControlsApi, type ImageViewerProps, type ImageViewerState, Input, type InputProps, Kanban, type KanbanCard, type KanbanColumn, type KanbanProps, Kbd, type KbdProps, Label, type LabelProps, List, type ListItemData, type ListProps, Menu, type MenuItemDef, type MenuProps, MultiSelect, type MultiSelectGroup, type MultiSelectOption, type MultiSelectProps, Navbar, type NavbarLink, type NavbarProps, type Option, type OptionGroup, Pagination, type PaginationProps, PieChart, type PieChartDatum, type PieChartProps, PolarAreaChart, type PolarAreaChartProps, type PolarAreaDatum, type PolymorphicAs, type PolymorphicComponent, type PolymorphicProps, type PolymorphicPropsWithRef, type PolymorphicRef, Popover, type PopoverProps, Portal, type PortalProps, Pre, type PreProps, Progress, type ProgressProps, RadarChart, type RadarChartProps, type RadarDatum, Radio, RadioGroup, type RadioGroupOption, type RadioGroupProps, type RadioProps, RangeChart, type RangeChartProps, type RangeDatum, Rating, type RatingProps, type ResponsiveColumns, ScatterChart, type ScatterChartProps, type ScatterPoint, type ScatterSeries, Select, type SelectGroup, type SelectOption, type SelectProps, Sidebar, type SidebarItem, type SidebarProps, Skeleton, type SkeletonProps, Slider, type SliderProps, Sparkline, type SparklineDatum, type SparklineProps, Spinner, type SpinnerProps, Stack, type StackOwnProps, type StackProps, Stat, type StatProps, type Step, Stepper, type StepperProps, Switch, type SwitchProps, type Sx, type TabItem, Table, type TableColumn, type TableProps, Tabs, type TabsProps, Tag, type TagProps, Text, type TextOwnProps, type TextProps, type TextTone, Textarea, type TextareaProps, type Theme, ThemeProvider, type ThemeProviderProps, type ThemeTokens, TimePicker, type TimePickerProps, Timeline, type TimelineItem, type TimelineProps, Toast, type ToastApi, type ToastOptions, type ToastProps, ToastProvider, type ToastProviderProps, type ToastTone, ToastViewport, type ToastViewportProps, ToggleGroup, type ToggleGroupItem, type ToggleGroupProps, type Tone, Tooltip, type TooltipProps, type TreeNode, TreeView, type TreeViewProps, Treemap, type TreemapDatum, type TreemapProps, TwicoProvider, type TwicoProviderProps, type UseColorSchemeOptions, type UseFilePickerOptions, type UseFocusTrapOptions, type UseKeyPressOptions, type UseLocalStorageOptions, type UseMediaQueryOptions, type UseWindowSizeOptions, VisuallyHidden, type VisuallyHiddenProps, type WithSx, createTheme, getColorSchemeScript, runDatatableQuery, useClickOutside, useColorScheme, useControllableState, useCopyToClipboard, useDebouncedCallback, useDebouncedValue, useDisclosure, useEventListener, useFilePicker, useFocusTrap, useHover, useId, useIntersectionObserver, useInterval, useIsomorphicLayoutEffect, useKeyPress, useLocalStorage, useMediaQuery, useMounted, usePortal, usePrefersReducedMotion, usePrevious, useScrollLock, useTimeout, useToast, useToggle, useWindowSize };
package/dist/index.mjs CHANGED
@@ -7227,8 +7227,8 @@ function Currency({
7227
7227
  }
7228
7228
  function useFallbackName(ref, hasOwnName) {
7229
7229
  const [needsFallback, setNeedsFallback] = React54__default.useState(false);
7230
- const useIso3 = typeof document !== "undefined" ? React54__default.useLayoutEffect : React54__default.useEffect;
7231
- useIso3(() => {
7230
+ const useIso2 = typeof document !== "undefined" ? React54__default.useLayoutEffect : React54__default.useEffect;
7231
+ useIso2(() => {
7232
7232
  if (hasOwnName) {
7233
7233
  setNeedsFallback(false);
7234
7234
  return;
@@ -7244,7 +7244,6 @@ function useFallbackName(ref, hasOwnName) {
7244
7244
  });
7245
7245
  return !hasOwnName && needsFallback;
7246
7246
  }
7247
- typeof document !== "undefined" ? React54__default.useLayoutEffect : React54__default.useEffect;
7248
7247
  var SELECT_CSS = `
7249
7248
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
7250
7249
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
@@ -12873,14 +12872,20 @@ function Datatable({
12873
12872
  const isHidden = typeof a.hidden === "function" ? a.hidden(selKeys, selectedRows) : !!a.hidden;
12874
12873
  if (isHidden) return null;
12875
12874
  const isDisabled = typeof a.disabled === "function" ? a.disabled(selKeys, selectedRows) : !!a.disabled;
12876
- return /* @__PURE__ */ jsxs(
12875
+ const reason = isDisabled && a.disabledReason != null ? typeof a.disabledReason === "function" ? a.disabledReason(selKeys, selectedRows) : a.disabledReason : null;
12876
+ const soft = isDisabled && reason != null;
12877
+ const btn = /* @__PURE__ */ jsxs(
12877
12878
  "button",
12878
12879
  {
12879
12880
  type: "button",
12880
12881
  className: "twc-dt__batch-btn",
12881
12882
  "data-danger": a.danger || void 0,
12882
- disabled: isDisabled,
12883
- onClick: (e) => a.onClick?.(selKeys, selectedRows, clearSelection, { anchorEl: e.currentTarget }),
12883
+ disabled: isDisabled && !soft,
12884
+ "aria-disabled": soft || void 0,
12885
+ onClick: (e) => {
12886
+ if (isDisabled) return;
12887
+ a.onClick?.(selKeys, selectedRows, clearSelection, { anchorEl: e.currentTarget });
12888
+ },
12884
12889
  children: [
12885
12890
  a.icon,
12886
12891
  a.label
@@ -12888,6 +12893,7 @@ function Datatable({
12888
12893
  },
12889
12894
  i
12890
12895
  );
12896
+ return reason != null ? /* @__PURE__ */ jsx(Tooltip, { label: reason, placement: "top", children: btn }, i) : btn;
12891
12897
  })
12892
12898
  ] })
12893
12899
  ] }) : null,
@@ -14318,6 +14324,42 @@ var CARDGRID_CSS = `
14318
14324
  .twc-cardgrid__footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
14319
14325
  .twc-cardgrid__count { font-size: var(--text-sm); color: var(--color-text-muted); }
14320
14326
  .twc-cardgrid__perpage { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-muted); }
14327
+
14328
+ /* #478: selection. The card's selected state is a RING, not a background tint: a card is usually a
14329
+ full-bleed thumbnail, so a tint is invisible behind the image. The ring is drawn with box-shadow on
14330
+ a wrapper so it never participates in layout (no reflow when a card becomes selected) and it reads
14331
+ in both themes, --color-primary clearing 3:1 against both surfaces as a non-text indicator. */
14332
+ .twc-cardgrid__item { position: relative; border-radius: var(--radius-lg); transition: box-shadow var(--duration-fast) var(--ease-standard); }
14333
+ .twc-cardgrid__item[data-selected="true"] { box-shadow: 0 0 0 var(--border-medium) var(--color-primary); }
14334
+ /* The overlay is the GRID's checkbox, not the card's: renderCard owns the whole body, so a
14335
+ consumer-rendered checkbox would compete with the card's own click target and have to be positioned
14336
+ against a thumbnail whose size only the consumer knows. Placement is configurable, and a consumer who
14337
+ wants it inline instead uses the third renderCard argument and sets selectionPlacement="none". */
14338
+ .twc-cardgrid__pick { position: absolute; z-index: 2; display: inline-flex; padding: var(--space-1);
14339
+ background: color-mix(in srgb, var(--color-surface) 82%, transparent); border-radius: var(--radius-md);
14340
+ backdrop-filter: blur(4px); }
14341
+ .twc-cardgrid__item[data-place="top-start"] .twc-cardgrid__pick { top: var(--space-2); inset-inline-start: var(--space-2); }
14342
+ .twc-cardgrid__item[data-place="top-end"] .twc-cardgrid__pick { top: var(--space-2); inset-inline-end: var(--space-2); }
14343
+ .twc-cardgrid__item[data-place="bottom-start"] .twc-cardgrid__pick { bottom: var(--space-2); inset-inline-start: var(--space-2); }
14344
+ .twc-cardgrid__item[data-place="bottom-end"] .twc-cardgrid__pick { bottom: var(--space-2); inset-inline-end: var(--space-2); }
14345
+ .twc-cardgrid__batch { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
14346
+ padding: var(--space-2) var(--space-3); border: var(--border-thin) solid var(--color-primary-border);
14347
+ background: var(--color-primary-subtle); border-radius: var(--radius-md); }
14348
+ .twc-cardgrid__batch-count { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-primary-subtle-fg); }
14349
+ .twc-cardgrid__batch-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-inline-start: auto; }
14350
+ .twc-cardgrid__batch-btn { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-sm);
14351
+ padding-inline: var(--space-3); border: var(--border-thin) solid var(--color-border); background: var(--color-surface);
14352
+ color: var(--color-text); font-family: inherit; font-size: var(--text-xs); font-weight: var(--font-semibold);
14353
+ border-radius: var(--radius-md); cursor: pointer; }
14354
+ .twc-cardgrid__batch-btn:hover:not(:disabled):not([aria-disabled="true"]) { border-color: var(--color-primary-subtle-fg); color: var(--color-primary-subtle-fg); }
14355
+ .twc-cardgrid__batch-btn:disabled, .twc-cardgrid__batch-btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
14356
+ .twc-cardgrid__batch-btn[data-danger="true"] { color: var(--color-danger-subtle-fg); }
14357
+ .twc-cardgrid__batch-btn svg { width: 15px; height: 15px; }
14358
+ .twc-cardgrid__batch-x { display: inline-grid; place-items: center; width: 26px; height: 26px; flex: none;
14359
+ border: none; background: transparent; color: var(--color-primary-subtle-fg); cursor: pointer; border-radius: var(--radius-sm); }
14360
+ .twc-cardgrid__batch-x:hover { background: color-mix(in srgb, var(--color-primary) 14%, transparent); }
14361
+ .twc-cardgrid__batch-x svg { width: 15px; height: 15px; }
14362
+ .twc-cardgrid__selall { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-muted); }
14321
14363
  `;
14322
14364
  function CardGrid({
14323
14365
  rows = [],
@@ -14354,6 +14396,16 @@ function CardGrid({
14354
14396
  sortMinWidth,
14355
14397
  // slots / states
14356
14398
  toolbar,
14399
+ // #478: selection + batch actions, deliberately the SAME vocabulary as Datatable rather than a new
14400
+ // one - a card grid is the surface where "pick several of these and do one thing to them" is close to
14401
+ // the whole point, and it was the only Datatable affordance CardGrid lacked.
14402
+ checkboxSelection = false,
14403
+ batchActions = [],
14404
+ onRowSelectionChange,
14405
+ selectionPlacement = "top-start",
14406
+ selectionLabel,
14407
+ showSelectAll = true,
14408
+ retainSelectedRows = false,
14357
14409
  emptyState = "No results.",
14358
14410
  label,
14359
14411
  id,
@@ -14430,6 +14482,62 @@ function CardGrid({
14430
14482
  return () => clearTimeout(t);
14431
14483
  }, [serverMode, queryKey]);
14432
14484
  const keyOf = typeof rowKey === "function" ? rowKey : (r2) => r2 && r2[rowKey];
14485
+ const [selected, setSelected] = React54__default.useState(() => /* @__PURE__ */ new Set());
14486
+ const retainCap = retainSelectedRows === true ? 1e3 : retainSelectedRows || 0;
14487
+ const retainedRef = React54__default.useRef(/* @__PURE__ */ new Map());
14488
+ const selKeys = React54__default.useMemo(() => [...selected], [selected]);
14489
+ const selectedRows = React54__default.useMemo(() => {
14490
+ const onPage = new Map(pageRows.map((r2, i) => [keyOf(r2, i), r2]));
14491
+ return selKeys.map((k) => onPage.get(k) ?? retainedRef.current.get(k)).filter((r2) => r2 !== void 0);
14492
+ }, [selKeys, pageRows]);
14493
+ const clearSelection = React54__default.useCallback(() => {
14494
+ retainedRef.current.clear();
14495
+ setSelected(/* @__PURE__ */ new Set());
14496
+ }, []);
14497
+ const toggleKey = React54__default.useCallback((key, row) => {
14498
+ setSelected((prev) => {
14499
+ const next = new Set(prev);
14500
+ if (next.has(key)) {
14501
+ next.delete(key);
14502
+ retainedRef.current.delete(key);
14503
+ } else {
14504
+ next.add(key);
14505
+ if (retainCap > 0 && row !== void 0 && retainedRef.current.size < retainCap) retainedRef.current.set(key, row);
14506
+ }
14507
+ return next;
14508
+ });
14509
+ }, [retainCap]);
14510
+ const pageKeys = pageRows.map((r2, i) => keyOf(r2, i));
14511
+ const pageSelectedCount = pageKeys.filter((k) => selected.has(k)).length;
14512
+ const allPageSelected = pageKeys.length > 0 && pageSelectedCount === pageKeys.length;
14513
+ const somePageSelected = pageSelectedCount > 0 && !allPageSelected;
14514
+ const togglePage = () => {
14515
+ setSelected((prev) => {
14516
+ const next = new Set(prev);
14517
+ pageRows.forEach((r2, i) => {
14518
+ const k = keyOf(r2, i);
14519
+ if (allPageSelected) {
14520
+ next.delete(k);
14521
+ retainedRef.current.delete(k);
14522
+ } else {
14523
+ next.add(k);
14524
+ if (retainCap > 0 && retainedRef.current.size < retainCap) retainedRef.current.set(k, r2);
14525
+ }
14526
+ });
14527
+ return next;
14528
+ });
14529
+ };
14530
+ const onSelRef = React54__default.useRef(onRowSelectionChange);
14531
+ onSelRef.current = onRowSelectionChange;
14532
+ const prevSelSigRef = React54__default.useRef(null);
14533
+ React54__default.useEffect(() => {
14534
+ const cb = onSelRef.current;
14535
+ if (!cb) return;
14536
+ const sig = selKeys.join(",");
14537
+ if (sig === prevSelSigRef.current) return;
14538
+ prevSelSigRef.current = sig;
14539
+ cb(selKeys, selectedRows);
14540
+ }, [selKeys, selectedRows]);
14433
14541
  const from = total === 0 ? 0 : Math.min(shownPage * sizeVal + 1, total);
14434
14542
  const to = paginated ? Math.min((shownPage + 1) * sizeVal, total) : total;
14435
14543
  const rppOptions = Array.from(new Set([...pageSizeOptions || [], pageSize].filter((n) => n > 0))).sort((a, b) => a - b).map((n) => ({ value: String(n), label: String(n) }));
@@ -14482,6 +14590,64 @@ function CardGrid({
14482
14590
  )
14483
14591
  ] }) : null
14484
14592
  ] }) : null,
14593
+ checkboxSelection && showSelectAll && pageRows.length > 0 && selected.size === 0 ? /* @__PURE__ */ jsxs("label", { className: "twc-cardgrid__selall", children: [
14594
+ /* @__PURE__ */ jsx(
14595
+ Checkbox,
14596
+ {
14597
+ checked: allPageSelected,
14598
+ indeterminate: somePageSelected,
14599
+ onChange: togglePage,
14600
+ "aria-label": "Select all on this page"
14601
+ }
14602
+ ),
14603
+ "Select all on this page"
14604
+ ] }) : null,
14605
+ checkboxSelection && selected.size > 0 ? /* @__PURE__ */ jsxs("div", { className: "twc-cardgrid__batch", children: [
14606
+ /* @__PURE__ */ jsx("button", { type: "button", className: "twc-cardgrid__batch-x", onClick: clearSelection, "aria-label": "Clear selection", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }),
14607
+ /* @__PURE__ */ jsxs("span", { className: "twc-cardgrid__batch-count", children: [
14608
+ selected.size,
14609
+ " selected"
14610
+ ] }),
14611
+ showSelectAll && pageRows.length > 0 ? /* @__PURE__ */ jsxs("label", { className: "twc-cardgrid__selall", children: [
14612
+ /* @__PURE__ */ jsx(
14613
+ Checkbox,
14614
+ {
14615
+ checked: allPageSelected,
14616
+ indeterminate: somePageSelected,
14617
+ onChange: togglePage,
14618
+ "aria-label": "Select all on this page"
14619
+ }
14620
+ ),
14621
+ "This page"
14622
+ ] }) : null,
14623
+ /* @__PURE__ */ jsx("div", { className: "twc-cardgrid__batch-actions", children: batchActions.map((a, i) => {
14624
+ const isHidden = typeof a.hidden === "function" ? a.hidden(selKeys, selectedRows) : !!a.hidden;
14625
+ if (isHidden) return null;
14626
+ const isDisabled = typeof a.disabled === "function" ? a.disabled(selKeys, selectedRows) : !!a.disabled;
14627
+ const reason = isDisabled && a.disabledReason != null ? typeof a.disabledReason === "function" ? a.disabledReason(selKeys, selectedRows) : a.disabledReason : null;
14628
+ const soft = isDisabled && reason != null;
14629
+ const btn = /* @__PURE__ */ jsxs(
14630
+ "button",
14631
+ {
14632
+ type: "button",
14633
+ className: "twc-cardgrid__batch-btn",
14634
+ "data-danger": a.danger || void 0,
14635
+ disabled: isDisabled && !soft,
14636
+ "aria-disabled": soft || void 0,
14637
+ onClick: (e) => {
14638
+ if (isDisabled) return;
14639
+ a.onClick?.(selKeys, selectedRows, clearSelection, { anchorEl: e.currentTarget });
14640
+ },
14641
+ children: [
14642
+ a.icon,
14643
+ a.label
14644
+ ]
14645
+ },
14646
+ i
14647
+ );
14648
+ return reason != null ? /* @__PURE__ */ jsx(Tooltip, { label: reason, placement: "top", children: btn }, i) : btn;
14649
+ }) })
14650
+ ] }) : null,
14485
14651
  /* @__PURE__ */ jsxs("div", { className: "twc-cardgrid__body", "data-loading": loading || void 0, "aria-busy": loading || void 0, children: [
14486
14652
  loading ? /* @__PURE__ */ jsx("div", { className: "twc-cardgrid__overlay", children: /* @__PURE__ */ jsx("div", { className: "twc-cardgrid__spinner", role: "status", "aria-label": "Loading" }) }) : null,
14487
14653
  pageRows.length === 0 && !loading ? /* @__PURE__ */ jsx("div", { className: "twc-cardgrid__empty", children: emptyState }) : /* @__PURE__ */ jsx(
@@ -14493,7 +14659,31 @@ function CardGrid({
14493
14659
  gap,
14494
14660
  role: "list",
14495
14661
  "aria-labelledby": label ? `${fieldId}-label` : void 0,
14496
- children: pageRows.map((row, i) => /* @__PURE__ */ jsx("div", { role: "listitem", children: renderCard(row, i) }, keyOf(row, i) ?? i))
14662
+ children: pageRows.map((row, i) => {
14663
+ const k = keyOf(row, i);
14664
+ const isSel = checkboxSelection && selected.has(k);
14665
+ return /* @__PURE__ */ jsxs(
14666
+ "div",
14667
+ {
14668
+ role: "listitem",
14669
+ className: "twc-cardgrid__item",
14670
+ "data-selected": isSel || void 0,
14671
+ "data-place": checkboxSelection ? selectionPlacement : void 0,
14672
+ children: [
14673
+ checkboxSelection && selectionPlacement !== "none" ? /* @__PURE__ */ jsx("span", { className: "twc-cardgrid__pick", children: /* @__PURE__ */ jsx(
14674
+ Checkbox,
14675
+ {
14676
+ checked: isSel,
14677
+ onChange: () => toggleKey(k, row),
14678
+ "aria-label": selectionLabel ? selectionLabel(row, i) : "Select item"
14679
+ }
14680
+ ) }) : null,
14681
+ renderCard(row, i, checkboxSelection ? { selected: isSel, toggle: () => toggleKey(k, row) } : void 0)
14682
+ ]
14683
+ },
14684
+ k ?? i
14685
+ );
14686
+ })
14497
14687
  }
14498
14688
  )
14499
14689
  ] }),
@@ -19510,7 +19700,7 @@ function Tag({
19510
19700
  }, "aria-label": _removeLabel, type: "button", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) }) }) : null
19511
19701
  ] });
19512
19702
  }
19513
- var useIso2 = typeof document !== "undefined" ? React54__default.useLayoutEffect : React54__default.useEffect;
19703
+ var useIso = typeof document !== "undefined" ? React54__default.useLayoutEffect : React54__default.useEffect;
19514
19704
  var TEXTAREA_CSS = `
19515
19705
  .twc-field { display: flex; flex-direction: column; gap: var(--space-1-5); font-family: var(--font-sans); }
19516
19706
  .twc-field__label { font-size: var(--text-sm); font-weight: var(--font-semibold); color: var(--color-text); display: flex; gap: 4px; align-items: center; }
@@ -19609,7 +19799,7 @@ var Textarea = React54__default.forwardRef(function Textarea2({
19609
19799
  el.style.height = `${next}px`;
19610
19800
  el.style.overflowY = el.scrollHeight + vBorder > maxH ? "auto" : "hidden";
19611
19801
  };
19612
- useIso2(() => {
19802
+ useIso(() => {
19613
19803
  if (autosize && innerRef.current) resize();
19614
19804
  }, [autosize, rest.value, minRows, maxRows, rows]);
19615
19805
  const handleInput = (e) => {