@develemit/web-hooks 0.2.0 → 0.3.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 (57) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +157 -0
  3. package/dist/chunk-4PVVTQ5V.js +74 -0
  4. package/dist/{chunk-OZ4PU4E3.js → chunk-5XRVPZHD.js} +14 -4
  5. package/dist/{chunk-3UGRXQIA.js → chunk-DQBKU7HI.js} +10 -3
  6. package/dist/chunk-EXEJI4FK.js +43 -0
  7. package/dist/{chunk-3CACI4OQ.js → chunk-FE3BQD2I.js} +1 -25
  8. package/dist/chunk-HTVNN2NM.js +29 -0
  9. package/dist/chunk-R2PFNE3U.js +154 -0
  10. package/dist/chunk-TNEJ34T5.js +86 -0
  11. package/dist/chunk-UVJBJETY.js +121 -0
  12. package/dist/{chunk-UZADAINO.js → chunk-WVM2LTWQ.js} +2 -21
  13. package/dist/chunk-WXL22EWK.js +21 -0
  14. package/dist/chunk-YFMMSLMB.js +41 -0
  15. package/dist/chunk-Z3X27PCA.js +108 -0
  16. package/dist/clipboard.cjs +13 -0
  17. package/dist/clipboard.d.cts +10 -1
  18. package/dist/clipboard.d.ts +10 -1
  19. package/dist/clipboard.js +6 -2
  20. package/dist/drawer-swipe.cjs +16 -4
  21. package/dist/drawer-swipe.d.cts +7 -3
  22. package/dist/drawer-swipe.d.ts +7 -3
  23. package/dist/drawer-swipe.js +5 -1
  24. package/dist/index.cjs +637 -19
  25. package/dist/index.d.cts +8 -2
  26. package/dist/index.d.ts +8 -2
  27. package/dist/index.js +77 -7
  28. package/dist/keyboard-inset.cjs +113 -0
  29. package/dist/keyboard-inset.d.cts +25 -0
  30. package/dist/keyboard-inset.d.ts +25 -0
  31. package/dist/keyboard-inset.js +11 -0
  32. package/dist/media-query.js +5 -3
  33. package/dist/online-status.cjs +46 -0
  34. package/dist/online-status.d.cts +3 -0
  35. package/dist/online-status.d.ts +3 -0
  36. package/dist/online-status.js +7 -0
  37. package/dist/outside-press.cjs +10 -3
  38. package/dist/outside-press.d.cts +4 -2
  39. package/dist/outside-press.d.ts +4 -2
  40. package/dist/outside-press.js +1 -1
  41. package/dist/push.cjs +209 -0
  42. package/dist/push.d.cts +38 -0
  43. package/dist/push.d.ts +38 -0
  44. package/dist/push.js +14 -0
  45. package/dist/pwa.cjs +198 -0
  46. package/dist/pwa.d.cts +35 -0
  47. package/dist/pwa.d.ts +35 -0
  48. package/dist/pwa.js +22 -0
  49. package/dist/service-worker.cjs +148 -0
  50. package/dist/service-worker.d.cts +24 -0
  51. package/dist/service-worker.d.ts +24 -0
  52. package/dist/service-worker.js +11 -0
  53. package/dist/share.cjs +139 -0
  54. package/dist/share.d.cts +23 -0
  55. package/dist/share.d.ts +23 -0
  56. package/dist/share.js +16 -0
  57. package/package.json +31 -1
