@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/dist/index.js CHANGED
@@ -1,4 +1,87 @@
1
1
  import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react";
2
+ import * as __WEBPACK_EXTERNAL_MODULE__react_aria_interactions_8422cce9__ from "@react-aria/interactions";
3
+ function useBeforeUnloadWarning(enabled) {
4
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
5
+ if (!enabled || "undefined" == typeof window) return;
6
+ const handle = (event)=>{
7
+ event.preventDefault();
8
+ event.returnValue = "";
9
+ };
10
+ window.addEventListener("beforeunload", handle);
11
+ return ()=>window.removeEventListener("beforeunload", handle);
12
+ }, [
13
+ enabled
14
+ ]);
15
+ }
16
+ const MOBILE_MAX = 767;
17
+ const TABLET_MAX = 1023;
18
+ function getBreakpoint() {
19
+ if ("undefined" == typeof window) return "desktop";
20
+ if (window.matchMedia(`(max-width: ${MOBILE_MAX}px)`).matches) return "mobile";
21
+ if (window.matchMedia(`(max-width: ${TABLET_MAX}px)`).matches) return "tablet";
22
+ return "desktop";
23
+ }
24
+ function toState(breakpoint) {
25
+ return {
26
+ breakpoint,
27
+ isMobile: "mobile" === breakpoint,
28
+ isTablet: "tablet" === breakpoint,
29
+ isDesktop: "desktop" === breakpoint,
30
+ isMobileOrTablet: "desktop" !== breakpoint,
31
+ isTabletOrDesktop: "mobile" !== breakpoint
32
+ };
33
+ }
34
+ function useBreakpoint() {
35
+ const [state, setState] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(()=>toState(getBreakpoint()));
36
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
37
+ if ("undefined" == typeof window) return;
38
+ const mobileQuery = window.matchMedia(`(max-width: ${MOBILE_MAX}px)`);
39
+ const tabletQuery = window.matchMedia(`(max-width: ${TABLET_MAX}px)`);
40
+ const handleChange = ()=>{
41
+ setState((prev)=>{
42
+ const next = getBreakpoint();
43
+ return prev.breakpoint === next ? prev : toState(next);
44
+ });
45
+ };
46
+ handleChange();
47
+ mobileQuery.addEventListener("change", handleChange);
48
+ tabletQuery.addEventListener("change", handleChange);
49
+ return ()=>{
50
+ mobileQuery.removeEventListener("change", handleChange);
51
+ tabletQuery.removeEventListener("change", handleChange);
52
+ };
53
+ }, []);
54
+ return state;
55
+ }
56
+ function useContainerSize() {
57
+ const ref = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
58
+ const [size, setSize] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)({
59
+ width: 0,
60
+ height: 0
61
+ });
62
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
63
+ const el = ref.current;
64
+ if (!el) return;
65
+ const observer = new ResizeObserver((entries)=>{
66
+ const rect = entries[0]?.contentRect;
67
+ if (rect) setSize({
68
+ width: rect.width,
69
+ height: rect.height
70
+ });
71
+ });
72
+ observer.observe(el);
73
+ const rect = el.getBoundingClientRect();
74
+ setSize({
75
+ width: rect.width,
76
+ height: rect.height
77
+ });
78
+ return ()=>observer.disconnect();
79
+ }, []);
80
+ return [
81
+ ref,
82
+ size
83
+ ];
84
+ }
2
85
  function useControllableState(options) {
3
86
  const { defaultValue, onChange, value } = options;
4
87
  const [uncontrolledValue, setUncontrolledValue] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(defaultValue);
@@ -19,6 +102,27 @@ function useControllableState(options) {
19
102
  setValue
20
103
  ];
21
104
  }
