@boxcustodia/library 2.0.0-alpha.19 → 2.0.0-alpha.20

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 (265) hide show
  1. package/dist/components/button/button.cjs.js +1 -1
  2. package/dist/components/button/button.es.js +19 -18
  3. package/dist/components/button/components/base-button.cjs.js +1 -1
  4. package/dist/components/button/components/base-button.es.js +20 -20
  5. package/dist/components/calendar/calendar.cjs.js +1 -1
  6. package/dist/components/calendar/calendar.es.js +1 -0
  7. package/dist/components/date-picker/date-input.cjs.js +1 -1
  8. package/dist/components/date-picker/date-input.es.js +92 -75
  9. package/dist/components/date-picker/date-picker.cjs.js +1 -1
  10. package/dist/components/date-picker/date-picker.es.js +104 -95
  11. package/dist/components/date-picker/date-picker.utils.cjs.js +1 -1
  12. package/dist/components/date-picker/date-picker.utils.es.js +51 -43
  13. package/dist/components/date-picker/use-hidden-field-value.cjs.js +1 -0
  14. package/dist/components/date-picker/use-hidden-field-value.es.js +11 -0
  15. package/dist/components/menu/menu.es.js +1 -9
  16. package/dist/components/otp/otp.cjs.js +2 -0
  17. package/dist/components/otp/otp.es.js +93 -0
  18. package/dist/components/password/password.cjs.js +1 -1
  19. package/dist/components/password/password.es.js +2 -2
  20. package/dist/components/select/select.cjs.js +1 -1
  21. package/dist/components/select/select.es.js +68 -60
  22. package/dist/hooks/internal/is-apple-device.cjs.js +1 -0
  23. package/dist/hooks/internal/is-apple-device.es.js +9 -0
  24. package/dist/hooks/internal/use-latest-ref.cjs.js +1 -0
  25. package/dist/hooks/internal/use-latest-ref.es.js +11 -0
  26. package/dist/hooks/use-array/use-array.cjs.js +1 -1
  27. package/dist/hooks/use-array/use-array.es.js +54 -42
  28. package/dist/hooks/use-async/use-async.cjs.js +1 -1
  29. package/dist/hooks/use-async/use-async.es.js +53 -20
  30. package/dist/hooks/use-boolean/use-boolean.cjs.js +1 -0
  31. package/dist/hooks/use-boolean/use-boolean.es.js +25 -0
  32. package/dist/hooks/use-click-outside/use-click-outside.cjs.js +1 -1
  33. package/dist/hooks/use-click-outside/use-click-outside.es.js +26 -12
  34. package/dist/hooks/use-debounce-callback/use-debounced-callback.cjs.js +1 -1
  35. package/dist/hooks/use-debounce-callback/use-debounced-callback.es.js +27 -10
  36. package/dist/hooks/use-debounce-value/use-debounced-value.cjs.js +1 -1
  37. package/dist/hooks/use-debounce-value/use-debounced-value.es.js +7 -9
  38. package/dist/hooks/use-disclosure/use-disclosure.cjs.js +1 -1
  39. package/dist/hooks/use-disclosure/use-disclosure.es.js +21 -11
  40. package/dist/hooks/use-document-title/use-document-title.cjs.js +1 -1
  41. package/dist/hooks/use-document-title/use-document-title.es.js +14 -12
  42. package/dist/hooks/use-event-listener/use-event-listener.cjs.js +1 -1
  43. package/dist/hooks/use-event-listener/use-event-listener.es.js +17 -9
  44. package/dist/hooks/use-hotkey/use-hotkey.cjs.js +1 -1
  45. package/dist/hooks/use-hotkey/use-hotkey.es.js +30 -14
  46. package/dist/hooks/use-hotkey/utils/is-input-field.cjs.js +1 -1
  47. package/dist/hooks/use-hotkey/utils/is-input-field.es.js +4 -2
  48. package/dist/hooks/use-hotkey/utils/match-and-run.cjs.js +1 -0
  49. package/dist/hooks/use-hotkey/utils/match-and-run.es.js +12 -0
  50. package/dist/hooks/use-hotkey/utils/match-key-modifiers.cjs.js +1 -1
  51. package/dist/hooks/use-hotkey/utils/match-key-modifiers.es.js +13 -12
  52. package/dist/hooks/use-hover/use-hover.cjs.js +1 -1
  53. package/dist/hooks/use-hover/use-hover.es.js +32 -17
  54. package/dist/hooks/use-is-visible/use-is-visible.cjs.js +1 -1
  55. package/dist/hooks/use-is-visible/use-is-visible.es.js +31 -27
  56. package/dist/hooks/use-local-storage/use-local-storage.cjs.js +1 -1
  57. package/dist/hooks/use-local-storage/use-local-storage.es.js +52 -20
  58. package/dist/hooks/use-media-query/use-media-query.cjs.js +1 -1
  59. package/dist/hooks/use-media-query/use-media-query.es.js +21 -11
  60. package/dist/hooks/use-mutation/use-mutation.cjs.js +1 -1
  61. package/dist/hooks/use-mutation/use-mutation.es.js +36 -22
  62. package/dist/hooks/use-object/use-object.cjs.js +1 -1
  63. package/dist/hooks/use-object/use-object.es.js +26 -22
  64. package/dist/hooks/use-prevent-page-close/use-prevent-page-close.cjs.js +1 -0
  65. package/dist/hooks/use-prevent-page-close/use-prevent-page-close.es.js +14 -0
  66. package/dist/hooks/use-step/use-step.cjs.js +1 -1
  67. package/dist/hooks/use-step/use-step.es.js +25 -24
  68. package/dist/index.cjs.js +1 -1
  69. package/dist/index.es.js +308 -300
  70. package/dist/src/components/date-picker/date-picker.utils.d.ts +17 -0
  71. package/dist/src/components/date-picker/use-hidden-field-value.d.ts +12 -0
  72. package/dist/src/components/index.d.ts +1 -0
  73. package/dist/src/hooks/index.d.ts +2 -2
  74. package/dist/src/hooks/internal/index.d.ts +2 -0
  75. package/dist/src/hooks/internal/is-apple-device.d.ts +12 -0
  76. package/dist/src/hooks/internal/use-latest-ref.d.ts +12 -0
  77. package/dist/src/hooks/use-array/use-array.d.ts +24 -11
  78. package/dist/src/hooks/use-async/use-async.d.ts +16 -13
  79. package/dist/src/hooks/use-boolean/index.d.ts +1 -0
  80. package/dist/src/hooks/use-boolean/use-boolean.d.ts +15 -0
  81. package/dist/src/hooks/use-boolean/use-boolean.test.d.ts +1 -0
  82. package/dist/src/hooks/use-click-outside/use-click-outside.d.ts +23 -1
  83. package/dist/src/hooks/use-debounce-callback/use-debounced-callback.d.ts +19 -1
  84. package/dist/src/hooks/use-debounce-value/use-debounced-value.d.ts +10 -1
  85. package/dist/src/hooks/use-disclosure/use-disclosure.d.ts +17 -8
  86. package/dist/src/hooks/use-document-title/use-document-title.d.ts +11 -0
  87. package/dist/src/hooks/use-event-listener/use-event-listener.d.ts +18 -1
  88. package/dist/src/hooks/use-hotkey/index.d.ts +2 -1
  89. package/dist/src/hooks/use-hotkey/use-hotkey.d.ts +62 -5
  90. package/dist/src/hooks/use-hotkey/utils/index.d.ts +4 -3
  91. package/dist/src/hooks/use-hotkey/utils/is-input-field.d.ts +12 -2
  92. package/dist/src/hooks/use-hotkey/utils/is-input-field.test.d.ts +1 -0
  93. package/dist/src/hooks/use-hotkey/utils/match-and-run.d.ts +36 -0
  94. package/dist/src/hooks/use-hotkey/utils/match-and-run.test.d.ts +1 -0
  95. package/dist/src/hooks/use-hotkey/utils/match-key-modifiers.d.ts +20 -6
  96. package/dist/src/hooks/use-hotkey/utils/match-key-modifiers.test.d.ts +1 -0
  97. package/dist/src/hooks/use-hover/use-hover.d.ts +8 -4
  98. package/dist/src/hooks/use-is-visible/use-is-visible.d.ts +28 -4
  99. package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +13 -2
  100. package/dist/src/hooks/use-media-query/use-media-query.d.ts +10 -1
  101. package/dist/src/hooks/use-media-query/use-media-query.test.d.ts +1 -0
  102. package/dist/src/hooks/use-mutation/use-mutation.d.ts +18 -11
  103. package/dist/src/hooks/use-object/use-object.d.ts +15 -6
  104. package/dist/src/hooks/use-prevent-page-close/index.d.ts +1 -0
  105. package/dist/src/hooks/use-prevent-page-close/use-prevent-page-close.d.ts +10 -0
  106. package/dist/src/hooks/use-prevent-page-close/use-prevent-page-close.test.d.ts +1 -0
  107. package/dist/src/hooks/use-step/use-step.d.ts +18 -11
  108. package/dist/src/utils/form.d.ts +10 -0
  109. package/package.json +1 -1
  110. package/src/components/alert-dialog/alert-dialog.test.tsx +13 -9
  111. package/src/components/auto-complete/auto-complete.test.tsx +4 -14
  112. package/src/components/avatar/avatar.test.tsx +7 -12
  113. package/src/components/button/button.test.tsx +10 -15
  114. package/src/components/button/button.tsx +14 -9
  115. package/src/components/button/components/base-button.tsx +2 -4
  116. package/src/components/calendar/calendar.test.tsx +12 -19
  117. package/src/components/calendar/calendar.tsx +4 -0
  118. package/src/components/card/card.test.tsx +4 -6
  119. package/src/components/checkbox/checkbox.test.tsx +12 -8
  120. package/src/components/checkbox-group/checkbox-group.test.tsx +7 -8
  121. package/src/components/combobox/combobox.test.tsx +24 -21
  122. package/src/components/date-picker/date-input-form.test.tsx +77 -0
  123. package/src/components/date-picker/date-input.stories.tsx +30 -18
  124. package/src/components/date-picker/date-input.tsx +77 -44
  125. package/src/components/date-picker/date-picker.stories.tsx +31 -1
  126. package/src/components/date-picker/date-picker.test.tsx +3 -13
  127. package/src/components/date-picker/date-picker.tsx +35 -16
  128. package/src/components/date-picker/date-picker.utils.test.ts +32 -14
  129. package/src/components/date-picker/date-picker.utils.ts +33 -0
  130. package/src/components/date-picker/use-date-input-popover.test.ts +3 -1
  131. package/src/components/date-picker/use-hidden-field-value.ts +23 -0
  132. package/src/components/dialog/dialog.test.tsx +10 -8
  133. package/src/components/dropzone/dropzone.test.tsx +11 -13
  134. package/src/components/empty/empty.test.tsx +4 -3
  135. package/src/components/field/field.test.tsx +12 -13
  136. package/src/components/form/form.stories.tsx +16 -1
  137. package/src/components/index.ts +1 -0
  138. package/src/components/label/label.test.tsx +3 -3
  139. package/src/components/menu/menu.tsx +1 -5
  140. package/src/components/number-input/number-input.test.tsx +6 -2
  141. package/src/components/password/password.test.tsx +20 -6
  142. package/src/components/password/password.tsx +2 -2
  143. package/src/components/popover/popover.test.tsx +4 -4
  144. package/src/components/progress/progress.test.tsx +7 -8
  145. package/src/components/radio-group/radio-group.test.tsx +17 -11
  146. package/src/components/select/select.test.tsx +10 -10
  147. package/src/components/select/select.tsx +9 -1
  148. package/src/components/stepper/stepper.stories.tsx +11 -15
  149. package/src/components/stepper/stepper.test.tsx +6 -4
  150. package/src/components/switch/switch.test.tsx +3 -3
  151. package/src/components/table/table.test.tsx +9 -3
  152. package/src/components/tabs/tabs.test.tsx +6 -2
  153. package/src/components/tag/tag.test.tsx +1 -3
  154. package/src/components/textarea/textarea.test.tsx +4 -1
  155. package/src/components/timeline/timeline.test.tsx +10 -5
  156. package/src/components/toast/toast.test.tsx +11 -14
  157. package/src/components/tooltip/tooltip.test.tsx +1 -5
  158. package/src/components/tree/tree.test.tsx +3 -1
  159. package/src/hooks/index.ts +2 -2
  160. package/src/hooks/internal/index.ts +2 -0
  161. package/src/hooks/internal/is-apple-device.test.ts +41 -0
  162. package/src/hooks/internal/is-apple-device.ts +33 -0
  163. package/src/hooks/internal/use-isomorphic-layout-effect.ts +3 -1
  164. package/src/hooks/internal/use-latest-ref.ts +21 -0
  165. package/src/hooks/use-array/use-array.stories.tsx +435 -64
  166. package/src/hooks/use-array/use-array.test.tsx +398 -15
  167. package/src/hooks/use-array/use-array.ts +105 -66
  168. package/src/hooks/use-async/use-async.stories.tsx +255 -131
  169. package/src/hooks/use-async/use-async.test.ts +397 -0
  170. package/src/hooks/use-async/use-async.ts +117 -39
  171. package/src/hooks/use-boolean/index.ts +1 -0
  172. package/src/hooks/use-boolean/use-boolean.stories.tsx +377 -0
  173. package/src/hooks/use-boolean/use-boolean.test.tsx +177 -0
  174. package/src/hooks/use-boolean/use-boolean.ts +50 -0
  175. package/src/hooks/use-click-outside/use-click-outside.stories.tsx +188 -18
  176. package/src/hooks/use-click-outside/use-click-outside.test.tsx +89 -10
  177. package/src/hooks/use-click-outside/use-click-outside.ts +62 -16
  178. package/src/hooks/use-debounce-callback/use-debounced-callback.stories.tsx +141 -41
  179. package/src/hooks/use-debounce-callback/use-debounced-callback.test.ts +217 -9
  180. package/src/hooks/use-debounce-callback/use-debounced-callback.ts +71 -11
  181. package/src/hooks/use-debounce-value/use-debounced-value.stories.tsx +247 -47
  182. package/src/hooks/use-debounce-value/use-debounced-value.test.ts +105 -10
  183. package/src/hooks/use-debounce-value/use-debounced-value.ts +19 -10
  184. package/src/hooks/use-disclosure/use-disclosure.stories.tsx +305 -14
  185. package/src/hooks/use-disclosure/use-disclosure.test.ts +198 -50
  186. package/src/hooks/use-disclosure/use-disclosure.ts +49 -29
  187. package/src/hooks/use-document-title/use-document-title.stories.tsx +54 -0
  188. package/src/hooks/use-document-title/use-document-title.test.tsx +26 -0
  189. package/src/hooks/use-document-title/{use-document-title.tsx → use-document-title.ts} +17 -3
  190. package/src/hooks/use-event-listener/use-event-listener.stories.tsx +105 -9
  191. package/src/hooks/use-event-listener/use-event-listener.test.tsx +77 -10
  192. package/src/hooks/use-event-listener/use-event-listener.ts +71 -11
  193. package/src/hooks/use-focus-trap/use-focus-trap.test.ts +31 -6
  194. package/src/hooks/use-focus-trap/use-focus-trap.ts +3 -2
  195. package/src/hooks/use-hotkey/index.ts +9 -1
  196. package/src/hooks/use-hotkey/use-hotkey.stories.tsx +279 -74
  197. package/src/hooks/use-hotkey/use-hotkey.test.tsx +286 -34
  198. package/src/hooks/use-hotkey/use-hotkey.ts +141 -17
  199. package/src/hooks/use-hotkey/utils/index.ts +8 -3
  200. package/src/hooks/use-hotkey/utils/is-input-field.test.ts +78 -0
  201. package/src/hooks/use-hotkey/utils/is-input-field.ts +31 -10
  202. package/src/hooks/use-hotkey/utils/match-and-run.test.ts +203 -0
  203. package/src/hooks/use-hotkey/utils/match-and-run.ts +62 -0
  204. package/src/hooks/use-hotkey/utils/match-key-modifiers.test.ts +65 -0
  205. package/src/hooks/use-hotkey/utils/match-key-modifiers.ts +39 -12
  206. package/src/hooks/use-hover/use-hover.stories.tsx +258 -80
  207. package/src/hooks/use-hover/use-hover.test.tsx +266 -26
  208. package/src/hooks/use-hover/use-hover.tsx +93 -28
  209. package/src/hooks/use-is-visible/use-is-visible.stories.tsx +193 -46
  210. package/src/hooks/use-is-visible/use-is-visible.test.tsx +235 -7
  211. package/src/hooks/use-is-visible/use-is-visible.ts +114 -0
  212. package/src/hooks/use-local-storage/use-local-storage.stories.tsx +129 -29
  213. package/src/hooks/use-local-storage/use-local-storage.test.ts +106 -41
  214. package/src/hooks/use-local-storage/use-local-storage.ts +100 -31
  215. package/src/hooks/use-media-query/use-media-query.stories.tsx +86 -26
  216. package/src/hooks/use-media-query/use-media-query.test.ts +132 -0
  217. package/src/hooks/use-media-query/use-media-query.ts +39 -14
  218. package/src/hooks/use-memoized-fn/use-memoized-fn.ts +0 -1
  219. package/src/hooks/use-mutation/use-mutation.stories.tsx +260 -94
  220. package/src/hooks/use-mutation/use-mutation.test.ts +359 -0
  221. package/src/hooks/use-mutation/use-mutation.ts +97 -0
  222. package/src/hooks/use-object/use-object.stories.tsx +310 -79
  223. package/src/hooks/use-object/use-object.test.tsx +235 -56
  224. package/src/hooks/use-object/use-object.ts +59 -0
  225. package/src/hooks/use-pagination/use-pagination.tsx +0 -1
  226. package/src/hooks/use-prevent-page-close/index.ts +1 -0
  227. package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +39 -0
  228. package/src/hooks/use-prevent-page-close/use-prevent-page-close.test.ts +89 -0
  229. package/src/hooks/use-prevent-page-close/use-prevent-page-close.ts +27 -0
  230. package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +1 -1
  231. package/src/hooks/use-range-pagination/use-range-pagination.tsx +1 -1
  232. package/src/hooks/use-selection/use-selection.ts +0 -1
  233. package/src/hooks/use-step/use-step.stories.tsx +178 -65
  234. package/src/hooks/use-step/use-step.test.ts +178 -53
  235. package/src/hooks/use-step/use-step.ts +57 -49
  236. package/src/utils/form.test.tsx +13 -8
  237. package/src/utils/form.tsx +10 -0
  238. package/src/utils/functions/getFormData.test.ts +1 -1
  239. package/dist/hooks/use-hotkey/utils/create-hotkey-listener.cjs.js +0 -1
  240. package/dist/hooks/use-hotkey/utils/create-hotkey-listener.es.js +0 -10
  241. package/dist/hooks/use-prevent-close-window/use-prevent-close-window.cjs.js +0 -1
  242. package/dist/hooks/use-prevent-close-window/use-prevent-close-window.es.js +0 -15
  243. package/dist/hooks/use-toggle/use-toggle.cjs.js +0 -1
  244. package/dist/hooks/use-toggle/use-toggle.es.js +0 -10
  245. package/dist/src/hooks/use-hotkey/utils/create-hotkey-listener.d.ts +0 -1
  246. package/dist/src/hooks/use-prevent-close-window/index.d.ts +0 -1
  247. package/dist/src/hooks/use-prevent-close-window/use-prevent-close-window.d.ts +0 -13
  248. package/dist/src/hooks/use-toggle/index.d.ts +0 -1
  249. package/dist/src/hooks/use-toggle/use-toggle.d.ts +0 -3
  250. package/src/hooks/use-async/use-async.test.tsx +0 -68
  251. package/src/hooks/use-hotkey/utils/create-hotkey-listener.ts +0 -25
  252. package/src/hooks/use-is-visible/use-is-visible.tsx +0 -49
  253. package/src/hooks/use-mutation/use-mutation.test.tsx +0 -83
  254. package/src/hooks/use-mutation/use-mutation.tsx +0 -59
  255. package/src/hooks/use-object/use-object.tsx +0 -46
  256. package/src/hooks/use-prevent-close-window/index.ts +0 -1
  257. package/src/hooks/use-prevent-close-window/use-prevent-close-window.stories.tsx +0 -32
  258. package/src/hooks/use-prevent-close-window/use-prevent-close-window.test.ts +0 -79
  259. package/src/hooks/use-prevent-close-window/use-prevent-close-window.ts +0 -33
  260. package/src/hooks/use-toggle/index.ts +0 -1
  261. package/src/hooks/use-toggle/use-toggle.stories.tsx +0 -25
  262. package/src/hooks/use-toggle/use-toggle.test.tsx +0 -64
  263. package/src/hooks/use-toggle/use-toggle.ts +0 -14
  264. /package/dist/src/{hooks/use-prevent-close-window/use-prevent-close-window.test.d.ts → components/date-picker/date-input-form.test.d.ts} +0 -0
  265. /package/dist/src/hooks/{use-toggle/use-toggle.test.d.ts → internal/is-apple-device.test.d.ts} +0 -0