@@ -0,0 +1,86 @@
1
+ "use client";
2
+
3
+ // src/keyboard-inset/use-keyboard-inset.ts
4
+ import { useEffect, useState } from "react";
5
+
6
+ // src/keyboard-inset/compute-inset.ts
7
+ var DEFAULT_MIN_INSET = 150;
8
+ function computeKeyboardInset(r) {
9
+ if (!r.editableFocused) return 0;
10
+ const occluded = r.layoutHeight - r.visualHeight - r.visualOffsetTop;
11
+ return occluded >= (r.minInset ?? DEFAULT_MIN_INSET) ? Math.round(occluded) : 0;
12
+ }
13
+ var NON_TEXT_INPUT_TYPES = /* @__PURE__ */ new Set([
14
+ "button",
15
+ "checkbox",
16
+ "radio",
17
+ "range",
18
+ "submit",
19
+ "reset",
20
+ "file",
21
+ "color",
22
+ "image",
23
+ "hidden"
24
+ ]);
25
+ function isEditableElement(el) {
26
+ if (!el) return false;
27
+ const tag = el.tagName.toLowerCase();
28
+ if (tag === "select") return !el.disabled;
29
+ if (tag === "textarea" || tag === "input") {
30
+ const field = el;
31
+ if (field.readOnly || field.disabled) return false;
32
+ return tag === "textarea" || !NON_TEXT_INPUT_TYPES.has(field.type);
33
+ }
34
+ if (el.isContentEditable === true) return true;
35
+ const attr = el.getAttribute("contenteditable");
36
+ return attr !== null && attr !== "false";
37
+ }
38
+
39
+ // src/keyboard-inset/use-keyboard-inset.ts
40
+ function useKeyboardInset({ minInset = DEFAULT_MIN_INSET, enabled = true } = {}) {
41
+ const [inset, setInset] = useState(0);
42
+ useEffect(() => {
43
+ const vv = typeof window === "undefined" ? null : window.visualViewport;
44
+ if (!enabled || !vv) {
45
+ setInset(0);
46
+ return;
47
+ }
48
+ const update = () => setInset(
49
+ computeKeyboardInset({
50
+ layoutHeight: window.innerHeight,
51
+ visualHeight: vv.height,
52
+ visualOffsetTop: vv.offsetTop,
53
+ editableFocused: isEditableElement(document.activeElement),
54
+ minInset
55
+ })
56
+ );
57
+ let frame = 0;
58
+ const onFocusOut = () => {
59
+ cancelAnimationFrame(frame);
60
+ frame = requestAnimationFrame(update);
61
+ };
62
+ const onFocusIn = () => {
63
+ cancelAnimationFrame(frame);
64
+ update();
65
+ };
66
+ vv.addEventListener("resize", update);
67
+ vv.addEventListener("scroll", update);
68
+ document.addEventListener("focusin", onFocusIn);
69
+ document.addEventListener("focusout", onFocusOut);
70
+ update();
71
+ return () => {
72
+ cancelAnimationFrame(frame);
73
+ vv.removeEventListener("resize", update);
74
+ vv.removeEventListener("scroll", update);
75
+ document.removeEventListener("focusin", onFocusIn);
76
+ document.removeEventListener("focusout", onFocusOut);
77
+ };
78
+ }, [enabled, minInset]);
79
+ return inset;
80
+ }
81
+
82
+ export {
83
+ computeKeyboardInset,
84
+ isEditableElement,
85
+ useKeyboardInset
86
+ };
@@ -0,0 +1,121 @@
1
+ "use client";
2
+
3
+ // src/service-worker/use-service-worker.ts
4
+ import { useCallback, useEffect, useRef, useState } from "react";
5
+
6
+ // src/service-worker/update-state.ts
7
+ function deriveUpdateState({ hasController: hasController2, waiting, installingState }) {
8
+ if (!hasController2) return false;
9
+ return waiting || installingState === "installed";
10
+ }
11
+
12
+ // src/service-worker/register.ts
13
+ var reloaded = false;
14
+ function isServiceWorkerSupported() {
15
+ return typeof navigator !== "undefined" && "serviceWorker" in navigator;
16
+ }
17
+ var hasController = () => Boolean(navigator.serviceWorker.controller);
18
+ async function unregisterAll() {
19
+ if (!isServiceWorkerSupported()) return;
20
+ try {
21
+ const registrations = await navigator.serviceWorker.getRegistrations();
22
+ await Promise.all(registrations.map((r) => r.unregister()));
23
+ } catch {
24
+ }
25
+ }
26
+ function registerServiceWorker({ url, scope, onUpdateAvailable, onError }, { reload = () => window.location.reload() } = {}) {
27
+ let registration;
28
+ let disposed = false;
29
+ let applying = false;
30
+ const cleanups = [];
31
+ const controlledAtStart = hasController();
32
+ let activatedUnasked = false;
33
+ const reloadOnce = () => {
34
+ if (reloaded) return;
35
+ reloaded = true;
36
+ reload();
37
+ };
38
+ const onControllerChange = () => {
39
+ if (applying) return reloadOnce();
40
+ if (!controlledAtStart || activatedUnasked) return;
41
+ activatedUnasked = true;
42
+ onUpdateAvailable();
43
+ };
44
+ navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
45
+ cleanups.push(() => navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange));
46
+ const watchInstalling = (reg) => {
47
+ const worker = reg.installing;
48
+ if (!worker) return;
49
+ const onState = () => {
50
+ if (deriveUpdateState({ hasController: hasController(), waiting: false, installingState: worker.state })) {
51
+ onUpdateAvailable();
52
+ }
53
+ };
54
+ worker.addEventListener("statechange", onState);
55
+ cleanups.push(() => worker.removeEventListener("statechange", onState));
56
+ };
57
+ navigator.serviceWorker.register(url, scope === void 0 ? void 0 : { scope }).then((reg) => {
58
+ if (disposed) return;
59
+ registration = reg;
60
+ if (deriveUpdateState({ hasController: hasController(), waiting: Boolean(reg.waiting) })) {
61
+ onUpdateAvailable();
62
+ }
63
+ const onUpdateFound = () => watchInstalling(reg);
64
+ reg.addEventListener("updatefound", onUpdateFound);
65
+ cleanups.push(() => reg.removeEventListener("updatefound", onUpdateFound));
66
+ }).catch((error) => {
67
+ if (disposed) return;
68
+ try {
69
+ onError?.(error);
70
+ } catch {
71
+ }
72
+ });
73
+ return {
74
+ applyUpdate() {
75
+ if (activatedUnasked) return reloadOnce();
76
+ const waiting = registration?.waiting;
77
+ if (!waiting) return;
78
+ applying = true;
79
+ waiting.postMessage({ type: "SKIP_WAITING" });
80
+ },
81
+ dispose() {
82
+ disposed = true;
83
+ cleanups.splice(0).forEach((fn) => fn());
84
+ }
85
+ };
86
+ }
87
+
88
+ // src/service-worker/use-service-worker.ts
89
+ function useServiceWorker({ url, scope, enabled = true, onError }) {
90
+ const [supported, setSupported] = useState(true);
91
+ const [updateAvailable, setUpdateAvailable] = useState(false);
92
+ const onErrorRef = useRef(onError);
93
+ onErrorRef.current = onError;
94
+ const handle = useRef(void 0);
95
+ useEffect(() => {
96
+ if (!enabled) return;
97
+ if (!isServiceWorkerSupported()) {
98
+ setSupported(false);
99
+ return;
100
+ }
101
+ const registered = registerServiceWorker({
102
+ url,
103
+ scope,
104
+ onUpdateAvailable: () => setUpdateAvailable(true),
105
+ onError: (error) => onErrorRef.current?.(error)
106
+ });
107
+ handle.current = registered;
108
+ return () => {
109
+ registered.dispose();
110
+ handle.current = void 0;
111
+ };
112
+ }, [url, scope, enabled]);
113
+ const applyUpdate = useCallback(() => handle.current?.applyUpdate(), []);
114
+ return { supported, updateAvailable, applyUpdate };
115
+ }
116
+
117
+ export {
118
+ deriveUpdateState,
119
+ unregisterAll,
120
+ useServiceWorker
121
+ };
@@ -1,8 +1,5 @@
1
1
  "use client";
