@ox0/hooks 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +225 -0
  3. package/dist/index.d.ts +26 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +642 -9
  6. package/dist/use-before-unload-warning.d.ts +6 -0
  7. package/dist/use-before-unload-warning.d.ts.map +1 -0
  8. package/dist/use-breakpoint.d.ts +12 -0
  9. package/dist/use-breakpoint.d.ts.map +1 -0
  10. package/dist/use-container-size.d.ts +18 -0
  11. package/dist/use-container-size.d.ts.map +1 -0
  12. package/dist/use-controllable-state.d.ts +15 -0
  13. package/dist/use-controllable-state.d.ts.map +1 -0
  14. package/dist/use-controllable-state.test.d.ts +2 -0
  15. package/dist/use-controllable-state.test.d.ts.map +1 -0
  16. package/dist/use-copy-to-clipboard.d.ts +6 -0
  17. package/dist/use-copy-to-clipboard.d.ts.map +1 -0
  18. package/dist/use-debounced-value.d.ts +2 -0
  19. package/dist/use-debounced-value.d.ts.map +1 -0
  20. package/dist/use-debounced-value.test.d.ts +2 -0
  21. package/dist/use-debounced-value.test.d.ts.map +1 -0
  22. package/dist/use-deferred-presence.d.ts +15 -0
  23. package/dist/use-deferred-presence.d.ts.map +1 -0
  24. package/dist/use-disclosure.d.ts +8 -0
  25. package/dist/use-disclosure.d.ts.map +1 -0
  26. package/dist/use-disclosure.test.d.ts +2 -0
  27. package/dist/use-disclosure.test.d.ts.map +1 -0
  28. package/dist/use-event-listener.d.ts +5 -0
  29. package/dist/use-event-listener.d.ts.map +1 -0
  30. package/dist/use-hover.d.ts +5 -0
  31. package/dist/use-hover.d.ts.map +1 -0
  32. package/dist/use-interval.d.ts +2 -0
  33. package/dist/use-interval.d.ts.map +1 -0
  34. package/dist/use-key-down.d.ts +9 -0
  35. package/dist/use-key-down.d.ts.map +1 -0
  36. package/dist/use-long-press.d.ts +23 -0
  37. package/dist/use-long-press.d.ts.map +1 -0
  38. package/dist/use-media-query.d.ts +2 -0
  39. package/dist/use-media-query.d.ts.map +1 -0
  40. package/dist/use-media-query.test.d.ts +2 -0
  41. package/dist/use-media-query.test.d.ts.map +1 -0
  42. package/dist/use-navigator-online.d.ts +3 -0
  43. package/dist/use-navigator-online.d.ts.map +1 -0
  44. package/dist/use-now.d.ts +6 -0
  45. package/dist/use-now.d.ts.map +1 -0
  46. package/dist/use-os.d.ts +8 -0
  47. package/dist/use-os.d.ts.map +1 -0
  48. package/dist/use-pointer-coarse.d.ts +3 -0
  49. package/dist/use-pointer-coarse.d.ts.map +1 -0
  50. package/dist/use-prefers-reduced-motion.d.ts +3 -0
  51. package/dist/use-prefers-reduced-motion.d.ts.map +1 -0
  52. package/dist/use-press.d.ts +3 -0
  53. package/dist/use-press.d.ts.map +1 -0
  54. package/dist/use-previous.d.ts +2 -0
  55. package/dist/use-previous.d.ts.map +1 -0
  56. package/dist/use-stored-state.d.ts +23 -0
  57. package/dist/use-stored-state.d.ts.map +1 -0
  58. package/dist/use-stored-state.test.d.ts +2 -0
  59. package/dist/use-stored-state.test.d.ts.map +1 -0
  60. package/dist/use-timeout.d.ts +2 -0
  61. package/dist/use-timeout.d.ts.map +1 -0
  62. package/dist/use-viewport.d.ts +12 -0
  63. package/dist/use-viewport.d.ts.map +1 -0
  64. package/dist/use-window-size.d.ts +7 -0
  65. package/dist/use-window-size.d.ts.map +1 -0
  66. package/package.json +11 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Erik Sturcke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # @ox0/hooks
