@nexub/react 7.5.0 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/accessibility.json +1 -1
  2. package/dist/components/Badge/Badge.d.ts +9 -0
  3. package/dist/components/Badge/Badge.js +40 -2
  4. package/dist/components/Button/Button.d.ts +1 -1
  5. package/dist/components/CommandPalette/CommandPalette.d.ts +34 -13
  6. package/dist/components/CommandPalette/CommandPalette.js +174 -40
  7. package/dist/components/CommandPalette/index.d.ts +1 -1
  8. package/dist/components/DataTable/DataTable.d.ts +16 -2
  9. package/dist/components/DataTable/DataTable.js +131 -76
  10. package/dist/components/DataTable/ViewOptions.d.ts +15 -0
  11. package/dist/components/DataTable/ViewOptions.js +33 -0
  12. package/dist/components/DataTableVirtual/DataTableVirtual.d.ts +1 -1
  13. package/dist/components/Dialog/Dialog.d.ts +7 -0
  14. package/dist/components/Dialog/Dialog.js +35 -8
  15. package/dist/components/EmptyState/EmptyState.d.ts +7 -1
  16. package/dist/components/EmptyState/EmptyState.js +21 -5
  17. package/dist/components/EmptyState/EmptyStatePreset.d.ts +2 -1
  18. package/dist/components/FilterBuilder/FilterBuilder.js +33 -15
  19. package/dist/components/Heading/Heading.d.ts +1 -1
  20. package/dist/components/IconButton/IconButton.d.ts +5 -1
  21. package/dist/components/IconButton/IconButton.js +14 -3
  22. package/dist/components/Kanban/Kanban.d.ts +1 -1
  23. package/dist/components/Sortable/Sortable.d.ts +2 -2
  24. package/dist/components/Sortable/reorder.d.ts +10 -11
  25. package/dist/components/Sortable/reorder.js +185 -27
  26. package/dist/components/Tabs/Tabs.d.ts +5 -1
  27. package/dist/components/Tabs/Tabs.js +98 -35
  28. package/dist/components/Text/Text.d.ts +1 -1
  29. package/dist/components/Tooltip/Tooltip.d.ts +10 -3
  30. package/dist/components/Tooltip/Tooltip.js +16 -4
  31. package/dist/index.d.ts +2 -0
  32. package/dist/styles.css +1 -1
  33. package/package.json +8 -7
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.5.0",
2
+ "version": "7.7.0",
3
3
  "standard": "WCAG 2.2 Level AA",