2
2
 
3
- // src/media-query/use-media-query.ts
4
- import { useCallback, useSyncExternalStore } from "react";
5
-
6
3
  // src/media-query/media-query.ts
7
4
  var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
8
5
  var COARSE_POINTER_QUERY = "(pointer: coarse)";
@@ -37,25 +34,9 @@ function subscribe(query, onChange) {
37
34
  return () => list.removeListener(onChange);
38
35
  }
39
36
 
40
- // src/media-query/use-media-query.ts
41
- function useMediaQuery(query, options = {}) {
42
- const serverDefault = options.serverDefault ?? false;
43
- const subscribeToQuery = useCallback((onChange) => subscribe(query, onChange), [query]);
44
- const getSnapshot = useCallback(() => getMatches(query) ?? serverDefault, [query, serverDefault]);
45
- const getServerSnapshot = useCallback(() => serverDefault, [serverDefault]);
46
- return useSyncExternalStore(subscribeToQuery, getSnapshot, getServerSnapshot);
47
- }
48
- function useReducedMotion(options) {
49
- return useMediaQuery(REDUCED_MOTION_QUERY, options);
50
- }
51
- function useCoarsePointer(options) {
52
- return useMediaQuery(COARSE_POINTER_QUERY, options);
53
- }
54
-
55
37
  export {
56
38
  REDUCED_MOTION_QUERY,
57
39
  COARSE_POINTER_QUERY,
58
- useMediaQuery,
59
- useReducedMotion,
60
- useCoarsePointer
40
+ getMatches,
41
+ subscribe
61
42
  };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ // src/online-status/use-online-status.ts