105
+ function useCopyToClipboard(resetMs = 2000) {
106
+ const [copied, setCopied] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(false);
107
+ const timerRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
108
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>()=>{
109
+ if (null !== timerRef.current) clearTimeout(timerRef.current);
110
+ }, []);
111
+ const copy = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)(async (text)=>{
112
+ try {
113
+ await navigator.clipboard.writeText(text);
114
+ setCopied(true);
115
+ if (null !== timerRef.current) clearTimeout(timerRef.current);
116
+ timerRef.current = setTimeout(()=>setCopied(false), resetMs);
117
+ } catch {}
118
+ }, [
119
+ resetMs
120
+ ]);
121
+ return {
122
+ copy,
123
+ copied
124
+ };
125
+ }
22
126
  function useDebouncedValue(value, delay = 200) {
23
127
  const [debouncedValue, setDebouncedValue] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(value);
24
128
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
@@ -34,6 +138,50 @@ function useDebouncedValue(value, delay = 200) {
34
138
  ]);
35
139
  return debouncedValue;
36
140
  }
141
+ function useDeferredPresence(isPresent, { delayMs, exitMs, minimumVisibleMs }) {
142
+ const [state, setState] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)({
143
+ phase: "hidden"
144
+ });
145
+ const timerRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(void 0);
146
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
147
+ clearTimeout(timerRef.current);
148
+ if (isPresent) {
149
+ if ("visible" === state.phase) return;
150
+ if ("exiting" === state.phase) {
151
+ setState({
152
+ phase: "visible",
153
+ shownAt: state.shownAt
154
+ });
155
+ return;
156
+ }
157
+ timerRef.current = window.setTimeout(()=>{
158
+ setState({
159
+ phase: "visible",
160
+ shownAt: Date.now()
161
+ });
162
+ }, delayMs);
163
+ } else if ("visible" === state.phase) {
164
+ const visibleMs = Date.now() - state.shownAt;
165
+ const delay = Math.max(0, minimumVisibleMs - visibleMs);
166
+ timerRef.current = window.setTimeout(()=>{
167
+ setState({
168
+ phase: "exiting",
169
+ shownAt: state.shownAt
170
+ });
171
+ }, delay);
172
+ } else if ("exiting" === state.phase) timerRef.current = window.setTimeout(()=>setState({
173
+ phase: "hidden"
174
+ }), exitMs);
175
+ return ()=>clearTimeout(timerRef.current);
176
+ }, [
177
+ delayMs,
178
+ exitMs,
179
+ isPresent,
180
+ minimumVisibleMs,
181
+ state
182
+ ]);
183
+ return state.phase;
184
+ }
37
185
  function useDisclosure(initialOpen = false) {
38
186
  const [isOpen, setIsOpen] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(initialOpen);
39
187
  const onClose = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)(()=>{
@@ -52,24 +200,376 @@ function useDisclosure(initialOpen = false) {
52
200
  onToggle
53
201
  };
54
202
  }
55
- function useLocalStorage(key, initialValue) {
56
- const [value, setValue] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(()=>{
57
- if ("undefined" == typeof window) return initialValue;
58
- const storedValue = window.localStorage.getItem(key);
59
- return storedValue ? JSON.parse(storedValue) : initialValue;
60
- });
203
+ function useEventListener(type, listener, target) {
204
+ const listenerRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(listener);
61
205
  (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
62
- if ("undefined" == typeof window) return;
63
- window.localStorage.setItem(key, JSON.stringify(value));
206
+ listenerRef.current = listener;
207
+ }, [
208
+ listener
209
+ ]);
210
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
211
+ const el = void 0 === target ? window : target && "current" in target ? target.current : target;
212
+ if (!el) return;
213
+ const handler = (e)=>listenerRef.current(e);
214
+ el.addEventListener(type, handler);
215
+ return ()=>el.removeEventListener(type, handler);
216
+ }, [
217
+ type,
218
+ target
219
+ ]);
220
+ }
221
+ function useHover() {
222
+ const ref = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
223
+ const [hovered, setHovered] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(false);
224
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
225
+ const el = ref.current;
226
+ if (!el) return;
227
+ const enter = ()=>setHovered(true);
228
+ const leave = ()=>setHovered(false);
229
+ el.addEventListener("pointerenter", enter);
230
+ el.addEventListener("pointerleave", leave);
231
+ return ()=>{
232
+ el.removeEventListener("pointerenter", enter);
233
+ el.removeEventListener("pointerleave", leave);
234
+ };
235
+ }, []);
236
+ return [
237
+ ref,
238
+ hovered
239
+ ];
240
+ }
241
+ function useInterval(callback, delay) {
242
+ const callbackRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(callback);
243
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
244
+ callbackRef.current = callback;
64
245
  }, [
246
+ callback
247
+ ]);
248
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
249
+ if (null === delay) return;
250
+ const id = window.setInterval(()=>callbackRef.current(), delay);
251
+ return ()=>window.clearInterval(id);
252
+ }, [
253
+ delay
254
+ ]);
255
+ }
256
+ function useKeyDown(key, callback, { ctrl = false, meta = false, shift = false, alt = false, enabled = true } = {}) {
257
+ const callbackRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(callback);
258
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
259
+ callbackRef.current = callback;
260
+ }, [
261
+ callback
262
+ ]);
263
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
264
+ if (!enabled) return;
265
+ const handler = (e)=>{
266
+ if (e.key !== key) return;
267
+ if (ctrl && !e.ctrlKey) return;
268
+ if (meta && !e.metaKey) return;
269
+ if (shift && !e.shiftKey) return;
270
+ if (alt && !e.altKey) return;
271
+ callbackRef.current(e);
272
+ };
273
+ window.addEventListener("keydown", handler);
274
+ return ()=>window.removeEventListener("keydown", handler);
275
+ }, [
276
+ key,
277
+ ctrl,
278
+ meta,
279
+ shift,
280
+ alt,
281
+ enabled
282
+ ]);
283
+ }
284
+ const instanceId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
285
+ const tabSubscribers = new Map();
286
+ function tabSubscribe(key, fn) {
287
+ let tabSet = tabSubscribers.get(key);
288
+ if (!tabSet) {
289
+ tabSet = new Set();
290
+ tabSubscribers.set(key, tabSet);
291
+ }
292
+ tabSet.add(fn);
293
+ return ()=>{
294
+ const set = tabSubscribers.get(key);
295
+ set?.delete(fn);
296
+ if (set?.size === 0) tabSubscribers.delete(key);
297
+ };
298
+ }
299
+ function tabNotify(key, value) {
300
+ tabSubscribers.get(key)?.forEach((fn)=>fn(value));
301
+ }
302
+ const crossTabSubscribers = new Map();
303
+ let broadcastChannel = null;
304
+ function getChannel() {
305
+ if ("undefined" == typeof window || "undefined" == typeof BroadcastChannel) return null;
306
+ if (!broadcastChannel) {
307
+ broadcastChannel = new BroadcastChannel("ox0:storage");
308
+ broadcastChannel.addEventListener("message", (e)=>{
309
+ crossTabSubscribers.get(e.data.key)?.forEach((fn)=>fn(e.data));
310
+ });
311
+ }
312
+ return broadcastChannel;
313
+ }
314
+ function crossTabSubscribe(key, fn) {
315
+ let crossTabSet = crossTabSubscribers.get(key);
316
+ if (!crossTabSet) {
317
+ crossTabSet = new Set();
318
+ crossTabSubscribers.set(key, crossTabSet);
319
+ }
320
+ crossTabSet.add(fn);
321
+ getChannel();
322
+ return ()=>{
323
+ const set = crossTabSubscribers.get(key);
324
+ set?.delete(fn);
325
+ if (set?.size === 0) crossTabSubscribers.delete(key);
326
+ };
327
+ }
328
+ function getTabId() {
329
+ if ("undefined" == typeof sessionStorage) return instanceId;
330
+ const existing = sessionStorage.getItem("ox0:tab-id");
331
+ if (existing) return existing;
332
+ const next = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
333
+ sessionStorage.setItem("ox0:tab-id", next);
334
+ return next;
335
+ }
336
+ function getStorage(kind) {
337
+ if ("undefined" == typeof window) return null;
338
+ return "persistent" === kind ? window.localStorage : window.sessionStorage;
339
+ }
340
+ function makeStorageKey(key, kind, shared, scope) {
341
+ const scoped = null !== scope ? `${scope}:${key}` : key;
342
+ const base = "session" === kind ? `session:${scoped}` : scoped;
343
+ return shared ? base : `${base}::${getTabId()}`;
344
+ }
345
+ function readSafe(raw, parser, defaultValue, version, migrate) {
346
+ if (null === raw) return {
347
+ value: defaultValue,
348
+ migrated: false
349
+ };
350
+ try {
351
+ const parsed = JSON.parse(raw);
352
+ if (!parsed || "object" != typeof parsed || !("v" in parsed) || !("value" in parsed)) return {
353
+ value: defaultValue,
354
+ migrated: false
355
+ };
356
+ if (parsed.v !== version) return migrate ? {
357
+ value: migrate(parsed.value, parsed.v),
358
+ migrated: true
359
+ } : {
360
+ value: defaultValue,
361
+ migrated: false
362
+ };
363
+ return {
364
+ value: parser.parse(parsed.value),
365
+ migrated: false
366
+ };
367
+ } catch {
368
+ return {
369
+ value: defaultValue,
370
+ migrated: false
371
+ };
372
+ }
373
+ }
374
+ function writeStorage(storage, key, value, version) {
375
+ const payload = {
376
+ v: version,
377
+ value
378
+ };
379
+ const raw = JSON.stringify(payload);
380
+ try {
381
+ storage?.setItem(key, raw);
382
+ } catch {}
383
+ return raw;
384
+ }
385
+ const StorageScopeContext = (0, __WEBPACK_EXTERNAL_MODULE_react__.createContext)(void 0);
386
+ function StorageProvider({ scope, children }) {
387
+ return (0, __WEBPACK_EXTERNAL_MODULE_react__.createElement)(StorageScopeContext.Provider, {
388
+ value: scope
389
+ }, children);
390
+ }
391
+ function useStorageScope() {
392
+ const scope = (0, __WEBPACK_EXTERNAL_MODULE_react__.useContext)(StorageScopeContext);
393
+ if (void 0 === scope) throw new Error("useStoredState: no StorageProvider found. Wrap your app with <StorageProvider scope={userId ?? null}>.");
394
+ return scope;
395
+ }
396
+ function useStoredState(kind, key, parser, { defaultValue, version = 1, migrate }, shared) {
397
+ const scope = useStorageScope();
398
+ const storage = getStorage(kind);
399
+ const storageKey = (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>makeStorageKey(key, kind, shared, scope), [
65
400
  key,
401
+ kind,
402
+ shared,
403
+ scope
404
+ ]);
405
+ const parserRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(parser);
406
+ const defaultValueRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(defaultValue);
407
+ const versionRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(version);
408
+ const migrateRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(migrate);
409
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
410
+ parserRef.current = parser;
411
+ defaultValueRef.current = defaultValue;
412
+ versionRef.current = version;
413
+ migrateRef.current = migrate;
414
+ });
415
+ const [value, setValueState] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(()=>{
416
+ const raw = storage?.getItem(storageKey) ?? null;
417
+ const result = readSafe(raw, parser, defaultValue, version, migrate);
418
+ if (result.migrated) writeStorage(storage, storageKey, result.value, version);
419
+ return result.value;
420
+ });
421
+ const valueRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(value);
422
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
423
+ valueRef.current = value;
424
+ }, [
66
425
  value
67
426
  ]);