@@ -1,90 +1,268 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
  import { useHover } from "./use-hover";
3
3
 
4
- /**
5
- * Track whether the pointer is over a specific element.
6
- *
7
- * Returns a `ref` callback you attach to the target element and a
8
- * `hovered` boolean that flips while the pointer is inside its bounds.
9
- * Powered by `mouseenter` / `mouseleave` — children inside the target
10
- * do **not** flicker the state on and off.
11
- *
12
- * ```tsx
13
- * const { ref, hovered } = useHover<HTMLDivElement>();
14
- *
15
- * <div ref={ref} className={hovered ? "bg-accent" : ""}>
16
- * Hover me
17
- * </div>
18
- * ```
19
- *
20
- * Optional callbacks for imperative side effects (analytics, prefetch,
21
- * audio) they receive the native `MouseEvent` and don't force a render:
22
- *
23
- * ```tsx
24
- * useHover<HTMLAnchorElement>({
25
- * onHoverStart: () => prefetch("/dashboard"),
26
- * onHoverEnd: (event) => log("left", event.clientX, event.clientY),
27
- * });
28
- * ```
29
- *
30
- * Good to know:
31
- * - `ref` is a callback ref (not a `RefObject`). Pass it straight to a
32
- * DOM element — you can't read `.current` from outside the hook.
33
- * - Listeners are attached when the ref binds and removed when it
34
- * detaches (unmount, ref reassignment, conditional rendering). No
35
- * memory leaks.
36
- * - The callback identity is read fresh on every event, so passing
37
- * inline arrow functions is safe — no re-attaching, no stale closure.
38
- * - Generic param controls the element type:
39
- * `useHover<HTMLButtonElement>()` types the ref for a `<button>`.
40
- */
41
- const meta: Meta<typeof useHover> = {
42
- title: "hooks/useHover",
43
- parameters: { layout: "centered" },
44
- tags: ["beta"],
4
+ // ---------------------------------------------------------------------------
5
+ // Story components
6
+ // ---------------------------------------------------------------------------
7
+
8
+ function HoverBox({
9
+ openDelay,
10
+ closeDelay,
11
+ onHoverStart,
12
+ onHoverEnd,
13
+ label,
14
+ }: {
15
+ openDelay?: number;
16
+ closeDelay?: number;
17
+ onHoverStart?: (event: PointerEvent) => void;
18
+ onHoverEnd?: (event: PointerEvent) => void;
19
+ label?: string;
20
+ }) {
21
+ const { ref, hovered } = useHover<HTMLDivElement>({
22
+ openDelay,
23
+ closeDelay,
24
+ onHoverStart,
25
+ onHoverEnd,
26
+ });
27
+
28
+ return (
29
+ <div
30
+ ref={ref}
31
+ style={{
32
+ display: "inline-flex",
33
+ alignItems: "center",
34
+ justifyContent: "center",
35
+ width: 200,
36
+ height: 100,
37
+ borderRadius: 8,
38
+ border: "2px solid",
39
+ borderColor: hovered ? "#6366f1" : "#d1d5db",
40
+ backgroundColor: hovered ? "#eef2ff" : "#f9fafb",
41
+ color: hovered ? "#4338ca" : "#6b7280",
42
+ fontFamily: "sans-serif",
43
+ fontSize: 14,
44
+ fontWeight: 500,
45
+ cursor: "default",
46
+ transition: "all 150ms ease",
47
+ userSelect: "none",
48
+ }}
49
+ >
50
+ {label ?? (hovered ? "Hovered" : "Hover me")}
51
+ </div>
52
+ );
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Meta
57
+ // ---------------------------------------------------------------------------
58
+
59
+ const meta: Meta<typeof HoverBox> = {
60
+ title: "Hooks/useHover",
61
+ component: HoverBox,
62
+ parameters: {
63
+ layout: "centered",
64
+ docs: {
65
+ description: {
66
+ component: `
67
+ \`useHover\` tracks whether a pointer is over a DOM element. It returns a \`ref\`
68
+ to attach to the target and a \`hovered\` boolean.
69
+
70
+ \`\`\`ts
71
+ const { ref, hovered } = useHover<HTMLDivElement>({
72
+ openDelay, // ms before hovered becomes true (default 0 — synchronous)
73
+ closeDelay, // ms before hovered becomes false (default 0 — synchronous)
74
+ onHoverStart, // (event: PointerEvent) => void
75
+ onHoverEnd, // (event: PointerEvent) => void
76
+ });
77
+ \`\`\`
78
+
79
+ Uses \`pointerenter\`/\`pointerleave\` — touch-safe and no child-traversal flicker.
80
+ `.trim(),
81
+ },
82
+ },
83
+ },
84
+ argTypes: {
85
+ openDelay: {
86
+ control: { type: "number", min: 0, step: 50 },
87
+ description:
88
+ "Milliseconds before `hovered` becomes `true` after pointer entry. `0` = synchronous.",
89
+ defaultValue: 0,
90
+ table: { defaultValue: { summary: "0" } },
91
+ },
92
+ closeDelay: {
93
+ control: { type: "number", min: 0, step: 50 },
94
+ description:
95
+ "Milliseconds before `hovered` becomes `false` after pointer leave. `0` = synchronous.",
96
+ defaultValue: 0,
97
+ table: { defaultValue: { summary: "0" } },
98
+ },
99
+ onHoverStart: {
100
+ action: "onHoverStart",
101
+ description:
102
+ "Called with the originating `PointerEvent` when hover begins (after `openDelay`).",
103
+ table: { type: { summary: "(event: PointerEvent) => void" } },
104
+ },
105
+ onHoverEnd: {
106
+ action: "onHoverEnd",
107
+ description:
108
+ "Called with the originating `PointerEvent` when hover ends (after `closeDelay`).",
109
+ table: { type: { summary: "(event: PointerEvent) => void" } },
110
+ },
111
+ label: { table: { disable: true } },
112
+ },
113
+ args: {
114
+ openDelay: 0,
115
+ closeDelay: 0,
116
+ },
45
117
  };
46
118
 
47
119
  export default meta;
48
- type Story = StoryObj<typeof useHover>;
49
-
50
- /**
51
- * Move the pointer over the card. Background and label update in
52
- * real time. Notice the child elements (the icon and the caption) do
53
- * **not** flicker the state — `mouseenter` ignores child traversals.
54
- */
55
- export const Default: Story = {
120
+
121
+ type Story = StoryObj<typeof HoverBox>;
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Stories
125
+ // ---------------------------------------------------------------------------
126
+
127
+ export const Basic: Story = {
128
+ args: {
129
+ openDelay: 0,
130
+ closeDelay: 0,
131
+ },
132
+ parameters: {
133
+ docs: {
134
+ description: {
135
+ story:
136
+ "Hover state updates synchronously — no delays. The controls panel lets you change `openDelay` and `closeDelay` live.",
137
+ },
138
+ },
139
+ },
140
+ render: (args) => <HoverBox {...args} />,
141
+ };
142
+
143
+ export const OpenDelay: Story = {
144
+ args: {
145
+ openDelay: 300,
146
+ closeDelay: 0,
147
+ label: "Open delay 300 ms",
148
+ },
149
+ parameters: {
150
+ docs: {
151
+ description: {
152
+ story:
153
+ "`openDelay={300}` — `hovered` only becomes `true` after 300 ms of continuous hover. Move the pointer in and out quickly to see that fast pass-throughs are ignored.",
154
+ },
155
+ },
156
+ },
157
+ render: (args) => <HoverBox {...args} />,
158
+ };
159
+
160
+ export const CloseDelay: Story = {
161
+ args: {
162
+ openDelay: 0,
163
+ closeDelay: 200,
164
+ label: "Close delay 200 ms",
165
+ },
166
+ parameters: {
167
+ docs: {
168
+ description: {
169
+ story:
170
+ "`closeDelay={200}` — `hovered` lingers 200 ms after the pointer leaves. Useful for tooltips and submenus where the pointer briefly exits before reaching the popup.",
171
+ },
172
+ },
173
+ },
174
+ render: (args) => <HoverBox {...args} />,
175
+ };
176
+
177
+ export const BothDelays: Story = {
178
+ args: {
179
+ openDelay: 300,
180
+ closeDelay: 200,
181
+ label: "Open 300 ms / Close 200 ms",
182
+ },
183
+ parameters: {
184
+ docs: {
185
+ description: {
186
+ story:
187
+ "Both delays active: entry is debounced and exit lingers. Ideal for hover cards where accidental traversal should never trigger the reveal.",
188
+ },
189
+ },
190
+ },
191
+ render: (args) => <HoverBox {...args} />,
192
+ };
193
+
194
+ export const Callbacks: Story = {
195
+ args: {
196
+ openDelay: 0,
197
+ closeDelay: 0,
198
+ label: "Check Actions panel",
199
+ },
200
+ parameters: {
201
+ docs: {
202
+ description: {
203
+ story:
204
+ "`onHoverStart` and `onHoverEnd` receive the originating `PointerEvent`. Open the **Actions** panel (or browser console) to see the events fire. Useful for analytics, prefetch, or audio triggers.",
205
+ },
206
+ },
207
+ },
208
+ render: (args) => <HoverBox {...args} />,
209
+ };
210
+
211
+ export const NestedElements: Story = {
212
+ parameters: {
213
+ docs: {
214
+ description: {
215
+ story:
216
+ "Moving the pointer into a child element does not re-trigger the parent — `pointerenter`/`pointerleave` do not bubble. The badge inside the box is a child element; hover over it and the parent state stays stable.",
217
+ },
218
+ },
219
+ },
56
220
  render: () => {
57
- const { ref, hovered } = useHover<HTMLDivElement>();
58
-
59
- return (
60
- <div
61
- ref={ref}
62
- style={{
63
- width: 280,
64
- padding: 24,
65
- borderRadius: 12,
66
- cursor: "pointer",
67
- transition: "background-color 150ms, transform 150ms",
68
- transform: hovered ? "translateY(-2px)" : "translateY(0)",
69
- }}
70
- className={
71
- hovered
72
- ? "border border-primary bg-primary/10 text-primary"
73
- : "border border-border bg-card"
74
- }
75
- >
76
- <div className="flex items-center gap-3">
77
- <span style={{ fontSize: 28 }}>{hovered ? "👋" : "🙂"}</span>
78
- <div className="flex flex-col">
79
- <span className="text-sm font-medium">
80
- {hovered ? "Hovering" : "Hover me"}
81
- </span>
82
- <span className="text-xs text-muted-foreground">
83
- hovered = {String(hovered)}
84
- </span>
85
- </div>
221
+ function NestedDemo() {
222
+ const { ref, hovered } = useHover<HTMLDivElement>();
223
+
224
+ return (
225
+ <div
226
+ ref={ref}
227
+ style={{
228
+ display: "inline-flex",
229
+ flexDirection: "column",
230
+ alignItems: "center",
231
+ justifyContent: "center",
232
+ gap: 8,
233
+ width: 220,
234
+ height: 130,
235
+ borderRadius: 8,
236
+ border: "2px solid",
237
+ borderColor: hovered ? "#6366f1" : "#d1d5db",
238
+ backgroundColor: hovered ? "#eef2ff" : "#f9fafb",
239
+ color: hovered ? "#4338ca" : "#6b7280",
240
+ fontFamily: "sans-serif",
241
+ fontSize: 14,
242
+ cursor: "default",
243
+ transition: "all 150ms ease",
244
+ userSelect: "none",
245
+ }}
246
+ >
247
+ <span>{hovered ? "Hovered (no flicker)" : "Hover me"}</span>
248
+ {/* Nested child — traversal between the parent and this badge
249
+ does not re-trigger the parent's pointerleave/pointerenter */}
250
+ <span
251
+ style={{
252
+ padding: "2px 10px",
253
+ borderRadius: 999,
254
+ backgroundColor: hovered ? "#6366f1" : "#e5e7eb",
255
+ color: hovered ? "#fff" : "#374151",
256
+ fontSize: 12,
257
+ transition: "all 150ms ease",
258
+ }}
259
+ >
260
+ child element
261
+ </span>
86
262
  </div>
87
- </div>
88
- );
263
+ );
264
+ }
265
+
266
+ return <NestedDemo />;
89
267
  },
90
268
  };