@boxcustodia/library 2.0.0-alpha.22 → 2.0.0-alpha.23

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 (102) hide show
  1. package/dist/components/calendar/calendar.cjs.js +1 -1
  2. package/dist/components/calendar/calendar.es.js +43 -44
  3. package/dist/components/date-picker/date-input.cjs.js +1 -1
  4. package/dist/components/date-picker/date-input.es.js +160 -140
  5. package/dist/components/pagination/pagination.cjs.js +1 -1
  6. package/dist/components/pagination/pagination.es.js +37 -35
  7. package/dist/components/scroll-area/scroll-area.cjs.js +1 -1
  8. package/dist/components/scroll-area/scroll-area.es.js +4 -4
  9. package/dist/components/select/select.cjs.js +1 -1
  10. package/dist/components/select/select.es.js +94 -90
  11. package/dist/hooks/use-action/use-action.cjs.js +1 -0
  12. package/dist/hooks/use-action/use-action.es.js +41 -0
  13. package/dist/hooks/use-pagination/use-pagination.cjs.js +1 -1
  14. package/dist/hooks/use-pagination/use-pagination.es.js +77 -32
  15. package/dist/hooks/use-range-pagination/use-range-pagination.cjs.js +1 -1
  16. package/dist/hooks/use-range-pagination/use-range-pagination.es.js +8 -5
  17. package/dist/hooks/use-selection/use-selection.cjs.js +1 -1
  18. package/dist/hooks/use-selection/use-selection.es.js +95 -33
  19. package/dist/hooks/use-session-storage/use-session-storage.cjs.js +1 -0
  20. package/dist/hooks/use-session-storage/use-session-storage.es.js +57 -0
  21. package/dist/index.cjs.js +1 -1
  22. package/dist/index.es.js +61 -63
  23. package/dist/src/components/select/select.d.ts +9 -2
  24. package/dist/src/hooks/index.d.ts +2 -3
  25. package/dist/src/hooks/internal/index.d.ts +1 -0
  26. package/dist/src/hooks/internal/serializer.d.ts +4 -0
  27. package/dist/src/hooks/use-action/index.d.ts +1 -0
  28. package/dist/src/hooks/use-action/use-action.d.ts +22 -0
  29. package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +2 -4
  30. package/dist/src/hooks/use-pagination/use-pagination.d.ts +47 -32
  31. package/dist/src/hooks/use-range-pagination/use-range-pagination.d.ts +16 -10
  32. package/dist/src/hooks/use-selection/use-selection.d.ts +39 -45
  33. package/dist/src/hooks/use-session-storage/index.d.ts +1 -0
  34. package/dist/src/hooks/use-session-storage/use-session-storage.d.ts +11 -0
  35. package/package.json +1 -1
  36. package/src/components/calendar/calendar.tsx +10 -8
  37. package/src/components/combobox/combobox.stories.tsx +16 -0
  38. package/src/components/date-picker/date-input.tsx +23 -2
  39. package/src/components/form/form.tsx +3 -2
  40. package/src/components/pagination/pagination.tsx +5 -3
  41. package/src/components/scroll-area/scroll-area.tsx +2 -2
  42. package/src/components/select/select.tsx +14 -3
  43. package/src/hooks/index.ts +2 -3
  44. package/src/hooks/internal/index.ts +1 -0
  45. package/src/hooks/internal/serializer.ts +4 -0
  46. package/src/hooks/use-action/index.ts +1 -0
  47. package/src/hooks/{use-mutation/use-mutation.stories.tsx → use-action/use-action.stories.tsx} +34 -34
  48. package/src/hooks/{use-mutation/use-mutation.test.ts → use-action/use-action.test.ts} +53 -53
  49. package/src/hooks/{use-mutation/use-mutation.ts → use-action/use-action.ts} +20 -20
  50. package/src/hooks/use-click-outside/use-click-outside.stories.tsx +0 -1
  51. package/src/hooks/use-clipboard/use-clipboard.stories.tsx +0 -1
  52. package/src/hooks/use-document-title/use-document-title.stories.tsx +0 -1
  53. package/src/hooks/use-is-visible/use-is-visible.test.tsx +1 -1
  54. package/src/hooks/use-local-storage/use-local-storage.stories.tsx +0 -1
  55. package/src/hooks/use-local-storage/use-local-storage.ts +2 -5
  56. package/src/hooks/use-media-query/use-media-query.stories.tsx +0 -1
  57. package/src/hooks/use-pagination/use-pagination.stories.tsx +720 -57
  58. package/src/hooks/use-pagination/use-pagination.test.tsx +560 -48
  59. package/src/hooks/use-pagination/use-pagination.ts +266 -0
  60. package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +0 -1
  61. package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +2 -2
  62. package/src/hooks/use-range-pagination/use-range-pagination.tsx +24 -21
  63. package/src/hooks/use-selection/use-selection.stories.tsx +339 -84
  64. package/src/hooks/use-selection/use-selection.test.tsx +417 -2
  65. package/src/hooks/use-selection/use-selection.ts +212 -102
  66. package/src/hooks/use-session-storage/index.ts +1 -0
  67. package/src/hooks/use-session-storage/use-session-storage.stories.tsx +122 -0
  68. package/src/hooks/use-session-storage/use-session-storage.test.ts +164 -0
  69. package/src/hooks/use-session-storage/use-session-storage.ts +115 -0
  70. package/dist/hooks/use-async/use-async.cjs.js +0 -1
  71. package/dist/hooks/use-async/use-async.es.js +0 -57
  72. package/dist/hooks/use-focus-trap/scope-tab.cjs.js +0 -1
  73. package/dist/hooks/use-focus-trap/scope-tab.es.js +0 -21
  74. package/dist/hooks/use-focus-trap/tabbable.cjs.js +0 -1
  75. package/dist/hooks/use-focus-trap/tabbable.es.js +0 -38
  76. package/dist/hooks/use-focus-trap/use-focus-trap.cjs.js +0 -1
  77. package/dist/hooks/use-focus-trap/use-focus-trap.es.js +0 -34
  78. package/dist/hooks/use-mutation/use-mutation.cjs.js +0 -1
  79. package/dist/hooks/use-mutation/use-mutation.es.js +0 -41
  80. package/dist/src/hooks/use-async/index.d.ts +0 -1
  81. package/dist/src/hooks/use-async/use-async.d.ts +0 -21
  82. package/dist/src/hooks/use-focus-trap/index.d.ts +0 -1
  83. package/dist/src/hooks/use-focus-trap/scope-tab.d.ts +0 -1
  84. package/dist/src/hooks/use-focus-trap/tabbable.d.ts +0 -4
  85. package/dist/src/hooks/use-focus-trap/use-focus-trap.d.ts +0 -1
  86. package/dist/src/hooks/use-mutation/index.d.ts +0 -1
  87. package/dist/src/hooks/use-mutation/use-mutation.d.ts +0 -22
  88. package/dist/src/hooks/use-mutation/use-mutation.test.d.ts +0 -1
  89. package/src/hooks/use-async/index.ts +0 -1
  90. package/src/hooks/use-async/use-async.stories.tsx +0 -272
  91. package/src/hooks/use-async/use-async.test.ts +0 -397
  92. package/src/hooks/use-async/use-async.ts +0 -135
  93. package/src/hooks/use-focus-trap/index.ts +0 -1
  94. package/src/hooks/use-focus-trap/scope-tab.ts +0 -38
  95. package/src/hooks/use-focus-trap/tabbable.ts +0 -70
  96. package/src/hooks/use-focus-trap/use-focus-trap.stories.tsx +0 -37
  97. package/src/hooks/use-focus-trap/use-focus-trap.test.ts +0 -355
  98. package/src/hooks/use-focus-trap/use-focus-trap.ts +0 -78
  99. package/src/hooks/use-mutation/index.ts +0 -1
  100. package/src/hooks/use-pagination/use-pagination.tsx +0 -84
  101. /package/dist/src/hooks/{use-async/use-async.test.d.ts → use-action/use-action.test.d.ts} +0 -0
  102. /package/dist/src/hooks/{use-focus-trap/use-focus-trap.test.d.ts → use-session-storage/use-session-storage.test.d.ts} +0 -0