427
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
428
+ const raw = storage?.getItem(storageKey) ?? null;
429
+ const result = readSafe(raw, parserRef.current, defaultValueRef.current, versionRef.current, migrateRef.current);
430
+ setValueState((prev)=>Object.is(prev, result.value) ? prev : result.value);
431
+ if (result.migrated) writeStorage(storage, storageKey, result.value, versionRef.current);
432
+ }, [
433
+ storage,
434
+ storageKey
435
+ ]);
436
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>tabSubscribe(storageKey, (next)=>{
437
+ setValueState((prev)=>Object.is(prev, next) ? prev : next);
438
+ }), [
439
+ storageKey
440
+ ]);
441
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
442
+ if (!shared) return;
443
+ return crossTabSubscribe(storageKey, (msg)=>{
444
+ if (msg.storage !== kind || msg.sourceId === instanceId) return;
445
+ const result = readSafe(msg.payload, parserRef.current, defaultValueRef.current, versionRef.current, migrateRef.current);
446
+ setValueState((prev)=>Object.is(prev, result.value) ? prev : result.value);
447
+ tabNotify(storageKey, result.value);
448
+ if (result.migrated) writeStorage(storage, storageKey, result.value, versionRef.current);
449
+ });
450
+ }, [
451
+ kind,
452
+ shared,
453
+ storage,
454
+ storageKey
455
+ ]);
456
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
457
+ if (!shared || "persistent" !== kind || "undefined" == typeof window) return;
458
+ const handler = (event)=>{
459
+ if (event.key !== storageKey) return;
460
+ const result = readSafe(event.newValue, parserRef.current, defaultValueRef.current, versionRef.current, migrateRef.current);
461
+ setValueState((prev)=>Object.is(prev, result.value) ? prev : result.value);
462
+ tabNotify(storageKey, result.value);
463
+ if (result.migrated) writeStorage(storage, storageKey, result.value, versionRef.current);
464
+ };
465
+ window.addEventListener("storage", handler);
466
+ return ()=>window.removeEventListener("storage", handler);
467
+ }, [
468
+ kind,
469
+ shared,
470
+ storage,
471
+ storageKey
472
+ ]);
473
+ const setValue = (0, __WEBPACK_EXTERNAL_MODULE_react__.useCallback)((next)=>{
474
+ const resolved = "function" == typeof next ? next(valueRef.current) : next;
475
+ if (Object.is(valueRef.current, resolved)) return;
476
+ valueRef.current = resolved;
477
+ setValueState(resolved);
478
+ const raw = writeStorage(storage, storageKey, resolved, versionRef.current);
479
+ tabNotify(storageKey, resolved);
480
+ if (shared) getChannel()?.postMessage({
481
+ key: storageKey,
482
+ payload: raw,
483
+ storage: kind,
484
+ sourceId: instanceId
485
+ });
486
+ }, [
487
+ kind,
488
+ shared,
489
+ storage,
490
+ storageKey
491
+ ]);
68
492
  return [
69
493
  value,
70
494
  setValue
71
495
  ];
