@ox0/hooks 0.2.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.
- package/LICENSE +21 -0
- package/README.md +225 -0
- package/dist/index.js +12 -4
- package/dist/use-controllable-state.d.ts +8 -0
- package/dist/use-controllable-state.d.ts.map +1 -1
- package/dist/use-controllable-state.test.d.ts +2 -0
- package/dist/use-controllable-state.test.d.ts.map +1 -0
- package/dist/use-debounced-value.d.ts.map +1 -1
- package/dist/use-debounced-value.test.d.ts +2 -0
- package/dist/use-debounced-value.test.d.ts.map +1 -0
- package/dist/use-disclosure.test.d.ts +2 -0
- package/dist/use-disclosure.test.d.ts.map +1 -0
- package/dist/use-media-query.test.d.ts +2 -0
- package/dist/use-media-query.test.d.ts.map +1 -0
- package/dist/use-stored-state.d.ts.map +1 -1
- package/dist/use-stored-state.test.d.ts +2 -0
- package/dist/use-stored-state.test.d.ts.map +1 -0
- package/package.json +6 -1
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.
|
package/dist/index.js
CHANGED
|
@@ -284,8 +284,12 @@ function useKeyDown(key, callback, { ctrl = false, meta = false, shift = false,
|
|
|
284
284
|
const instanceId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
285
285
|
const tabSubscribers = new Map();
|
|
286
286
|
function tabSubscribe(key, fn) {
|
|
287
|
-
|
|
288
|
-
|
|
287
|
+
let tabSet = tabSubscribers.get(key);
|
|
288
|
+
if (!tabSet) {
|
|
289
|
+
tabSet = new Set();
|
|
290
|
+
tabSubscribers.set(key, tabSet);
|
|
291
|
+
}
|
|
292
|
+
tabSet.add(fn);
|
|
289
293
|
return ()=>{
|
|
290
294
|
const set = tabSubscribers.get(key);
|
|
291
295
|
set?.delete(fn);
|
|
@@ -308,8 +312,12 @@ function getChannel() {
|
|
|
308
312
|
return broadcastChannel;
|
|
309
313
|
}
|
|
310
314
|
function crossTabSubscribe(key, fn) {
|
|
311
|
-
|
|
312
|
-
|
|
315
|
+
let crossTabSet = crossTabSubscribers.get(key);
|
|
316
|
+
if (!crossTabSet) {
|
|
317
|
+
crossTabSet = new Set();
|
|
318
|
+
crossTabSubscribers.set(key, crossTabSet);
|
|
319
|
+
}
|
|
320
|
+
crossTabSet.add(fn);
|
|
313
321
|
getChannel();
|
|
314
322
|
return ()=>{
|
|
315
323
|
const set = crossTabSubscribers.get(key);
|
|
@@ -3,5 +3,13 @@ export interface UseControllableStateOptions<TValue> {
|
|
|
3
3
|
readonly onChange?: (value: TValue) => void;
|
|
4
4
|
readonly value?: TValue;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Controlled/uncontrolled state bridge.
|
|
8
|
+
*
|
|
9
|
+
* Design decision: in uncontrolled mode, internal state resets to `defaultValue` whenever
|
|
10
|
+
* `defaultValue` changes. This is intentional — it supports controlled→uncontrolled transitions
|
|
11
|
+
* (e.g. when a parent stops passing `value`). Pass a stable primitive as `defaultValue` to avoid
|
|
12
|
+
* surprise resets.
|
|
13
|
+
*/
|
|
6
14
|
export declare function useControllableState<TValue>(options: UseControllableStateOptions<TValue>): [TValue, (nextValue: TValue) => void];
|
|
7
15
|
//# sourceMappingURL=use-controllable-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-controllable-state.d.ts","sourceRoot":"","sources":["../src/use-controllable-state.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,2BAA2B,CAAC,MAAM;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EACzC,OAAO,EAAE,2BAA2B,CAAC,MAAM,CAAC,GAC3C,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,CAoBvC"}
|
|
1
|
+
{"version":3,"file":"use-controllable-state.d.ts","sourceRoot":"","sources":["../src/use-controllable-state.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,2BAA2B,CAAC,MAAM;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EACzC,OAAO,EAAE,2BAA2B,CAAC,MAAM,CAAC,GAC3C,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,CAoBvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-controllable-state.test.d.ts","sourceRoot":"","sources":["../src/use-controllable-state.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-debounced-value.d.ts","sourceRoot":"","sources":["../src/use-debounced-value.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"use-debounced-value.d.ts","sourceRoot":"","sources":["../src/use-debounced-value.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,MAAM,CAe5E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-debounced-value.test.d.ts","sourceRoot":"","sources":["../src/use-debounced-value.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-disclosure.test.d.ts","sourceRoot":"","sources":["../src/use-disclosure.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-media-query.test.d.ts","sourceRoot":"","sources":["../src/use-media-query.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-stored-state.d.ts","sourceRoot":"","sources":["../src/use-stored-state.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,2EAA2E;AAC3E,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,CAAA;CAAE,CAAA;AAExD,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI;IAClC,YAAY,EAAE,CAAC,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC,CAAA;CACrD,CAAA;
|
|
1
|
+
{"version":3,"file":"use-stored-state.d.ts","sourceRoot":"","sources":["../src/use-stored-state.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,2EAA2E;AAC3E,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,CAAA;CAAE,CAAA;AAExD,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI;IAClC,YAAY,EAAE,CAAC,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC,CAAA;CACrD,CAAA;AA0HD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,SAAS,CAAA;CACpB,sGAEA;AAuID,iCAAiC;AACjC,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,uCAzHO,IAAI,EA4H1C;AAED,wCAAwC;AACxC,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,uCAlIO,IAAI,EAqI1C;AAED,mCAAmC;AACnC,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,uCAxI1D,IAAI,EA0I1C;AAED,yCAAyC;AACzC,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,uCAhJO,IAAI,EAmJ1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-stored-state.test.d.ts","sourceRoot":"","sources":["../src/use-stored-state.test.tsx"],"names":[],"mappings":""}
|