4
4
  "method": {
5
5
  "automated": "axe-core rules tagged wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa, run on every Storybook story in light and dark, and on open popups (menus, listboxes, dialogs), on every pull request; a new violation fails the build.",
@@ -8,5 +8,14 @@ export declare const badgeVariants: (props?: ({
8
8
  export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {
9
9
  tone?: ToneValue;
10
10
  leadingIcon?: React.ReactNode;
11
+ /**
12
+ * A status dot. Before the label when there is one; on its own (no children
13
+ * or count) it is just the dot: give it an `aria-label`, or it's decorative.
14
+ */
15
+ dot?: boolean;
16
+ /** A number to show. Above `max` it reads "99+"; screen readers get the full number. */
17
+ count?: number;
18
+ /** @default 99 */
19
+ max?: number;
11
20
  }
12
21
  export declare const Badge: React.ForwardRefExoticComponent<BadgeProps & React.RefAttributes<HTMLSpanElement>>;
@@ -72,10 +72,37 @@ const toneClasses = {
72
72
  outline: "border-[color:light-dark(var(--color-palette-founderub-80),var(--color-palette-founderub-40))] text-[var(--color-founderub)]"
73
73
  }
74
74
  };
75
+ const dotClasses = {
76
+ neutral: "bg-[var(--color-outline)]",
77
+ info: "bg-[var(--color-info)]",
78
+ success: "bg-[var(--color-success)]",
79
+ warning: "bg-[var(--color-warning)]",
80
+ danger: "bg-[var(--color-error)]",
81
+ leap: "bg-[var(--color-leap)]",
82
+ bookub: "bg-[var(--color-bookub)]",
83
+ payub: "bg-[var(--color-payub)]",
84
+ founderub: "bg-[var(--color-founderub)]"
85
+ };
86
+ const dotSizes = { sm: "size-1.5", md: "size-2", lg: "size-2.5" };
75
87
  const Badge = React.forwardRef(
76
- ({ className, size, variant = "solid", tone = "neutral", leadingIcon, children, ...props }, ref) => {
88
+ ({ className, size, variant = "solid", tone = "neutral", leadingIcon, dot, count, max = 99, children, ...props }, ref) => {
77
89
  const resolvedVariant = variant ?? "solid";
78
90
  const toneStyle = toneClasses[tone][resolvedVariant];
91
+ const dotSize = dotSizes[size ?? "md"];
92
+ if (dot && children == null && count == null) {
93
+ const labelled = props["aria-label"] != null || props["aria-labelledby"] != null;
94
+ return /* @__PURE__ */ jsx(
95
+ "span",
96
+ {
97
+ ref,
98
+ role: labelled ? "img" : void 0,
99
+ "aria-hidden": labelled ? void 0 : true,
100
+ className: cn("inline-block shrink-0 rounded-full", dotSize, dotClasses[tone], className),
101
+ ...props
102
+ }
103
+ );
104
+ }
105
+ const capped = count != null && count > max;
79
106
  return /* @__PURE__ */ jsxs(
80
107
  "span",
81
108
  {
@@ -83,8 +110,19 @@ const Badge = React.forwardRef(
83
110
  className: cn(badgeVariants({ size, variant: resolvedVariant }), toneStyle, className),
84
111
  ...props,
85
112
  children: [
113
+ dot && /* @__PURE__ */ jsx(
114
+ "span",
115
+ {
116
+ "aria-hidden": "true",
117
+ className: cn("shrink-0 rounded-full", dotSize, resolvedVariant === "solid" ? "bg-current" : dotClasses[tone])
118
+ }
119
+ ),
86
120
  leadingIcon && /* @__PURE__ */ jsx("span", { className: "shrink-0 inline-flex items-center", children: leadingIcon }),
87
- children
121
+ children,
122
+ count != null && /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
123
+ /* @__PURE__ */ jsx("span", { "aria-hidden": capped || void 0, className: "nx-tick", children: capped ? `${max}+` : count }, count),
124
+ capped && /* @__PURE__ */ jsx("span", { className: "sr-only", children: count })
125
+ ] })
88
126
  ]
89
127
  }
90
128
  );
@@ -4,7 +4,7 @@ import type { ToneValue } from '@nexub/core';
4
4
  export type { ToneValue };
5
5
  export { toneTextClasses } from '../../utils/control.js';
6
6
  export declare const buttonVariants: (props?: ({
7
- variant?: "accent" | "text" | "link" | "outline" | "primary" | "glass" | "ghost" | "subtle" | "filled" | "tonal" | "outlined" | "hero" | "secondary" | "destructive" | null | undefined;
7
+ variant?: "accent" | "text" | "link" | "outline" | "primary" | "glass" | "filled" | "tonal" | "outlined" | "hero" | "secondary" | "subtle" | "ghost" | "destructive" | null | undefined;
8
8
  size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
9
9
  fullWidth?: boolean | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import * as RadixDialog from '@radix-ui/react-dialog';
3
+ import { Command } from 'cmdk';
3
4
  export interface CommandPaletteRootProps extends RadixDialog.DialogProps {
4
5
  className?: string;
5
6
  /** Accessible name of the dialog. @default 'Command palette' */
@@ -17,6 +18,36 @@ export interface CommandPaletteRootProps extends RadixDialog.DialogProps {
17
18
  * the page, e.g. `'k'` for Cmd+K / Ctrl+K. Off by default.
18
19
  */
19
20
  shortcut?: string;
21
+ /** The open page stack (ids of `CommandPalette.Page`s), deepest last. Empty = the root page. */
22
+ pages?: string[];
23
+ defaultPages?: string[];
24
+ onPagesChange?: (pages: string[]) => void;
25
+ /** Shows a loading row (e.g. while async results arrive) and hides `Empty`. */
26
+ loading?: boolean;
27
+ /**
28
+ * Opt-in recents: the last `recentLimit` items picked on the root page are
29
+ * kept in localStorage under this key and shown as a "Recent" group while
30
+ * the query is empty.
31
+ */
32
+ recentKey?: string;
33
+ /** @default 5 */
34
+ recentLimit?: number;
35
+ }
36
+ export interface CommandPaletteItemProps extends React.ComponentProps<typeof Command.Item> {
37
+ /** Opens this `CommandPalette.Page` (by id) when selected. */
38
+ page?: string;
39
+ }
40
+ export interface CommandPalettePageProps {
41
+ /** Pushed by `<CommandPalette.Item page="id">`. Leave out for the root page. */
42
+ id?: string;
43
+ /** Shown in the input's breadcrumb. Defaults to the id. */
44
+ title?: string;
45
+ children?: React.ReactNode;
46
+ }
47
+ /** Content shown only while this page is on top of the stack (no id: the root page). */
48
+ declare function Page({ id, title, children }: CommandPalettePageProps): React.JSX.Element | null;
49
+ declare namespace Page {
50
+ var displayName: string;
20
51
  }
21
52
  export declare const CommandPalette: {
22
53
  Root: React.ForwardRefExoticComponent<CommandPaletteRootProps & React.RefAttributes<HTMLDivElement>>;
@@ -55,19 +86,7 @@ export declare const CommandPalette: {
55
86
  value?: string;
56
87
  forceMount?: boolean;
57
88
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
58
- Item: React.ForwardRefExoticComponent<Omit<{
59
- children?: React.ReactNode;
60
- } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
61
- ref?: React.Ref<HTMLDivElement>;
62
- } & {
63
- asChild?: boolean;
64
- }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>>, "value" | "disabled" | "onSelect"> & {
65
- disabled?: boolean;
66
- onSelect?: (value: string) => void;
67
- value?: string;
68
- keywords?: string[];
69
- forceMount?: boolean;
70
- } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
89
+ Item: React.ForwardRefExoticComponent<Omit<CommandPaletteItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
71
90
  Separator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
72
91
  ref?: React.Ref<HTMLDivElement>;
73
92
  } & {
@@ -75,4 +94,6 @@ export declare const CommandPalette: {
75
94
  }, "asChild" | "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
76
95
  alwaysRender?: boolean;
77
96
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
97
+ Page: typeof Page;
78
98
  };
99
+ export {};
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import * as RadixDialog from "@radix-ui/react-dialog";
5
- import { Command } from "cmdk";
5
+ import { Command, useCommandState } from "cmdk";
6
6
  import { Search } from "@nexub/icons";
7
7
  import { cn } from "../../utils/cn.js";
8
8
  import { useNexubMessages } from "../NexubProvider/NexubProvider.js";
@@ -10,15 +10,72 @@ import { overlaySurface, scrim } from "../../utils/surface.js";
10
10
  import { useClassScrollLock } from "../../hooks/useClassScrollLock.js";
11
11
  import { useControllableState } from "../../hooks/useControllableState.js";
12
12
  import { useMergedRefs } from "../../hooks/useMergedRefs.js";
13
+ import { useIsomorphicLayoutEffect } from "../../hooks/useIsomorphicLayoutEffect.js";
14
+ import { Badge } from "../Badge/Badge.js";
15
+ import { Spinner } from "../Spinner/Spinner.js";
16
+ const PaletteContext = React.createContext(null);
17
+ const usePalette = () => React.useContext(PaletteContext);
18
+ function readRecents(key) {
19
+ try {
20
+ const parsed = JSON.parse(localStorage.getItem(key) ?? "[]");
21
+ return Array.isArray(parsed) ? parsed.filter((r) => typeof r?.value === "string" && typeof r?.label === "string") : [];
22
+ } catch {
23
+ return [];
24
+ }
25
+ }
13
26
  const overlayClasses = cn("fixed inset-0 z-[var(--z-modal)] nx-motion", scrim);
14
27
  function ClassLockOverlay() {
15
28
  useClassScrollLock();
16
29
  return /* @__PURE__ */ jsx("div", { "data-state": "open", className: overlayClasses });
17
30
  }
18
31
  const Root = React.forwardRef(
19
- ({ children, label: labelProp, scrollLock = "style", shouldFilter, shortcut, open: openProp, defaultOpen, onOpenChange, ...props }, ref) => {
32
+ ({
33
+ children,
34
+ label: labelProp,
35
+ scrollLock = "style",
36
+ shouldFilter,
37
+ shortcut,
38
+ open: openProp,
39
+ defaultOpen,
40
+ onOpenChange,
41
+ pages: pagesProp,
42
+ defaultPages,
43
+ onPagesChange,
44
+ loading,
45
+ recentKey,
46
+ recentLimit = 5,
47
+ ...props
48
+ }, ref) => {
20
49
  const messages = useNexubMessages();
21
- const [open = false, setOpen] = useControllableState({ value: openProp, defaultValue: defaultOpen, onChange: onOpenChange });
50
+ const [pages = [], setPages] = useControllableState({ value: pagesProp, defaultValue: defaultPages, onChange: onPagesChange });
51
+ const [open = false, setOpenState] = useControllableState({ value: openProp, defaultValue: defaultOpen, onChange: onOpenChange });
52
+ const setOpen = (next) => {
53
+ setOpenState(next);
54
+ if (!next && pages.length) setPages([]);
55
+ };
56
+ const [titles, setTitles] = React.useState({});
57
+ const [recents, setRecents] = React.useState([]);
58
+ const handlers = React.useRef(/* @__PURE__ */ new Map()).current;
59
+ React.useEffect(() => setRecents(recentKey ? readRecents(recentKey) : []), [recentKey]);
60
+ const ctx = {
61
+ pages,
62
+ push: (page) => setPages([...pages, page]),
63
+ pop: () => setPages(pages.slice(0, -1)),
64
+ titles,
65
+ setTitle: (page, title) => setTitles((t) => t[page] === title ? t : { ...t, [page]: title }),
66
+ loading,
67
+ recents,
68
+ record: (recent) => {
69
+ if (!recentKey || pages.length) return;
70
+ const next = [recent, ...recents.filter((r) => r.value !== recent.value)].slice(0, recentLimit);
71
+ setRecents(next);
72
+ try {
73
+ localStorage.setItem(recentKey, JSON.stringify(next));
74
+ } catch {
75
+ }
76
+ },
77
+ handlers
78
+ };
22
79
  React.useEffect(() => {
23
80
  if (!shortcut) return;
24
81
  const onKeyDown = (e) => {
@@ -60,7 +117,7 @@ const Root = React.forwardRef(
60
117
  shouldFilter,
61
118
  loop: true,
62
119
  className: "flex h-full w-full flex-col overflow-hidden text-[var(--color-on-surface)]",
63
- children
120
+ children: /* @__PURE__ */ jsx(PaletteContext.Provider, { value: ctx, children })
64
121
  }
65
122
  )
66
123
  ]
@@ -70,31 +127,75 @@ const Root = React.forwardRef(
70
127
  }
71
128
  );
72
129
  Root.displayName = "CommandPalette.Root";
73
- const Input = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs("div", { className: "flex items-center border-b border-[var(--color-outline-variant)] px-3", children: [
74
- /* @__PURE__ */ jsx(Search, { size: "sm", className: "me-2 shrink-0 opacity-50", "aria-hidden": "true" }),
75
- /* @__PURE__ */ jsx(
76
- Command.Input,
130
+ const Input = React.forwardRef(({ className, value: valueProp, onValueChange, onKeyDown, ...props }, ref) => {
131
+ const palette = usePalette();
132
+ const pages = palette?.pages ?? [];
133
+ const [value = "", setValue] = useControllableState({ value: valueProp, defaultValue: "", onChange: onValueChange });
134
+ const depth = pages.length;
135
+ const first = React.useRef(true);
136
+ React.useEffect(() => {
137
+ if (first.current) first.current = false;
138
+ else setValue("");
139
+ }, [depth]);
140
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 border-b border-[var(--color-outline-variant)] px-3", children: [
141
+ /* @__PURE__ */ jsx(Search, { size: "sm", className: "me-1 shrink-0 opacity-50", "aria-hidden": "true" }),
142
+ pages.map((page) => /* @__PURE__ */ jsx(Badge, { variant: "soft", size: "sm", className: "shrink-0", children: palette?.titles[page] ?? page }, page)),
143
+ /* @__PURE__ */ jsx(
144
+ Command.Input,
145
+ {
146
+ ref,
147
+ value,
148
+ onValueChange: setValue,
149
+ onKeyDown: (e) => {
150
+ onKeyDown?.(e);
151
+ if (!e.defaultPrevented && e.key === "Backspace" && !e.currentTarget.value && depth) {
152
+ e.preventDefault();
153
+ palette?.pop();
154
+ }
155
+ },
156
+ className: cn(
157
+ "ms-1 flex h-control-xl w-full bg-transparent text-body-lg outline-none placeholder:text-[var(--color-on-surface-muted)] disabled:cursor-not-allowed disabled:text-[var(--color-text-disabled)]",
158
+ className
159
+ ),
160
+ ...props
161
+ }
162
+ )
163
+ ] });
164
+ });
165
+ Input.displayName = "CommandPalette.Input";
166
+ const List = React.forwardRef(({ className, children, ...props }, ref) => {
167
+ const palette = usePalette();
168
+ const messages = useNexubMessages();
169
+ const search = useCommandState((state) => state.search);
170
+ const showRecents = palette && !palette.loading && !palette.pages.length && !search && palette.recents.length > 0;
171
+ return /* @__PURE__ */ jsxs(
172
+ Command.List,
77
173
  {
78
174
  ref,
79
- className: cn(
80
- "flex h-control-xl w-full bg-transparent text-body-lg outline-none placeholder:text-[var(--color-on-surface-muted)] disabled:cursor-not-allowed disabled:text-[var(--color-text-disabled)]",
81
- className
82
- ),
83
- ...props
175
+ className: cn("max-h-[400px] overflow-y-auto overflow-x-hidden p-2", className),
176
+ ...props,
177
+ children: [
178
+ palette?.loading && /* @__PURE__ */ jsxs(Command.Loading, { label: messages.loading, className: cn(itemClasses, "text-[var(--color-on-surface-muted)]"), children: [
179
+ /* @__PURE__ */ jsx(Spinner, { size: "sm", "aria-hidden": "true" }),
180
+ messages.loading
181
+ ] }),
182
+ showRecents && /* @__PURE__ */ jsx(Group, { heading: messages.recent, children: palette.recents.map((r) => /* @__PURE__ */ jsx(
183
+ Command.Item,
184
+ {
185
+ value: `recent:${r.value}`,
186
+ onSelect: () => palette.handlers.get(r.value)?.(r.value),
187
+ className: itemClasses,
188
+ children: r.label
189
+ },
190
+ r.value
191
+ )) }),
192
+ children
193
+ ]
84
194
  }
85
- )
86
- ] }));
87
- Input.displayName = "CommandPalette.Input";
88
- const List = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
89
- Command.List,
90
- {
91
- ref,
92
- className: cn("max-h-[400px] overflow-y-auto overflow-x-hidden p-2", className),
93
- ...props
94
- }
95
- ));
195
+ );
196
+ });
96
197
  List.displayName = "CommandPalette.List";
97
- const Empty = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
198
+ const Empty = React.forwardRef(({ className, ...props }, ref) => usePalette()?.loading ? null : /* @__PURE__ */ jsx(
98
199
  Command.Empty,
99
200
  {
100
201
  ref,
@@ -106,6 +207,11 @@ const Empty = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
106
207
  }
107
208
  ));
108
209
  Empty.displayName = "CommandPalette.Empty";
210
+ const itemClasses = cn(
211
+ "relative flex cursor-default select-none items-center gap-2 rounded-shape-container px-3 py-2.5 text-body-sm outline-none",
212
+ "aria-selected:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)]",
213
+ "data-[disabled=true]:pointer-events-none data-[disabled=true]:text-[var(--color-text-disabled)]"
214
+ );
109
215
  const Group = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
110
216
  Command.Group,
111
217
  {
@@ -119,20 +225,37 @@ const Group = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
119
225
  }
120
226
  ));
121
227
  Group.displayName = "CommandPalette.Group";
122
- const Item = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
123
- Command.Item,
124
- {
125
- ref,
126
- className: cn(
127
- // Concentric: overlay 24px − 8px padding = 16px.
128
- "relative flex cursor-default select-none items-center gap-2 rounded-shape-container px-3 py-2.5 text-body-sm outline-none",
129
- "aria-selected:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)]",
130
- "data-[disabled=true]:pointer-events-none data-[disabled=true]:text-[var(--color-text-disabled)]",
131
- className
132
- ),
133
- ...props
228
+ const Item = React.forwardRef(
229
+ ({ className, page, onSelect, ...props }, ref) => {
230
+ const palette = usePalette();
231
+ const local = React.useRef(null);
232
+ const select = (value) => {
233
+ onSelect?.(value);
234
+ if (page) palette?.push(page);
235
+ palette?.record({ value, label: local.current?.textContent?.trim() ?? value });
236
+ };
237
+ const selectRef = React.useRef(select);
238
+ selectRef.current = select;
239
+ React.useEffect(() => {
240
+ const value = local.current?.getAttribute("data-value");
241
+ if (!palette || !value) return;
242
+ const run = (v) => selectRef.current(v);
243
+ palette.handlers.set(value, run);
244
+ return () => {
245
+ if (palette.handlers.get(value) === run) palette.handlers.delete(value);
246
+ };
247
+ });
248
+ return /* @__PURE__ */ jsx(
249
+ Command.Item,
250
+ {
251
+ ref: useMergedRefs(ref, local),
252
+ onSelect: select,
253
+ className: cn(itemClasses, className),
254
+ ...props
255
+ }
256
+ );
134
257
  }
135
- ));
258
+ );
136
259
  Item.displayName = "CommandPalette.Item";
137
260
  const Separator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
138
261
  Command.Separator,
@@ -143,7 +266,18 @@ const Separator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
143
266
  }
144
267
  ));