72
496
  }
497
+ function usePersistentState(key, parser, options) {
498
+ return useStoredState("persistent", key, parser, options, false);
499
+ }
500
+ function usePersistentSharedState(key, parser, options) {
501
+ return useStoredState("persistent", key, parser, options, true);
502
+ }
503
+ function useSessionState(key, parser, options) {
504
+ return useStoredState("session", key, parser, options, false);
505
+ }
506
+ function useSessionSharedState(key, parser, options) {
507
+ return useStoredState("session", key, parser, options, true);
508
+ }
509
+ function useLongPress({ delayMs = 500, moveThresholdPx = 8, disabled = false, onLongPress }) {
510
+ const timerRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
511
+ const pointerIdRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
512
+ const startRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(null);
513
+ const triggeredRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(false);
514
+ const clearTimer = ()=>{
515
+ if (null == timerRef.current) return;
516
+ window.clearTimeout(timerRef.current);
517
+ timerRef.current = null;
518
+ };
519
+ const reset = ()=>{
520
+ clearTimer();
521
+ pointerIdRef.current = null;
522
+ startRef.current = null;
523
+ };
524
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
525
+ if (disabled) {
526
+ triggeredRef.current = false;
527
+ reset();
528
+ }
529
+ }, [
530
+ disabled
531
+ ]);
532
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>()=>clearTimer(), []);
533
+ return {
534
+ onPointerDown (event) {
535
+ if (disabled || 0 !== event.button) return;
536
+ const target = event.target instanceof HTMLElement ? event.target : null;
537
+ if (target?.closest("button,a,input,select,textarea")) return;
538
+ triggeredRef.current = false;
539
+ pointerIdRef.current = event.pointerId;
540
+ startRef.current = {
541
+ x: event.clientX,
542
+ y: event.clientY
543
+ };
544
+ clearTimer();
545
+ timerRef.current = window.setTimeout(()=>{
546
+ triggeredRef.current = true;
547
+ onLongPress();
548
+ }, delayMs);
549
+ },
550
+ onPointerMove (event) {
551
+ if (pointerIdRef.current !== event.pointerId || !startRef.current) return;
552
+ const dx = event.clientX - startRef.current.x;
553
+ const dy = event.clientY - startRef.current.y;
554
+ if (Math.hypot(dx, dy) > moveThresholdPx) reset();
555
+ },
556
+ onPointerUp (event) {
557
+ if (pointerIdRef.current !== event.pointerId) return;
558
+ clearTimer();
559
+ },
560
+ onPointerCancel (event) {
561
+ if (pointerIdRef.current !== event.pointerId) return;
562
+ reset();
563
+ },
564
+ onClickCapture (event) {
565
+ if (!triggeredRef.current) return;
566
+ event.preventDefault();
567
+ event.stopPropagation();
568
+ triggeredRef.current = false;
569
+ reset();
570
+ }
571
+ };
572
+ }
73
573
  function useMediaQuery(query) {
74
574
  const getMatches = ()=>"undefined" != typeof window && window.matchMedia(query).matches;
75
575
  const [matches, setMatches] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(getMatches);
@@ -89,4 +589,137 @@ function useMediaQuery(query) {
89
589
  ]);