2
+
3
+ Shared React hooks for ox0 applications. Every hook is SSR-safe unless noted.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @ox0/hooks
9
+ ```
10
+
11
+ ## Hooks
12
+
13
+ ### State and control
14
+
15
+ #### `useControllableState<T>(options)`
16
+
17
+ Controlled/uncontrolled state bridge. Pass `value` + `onChange` for controlled mode; omit `value`
18
+ for uncontrolled. When uncontrolled, internal state resets to `defaultValue` whenever `defaultValue`
19
+ changes — pass stable primitives to avoid surprise resets.
20
+
21
+ ```ts
22
+ const [val, setVal] = useControllableState({ defaultValue: "initial", value, onChange })
23
+ ```
24
+
25
+ #### `useDisclosure(initialOpen?)`
26
+
27
+ Boolean open/close state. Returns `{ isOpen, onOpen, onClose, onToggle }` with stable callbacks.
28
+
29
+ ```ts
30
+ const { isOpen, onOpen, onClose } = useDisclosure()
31
+ ```
32
+
33
+ ---
34
+
35
+ ### Persistence
36
+
37
+ All storage hooks require a `<StorageProvider scope={userId ?? null}>` ancestor. The scope string
38
+ prefixes every key so different users sharing a browser do not mix data. Pass `scope={null}` to
39
+ opt out of scoping.
40
+
41
+ ```tsx
42
+ import { StorageProvider } from "@ox0/hooks"
43
+
44
+ function App() {
45
+ return <StorageProvider scope={currentUserId}>{children}</StorageProvider>
46
+ }
47
+ ```
48
+
49
+ #### `usePersistentState(key, parser, options)`
50
+
51
+ `localStorage`, tab-private. Changes are not visible in other open tabs.
52
+
53
+ #### `usePersistentSharedState(key, parser, options)`
54
+
55
+ `localStorage`, shared across tabs. Changes broadcast via `BroadcastChannel` and the `storage`
56
+ event. All tabs with the same key stay in sync.
57
+
58
+ #### `useSessionState(key, parser, options)`
59
+
60
+ `sessionStorage`, tab-private.
61
+
62
+ #### `useSessionSharedState(key, parser, options)`
63
+
64
+ `sessionStorage`, shared within the tab (multiple hook instances on the same key sync via
65
+ in-tab pub/sub).
66
+
67
+ **Shared options:**
68
+
69
+ ```ts
70
+ type StoredStateOptions<T> = {
71
+ defaultValue: T
72
+ version?: number // @default 1
73
+ migrate?: (value: unknown, fromVersion: number) => T
74
+ }
75
+ ```
76
+
77
+ Pass any object with a `.parse(unknown): T` method as the `parser` (Zod schemas work directly).
78
+ Use `version` + `migrate` to transform stored data when the shape changes:
79
+
80
+ ```ts
81
+ const [theme, setTheme] = usePersistentSharedState("ui:theme", ThemeSchema, {
82
+ defaultValue: "system",
83
+ version: 2,
84
+ migrate: (old, from) => (from === 1 ? "system" : "system"),
85
+ })
86
+ ```
87
+
88
+ ---
89
+
90
+ ### Interaction
91
+
92
+ #### `usePress(props)` — re-export from React Aria
93
+
94
+ Cross-device press normalization for mouse, touch, keyboard (Space/Enter), and virtual pointer
95
+ (screen reader). Handles iOS text-selection conflicts and scroll-gesture cancellation. Prefer this
96
+ over raw `onClick` for custom interactive elements.
97
+
98
+ #### `useLongPress(options)`
99
+
100
+ Returns pointer event handlers that fire `onLongPress` after the pointer has been held for
101
+ `delayMs` (default 500ms) without moving beyond `moveThresholdPx` (default 8px). Cancels on
102
+ pointer up or excessive movement; suppresses the subsequent click.
103
+
104
+ #### `useKeyDown(key, callback, options?)`
105
+
106
+ Attaches a `keydown` listener to `window`. Modifier options: `ctrl`, `meta`, `shift`, `alt`.
107
+ Pass `enabled: false` to disable without removing the hook call.
108
+
109
+ ---
110
+
111
+ ### DOM and browser
112
+
113
+ #### `useContainerSize<T extends HTMLElement>()`
114
+
115
+ Measures element content size via `ResizeObserver`. Returns `[ref, { width, height }]`. Attach
116
+ the ref to the element to observe.
117
+
118
+ ```ts
119
+ const [ref, { width }] = useContainerSize<HTMLDivElement>()
120
+ ```
121
+
122
+ #### `useEventListener(type, listener, target?)`
123
+
124
+ Attaches a typed event listener to `window` (default), a `Document`, `HTMLElement`, or a ref.
125
+ The listener ref is always up to date — no stale-closure issues.
126
+
127
+ #### `useHover<T extends HTMLElement>()`
128
+
129
+ Returns `[ref, isHovered]`. Uses `pointerenter`/`pointerleave` events.
130
+
131
+ ---
132
+
133
+ ### Viewport and layout
134
+
135
+ #### `useBreakpoint()`
136
+
137
+ Tracks the current responsive breakpoint. Returns
138
+ `{ breakpoint, isMobile, isTablet, isDesktop, isMobileOrTablet, isTabletOrDesktop }`.
139
+
140
+ Breakpoints: mobile ≤ 767px, tablet ≤ 1023px, desktop > 1023px.
141
+
142
+ #### `useWindowSize()`
143
+
144
+ Tracks `{ width, height }` from `window.innerWidth`/`innerHeight`.
145
+
146
+ #### `useViewport()`
147
+
148
+ Tracks `{ visualViewportHeight, keyboardOpen }`. `keyboardOpen` is true when the on-screen
149
+ keyboard reduces the visual viewport by more than 150px — useful for mobile layouts.
150
+
151
+ ---
152
+
153
+ ### Media queries and preferences
154
+
155
+ #### `useMediaQuery(query)`
156
+
157
+ Returns `true` when the CSS media query matches. Updates on change.
158
+
159
+ #### `usePointerCoarse()`
160
+
161
+ Returns `true` when the primary pointer is coarse (touch screen). Shorthand for
162
+ `useMediaQuery("(pointer: coarse)")`.
163
+
164
+ #### `usePrefersReducedMotion()`
165
+
166
+ Returns `true` when the user has requested reduced motion.
167
+
168
+ #### `useNavigatorOnline()`
169
+
170
+ Returns `true` when the browser reports a network connection. Updates on `online`/`offline` events.
171
+
172
+ ---
173
+
174
+ ### OS and environment
175
+
176
+ #### `useOS(override?)`
177
+
178
+ Detects the runtime OS: `"mac" | "windows" | "linux" | "mobile"`. Useful for keyboard shortcut
179
+ labels. Pass `override` to force a value in tests or demos.
180
+
181
+ ---
182
+
183
+ ### Timing
184
+
185
+ #### `useDebouncedValue<T>(value, delay?)`
186
+
187
+ Returns a debounced copy of `value` that only updates after `delay` ms (default 200ms) of no
188
+ changes.
189
+
190
+ #### `useInterval(callback, delay)`
191
+
192
+ Calls `callback` every `delay` ms. Pass `delay = null` to pause. Callback ref is always current.
193
+
194
+ #### `useTimeout(callback, delay)`
195
+
196
+ Calls `callback` once after `delay` ms. Pass `delay = null` to disable. Callback ref is always
197
+ current.
198
+
199
+ #### `useNow(options?)`
200
+
201
+ Returns the current `Date.now()` value, updating every `update` ms (default 1000ms). Pass
202
+ `update: false` to get a stable snapshot.
203
+
204
+ ---
205
+
206
+ ### Utilities
207
+
208
+ #### `usePrevious<T>(value)`
209
+
210
+ Returns the value from the previous render. Returns `undefined` on the first render.
211
+
212
+ #### `useCopyToClipboard(resetMs?)`
213
+
214
+ Returns `{ copy: (text: string) => Promise<void>, copied: boolean }`. `copied` resets to `false`
215
+ after `resetMs` ms (default 2000ms).
216
+
217
+ #### `useBeforeUnloadWarning(enabled)`
218
+
219
+ Shows the browser's native "leave page?" dialog when `enabled` is `true`. Use for unsaved-changes
220
+ guards.
221
+
222
+ #### `useDeferredPresence(options)`
223
+
224
+ Manages show/hide lifecycle with entry delay, minimum visible time, and exit duration. Returns
225
+ `"hidden" | "visible" | "exiting"`. Useful for tooltips, toasts, and animated presence.
@@ -0,0 +1,26 @@
1
+ export * from "./use-before-unload-warning";
2
+ export * from "./use-breakpoint";
3
+ export * from "./use-container-size";
4
+ export * from "./use-controllable-state";
5
+ export * from "./use-copy-to-clipboard";
6
+ export * from "./use-debounced-value";
7
+ export * from "./use-deferred-presence";
8
+ export * from "./use-disclosure";
9
+ export * from "./use-event-listener";
10
+ export * from "./use-hover";
11
+ export * from "./use-interval";
12
+ export * from "./use-key-down";
13
+ export * from "./use-stored-state";
14
+ export * from "./use-long-press";
15
+ export * from "./use-press";
16
+ export * from "./use-media-query";
17
+ export * from "./use-navigator-online";
18
+ export * from "./use-now";
19
+ export * from "./use-os";
20
+ export * from "./use-pointer-coarse";
21
+ export * from "./use-prefers-reduced-motion";
22
+ export * from "./use-previous";
23
+ export * from "./use-timeout";
24
+ export * from "./use-viewport";
25
+ export * from "./use-window-size";
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA;AAC3C,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,yBAAyB,CAAA;AACvC,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA;AACtC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA"}