4
+ import { useSyncExternalStore } from "react";
5
+ function subscribe(callback) {
6
+ window.addEventListener("online", callback);
7
+ window.addEventListener("offline", callback);
8
+ return () => {
9
+ window.removeEventListener("online", callback);
10
+ window.removeEventListener("offline", callback);
11
+ };
12
+ }
13
+ var getSnapshot = () => navigator.onLine;
14
+ var getServerSnapshot = () => true;
15
+ function useOnlineStatus() {
16
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
17
+ }
18
+
19
+ export {
20
+ useOnlineStatus
21
+ };
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ // src/pwa/detect.ts
4
+ var IOS_UA = /iPhone|iPad|iPod/;
5
+ var IOS_OTHER_BROWSER = /CriOS|FxiOS|EdgiOS|OPiOS/;
6
+ var IN_APP_WEBVIEW = /FBAN|FBAV|Instagram/;
7
+ var STANDALONE_QUERY = "(display-mode: standalone)";
8
+ function detectPlatform(env) {
9
+ const isIos2 = IOS_UA.test(env.userAgent) || env.platform === "MacIntel" && env.maxTouchPoints > 1;
10
+ const isIosSafari2 = isIos2 && !IOS_OTHER_BROWSER.test(env.userAgent) && !IN_APP_WEBVIEW.test(env.userAgent);
11
+ return { isIos: isIos2, isIosSafari: isIosSafari2, isStandalone: env.standaloneMedia || env.navigatorStandalone };
12
+ }
13
+ function readStandaloneMedia() {
14
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
15
+ return window.matchMedia(STANDALONE_QUERY).matches;
16
+ }
17
+ function readNavigatorStandalone() {
18
+ if (typeof navigator === "undefined") return false;
19
+ return navigator.standalone === true;
20
+ }
21
+ function readPlatform() {
22
+ if (typeof navigator === "undefined") return { isIos: false, isIosSafari: false, isStandalone: false };
23
+ return detectPlatform({
24
+ userAgent: navigator.userAgent ?? "",
25
+ platform: navigator.platform ?? "",
26
+ maxTouchPoints: navigator.maxTouchPoints ?? 0,
27
+ standaloneMedia: readStandaloneMedia(),
28
+ navigatorStandalone: readNavigatorStandalone()
29
+ });
30
+ }
31
+ var isStandalone = () => readPlatform().isStandalone;
32
+ var isIos = () => readPlatform().isIos;
33
+ var isIosSafari = () => readPlatform().isIosSafari;
34
+
35
+ export {
36
+ STANDALONE_QUERY,
37
+ detectPlatform,
38
+ isStandalone,
39
+ isIos,
40
+ isIosSafari
41
+ };
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import {
3
+ STANDALONE_QUERY,
4
+ isIosSafari,
5
+ isStandalone
6
+ } from "./chunk-YFMMSLMB.js";
7
+ import {
8
+ subscribe
9
+ } from "./chunk-WVM2LTWQ.js";
10
+
11
+ // src/pwa/use-is-standalone.ts
12
+ import { useCallback, useSyncExternalStore } from "react";
13
+ function useIsStandalone() {
14
+ const subscribeToMode = useCallback((onChange) => subscribe(STANDALONE_QUERY, onChange), []);
15
+ return useSyncExternalStore(subscribeToMode, isStandalone, () => false);
16
+ }
17
+
18
+ // src/pwa/use-install-prompt.ts
19
+ import { useEffect, useSyncExternalStore as useSyncExternalStore2 } from "react";
20
+
21
+ // src/pwa/install-store.ts
22
+ var state = { event: null, installed: false };
23
+ var subscribers = /* @__PURE__ */ new Set();
24
+ var permanent = false;
25
+ var holders = 0;
26
+ var attached = false;
27
+ function set(next) {
28
+ state = next;
29
+ subscribers.forEach((fn) => fn());
30
+ }
31
+ function onBeforeInstallPrompt(e) {
32
+ e.preventDefault();
33
+ set({ ...state, event: e });
34
+ }
35
+ function onAppInstalled() {
36
+ set({ event: null, installed: true });
37
+ }
38
+ function sync() {
39
+ const wanted = permanent || holders > 0;
40
+ if (wanted === attached || typeof window === "undefined") return;
41
+ attached = wanted;
42
+ const toggle = wanted ? window.addEventListener : window.removeEventListener;
43
+ toggle.call(window, "beforeinstallprompt", onBeforeInstallPrompt);
44
+ toggle.call(window, "appinstalled", onAppInstalled);
45
+ }
46
+ function startInstallPromptCapture() {
47
+ permanent = true;
48
+ sync();
49
+ }
50
+ function acquireCapture() {
51
+ holders += 1;
52
+ sync();
53
+ let released = false;
54
+ return () => {
55
+ if (released) return;
56
+ released = true;
57
+ holders -= 1;
58
+ sync();
59
+ };
60
+ }
61
+ function getInstallState() {
62
+ return state;
63
+ }
64
+ function subscribeInstallState(onChange) {
65
+ subscribers.add(onChange);
66
+ return () => {
67
+ subscribers.delete(onChange);
68
+ };
69
+ }
70
+ async function promptInstall() {
71
+ const event = state.event;
72
+ if (!event) return "unavailable";
73
+ set({ ...state, event: null });
74
+ try {
75
+ await event.prompt();
76
+ const { outcome } = await event.userChoice;
77
+ return outcome;
78
+ } catch {
79
+ return "dismissed";
80
+ }
81
+ }
82
+
83
+ // src/pwa/use-install-prompt.ts
84
+ var serverState = { event: null, installed: false };
85
+ function useInstallPrompt() {
86
+ const state2 = useSyncExternalStore2(subscribeInstallState, getInstallState, () => serverState);
87
+ const standalone = useIsStandalone();
88
+ const iosSafari = useSyncExternalStore2(
89
+ () => () => {
90
+ },
91
+ isIosSafari,
92
+ () => false
93
+ );
94
+ useEffect(() => acquireCapture(), []);
95
+ const installed = state2.installed || standalone;
96
+ return {
97
+ canPrompt: state2.event !== null && !installed,
98
+ prompt: promptInstall,
99
+ installed,
100
+ needsIosInstructions: iosSafari && !installed
101
+ };
102
+ }
103
+
104
+ export {
105
+ useIsStandalone,
106
+ startInstallPromptCapture,
107
+ useInstallPrompt
108
+ };
@@ -22,6 +22,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
  var clipboard_exports = {};