90
590
  return matches;
91
591
  }
92
- export { useControllableState, useDebouncedValue, useDisclosure, useLocalStorage, useMediaQuery };
592
+ function useNavigatorOnline() {
593
+ const [isOnline, setIsOnline] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(()=>"undefined" == typeof navigator ? true : navigator.onLine);
594
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
595
+ if ("undefined" == typeof window) return;
596
+ const sync = ()=>setIsOnline(navigator.onLine);
597
+ window.addEventListener("online", sync);
598
+ window.addEventListener("offline", sync);
599
+ return ()=>{
600
+ window.removeEventListener("online", sync);
601
+ window.removeEventListener("offline", sync);
602
+ };
603
+ }, []);
604
+ return isOnline;
605
+ }
606
+ function useNow({ update = 1000 } = {}) {
607
+ const [now, setNow] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(()=>Date.now());
608
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
609
+ if (false === update) return;
610
+ const id = window.setInterval(()=>setNow(Date.now()), update);
611
+ return ()=>window.clearInterval(id);
612
+ }, [
613
+ update
614
+ ]);
615
+ return now;
616
+ }
617
+ function detectOS() {
618
+ if ("undefined" == typeof navigator) return "windows";
619
+ const ua = navigator.userAgent.toLowerCase();
620
+ const platform = (navigator.platform ?? "").toLowerCase();
621
+ const combined = `${ua} ${platform}`;
622
+ if (/iphone|ipad|ipod|android|mobile/.test(combined)) return "mobile";
623
+ if (/mac|macintosh|mac os x/.test(combined)) return "mac";
624
+ if (/win|windows/.test(combined)) return "windows";
625
+ if (/linux|x11/.test(combined)) return "linux";
626
+ return "windows";
627
+ }
628
+ function useOS(override) {
629
+ return (0, __WEBPACK_EXTERNAL_MODULE_react__.useMemo)(()=>override ?? detectOS(), [
630
+ override
631
+ ]);
632
+ }
633
+ function usePointerCoarse() {
634
+ return useMediaQuery("(pointer: coarse)");
635
+ }
636
+ function usePrefersReducedMotion() {
637
+ return useMediaQuery("(prefers-reduced-motion: reduce)");
638
+ }
639
+ function usePrevious(value) {
640
+ const ref = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(void 0);
641
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
642
+ ref.current = value;
643
+ }, [
644
+ value
645
+ ]);
646
+ return ref.current;
647
+ }
648
+ function useTimeout(callback, delay) {
649
+ const callbackRef = (0, __WEBPACK_EXTERNAL_MODULE_react__.useRef)(callback);
650
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
651
+ callbackRef.current = callback;
652
+ }, [
653
+ callback
654
+ ]);
655
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
656
+ if (null === delay) return;
657
+ const id = window.setTimeout(()=>callbackRef.current(), delay);
658
+ return ()=>window.clearTimeout(id);
659
+ }, [
660
+ delay
661
+ ]);
662
+ }
663
+ const KEYBOARD_OPEN_DELTA = 150;
664
+ function getViewportState() {
665
+ if ("undefined" == typeof window) return {
666
+ visualViewportHeight: 0,
667
+ keyboardOpen: false
668
+ };
669
+ const visual = window.visualViewport?.height ?? window.innerHeight;
670
+ const layout = window.innerHeight;
671
+ return {
672
+ visualViewportHeight: visual,
673
+ keyboardOpen: layout - visual > KEYBOARD_OPEN_DELTA
674
+ };
675
+ }
676
+ function useViewport() {
677
+ const [state, setState] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(()=>getViewportState());
678
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
679
+ if ("undefined" == typeof window) return;
680
+ const target = window.visualViewport ?? window;
681
+ const handleChange = ()=>{
682
+ setState((prev)=>{
683
+ const next = getViewportState();
684
+ return prev.visualViewportHeight === next.visualViewportHeight && prev.keyboardOpen === next.keyboardOpen ? prev : next;
685
+ });
686
+ };
687
+ handleChange();
688
+ target.addEventListener("resize", handleChange);
689
+ window.addEventListener("resize", handleChange);
690
+ return ()=>{
691
+ target.removeEventListener("resize", handleChange);
692
+ window.removeEventListener("resize", handleChange);
693
+ };
694
+ }, []);
695
+ return state;
696
+ }
697
+ function getWindowSize() {
698
+ if ("undefined" == typeof window) return {
699
+ width: 0,
700
+ height: 0
701
+ };
702
+ return {
703
+ width: window.innerWidth,
704
+ height: window.innerHeight
705
+ };
706
+ }
707
+ function useWindowSize() {
708
+ const [size, setSize] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(getWindowSize);
709
+ (0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
710
+ if ("undefined" == typeof window) return;
711
+ const handleResize = ()=>{
712
+ setSize((prev)=>{
713
+ const next = getWindowSize();
714
+ return prev.width === next.width && prev.height === next.height ? prev : next;
715
+ });
716
+ };
717
+ handleResize();
718
+ window.addEventListener("resize", handleResize);
719
+ return ()=>window.removeEventListener("resize", handleResize);
720
+ }, []);
721
+ return size;
722
+ }
723
+ var __webpack_exports__isFocusVisible = __WEBPACK_EXTERNAL_MODULE__react_aria_interactions_8422cce9__.isFocusVisible;
724
+ var __webpack_exports__usePress = __WEBPACK_EXTERNAL_MODULE__react_aria_interactions_8422cce9__.usePress;
725
+ export { StorageProvider, useBeforeUnloadWarning, useBreakpoint, useContainerSize, useControllableState, useCopyToClipboard, useDebouncedValue, useDeferredPresence, useDisclosure, useEventListener, useHover, useInterval, useKeyDown, useLongPress, useMediaQuery, useNavigatorOnline, useNow, useOS, usePersistentSharedState, usePersistentState, usePointerCoarse, usePrefersReducedMotion, usePrevious, useSessionSharedState, useSessionState, useTimeout, useViewport, useWindowSize, __webpack_exports__isFocusVisible as isFocusVisible, __webpack_exports__usePress as usePress };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shows the browser's native "leave page?" dialog when `enabled` is true.
3
+ * Useful for unsaved-changes guards.
4
+ */
5
+ export declare function useBeforeUnloadWarning(enabled: boolean): void;
6
+ //# sourceMappingURL=use-before-unload-warning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-before-unload-warning.d.ts","sourceRoot":"","sources":["../src/use-before-unload-warning.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU7D"}