@@ -3,7 +3,7 @@
3
3
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
4
4
  import { format, isDate } from "date-fns";
5
5
  import { CalendarIcon } from "lucide-react";
6
- import { KeyboardEvent } from "react";
6
+ import { KeyboardEvent, useState } from "react";
7
7
  import {
8
8
  Calendar,
9
9
  FieldControl,
@@ -15,6 +15,7 @@ import {
15
15
  } from "../../components";
16
16
  import { cn } from "../../lib";
17
17
  import type { DateInputProps } from "./date-picker.model";
18
+ import { parseInputToDate } from "./date-picker.utils";
18
19
  import { useDateInput } from "./use-date-input";
19
20
  import { useDateInputPopover } from "./use-date-input-popover";
20
21
  import { useHiddenFieldValue } from "./use-hidden-field-value";
@@ -67,6 +68,20 @@ export const DateInput = (props: DateInputProps) => {
67
68
  inputRef,
68
69
  } = useDateInputPopover({ disabled });
69
70
 
71
+ // Month shown by the calendar. Controlled so opening the popover can jump to
72
+ // the date currently typed in the input — even before it's committed.
73
+ const [calendarMonth, setCalendarMonth] = useState<Date | undefined>(
74
+ undefined,
75
+ );
76
+
77
+ const handleOpenChange = (next: boolean) => {
78
+ if (next) {
79
+ const typed = parseInputToDate(inputValue);
80
+ setCalendarMonth(typed ?? selectedDate ?? undefined);
81
+ }
82
+ setOpen(next);
83
+ };
84
+
70
85
  const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
71
86
  handleInputKeyDown(event);
72
87
  handlePopoverKeyDown(event);
@@ -95,7 +110,11 @@ export const DateInput = (props: DateInputProps) => {
95
110
  });