23
23
  __export(clipboard_exports, {
24
24
  copyText: () => copyText,
25
+ readText: () => readText,
25
26
  useCopyFeedback: () => useCopyFeedback
26
27
  });
27
28
  module.exports = __toCommonJS(clipboard_exports);
@@ -87,8 +88,20 @@ function useCopyFeedback({ resetMs = 1500 } = {}) {
87
88
  );
88
89
  return { copy, status };
89
90
  }
91
+
92
+ // src/clipboard/read-text.ts
93
+ async function readText() {
94
+ const hasClipboardApi = typeof navigator !== "undefined" && !!navigator.clipboard?.readText && typeof window !== "undefined" && window.isSecureContext;
95
+ if (!hasClipboardApi) return { ok: false, reason: "unavailable" };
96
+ try {
97
+ return { ok: true, text: await navigator.clipboard.readText() };
98
+ } catch {
99
+ return { ok: false, reason: "denied" };
100
+ }
101
+ }
90
102
  // Annotate the CommonJS export names for ESM import in node:
91
103
  0 && (module.exports = {
92
104
  copyText,
105
+ readText,
93
106
  useCopyFeedback
94
107
  });
@@ -14,4 +14,13 @@ declare function useCopyFeedback({ resetMs }?: {
14
14
  status: CopyStatus;
15
15
  };
16
16
 
17
- export { type CopyResult, type CopyStatus, copyText, useCopyFeedback };
17
+ type ReadResult = {
18
+ ok: true;
19
+ text: string;
20
+ } | {
21
+ ok: false;
22
+ reason: 'unavailable' | 'denied';
23
+ };
24
+ declare function readText(): Promise<ReadResult>;
25
+
26
+ export { type CopyResult, type CopyStatus, type ReadResult, copyText, readText, useCopyFeedback };
@@ -14,4 +14,13 @@ declare function useCopyFeedback({ resetMs }?: {
14
14
  status: CopyStatus;
15
15
  };
16
16
 
17
- export { type CopyResult, type CopyStatus, copyText, useCopyFeedback };
17
+ type ReadResult = {
18
+ ok: true;
19
+ text: string;
20
+ } | {
21
+ ok: false;
22
+ reason: 'unavailable' | 'denied';
23
+ };
24
+ declare function readText(): Promise<ReadResult>;
25
+
26
+ export { type CopyResult, type CopyStatus, type ReadResult, copyText, readText, useCopyFeedback };
package/dist/clipboard.js CHANGED
@@ -1,9 +1,13 @@
1
1
  "use client";
2
2
  import {
3
- copyText,
3
+ readText,
4
4
  useCopyFeedback
5
- } from "./chunk-3CACI4OQ.js";
5
+ } from "./chunk-EXEJI4FK.js";
6
+ import {
7
+ copyText
8
+ } from "./chunk-FE3BQD2I.js";
6
9
  export {
7
10
  copyText,
11
+ readText,
8
12
  useCopyFeedback
9
13
  };
@@ -23,10 +23,12 @@ var drawer_swipe_exports = {};
23
23
  __export(drawer_swipe_exports, {
24
24
  BACK_ZONE_PX: () => BACK_ZONE_PX,
25
25
  CLOSE_DISTANCE_PX: () => CLOSE_DISTANCE_PX,
26
+ DEFAULT_IGNORE_SELECTOR: () => DEFAULT_IGNORE_SELECTOR,
26
27
  FORWARD_ZONE_PX: () => FORWARD_ZONE_PX,
27
28
  MAX_SWIPE_DURATION_MS: () => MAX_SWIPE_DURATION_MS,
28
29
  OPEN_DISTANCE_PX: () => OPEN_DISTANCE_PX,
29
30
  VERTICAL_CANCEL_PX: () => VERTICAL_CANCEL_PX,
31
+ isInIgnoredRegion: () => isInIgnoredRegion,
30
32
  isInOpenZone: () => isInOpenZone,
31
33
  isInsideHorizontalScroller: () => isInsideHorizontalScroller,
32
34
  resolveSwipeIntent: () => resolveSwipeIntent,
@@ -45,7 +47,8 @@ var HORIZONTAL_DOMINANCE = 1.5;
45
47
  function isInOpenZone(x, viewportWidth) {
46
48
  return x > BACK_ZONE_PX && x < viewportWidth - FORWARD_ZONE_PX;
47
49
  }
48
- function resolveSwipeIntent(start, end, drawerOpen, viewportWidth) {
50
+ function resolveSwipeIntent(start, end, drawerOpen, viewportWidth, startedInIgnoredRegion = false) {
51
+ if (startedInIgnoredRegion) return null;
49
52
  const dx = end.x - start.x;
50
53
  const dy = end.y - start.y;
51
54
  if (end.time - start.time > MAX_SWIPE_DURATION_MS) return null;
@@ -67,6 +70,11 @@ function isInsideHorizontalScroller(target) {
67
70
  }
68
71
  return false;
69
72
  }
73
+ var DEFAULT_IGNORE_SELECTOR = "[data-no-swipe]";
74
+ function isInIgnoredRegion(target, selector) {
75
+ if (!selector || !(target instanceof Element)) return false;
76
+ return target.closest(selector) !== null;
77
+ }
70
78
 
71
79
  // src/drawer-swipe/use-drawer-swipe.ts
72
80
  var import_react = require("react");
@@ -78,17 +86,19 @@ function useDrawerSwipe({
78
86
  onOpen,
79
87
  onClose,
80
88
  enabled = true,
81
- maxViewportWidth = 768
89
+ maxViewportWidth = 768,
90
+ ignoreSelector = DEFAULT_IGNORE_SELECTOR
82
91
  }) {
83
92
  const start = (0, import_react.useRef)(null);
84
- const handlers = (0, import_react.useRef)({ drawerOpen, onOpen, onClose, maxViewportWidth });
85
- handlers.current = { drawerOpen, onOpen, onClose, maxViewportWidth };
93
+ const handlers = (0, import_react.useRef)({ drawerOpen, onOpen, onClose, maxViewportWidth, ignoreSelector });
94
+ handlers.current = { drawerOpen, onOpen, onClose, maxViewportWidth, ignoreSelector };
86
95
  (0, import_react.useEffect)(() => {
87
96
  if (!enabled) return;
88
97
  function handleStart(event) {
89
98
  start.current = null;
90
99
  if (event.touches.length !== 1) return;
91
100
  if (isWideViewport(handlers.current.maxViewportWidth)) return;
101
+ if (isInIgnoredRegion(event.target, handlers.current.ignoreSelector)) return;
92
102
  if (!handlers.current.drawerOpen && isInsideHorizontalScroller(event.target)) {
93
103
  return;
94
104
  }
@@ -138,10 +148,12 @@ function useDrawerSwipe({
138
148
  0 && (module.exports = {
139
149
  BACK_ZONE_PX,
140
150
  CLOSE_DISTANCE_PX,
151
+ DEFAULT_IGNORE_SELECTOR,
141
152
  FORWARD_ZONE_PX,
142
153
  MAX_SWIPE_DURATION_MS,
143
154
  OPEN_DISTANCE_PX,
144
155
  VERTICAL_CANCEL_PX,
156
+ isInIgnoredRegion,
145
157
  isInOpenZone,
146
158
  isInsideHorizontalScroller,
147
159
  resolveSwipeIntent,
@@ -11,8 +11,10 @@ declare const VERTICAL_CANCEL_PX = 40;
11
11
  declare const MAX_SWIPE_DURATION_MS = 450;
12
12
  type SwipeIntent = 'open' | 'close' | null;
13
13
  declare function isInOpenZone(x: number, viewportWidth: number): boolean;
14
- declare function resolveSwipeIntent(start: SwipePoint, end: SwipePoint, drawerOpen: boolean, viewportWidth: number): SwipeIntent;
14
+ declare function resolveSwipeIntent(start: SwipePoint, end: SwipePoint, drawerOpen: boolean, viewportWidth: number, startedInIgnoredRegion?: boolean): SwipeIntent;
15
15
  declare function isInsideHorizontalScroller(target: EventTarget | null): boolean;
16
+ declare const DEFAULT_IGNORE_SELECTOR = "[data-no-swipe]";
17
+ declare function isInIgnoredRegion(target: EventTarget | null, selector: string | null): boolean;
16
18
 
17
19
  type UseDrawerSwipeOptions = {
18
20
  drawerOpen: boolean;
@@ -20,7 +22,9 @@ type UseDrawerSwipeOptions = {
20
22
  onClose: () => void;
21
23
  enabled?: boolean;
22
24
  maxViewportWidth?: number;
25
+ /** Touches starting inside a match are ignored. Pass null to disable. */
26
+ ignoreSelector?: string | null;
23
27
  };
24
- declare function useDrawerSwipe({ drawerOpen, onOpen, onClose, enabled, maxViewportWidth, }: UseDrawerSwipeOptions): void;
28
+ declare function useDrawerSwipe({ drawerOpen, onOpen, onClose, enabled, maxViewportWidth, ignoreSelector, }: UseDrawerSwipeOptions): void;
25
29
 
26
- export { BACK_ZONE_PX, CLOSE_DISTANCE_PX, FORWARD_ZONE_PX, MAX_SWIPE_DURATION_MS, OPEN_DISTANCE_PX, type SwipeIntent, type SwipePoint, type UseDrawerSwipeOptions, VERTICAL_CANCEL_PX, isInOpenZone, isInsideHorizontalScroller, resolveSwipeIntent, useDrawerSwipe };
30
+ export { BACK_ZONE_PX, CLOSE_DISTANCE_PX, DEFAULT_IGNORE_SELECTOR, FORWARD_ZONE_PX, MAX_SWIPE_DURATION_MS, OPEN_DISTANCE_PX, type SwipeIntent, type SwipePoint, type UseDrawerSwipeOptions, VERTICAL_CANCEL_PX, isInIgnoredRegion, isInOpenZone, isInsideHorizontalScroller, resolveSwipeIntent, useDrawerSwipe };
@@ -11,8 +11,10 @@ declare const VERTICAL_CANCEL_PX = 40;
11
11
  declare const MAX_SWIPE_DURATION_MS = 450;
12
12
  type SwipeIntent = 'open' | 'close' | null;
13
13
  declare function isInOpenZone(x: number, viewportWidth: number): boolean;
14
- declare function resolveSwipeIntent(start: SwipePoint, end: SwipePoint, drawerOpen: boolean, viewportWidth: number): SwipeIntent;
14
+ declare function resolveSwipeIntent(start: SwipePoint, end: SwipePoint, drawerOpen: boolean, viewportWidth: number, startedInIgnoredRegion?: boolean): SwipeIntent;
15
15
  declare function isInsideHorizontalScroller(target: EventTarget | null): boolean;
16
+ declare const DEFAULT_IGNORE_SELECTOR = "[data-no-swipe]";
17
+ declare function isInIgnoredRegion(target: EventTarget | null, selector: string | null): boolean;
16
18
 
17
19
  type UseDrawerSwipeOptions = {
18
20
  drawerOpen: boolean;
@@ -20,7 +22,9 @@ type UseDrawerSwipeOptions = {
20
22
  onClose: () => void;
21
23
  enabled?: boolean;
22
24
  maxViewportWidth?: number;
25
+ /** Touches starting inside a match are ignored. Pass null to disable. */
26
+ ignoreSelector?: string | null;
23
27
  };
24
- declare function useDrawerSwipe({ drawerOpen, onOpen, onClose, enabled, maxViewportWidth, }: UseDrawerSwipeOptions): void;
28
+ declare function useDrawerSwipe({ drawerOpen, onOpen, onClose, enabled, maxViewportWidth, ignoreSelector, }: UseDrawerSwipeOptions): void;
25
29
 
26
- export { BACK_ZONE_PX, CLOSE_DISTANCE_PX, FORWARD_ZONE_PX, MAX_SWIPE_DURATION_MS, OPEN_DISTANCE_PX, type SwipeIntent, type SwipePoint, type UseDrawerSwipeOptions, VERTICAL_CANCEL_PX, isInOpenZone, isInsideHorizontalScroller, resolveSwipeIntent, useDrawerSwipe };
30
+ export { BACK_ZONE_PX, CLOSE_DISTANCE_PX, DEFAULT_IGNORE_SELECTOR, FORWARD_ZONE_PX, MAX_SWIPE_DURATION_MS, OPEN_DISTANCE_PX, type SwipeIntent, type SwipePoint, type UseDrawerSwipeOptions, VERTICAL_CANCEL_PX, isInIgnoredRegion, isInOpenZone, isInsideHorizontalScroller, resolveSwipeIntent, useDrawerSwipe };
@@ -2,22 +2,26 @@
2
2
  import {
3
3
  BACK_ZONE_PX,
4
4
  CLOSE_DISTANCE_PX,
5
+ DEFAULT_IGNORE_SELECTOR,
5
6
  FORWARD_ZONE_PX,
6
7
  MAX_SWIPE_DURATION_MS,
7
8
  OPEN_DISTANCE_PX,
8
9
  VERTICAL_CANCEL_PX,
10
+ isInIgnoredRegion,
9
11
  isInOpenZone,
10
12
  isInsideHorizontalScroller,
11
13
  resolveSwipeIntent,
12
14
  useDrawerSwipe
13
- } from "./chunk-OZ4PU4E3.js";
15
+ } from "./chunk-5XRVPZHD.js";
14
16
  export {
15
17
  BACK_ZONE_PX,
16
18
  CLOSE_DISTANCE_PX,
19
+ DEFAULT_IGNORE_SELECTOR,
17
20
  FORWARD_ZONE_PX,
18
21
  MAX_SWIPE_DURATION_MS,
19
22
  OPEN_DISTANCE_PX,
20
23
  VERTICAL_CANCEL_PX,
24
+ isInIgnoredRegion,
21
25
  isInOpenZone,
22
26
  isInsideHorizontalScroller,
23
27
  resolveSwipeIntent,