@develemit/web-hooks 0.1.0 → 0.2.0
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/CHANGELOG.md +16 -0
- package/README.md +166 -0
- package/dist/auto-grow.cjs +60 -0
- package/dist/auto-grow.d.cts +14 -0
- package/dist/auto-grow.d.ts +14 -0
- package/dist/auto-grow.js +9 -0
- package/dist/chunk-3CACI4OQ.js +68 -0
- package/dist/chunk-3UGRXQIA.js +39 -0
- package/dist/chunk-5CA5RIMH.js +34 -0
- package/dist/chunk-5KIH2XLV.js +136 -0
- package/dist/chunk-6KCRVSLS.js +80 -0
- package/dist/chunk-DNU57XEJ.js +82 -0
- package/dist/chunk-IWQDAJEP.js +69 -0
- package/dist/chunk-LZKAGDER.js +22 -0
- package/dist/chunk-NKLEB5TR.js +98 -0
- package/dist/chunk-OZ4PU4E3.js +115 -0
- package/dist/chunk-S4YKMGQE.js +23 -0
- package/dist/chunk-UPVNSGIK.js +72 -0
- package/dist/chunk-UZADAINO.js +61 -0
- package/dist/clipboard.cjs +5 -0
- package/dist/clipboard.js +4 -57
- package/dist/debounce.cjs +96 -0
- package/dist/debounce.d.cts +21 -0
- package/dist/debounce.d.ts +21 -0
- package/dist/debounce.js +11 -0
- package/dist/drawer-swipe.js +12 -101
- package/dist/escape.cjs +1 -0
- package/dist/escape.js +3 -17
- package/dist/focus-trap.cjs +3 -1
- package/dist/focus-trap.js +6 -72
- package/dist/hotkey.cjs +127 -0
- package/dist/hotkey.d.cts +31 -0
- package/dist/hotkey.d.ts +31 -0
- package/dist/hotkey.js +15 -0
- package/dist/index.cjs +613 -3
- package/dist/index.d.cts +14 -60
- package/dist/index.d.ts +14 -60
- package/dist/index.js +97 -247
- package/dist/media-query.cjs +90 -0
- package/dist/media-query.d.cts +12 -0
- package/dist/media-query.d.ts +12 -0
- package/dist/media-query.js +15 -0
- package/dist/outside-press.cjs +66 -0
- package/dist/outside-press.d.cts +15 -0
- package/dist/outside-press.d.ts +15 -0
- package/dist/outside-press.js +11 -0
- package/dist/scroll-lock.cjs +107 -0
- package/dist/scroll-lock.d.cts +8 -0
- package/dist/scroll-lock.d.ts +8 -0
- package/dist/scroll-lock.js +11 -0
- package/dist/storage-state.cjs +161 -0
- package/dist/storage-state.d.cts +14 -0
- package/dist/storage-state.d.ts +14 -0
- package/dist/storage-state.js +7 -0
- package/dist/unsaved-guard.cjs +48 -0
- package/dist/unsaved-guard.d.cts +5 -0
- package/dist/unsaved-guard.d.ts +5 -0
- package/dist/unsaved-guard.js +9 -0
- package/dist/visibility-polling.cjs +98 -0
- package/dist/visibility-polling.d.cts +14 -0
- package/dist/visibility-polling.d.ts +14 -0
- package/dist/visibility-polling.js +9 -0
- package/package.json +55 -2
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
"use strict";
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -22,21 +23,47 @@ var src_exports = {};
|
|
|
22
23
|
__export(src_exports, {
|
|
23
24
|
BACK_ZONE_PX: () => BACK_ZONE_PX,
|
|
24
25
|
CLOSE_DISTANCE_PX: () => CLOSE_DISTANCE_PX,
|
|
26
|
+
COARSE_POINTER_QUERY: () => COARSE_POINTER_QUERY,
|
|
25
27
|
FOCUSABLE_SELECTOR: () => FOCUSABLE_SELECTOR,
|
|
26
28
|
FORWARD_ZONE_PX: () => FORWARD_ZONE_PX,
|
|
27
29
|
MAX_SWIPE_DURATION_MS: () => MAX_SWIPE_DURATION_MS,
|
|
28
30
|
OPEN_DISTANCE_PX: () => OPEN_DISTANCE_PX,
|
|
31
|
+
REDUCED_MOTION_QUERY: () => REDUCED_MOTION_QUERY,
|
|
29
32
|
VERTICAL_CANCEL_PX: () => VERTICAL_CANCEL_PX,
|
|
30
33
|
copyText: () => copyText,
|
|
34
|
+
createDebouncer: () => createDebouncer,
|
|
35
|
+
detectApple: () => detectApple,
|
|
31
36
|
focusableWithin: () => focusableWithin,
|
|
37
|
+
handleBeforeUnload: () => handleBeforeUnload,
|
|
38
|
+
isEditableTarget: () => isEditableTarget,
|
|
32
39
|
isInOpenZone: () => isInOpenZone,
|
|
33
40
|
isInsideHorizontalScroller: () => isInsideHorizontalScroller,
|
|
41
|
+
isOutside: () => isOutside,
|
|
42
|
+
isPrimaryPress: () => isPrimaryPress,
|
|
43
|
+
lockScroll: () => lockScroll,
|
|
44
|
+
matchHotkey: () => matchHotkey,
|
|
45
|
+
nextDelay: () => nextDelay,
|
|
34
46
|
nextFocusTarget: () => nextFocusTarget,
|
|
47
|
+
nextHeight: () => nextHeight,
|
|
48
|
+
parseHotkey: () => parseHotkey,
|
|
35
49
|
resolveSwipeIntent: () => resolveSwipeIntent,
|
|
50
|
+
unlockScroll: () => unlockScroll,
|
|
51
|
+
useAutoGrowTextarea: () => useAutoGrowTextarea,
|
|
52
|
+
useCoarsePointer: () => useCoarsePointer,
|
|
36
53
|
useCopyFeedback: () => useCopyFeedback,
|
|
54
|
+
useDebouncedCallback: () => useDebouncedCallback,
|
|
55
|
+
useDebouncedValue: () => useDebouncedValue,
|
|
37
56
|
useDrawerSwipe: () => useDrawerSwipe,
|
|
38
57
|
useEscape: () => useEscape,
|
|
39
|
-
useFocusTrap: () => useFocusTrap
|
|
58
|
+
useFocusTrap: () => useFocusTrap,
|
|
59
|
+
useHotkey: () => useHotkey,
|
|
60
|
+
useMediaQuery: () => useMediaQuery,
|
|
61
|
+
useOutsidePress: () => useOutsidePress,
|
|
62
|
+
useReducedMotion: () => useReducedMotion,
|
|
63
|
+
useScrollLock: () => useScrollLock,
|
|
64
|
+
useStorageState: () => useStorageState,
|
|
65
|
+
useUnsavedChangesGuard: () => useUnsavedChangesGuard,
|
|
66
|
+
useVisibilityPolling: () => useVisibilityPolling
|
|
40
67
|
});
|
|
41
68
|
module.exports = __toCommonJS(src_exports);
|
|
42
69
|
|
|
@@ -171,6 +198,9 @@ function useFocusTrap(containerRef, {
|
|
|
171
198
|
if (active && !openerRef.current && typeof document !== "undefined") {
|
|
172
199
|
openerRef.current = document.activeElement;
|
|
173
200
|
}
|
|
201
|
+
(0, import_react2.useEffect)(() => {
|
|
202
|
+
if (!active) openerRef.current = null;
|
|
203
|
+
}, [active]);
|
|
174
204
|
(0, import_react2.useEffect)(() => {
|
|
175
205
|
const container = containerRef.current;
|
|
176
206
|
if (!active || !container) return;
|
|
@@ -207,7 +237,6 @@ function useFocusTrap(containerRef, {
|
|
|
207
237
|
document.removeEventListener("keydown", onKeyDown);
|
|
208
238
|
activeTraps.splice(activeTraps.indexOf(token), 1);
|
|
209
239
|
const opener = returnFocusRef?.current ?? openerRef.current;
|
|
210
|
-
openerRef.current = null;
|
|
211
240
|
if (restoreFocus && opener?.isConnected) opener.focus();
|
|
212
241
|
};
|
|
213
242
|
}, [active, containerRef, initialFocusRef, returnFocusRef, restoreFocus]);
|
|
@@ -223,6 +252,7 @@ function useEscape(onEscape, active = true) {
|
|
|
223
252
|
(0, import_react3.useEffect)(() => {
|
|
224
253
|
if (!active) return;
|
|
225
254
|
function onKeyDown(event) {
|
|
255
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
226
256
|
if (event.key === "Escape") handlerRef.current();
|
|
227
257
|
}
|
|
228
258
|
document.addEventListener("keydown", onKeyDown);
|
|
@@ -252,8 +282,10 @@ function execCommandCopy(text) {
|
|
|
252
282
|
textarea.style.top = "-1000px";
|
|
253
283
|
textarea.style.left = "-1000px";
|
|
254
284
|
textarea.style.fontSize = "16px";
|
|
285
|
+
const previouslyFocused = document.activeElement;
|
|
255
286
|
document.body.appendChild(textarea);
|
|
256
287
|
try {
|
|
288
|
+
textarea.focus({ preventScroll: true });
|
|
257
289
|
textarea.select();
|
|
258
290
|
textarea.setSelectionRange(0, text.length);
|
|
259
291
|
return document.execCommand("copy");
|
|
@@ -261,6 +293,9 @@ function execCommandCopy(text) {
|
|
|
261
293
|
return false;
|
|
262
294
|
} finally {
|
|
263
295
|
document.body.removeChild(textarea);
|
|
296
|
+
if (previouslyFocused instanceof HTMLElement && previouslyFocused !== document.body) {
|
|
297
|
+
previouslyFocused.focus({ preventScroll: true });
|
|
298
|
+
}
|
|
264
299
|
}
|
|
265
300
|
}
|
|
266
301
|
|
|
@@ -286,23 +321,598 @@ function useCopyFeedback({ resetMs = 1500 } = {}) {
|
|
|
286
321
|
);
|
|
287
322
|
return { copy, status };
|
|
288
323
|
}
|
|
324
|
+
|
|
325
|
+
// src/scroll-lock/use-scroll-lock.ts
|
|
326
|
+
var import_react5 = require("react");
|
|
327
|
+
|
|
328
|
+
// src/scroll-lock/lock-styles.ts
|
|
329
|
+
var LOCKED_PROPERTIES = [
|
|
330
|
+
"position",
|
|
331
|
+
"top",
|
|
332
|
+
"left",
|
|
333
|
+
"right",
|
|
334
|
+
"width",
|
|
335
|
+
"overflow",
|
|
336
|
+
"paddingRight"
|
|
337
|
+
];
|
|
338
|
+
function computeLockStyles({ scrollY, scrollbarWidth, existingPaddingRight }) {
|
|
339
|
+
const styles = {
|
|
340
|
+
position: "fixed",
|
|
341
|
+
top: `-${scrollY}px`,
|
|
342
|
+
left: "0",
|
|
343
|
+
right: "0",
|
|
344
|
+
width: "100%",
|
|
345
|
+
overflow: "hidden"
|
|
346
|
+
};
|
|
347
|
+
if (scrollbarWidth > 0) styles.paddingRight = `${existingPaddingRight + scrollbarWidth}px`;
|
|
348
|
+
return styles;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/scroll-lock/scroll-lock.ts
|
|
352
|
+
var count = 0;
|
|
353
|
+
var saved = null;
|
|
354
|
+
function applyFirstLock() {
|
|
355
|
+
const { body, documentElement } = document;
|
|
356
|
+
const inline = {};
|
|
357
|
+
for (const property of LOCKED_PROPERTIES) inline[property] = body.style[property];
|
|
358
|
+
saved = { scrollY: window.scrollY, inline };
|
|
359
|
+
const styles = computeLockStyles({
|
|
360
|
+
scrollY: saved.scrollY,
|
|
361
|
+
scrollbarWidth: Math.max(0, window.innerWidth - documentElement.clientWidth),
|
|
362
|
+
existingPaddingRight: parseFloat(getComputedStyle(body).paddingRight) || 0
|
|
363
|
+
});
|
|
364
|
+
Object.assign(body.style, styles);
|
|
365
|
+
}
|
|
366
|
+
function restoreLastLock() {
|
|
367
|
+
if (!saved) return;
|
|
368
|
+
const { scrollY, inline } = saved;
|
|
369
|
+
saved = null;
|
|
370
|
+
Object.assign(document.body.style, inline);
|
|
371
|
+
window.scrollTo(0, scrollY);
|
|
372
|
+
}
|
|
373
|
+
function lockScroll() {
|
|
374
|
+
if (count === 0) applyFirstLock();
|
|
375
|
+
count += 1;
|
|
376
|
+
let released = false;
|
|
377
|
+
return () => {
|
|
378
|
+
if (released) return;
|
|
379
|
+
released = true;
|
|
380
|
+
count -= 1;
|
|
381
|
+
if (count === 0) restoreLastLock();
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function unlockScroll() {
|
|
385
|
+
if (count === 0) return;
|
|
386
|
+
count -= 1;
|
|
387
|
+
if (count === 0) restoreLastLock();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/scroll-lock/use-scroll-lock.ts
|
|
391
|
+
function useScrollLock(active = true) {
|
|
392
|
+
(0, import_react5.useEffect)(() => {
|
|
393
|
+
if (!active) return;
|
|
394
|
+
return lockScroll();
|
|
395
|
+
}, [active]);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/media-query/use-media-query.ts
|
|
399
|
+
var import_react6 = require("react");
|
|
400
|
+
|
|
401
|
+
// src/media-query/media-query.ts
|
|
402
|
+
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
403
|
+
var COARSE_POINTER_QUERY = "(pointer: coarse)";
|
|
404
|
+
var lists = /* @__PURE__ */ new WeakMap();
|
|
405
|
+
function getList(query) {
|
|
406
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return null;
|
|
407
|
+
const owner = window.matchMedia;
|
|
408
|
+
let byQuery = lists.get(owner);
|
|
409
|
+
if (!byQuery) {
|
|
410
|
+
byQuery = /* @__PURE__ */ new Map();
|
|
411
|
+
lists.set(owner, byQuery);
|
|
412
|
+
}
|
|
413
|
+
let list = byQuery.get(query);
|
|
414
|
+
if (!list) {
|
|
415
|
+
list = owner.call(window, query);
|
|
416
|
+
byQuery.set(query, list);
|
|
417
|
+
}
|
|
418
|
+
return list;
|
|
419
|
+
}
|
|
420
|
+
function getMatches(query) {
|
|
421
|
+
return getList(query)?.matches ?? null;
|
|
422
|
+
}
|
|
423
|
+
function subscribe(query, onChange) {
|
|
424
|
+
const list = getList(query);
|
|
425
|
+
if (!list) return () => {
|
|
426
|
+
};
|
|
427
|
+
if (typeof list.addEventListener === "function") {
|
|
428
|
+
list.addEventListener("change", onChange);
|
|
429
|
+
return () => list.removeEventListener("change", onChange);
|
|
430
|
+
}
|
|
431
|
+
list.addListener(onChange);
|
|
432
|
+
return () => list.removeListener(onChange);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/media-query/use-media-query.ts
|
|
436
|
+
function useMediaQuery(query, options = {}) {
|
|
437
|
+
const serverDefault = options.serverDefault ?? false;
|
|
438
|
+
const subscribeToQuery = (0, import_react6.useCallback)((onChange) => subscribe(query, onChange), [query]);
|
|
439
|
+
const getSnapshot = (0, import_react6.useCallback)(() => getMatches(query) ?? serverDefault, [query, serverDefault]);
|
|
440
|
+
const getServerSnapshot = (0, import_react6.useCallback)(() => serverDefault, [serverDefault]);
|
|
441
|
+
return (0, import_react6.useSyncExternalStore)(subscribeToQuery, getSnapshot, getServerSnapshot);
|
|
442
|
+
}
|
|
443
|
+
function useReducedMotion(options) {
|
|
444
|
+
return useMediaQuery(REDUCED_MOTION_QUERY, options);
|
|
445
|
+
}
|
|
446
|
+
function useCoarsePointer(options) {
|
|
447
|
+
return useMediaQuery(COARSE_POINTER_QUERY, options);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/outside-press/outside-press.ts
|
|
451
|
+
function isOutside(target, elements) {
|
|
452
|
+
if (!target) return false;
|
|
453
|
+
if (!target.isConnected) return false;
|
|
454
|
+
return !elements.some((el) => el?.contains(target));
|
|
455
|
+
}
|
|
456
|
+
function isPrimaryPress(event) {
|
|
457
|
+
return !(event.pointerType === "mouse" && event.button !== 0);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/outside-press/use-outside-press.ts
|
|
461
|
+
var import_react7 = require("react");
|
|
462
|
+
function useOutsidePress(refs, onOutsidePress, { enabled = true } = {}) {
|
|
463
|
+
const refsRef = (0, import_react7.useRef)(refs);
|
|
464
|
+
const handlerRef = (0, import_react7.useRef)(onOutsidePress);
|
|
465
|
+
(0, import_react7.useEffect)(() => {
|
|
466
|
+
refsRef.current = refs;
|
|
467
|
+
handlerRef.current = onOutsidePress;
|
|
468
|
+
});
|
|
469
|
+
(0, import_react7.useEffect)(() => {
|
|
470
|
+
if (!enabled) return;
|
|
471
|
+
function onPointerDown(event) {
|
|
472
|
+
if (!isPrimaryPress(event)) return;
|
|
473
|
+
const list = Array.isArray(refsRef.current) ? refsRef.current : [refsRef.current];
|
|
474
|
+
const elements = list.map((ref) => ref.current);
|
|
475
|
+
if (isOutside(event.target, elements)) handlerRef.current(event);
|
|
476
|
+
}
|
|
477
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
478
|
+
return () => document.removeEventListener("pointerdown", onPointerDown, true);
|
|
479
|
+
}, [enabled]);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/visibility-polling/schedule.ts
|
|
483
|
+
function nextDelay(intervalMs, jitter, random) {
|
|
484
|
+
const fraction = Math.min(1, Math.max(0, jitter));
|
|
485
|
+
return intervalMs + random() * fraction * intervalMs;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/visibility-polling/use-visibility-polling.ts
|
|
489
|
+
var import_react8 = require("react");
|
|
490
|
+
var isHidden = () => document.visibilityState === "hidden";
|
|
491
|
+
function useVisibilityPolling(fn, intervalMs, { enabled = true, immediate = true, jitter = 0, runOnVisible = true } = {}) {
|
|
492
|
+
const fnRef = (0, import_react8.useRef)(fn);
|
|
493
|
+
const optionsRef = (0, import_react8.useRef)({ immediate, jitter, runOnVisible });
|
|
494
|
+
(0, import_react8.useEffect)(() => {
|
|
495
|
+
fnRef.current = fn;
|
|
496
|
+
optionsRef.current = { immediate, jitter, runOnVisible };
|
|
497
|
+
});
|
|
498
|
+
const runningRef = (0, import_react8.useRef)(false);
|
|
499
|
+
const scheduleRef = (0, import_react8.useRef)(null);
|
|
500
|
+
(0, import_react8.useEffect)(() => {
|
|
501
|
+
if (!enabled) return;
|
|
502
|
+
let active = true;
|
|
503
|
+
let timer = null;
|
|
504
|
+
function clearTimer() {
|
|
505
|
+
if (timer !== null) clearTimeout(timer);
|
|
506
|
+
timer = null;
|
|
507
|
+
}
|
|
508
|
+
function schedule() {
|
|
509
|
+
clearTimer();
|
|
510
|
+
if (!active || isHidden()) return;
|
|
511
|
+
timer = setTimeout(run, nextDelay(intervalMs, optionsRef.current.jitter, Math.random));
|
|
512
|
+
}
|
|
513
|
+
async function run() {
|
|
514
|
+
timer = null;
|
|
515
|
+
if (!active || runningRef.current || isHidden()) return;
|
|
516
|
+
runningRef.current = true;
|
|
517
|
+
try {
|
|
518
|
+
await fnRef.current();
|
|
519
|
+
} catch {
|
|
520
|
+
} finally {
|
|
521
|
+
runningRef.current = false;
|
|
522
|
+
}
|
|
523
|
+
scheduleRef.current?.();
|
|
524
|
+
}
|
|
525
|
+
function onVisibilityChange() {
|
|
526
|
+
if (isHidden()) {
|
|
527
|
+
clearTimer();
|
|
528
|
+
} else if (optionsRef.current.runOnVisible) {
|
|
529
|
+
clearTimer();
|
|
530
|
+
void run();
|
|
531
|
+
} else if (!runningRef.current) {
|
|
532
|
+
schedule();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
scheduleRef.current = schedule;
|
|
536
|
+
if (optionsRef.current.immediate && !isHidden()) void run();
|
|
537
|
+
else schedule();
|
|
538
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
539
|
+
return () => {
|
|
540
|
+
active = false;
|
|
541
|
+
scheduleRef.current = null;
|
|
542
|
+
clearTimer();
|
|
543
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
544
|
+
};
|
|
545
|
+
}, [enabled, intervalMs]);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/debounce/debouncer.ts
|
|
549
|
+
function createDebouncer(getFn, getMs) {
|
|
550
|
+
let timer;
|
|
551
|
+
let pending;
|
|
552
|
+
function clear() {
|
|
553
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
554
|
+
timer = void 0;
|
|
555
|
+
pending = void 0;
|
|
556
|
+
}
|
|
557
|
+
function run() {
|
|
558
|
+
const args = pending;
|
|
559
|
+
clear();
|
|
560
|
+
if (args) getFn()(...args);
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
call(...args) {
|
|
564
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
565
|
+
pending = args;
|
|
566
|
+
timer = setTimeout(run, getMs());
|
|
567
|
+
},
|
|
568
|
+
cancel: clear,
|
|
569
|
+
flush() {
|
|
570
|
+
if (pending) run();
|
|
571
|
+
},
|
|
572
|
+
isPending: () => pending !== void 0
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/debounce/use-debounced-value.ts
|
|
577
|
+
var import_react9 = require("react");
|
|
578
|
+
function useDebouncedValue(value, ms) {
|
|
579
|
+
const [debounced, setDebounced] = (0, import_react9.useState)(value);
|
|
580
|
+
(0, import_react9.useEffect)(() => {
|
|
581
|
+
const timer = setTimeout(() => setDebounced(value), ms);
|
|
582
|
+
return () => clearTimeout(timer);
|
|
583
|
+
}, [value, ms]);
|
|
584
|
+
return debounced;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/debounce/use-debounced-callback.ts
|
|
588
|
+
var import_react10 = require("react");
|
|
589
|
+
function useDebouncedCallback(fn, ms) {
|
|
590
|
+
const fnRef = (0, import_react10.useRef)(fn);
|
|
591
|
+
const msRef = (0, import_react10.useRef)(ms);
|
|
592
|
+
(0, import_react10.useEffect)(() => {
|
|
593
|
+
fnRef.current = fn;
|
|
594
|
+
msRef.current = ms;
|
|
595
|
+
});
|
|
596
|
+
const [debounced] = (0, import_react10.useState)(() => {
|
|
597
|
+
const debouncer = createDebouncer(
|
|
598
|
+
() => fnRef.current,
|
|
599
|
+
() => msRef.current
|
|
600
|
+
);
|
|
601
|
+
return Object.assign((...args) => debouncer.call(...args), {
|
|
602
|
+
cancel: debouncer.cancel,
|
|
603
|
+
flush: debouncer.flush
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
(0, import_react10.useEffect)(() => () => debounced.cancel(), [debounced]);
|
|
607
|
+
return debounced;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/storage-state/use-storage-state.ts
|
|
611
|
+
var import_react11 = require("react");
|
|
612
|
+
|
|
613
|
+
// src/storage-state/storage-store.ts
|
|
614
|
+
function getStorage(kind) {
|
|
615
|
+
try {
|
|
616
|
+
if (typeof window === "undefined") return null;
|
|
617
|
+
return kind === "session" ? window.sessionStorage : window.localStorage;
|
|
618
|
+
} catch {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function readRaw(storage, key) {
|
|
623
|
+
try {
|
|
624
|
+
return storage ? storage.getItem(key) : null;
|
|
625
|
+
} catch {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function writeRaw(storage, key, value) {
|
|
630
|
+
try {
|
|
631
|
+
if (!storage) return false;
|
|
632
|
+
storage.setItem(key, value);
|
|
633
|
+
return true;
|
|
634
|
+
} catch {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function removeRaw(storage, key) {
|
|
639
|
+
try {
|
|
640
|
+
if (!storage) return false;
|
|
641
|
+
storage.removeItem(key);
|
|
642
|
+
return true;
|
|
643
|
+
} catch {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function parseOr(raw, parse, fallback) {
|
|
648
|
+
if (raw === null) return fallback;
|
|
649
|
+
try {
|
|
650
|
+
return parse(raw);
|
|
651
|
+
} catch {
|
|
652
|
+
return fallback;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
var slot = (kind, key) => `${kind}\0${key}`;
|
|
656
|
+
var memory = /* @__PURE__ */ new Map();
|
|
657
|
+
var listeners = /* @__PURE__ */ new Map();
|
|
658
|
+
function readCurrent(kind, key) {
|
|
659
|
+
const held = memory.get(slot(kind, key));
|
|
660
|
+
if (held !== void 0) return held;
|
|
661
|
+
return readRaw(getStorage(kind), key);
|
|
662
|
+
}
|
|
663
|
+
function notify(id) {
|
|
664
|
+
for (const listener of [...listeners.get(id) ?? []]) listener();
|
|
665
|
+
}
|
|
666
|
+
function writeCurrent(kind, key, raw) {
|
|
667
|
+
const id = slot(kind, key);
|
|
668
|
+
if (writeRaw(getStorage(kind), key, raw)) memory.delete(id);
|
|
669
|
+
else memory.set(id, raw);
|
|
670
|
+
notify(id);
|
|
671
|
+
}
|
|
672
|
+
function removeCurrent(kind, key) {
|
|
673
|
+
const id = slot(kind, key);
|
|
674
|
+
if (removeRaw(getStorage(kind), key)) memory.delete(id);
|
|
675
|
+
else memory.set(id, null);
|
|
676
|
+
notify(id);
|
|
677
|
+
}
|
|
678
|
+
function onStorageEvent(event) {
|
|
679
|
+
const kinds = [];
|
|
680
|
+
if (!event.storageArea || event.storageArea === getStorage("local")) kinds.push("local");
|
|
681
|
+
if (!event.storageArea || event.storageArea === getStorage("session")) kinds.push("session");
|
|
682
|
+
for (const id of [...listeners.keys()]) {
|
|
683
|
+
const [kind, key] = id.split("\0");
|
|
684
|
+
if (!kinds.includes(kind)) continue;
|
|
685
|
+
if (event.key !== null && event.key !== key) continue;
|
|
686
|
+
memory.delete(id);
|
|
687
|
+
notify(id);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function subscribeToKey(kind, key, listener) {
|
|
691
|
+
const id = slot(kind, key);
|
|
692
|
+
const wasEmpty = listeners.size === 0;
|
|
693
|
+
let set = listeners.get(id);
|
|
694
|
+
if (!set) listeners.set(id, set = /* @__PURE__ */ new Set());
|
|
695
|
+
set.add(listener);
|
|
696
|
+
if (wasEmpty) window.addEventListener("storage", onStorageEvent);
|
|
697
|
+
return () => {
|
|
698
|
+
const current = listeners.get(id);
|
|
699
|
+
current?.delete(listener);
|
|
700
|
+
if (current?.size === 0) listeners.delete(id);
|
|
701
|
+
if (listeners.size === 0) window.removeEventListener("storage", onStorageEvent);
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/storage-state/use-storage-state.ts
|
|
706
|
+
function useStorageState(key, defaultValue, options = {}) {
|
|
707
|
+
const kind = options.storage ?? "local";
|
|
708
|
+
const serialize = options.serialize ?? JSON.stringify;
|
|
709
|
+
const parse = options.parse ?? JSON.parse;
|
|
710
|
+
const latest = (0, import_react11.useRef)({ serialize, parse, defaultValue });
|
|
711
|
+
(0, import_react11.useEffect)(() => {
|
|
712
|
+
latest.current = { serialize, parse, defaultValue };
|
|
713
|
+
});
|
|
714
|
+
const cache = (0, import_react11.useRef)(null);
|
|
715
|
+
const subscribe2 = (0, import_react11.useCallback)((onChange) => subscribeToKey(kind, key, onChange), [kind, key]);
|
|
716
|
+
const getSnapshot = (0, import_react11.useCallback)(() => {
|
|
717
|
+
const raw = readCurrent(kind, key);
|
|
718
|
+
const hit = cache.current;
|
|
719
|
+
if (hit && hit.kind === kind && hit.key === key && hit.raw === raw && hit.parse === parse) return hit.value;
|
|
720
|
+
const value2 = parseOr(raw, parse, defaultValue);
|
|
721
|
+
cache.current = { kind, key, raw, parse, value: value2 };
|
|
722
|
+
return value2;
|
|
723
|
+
}, [kind, key, parse, defaultValue]);
|
|
724
|
+
const getServerSnapshot = (0, import_react11.useCallback)(() => defaultValue, [defaultValue]);
|
|
725
|
+
const value = (0, import_react11.useSyncExternalStore)(subscribe2, getSnapshot, getServerSnapshot);
|
|
726
|
+
const set = (0, import_react11.useCallback)(
|
|
727
|
+
(next) => {
|
|
728
|
+
const { serialize: encode, parse: decode, defaultValue: fallback } = latest.current;
|
|
729
|
+
try {
|
|
730
|
+
const resolved = typeof next === "function" ? next(parseOr(readCurrent(kind, key), decode, fallback)) : next;
|
|
731
|
+
writeCurrent(kind, key, encode(resolved));
|
|
732
|
+
} catch {
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
[kind, key]
|
|
736
|
+
);
|
|
737
|
+
const remove = (0, import_react11.useCallback)(() => removeCurrent(kind, key), [kind, key]);
|
|
738
|
+
return [value, set, remove];
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/unsaved-guard/handle-before-unload.ts
|
|
742
|
+
function handleBeforeUnload(event) {
|
|
743
|
+
event.preventDefault();
|
|
744
|
+
event.returnValue = "";
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/unsaved-guard/use-unsaved-changes-guard.ts
|
|
748
|
+
var import_react12 = require("react");
|
|
749
|
+
function useUnsavedChangesGuard(dirty) {
|
|
750
|
+
(0, import_react12.useEffect)(() => {
|
|
751
|
+
if (!dirty) return;
|
|
752
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
753
|
+
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
754
|
+
}, [dirty]);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/hotkey/parse-hotkey.ts
|
|
758
|
+
var KEY_ALIASES = {
|
|
759
|
+
esc: "escape",
|
|
760
|
+
space: " ",
|
|
761
|
+
plus: "+",
|
|
762
|
+
up: "arrowup",
|
|
763
|
+
down: "arrowdown",
|
|
764
|
+
left: "arrowleft",
|
|
765
|
+
right: "arrowright"
|
|
766
|
+
};
|
|
767
|
+
function parseHotkey(combo) {
|
|
768
|
+
const parsed = { key: "", mod: false, ctrl: false, meta: false, alt: false, shift: false };
|
|
769
|
+
const parts = combo.trim().toLowerCase().split(/\+(?=.)/);
|
|
770
|
+
for (const part of parts) {
|
|
771
|
+
const token = part.trim();
|
|
772
|
+
if (token === "mod" || token === "ctrl" || token === "meta" || token === "alt" || token === "shift") {
|
|
773
|
+
parsed[token] = true;
|
|
774
|
+
} else {
|
|
775
|
+
parsed.key = KEY_ALIASES[token] ?? token;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (!parsed.key) throw new Error(`Invalid hotkey "${combo}": no key`);
|
|
779
|
+
return parsed;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// src/hotkey/match-hotkey.ts
|
|
783
|
+
var CODE_FOR_SYMBOL = {
|
|
784
|
+
"/": "Slash",
|
|
785
|
+
".": "Period",
|
|
786
|
+
",": "Comma",
|
|
787
|
+
";": "Semicolon",
|
|
788
|
+
"'": "Quote",
|
|
789
|
+
"[": "BracketLeft",
|
|
790
|
+
"]": "BracketRight",
|
|
791
|
+
"\\": "Backslash",
|
|
792
|
+
"-": "Minus",
|
|
793
|
+
"=": "Equal",
|
|
794
|
+
"`": "Backquote"
|
|
795
|
+
};
|
|
796
|
+
function physicalCode(key) {
|
|
797
|
+
if (/^[a-z]$/.test(key)) return `Key${key.toUpperCase()}`;
|
|
798
|
+
if (/^[0-9]$/.test(key)) return `Digit${key}`;
|
|
799
|
+
return CODE_FOR_SYMBOL[key] ?? null;
|
|
800
|
+
}
|
|
801
|
+
function keyMatches(event, parsed) {
|
|
802
|
+
if (event.key.toLowerCase() === parsed.key) return true;
|
|
803
|
+
return parsed.shift && physicalCode(parsed.key) === event.code;
|
|
804
|
+
}
|
|
805
|
+
function matchHotkey(event, combo, isApple) {
|
|
806
|
+
const parsed = typeof combo === "string" ? parseHotkey(combo) : combo;
|
|
807
|
+
const wantMeta = parsed.meta || parsed.mod && isApple;
|
|
808
|
+
const wantCtrl = parsed.ctrl || parsed.mod && !isApple;
|
|
809
|
+
return event.metaKey === wantMeta && event.ctrlKey === wantCtrl && event.altKey === parsed.alt && event.shiftKey === parsed.shift && keyMatches(event, parsed);
|
|
810
|
+
}
|
|
811
|
+
function detectApple(nav) {
|
|
812
|
+
if (!nav) return false;
|
|
813
|
+
const platform = nav.userAgentData?.platform || nav.platform || "";
|
|
814
|
+
return /mac|iphone|ipad|ipod/i.test(platform);
|
|
815
|
+
}
|
|
816
|
+
function isEditableTarget(target) {
|
|
817
|
+
const el = target;
|
|
818
|
+
if (!el || typeof el.tagName !== "string") return false;
|
|
819
|
+
const tag = el.tagName.toLowerCase();
|
|
820
|
+
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable === true;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// src/hotkey/use-hotkey.ts
|
|
824
|
+
var import_react13 = require("react");
|
|
825
|
+
function useHotkey(combo, onHotkey, options = {}) {
|
|
826
|
+
const { enabled = true, allowInInputs = false, preventDefault = true } = options;
|
|
827
|
+
const handlerRef = (0, import_react13.useRef)(onHotkey);
|
|
828
|
+
(0, import_react13.useEffect)(() => {
|
|
829
|
+
handlerRef.current = onHotkey;
|
|
830
|
+
});
|
|
831
|
+
(0, import_react13.useEffect)(() => {
|
|
832
|
+
if (!enabled) return;
|
|
833
|
+
const parsed = parseHotkey(combo);
|
|
834
|
+
function onKeyDown(event) {
|
|
835
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
836
|
+
if (!allowInInputs && isEditableTarget(event.target)) return;
|
|
837
|
+
if (!matchHotkey(event, parsed, detectApple(navigator))) return;
|
|
838
|
+
if (preventDefault) event.preventDefault();
|
|
839
|
+
handlerRef.current(event);
|
|
840
|
+
}
|
|
841
|
+
document.addEventListener("keydown", onKeyDown);
|
|
842
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
843
|
+
}, [combo, enabled, allowInInputs, preventDefault]);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/auto-grow/next-height.ts
|
|
847
|
+
function nextHeight(scrollHeight, maxHeight) {
|
|
848
|
+
const content = Math.ceil(scrollHeight);
|
|
849
|
+
if (maxHeight !== void 0 && content > maxHeight) return { height: maxHeight, capped: true };
|
|
850
|
+
return { height: content, capped: false };
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/auto-grow/use-auto-grow-textarea.ts
|
|
854
|
+
var import_react14 = require("react");
|
|
855
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? import_react14.useEffect : import_react14.useLayoutEffect;
|
|
856
|
+
function useAutoGrowTextarea(ref, value, { maxHeight } = {}) {
|
|
857
|
+
const resize = (0, import_react14.useCallback)(() => {
|
|
858
|
+
const el = ref.current;
|
|
859
|
+
if (!el) return;
|
|
860
|
+
el.style.height = "auto";
|
|
861
|
+
const { height, capped } = nextHeight(el.scrollHeight, maxHeight);
|
|
862
|
+
el.style.height = `${height}px`;
|
|
863
|
+
el.style.overflowY = capped ? "auto" : "hidden";
|
|
864
|
+
}, [ref, maxHeight]);
|
|
865
|
+
useIsomorphicLayoutEffect(resize, [resize, value]);
|
|
866
|
+
(0, import_react14.useEffect)(() => {
|
|
867
|
+
const viewport = window.visualViewport;
|
|
868
|
+
const target = viewport ?? window;
|
|
869
|
+
target.addEventListener("resize", resize);
|
|
870
|
+
return () => target.removeEventListener("resize", resize);
|
|
871
|
+
}, [resize]);
|
|
872
|
+
}
|
|
289
873
|
// Annotate the CommonJS export names for ESM import in node:
|
|
290
874
|
0 && (module.exports = {
|
|
291
875
|
BACK_ZONE_PX,
|
|
292
876
|
CLOSE_DISTANCE_PX,
|
|
877
|
+
COARSE_POINTER_QUERY,
|
|
293
878
|
FOCUSABLE_SELECTOR,
|
|
294
879
|
FORWARD_ZONE_PX,
|
|
295
880
|
MAX_SWIPE_DURATION_MS,
|
|
296
881
|
OPEN_DISTANCE_PX,
|
|
882
|
+
REDUCED_MOTION_QUERY,
|
|
297
883
|
VERTICAL_CANCEL_PX,
|
|
298
884
|
copyText,
|
|
885
|
+
createDebouncer,
|
|
886
|
+
detectApple,
|
|
299
887
|
focusableWithin,
|
|
888
|
+
handleBeforeUnload,
|
|
889
|
+
isEditableTarget,
|
|
300
890
|
isInOpenZone,
|
|
301
891
|
isInsideHorizontalScroller,
|
|
892
|
+
isOutside,
|
|
893
|
+
isPrimaryPress,
|
|
894
|
+
lockScroll,
|
|
895
|
+
matchHotkey,
|
|
896
|
+
nextDelay,
|
|
302
897
|
nextFocusTarget,
|
|
898
|
+
nextHeight,
|
|
899
|
+
parseHotkey,
|
|
303
900
|
resolveSwipeIntent,
|
|
901
|
+
unlockScroll,
|
|
902
|
+
useAutoGrowTextarea,
|
|
903
|
+
useCoarsePointer,
|
|
304
904
|
useCopyFeedback,
|
|
905
|
+
useDebouncedCallback,
|
|
906
|
+
useDebouncedValue,
|
|
305
907
|
useDrawerSwipe,
|
|
306
908
|
useEscape,
|
|
307
|
-
useFocusTrap
|
|
909
|
+
useFocusTrap,
|
|
910
|
+
useHotkey,
|
|
911
|
+
useMediaQuery,
|
|
912
|
+
useOutsidePress,
|
|
913
|
+
useReducedMotion,
|
|
914
|
+
useScrollLock,
|
|
915
|
+
useStorageState,
|
|
916
|
+
useUnsavedChangesGuard,
|
|
917
|
+
useVisibilityPolling
|
|
308
918
|
});
|