96
111
 
97
112
  return (
98
- <PopoverRoot data-slot="date-input" open={open} onOpenChange={setOpen}>
113
+ <PopoverRoot
114
+ data-slot="date-input"
115
+ open={open}
116
+ onOpenChange={handleOpenChange}
117
+ >
99
118
  <div
100
119
  className={cn(
101
120
  inputBaseClasses,
@@ -173,6 +192,8 @@ export const DateInput = (props: DateInputProps) => {
173
192
  <Calendar
174
193
  className="border-none"
175
194
  mode="single"
195
+ month={calendarMonth}
196
+ onMonthChange={setCalendarMonth}
176
197
  selected={selectedDate ?? undefined}
177
198
  onSelect={(next) => {
178
199
  if (!next || !isDate(next)) {
@@ -3,8 +3,9 @@
3
3
  import { Form as FormPrimitive } from "@base-ui/react/form";
4
4
  import type React from "react";
5
5
 
6
- export type FormProps<TValues extends Record<string, any> = Record<string, any>> =
7
- FormPrimitive.Props<TValues>;
6
+ export type FormProps<
7
+ TValues extends Record<string, any> = Record<string, any>,
8
+ > = FormPrimitive.Props<TValues>;
8
9
 
9
10
  export function Form<TValues extends Record<string, any> = Record<string, any>>(
10
11
  props: FormProps<TValues>,
@@ -130,9 +130,9 @@ export function PaginationRoot({
130
130
  const pagination = useRangePagination({
131
131
  totalItems,
132
132
  pageSize,
133
- currentPage,
134
- defaultCurrentPage,
135
- onCurrentPageChange,
133
+ page: currentPage,
134
+ defaultPage: defaultCurrentPage,
135
+ onPageChange: onCurrentPageChange,
136
136
  siblingCount,
137
137
  });
138
138
 
@@ -144,6 +144,8 @@ export function PaginationRoot({
144
144
  const value = useMemo<PaginationContextValue>(
145
145
  () => ({
146
146
  ...pagination,
147
+ currentPage: pagination.page,
148
+ maxPage: pagination.pageCount,
147
149
  pageSize,
148
150
  totalItems,
149
151
  setPageSize,
@@ -16,12 +16,12 @@ export function ScrollArea({
16
16
  }): React.ReactElement {
17
17
  return (
18
18
  <ScrollAreaPrimitive.Root
19
- className={cn("size-full min-h-0", className)}
19
+ className={cn("flex min-h-0 size-full flex-col", className)}
20
20
  {...props}
21
21
  >
22
22
  <ScrollAreaPrimitive.Viewport
23
23
  className={cn(
24
- "h-full rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-y:overscroll-y-contain data-has-overflow-x:overscroll-x-contain",
24
+ "min-h-0 flex-1 rounded-[inherit] outline-none transition-shadows focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-y:overscroll-y-contain data-has-overflow-x:overscroll-x-contain",
25
25
  scrollFade &&
26
26
  "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] [--fade-size:1.5rem]",
27
27
  scrollbarGutter &&
@@ -38,8 +38,12 @@ export function SelectRoot<
38
38
  export function SelectTrigger({
39
39
  className,
40
40
  children,
41
+ icon,
41
42
  ...props
42
- }: SelectPrimitive.Trigger.Props): React.ReactElement {
43
+ }: SelectPrimitive.Trigger.Props & {
44
+ /** Icon rendered inside the trigger. Defaults to a chevrons-up-down glyph. */
45
+ icon?: React.ReactNode;
46
+ }): React.ReactElement {
43
47
  return (
44
48
  <SelectPrimitive.Trigger
45
49
  className={cn(selectTriggerClasses, className)}
@@ -51,7 +55,7 @@ export function SelectTrigger({
51
55
  data-slot="select-icon"
52
56
  className="text-muted-foreground hover:text-foreground transition-colors"
53
57
  >
54
- <ChevronsUpDownIcon className={selectTriggerIconClassName} />
58
+ {icon ?? <ChevronsUpDownIcon className={selectTriggerIconClassName} />}
55
59
  </SelectPrimitive.Icon>
56
60
  </SelectPrimitive.Trigger>
57
61
  );
@@ -59,12 +63,15 @@ export function SelectTrigger({
59
63
 
60
64
  export type SelectButtonProps = useRender.ComponentProps<"button"> & {
61
65
  ref?: React.Ref<HTMLButtonElement>;
66
+ /** Icon rendered inside the button. Defaults to a chevrons-up-down glyph. */
67
+ icon?: React.ReactNode;
62
68
  };
63
69
 
64
70
  export function SelectButton({
65
71
  className,
66
72
  render,
67
73
  children,
74
+ icon,
68
75
  ...props
69
76
  }: SelectButtonProps): React.ReactElement {
70
77
  const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
@@ -76,7 +83,7 @@ export function SelectButton({
76
83
  <span className="flex-1 truncate in-data-placeholder:text-muted-foreground/72">
77
84
  {children}
78
85
  </span>
79
- <ChevronsUpDownIcon className={selectTriggerIconClassName} />
86
+ {icon ?? <ChevronsUpDownIcon className={selectTriggerIconClassName} />}
80
87
  </>
81
88
  ),
82
89
  className: cn(selectTriggerClasses, "min-w-0", className),
@@ -302,6 +309,8 @@ type SelectBaseProps<TItem = unknown> = Omit<
302
309
  renderItem?: (item: TItem) => React.ReactNode;
303
310
  placeholder?: string;
304
311
  className?: string;
312
+ /** Icon rendered inside the trigger. Defaults to a chevrons-up-down glyph. */
313
+ icon?: React.ReactNode;
305
314
  /** Styles applied to each internal slot. */
306
315
  classNames?: {
307
316
  /** Popup panel containing the item list. */
@@ -353,6 +362,7 @@ export function Select<TItem = unknown>(
353
362
  multiple,
354
363
  className,
355
364
  classNames,
365
+ icon,
356
366
  "aria-invalid": ariaInvalid,
357
367
  ...rest
358
368
  } = allProps as SelectBaseProps<TItem> & {
@@ -414,6 +424,7 @@ export function Select<TItem = unknown>(
414
424
  >
415
425
  <SelectTrigger
416
426
  className={className}
427
+ icon={icon}
417
428
  // Only forward `aria-invalid` when explicitly set. Base UI's Field
418
429
  // integration already sets it from validity state; passing `undefined`
419
430
  // here would clobber that value during prop merge and drop the error border.
@@ -1,6 +1,6 @@
1
1
  export { useControllableState } from "@radix-ui/react-use-controllable-state";
2
+ export * from "./use-action";
2
3
  export * from "./use-array";
3
- export * from "./use-async";
4
4
  export * from "./use-boolean";
5
5
  export * from "./use-click-outside";
6
6
  export * from "./use-clipboard";
@@ -9,14 +9,12 @@ export * from "./use-debounce-value";
9
9
  export * from "./use-disclosure";
10
10
  export * from "./use-document-title";
11
11
  export * from "./use-event-listener";
12
- export * from "./use-focus-trap";
13
12
  export * from "./use-hotkey";
14
13
  export * from "./use-hover";
15
14
  export * from "./use-is-visible";
16
15
  export * from "./use-local-storage";
17
16
  export * from "./use-media-query";
18
17
  export * from "./use-memoized-fn";
19
- export * from "./use-mutation";
20
18
  export * from "./use-object";
21
19
  export * from "./use-on-mount";
22
20
  export * from "./use-pagination";
@@ -24,4 +22,5 @@ export * from "./use-portal";
24
22
  export * from "./use-prevent-page-close";
25
23
  export * from "./use-range-pagination";
26
24
  export * from "./use-selection";
25
+ export * from "./use-session-storage";
27
26
  export * from "./use-step";
@@ -1,4 +1,5 @@
1
1
  export { isAppleDevice } from "./is-apple-device";
2
2
  export { isBrowser } from "./is-browser";
3
+ export type { Serializer } from "./serializer";
3
4
  export { useIsomorphicLayoutEffect } from "./use-isomorphic-layout-effect";
4
5
  export { useLatestRef } from "./use-latest-ref";
@@ -0,0 +1,4 @@
1
+ export interface Serializer<T> {
2
+ read: (raw: string) => T;
3
+ write: (value: T) => string;
4
+ }
@@ -0,0 +1 @@
1
+ export * from "./use-action";
@@ -1,7 +1,7 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
  import { useState } from "react";
3
3
  import { Button } from "../../components";
4
- import { type UseMutationOptions, useMutation } from "./use-mutation";
4
+ import { type UseActionOptions, useAction } from "./use-action";
5
5
 
6
6
  // ─── Demo component ──────────────────────────────────────────────────────────
7
7
 
@@ -14,21 +14,21 @@ interface DemoData {
14
14
  timestamp: number;
15
15
  }
16
16
 
17
- type MutationDemoProps = UseMutationOptions<DemoVariables, DemoData> & {
17
+ type ActionDemoProps = UseActionOptions<DemoVariables, DemoData> & {
18
18
  /** Simulated async delay in ms */
19
19
  delay?: number;
20
20
  /** When true the mutation will reject with an error */
21
21
  shouldFail?: boolean;
22
22
  };
23
23
 
24
- const MutationDemo = ({
24
+ const ActionDemo = ({
25
25
  delay = 800,
26
26
  shouldFail = false,
27
- onMutate,
27
+ onExecute,
28
28
  onSuccess,
29
29
  onError,
30
30
  onSettled,
31
- }: MutationDemoProps) => {
31
+ }: ActionDemoProps) => {
32
32
  const [log, setLog] = useState<string[]>([]);
33
33
 
34
34
  const addLog = (msg: string) =>
@@ -37,7 +37,7 @@ const MutationDemo = ({
37
37
  ...prev,
38
38
  ]);
39
39
 
40
- const mutation = useMutation<DemoVariables, DemoData>({
40
+ const action = useAction<DemoVariables, DemoData>({
41
41
  fn: async (vars) => {
42
42
  await new Promise((res, rej) =>
43
43
  setTimeout(
@@ -48,9 +48,9 @@ const MutationDemo = ({
48
48
  );
49
49
  return { message: `Created: ${vars.value}`, timestamp: Date.now() };
50
50
  },
51
- onMutate: (vars) => {
52
- addLog(`onMutate — vars: ${JSON.stringify(vars)}`);
53
- onMutate?.(vars);
51
+ onExecute: (vars) => {
52
+ addLog(`onExecute — vars: ${JSON.stringify(vars)}`);
53
+ onExecute?.(vars);
54
54
  },
55
55
  onSuccess: (data, vars) => {
56
56
  addLog(`onSuccess — data: ${data.message}`);
@@ -72,15 +72,15 @@ const MutationDemo = ({
72
72
  <div className="flex flex-col gap-4 max-w-sm">
73
73
  <div className="flex gap-2">
74
74
  <Button
75
- onClick={() => mutation.mutate({ value: "item-1" })}
76
- disabled={mutation.isPending}
75
+ onClick={() => action.execute({ value: "item-1" })}
76
+ disabled={action.isPending}
77
77
  >
78
- {mutation.isPending ? "Running…" : "Mutate"}
78
+ {action.isPending ? "Running…" : "Execute"}
79
79
  </Button>
80
80
  <Button
81
81
  variant="outline"
82
- onClick={() => mutation.reset()}
83
- disabled={mutation.isPending}
82
+ onClick={() => action.reset()}
83
+ disabled={action.isPending}
84
84
  >
85
85
  Reset
86
86
  </Button>
@@ -89,9 +89,9 @@ const MutationDemo = ({
89
89
  <pre className="rounded-md bg-slate-950 p-3 text-xs text-white leading-relaxed">
90
90
  {JSON.stringify(
91
91
  {
92
- status: mutation.status,
93
- data: mutation.data,
94
- error: mutation.error?.message ?? null,
92
+ status: action.status,
93
+ data: action.data,
94
+ error: action.error?.message ?? null,
95
95
  },
96
96
  null,
97
97
  2,
@@ -111,31 +111,31 @@ const MutationDemo = ({
111
111
 
112
112
  // ─── Meta ─────────────────────────────────────────────────────────────────────
113
113
 
114
- const meta: Meta<typeof MutationDemo> = {
115
- title: "hooks/useMutation",
116
- component: MutationDemo,
114
+ const meta: Meta<typeof ActionDemo> = {
115
+ title: "hooks/useAction",
116
+ component: ActionDemo,
117
117
  parameters: {
118
118
  docs: {
119
119
  description: {
120
120
  component: `
121
- \`useMutation\` manages async mutation lifecycle with stable callback identity.
121
+ \`useAction\` manages async mutation lifecycle with stable callback identity.
122
122
 
123
123
  **API summary**
124
124
 
125
125
  \`\`\`ts
126
- const { mutate, mutateAsync, reset, status, data, error, isPending, isSuccess, isError, isIdle } =
127
- useMutation({ fn, onMutate?, onSuccess?, onError?, onSettled? });
126
+ const { execute, executeAsync, reset, status, data, error, isPending, isSuccess, isError, isIdle } =
127
+ useAction({ fn, onExecute?, onSuccess?, onError?, onSettled? });
128
128
  \`\`\`
129
129
 
130
- **Lifecycle order (success):** \`onMutate\` → \`fn\` → \`onSuccess\` → \`onSettled\`
130
+ **Lifecycle order (success):** \`onExecute\` → \`fn\` → \`onSuccess\` → \`onSettled\`
131
131
 
132
- **Lifecycle order (error):** \`onMutate\` → \`fn\` throws → \`onError\` → \`onSettled\`
132
+ **Lifecycle order (error):** \`onExecute\` → \`fn\` throws → \`onError\` → \`onSettled\`
133
133
 
134
134
  **Key guarantees**
135
- - \`mutate\` is fire-and-forget: returns \`void\`, never throws.
136
- - \`mutateAsync\` returns a \`Promise\` that re-throws on failure.
135
+ - \`execute\` is fire-and-forget: returns \`void\`, never throws.
136
+ - \`executeAsync\` returns a \`Promise\` that re-throws on failure.
137
137
  - \`onSettled\` is always called — even if \`onError\` throws.
138
- - All callbacks read through \`useLatestRef\`, so inline functions never recreate \`mutate\`.
138
+ - All callbacks read through \`useLatestRef\`, so inline functions never recreate \`execute\`.
139
139
  `.trim(),
140
140
  },
141
141
  },
@@ -153,8 +153,8 @@ const { mutate, mutateAsync, reset, status, data, error, isPending, isSuccess, i
153
153
  "When true the demo's fn rejects — simulates an API error. Demo control only, not a hook option.",
154
154
  table: { category: "Demo controls" },
155
155
  },
156
- onMutate: {
157
- action: "onMutate",
156
+ onExecute: {
157
+ action: "onExecute",
158
158
  description:
159
159
  "Called synchronously with `variables` before `fn` is awaited. Use for optimistic updates.",
160
160
  table: {
@@ -192,11 +192,11 @@ const { mutate, mutateAsync, reset, status, data, error, isPending, isSuccess, i
192
192
  },
193
193
  },
194
194
  },
195
- render: (args) => <MutationDemo {...args} />,
195
+ render: (args) => <ActionDemo {...args} />,
196
196
  };
197
197
 
198
198
  export default meta;
199
- type Story = StoryObj<typeof MutationDemo>;
199
+ type Story = StoryObj<typeof ActionDemo>;
200
200
 
201
201
  // ─── Stories ──────────────────────────────────────────────────────────────────
202
202
 
@@ -224,7 +224,7 @@ export const Pending: Story = {
224
224
  docs: {
225
225
  description: {
226
226
  story:
227
- "Click **Mutate** to see `isPending=true` while the operation runs. The button disables until the promise settles.",
227
+ "Click **Execute** to see `isPending=true` while the operation runs. The button disables until the promise settles.",
228
228
  },
229
229
  },
230
230
  },
@@ -255,7 +255,7 @@ export const ErrorState: Story = {
255
255
  docs: {
256
256
  description: {
257
257
  story:
258
- "Toggle **shouldFail** in controls to simulate a failing API call. `status` becomes `'error'`, `error` is set, and `data` stays null. `mutate` swallows the rejection silently — use `mutateAsync` if you need to catch it.",
258
+ "Toggle **shouldFail** in controls to simulate a failing API call. `status` becomes `'error'`, `error` is set, and `data` stays null. `execute` swallows the rejection silently — use `executeAsync` if you need to catch it.",
259
259
  },
260
260
  },
261
261
  },