145
268
  Separator.displayName = "CommandPalette.Separator";
146
- const CommandPalette = { Root, Input, List, Empty, Group, Item, Separator };
269
+ function Page({ id, title, children }) {
270
+ const palette = usePalette();
271
+ const setTitle = palette?.setTitle;
272
+ useIsomorphicLayoutEffect(() => {
273
+ if (id && title) setTitle?.(id, title);
274
+ }, [id, title]);
275
+ const pages = palette?.pages ?? [];
276
+ const active = id ? pages[pages.length - 1] === id : pages.length === 0;
277
+ return active ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
278
+ }
279
+ Page.displayName = "CommandPalette.Page";
280
+ const CommandPalette = { Root, Input, List, Empty, Group, Item, Separator, Page };
147
281
  export {
148
282
  CommandPalette
149
283
  };
@@ -1,2 +1,2 @@
1
1
  export { CommandPalette } from './CommandPalette.js';
2
- export type { CommandPaletteRootProps } from './CommandPalette.js';
2
+ export type { CommandPaletteRootProps, CommandPaletteItemProps, CommandPalettePageProps } from './CommandPalette.js';
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type ColumnDef, type RowSelectionState, type RowData, type VisibilityState, type ColumnSizingState, type ColumnOrderState, type ColumnPinningState, type ExpandedState } from '@tanstack/react-table';
2
+ import { type ColumnDef, type SortingState, type RowSelectionState, type RowData, type VisibilityState, type ColumnSizingState, type ColumnOrderState, type ColumnPinningState, type ExpandedState } from '@tanstack/react-table';
3
3
  declare module '@tanstack/react-table' {
4
4
  interface ColumnMeta<TData extends RowData, TValue> {
5
5
  /** Horizontal alignment of the header and cells. Use 'end' for numbers. @default 'start' */
@@ -75,6 +75,20 @@ export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivEleme
75
75
  pagination?: {
76
76
  pageSize?: number;
77
77
  };
78
+ /** Controlled sort (with `enableSorting`): `[{ id: 'name', desc: false }]`. */
79
+ sort?: SortingState;
80
+ onSortChange?: (sort: SortingState) => void;
81
+ /** Rows arrive sorted (server-side): headers still toggle `sort`, but rows aren't reordered. */
82
+ manualSort?: boolean;
83
+ /** Controlled page, zero-based. Turns pagination on. */
84
+ page?: number;
85
+ /** Rows per page; overrides `pagination.pageSize`. Turns pagination on. @default 20 */
86
+ pageSize?: number;
87
+ onPageChange?: (page: number) => void;
88
+ /** `data` is the current page only (server-side); set `rowCount` for the page count. Turns pagination on. */
89
+ manualPagination?: boolean;
90
+ /** Total rows across all pages, with `manualPagination`. */
91
+ rowCount?: number;
78
92
  /**
79
93
  * Nested rows (a tree table): return a row's children. The first column gets
80
94
  * an expand toggle and indents each level. Sorting sorts within each level.
@@ -96,4 +110,4 @@ export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivEleme
96
110
  /** Shown in a full-width row when there are no rows. @default messages.noResults */
97
111
  emptyState?: React.ReactNode;
98
112
  }
99
- export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, selectionColumn, rowSelection: rowSelectionProp, onRowSelectionChange, getRowId, bulkActions, density: densityProp, defaultDensity, onDensityChange, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, viewOptions, toolbar, enableColumnResizing, columnSizing: columnSizingProp, onColumnSizingChange, enableColumnReordering, columnOrder: columnOrderProp, onColumnOrderChange, columnPinning: columnPinningProp, onColumnPinningChange, maxHeight, grid, rowWindow, pagination, getSubRows, expanded: expandedProp, defaultExpanded, onExpandedChange, onRowOrderChange, getRowLabel, loading, emptyState, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;
113
+ export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, selectionColumn, rowSelection: rowSelectionProp, onRowSelectionChange, getRowId, bulkActions, density: densityProp, defaultDensity, onDensityChange, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, viewOptions, toolbar, enableColumnResizing, columnSizing: columnSizingProp, onColumnSizingChange, enableColumnReordering, columnOrder: columnOrderProp, onColumnOrderChange, columnPinning: columnPinningProp, onColumnPinningChange, maxHeight, grid, rowWindow, pagination: paginationProp, sort, onSortChange, manualSort, page, pageSize: pageSizeProp, onPageChange, manualPagination, rowCount, getSubRows, expanded: expandedProp, defaultExpanded, onExpandedChange, onRowOrderChange, getRowLabel, loading, emptyState, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;