@mantine/hooks 9.1.1 → 9.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 (194) hide show
  1. package/cjs/index.cjs +2 -0
  2. package/cjs/use-click-outside/use-click-outside.cjs.map +1 -1
  3. package/cjs/use-clipboard/use-clipboard.cjs.map +1 -1
  4. package/cjs/use-collapse/use-collapse.cjs.map +1 -1
  5. package/cjs/use-collapse/use-horizontal-collapse.cjs.map +1 -1
  6. package/cjs/use-color-scheme/use-color-scheme.cjs.map +1 -1
  7. package/cjs/use-counter/use-counter.cjs.map +1 -1
  8. package/cjs/use-debounced-callback/use-debounced-callback.cjs.map +1 -1
  9. package/cjs/use-debounced-state/use-debounced-state.cjs.map +1 -1
  10. package/cjs/use-debounced-value/use-debounced-value.cjs +3 -0
  11. package/cjs/use-debounced-value/use-debounced-value.cjs.map +1 -1
  12. package/cjs/use-did-update/use-did-update.cjs.map +1 -1
  13. package/cjs/use-disclosure/use-disclosure.cjs.map +1 -1
  14. package/cjs/use-document-title/use-document-title.cjs.map +1 -1
  15. package/cjs/use-document-visibility/use-document-visibility.cjs.map +1 -1
  16. package/cjs/use-drag/use-drag.cjs +266 -0
  17. package/cjs/use-drag/use-drag.cjs.map +1 -0
  18. package/cjs/use-event-listener/use-event-listener.cjs.map +1 -1
  19. package/cjs/use-eye-dropper/use-eye-dropper.cjs.map +1 -1
  20. package/cjs/use-favicon/use-favicon.cjs.map +1 -1
  21. package/cjs/use-fetch/use-fetch.cjs.map +1 -1
  22. package/cjs/use-file-dialog/use-file-dialog.cjs.map +1 -1
  23. package/cjs/use-floating-window/use-floating-window.cjs.map +1 -1
  24. package/cjs/use-focus-return/use-focus-return.cjs.map +1 -1
  25. package/cjs/use-focus-trap/scope-tab.cjs.map +1 -1
  26. package/cjs/use-focus-trap/tabbable.cjs.map +1 -1
  27. package/cjs/use-focus-trap/use-focus-trap.cjs.map +1 -1
  28. package/cjs/use-focus-within/use-focus-within.cjs.map +1 -1
  29. package/cjs/use-force-update/use-force-update.cjs.map +1 -1
  30. package/cjs/use-fullscreen/use-fullscreen.cjs.map +1 -1
  31. package/cjs/use-hash/use-hash.cjs.map +1 -1
  32. package/cjs/use-headroom/use-headroom.cjs.map +1 -1
  33. package/cjs/use-hotkeys/parse-hotkey.cjs.map +1 -1
  34. package/cjs/use-hotkeys/use-hotkeys.cjs.map +1 -1
  35. package/cjs/use-hover/use-hover.cjs.map +1 -1
  36. package/cjs/use-id/use-id.cjs.map +1 -1
  37. package/cjs/use-idle/use-idle.cjs.map +1 -1
  38. package/cjs/use-in-viewport/use-in-viewport.cjs.map +1 -1
  39. package/cjs/use-input-state/use-input-state.cjs.map +1 -1
  40. package/cjs/use-intersection/use-intersection.cjs.map +1 -1
  41. package/cjs/use-interval/use-interval.cjs.map +1 -1
  42. package/cjs/use-is-first-render/use-is-first-render.cjs.map +1 -1
  43. package/cjs/use-list-state/use-list-state.cjs.map +1 -1
  44. package/cjs/use-local-storage/create-storage.cjs.map +1 -1
  45. package/cjs/use-local-storage/use-local-storage.cjs.map +1 -1
  46. package/cjs/use-logger/use-logger.cjs.map +1 -1
  47. package/cjs/use-long-press/use-long-press.cjs.map +1 -1
  48. package/cjs/use-map/use-map.cjs.map +1 -1
  49. package/cjs/use-mask/use-mask.cjs.map +1 -1
  50. package/cjs/use-media-query/use-media-query.cjs.map +1 -1
  51. package/cjs/use-merged-ref/use-merged-ref.cjs.map +1 -1
  52. package/cjs/use-mounted/use-mounted.cjs.map +1 -1
  53. package/cjs/use-mouse/use-mouse.cjs.map +1 -1
  54. package/cjs/use-move/use-move.cjs.map +1 -1
  55. package/cjs/use-mutation-observer/use-mutation-observer.cjs.map +1 -1
  56. package/cjs/use-network/use-network.cjs.map +1 -1
  57. package/cjs/use-orientation/use-orientation.cjs.map +1 -1
  58. package/cjs/use-os/use-os.cjs.map +1 -1
  59. package/cjs/use-page-leave/use-page-leave.cjs.map +1 -1
  60. package/cjs/use-pagination/use-pagination.cjs.map +1 -1
  61. package/cjs/use-previous/use-previous.cjs.map +1 -1
  62. package/cjs/use-queue/use-queue.cjs.map +1 -1
  63. package/cjs/use-radial-move/use-radial-move.cjs +12 -4
  64. package/cjs/use-radial-move/use-radial-move.cjs.map +1 -1
  65. package/cjs/use-reduced-motion/use-reduced-motion.cjs.map +1 -1
  66. package/cjs/use-resize-observer/use-resize-observer.cjs.map +1 -1
  67. package/cjs/use-roving-index/use-roving-index.cjs.map +1 -1
  68. package/cjs/use-scroll-direction/use-scroll-direction.cjs.map +1 -1
  69. package/cjs/use-scroll-into-view/use-scroll-into-view.cjs.map +1 -1
  70. package/cjs/use-scroll-spy/use-scroll-spy.cjs.map +1 -1
  71. package/cjs/use-scroller/use-scroller.cjs.map +1 -1
  72. package/cjs/use-selection/use-selection.cjs.map +1 -1
  73. package/cjs/use-session-storage/use-session-storage.cjs.map +1 -1
  74. package/cjs/use-set/use-set.cjs.map +1 -1
  75. package/cjs/use-set-state/use-set-state.cjs.map +1 -1
  76. package/cjs/use-shallow-effect/use-shallow-effect.cjs.map +1 -1
  77. package/cjs/use-state-history/use-state-history.cjs.map +1 -1
  78. package/cjs/use-text-selection/use-text-selection.cjs.map +1 -1
  79. package/cjs/use-throttled-callback/use-throttled-callback.cjs.map +1 -1
  80. package/cjs/use-throttled-state/use-throttled-state.cjs.map +1 -1
  81. package/cjs/use-throttled-value/use-throttled-value.cjs.map +1 -1
  82. package/cjs/use-timeout/use-timeout.cjs.map +1 -1
  83. package/cjs/use-toggle/use-toggle.cjs.map +1 -1
  84. package/cjs/use-uncontrolled/use-uncontrolled.cjs.map +1 -1
  85. package/cjs/use-validated-state/use-validated-state.cjs.map +1 -1
  86. package/cjs/use-viewport-size/use-viewport-size.cjs.map +1 -1
  87. package/cjs/use-window-event/use-window-event.cjs.map +1 -1
  88. package/cjs/use-window-scroll/use-window-scroll.cjs.map +1 -1
  89. package/cjs/utils/clamp/clamp.cjs.map +1 -1
  90. package/cjs/utils/lower-first/lower-first.cjs.map +1 -1
  91. package/cjs/utils/random-id/random-id.cjs.map +1 -1
  92. package/cjs/utils/range/range.cjs.map +1 -1
  93. package/cjs/utils/shallow-equal/shallow-equal.cjs.map +1 -1
  94. package/cjs/utils/upper-first/upper-first.cjs.map +1 -1
  95. package/cjs/utils/use-callback-ref/use-callback-ref.cjs.map +1 -1
  96. package/esm/index.mjs +2 -1
  97. package/esm/use-click-outside/use-click-outside.mjs.map +1 -1
  98. package/esm/use-clipboard/use-clipboard.mjs.map +1 -1
  99. package/esm/use-collapse/use-collapse.mjs.map +1 -1
  100. package/esm/use-collapse/use-horizontal-collapse.mjs.map +1 -1
  101. package/esm/use-color-scheme/use-color-scheme.mjs.map +1 -1
  102. package/esm/use-counter/use-counter.mjs.map +1 -1
  103. package/esm/use-debounced-callback/use-debounced-callback.mjs.map +1 -1
  104. package/esm/use-debounced-state/use-debounced-state.mjs.map +1 -1
  105. package/esm/use-debounced-value/use-debounced-value.mjs +3 -0
  106. package/esm/use-debounced-value/use-debounced-value.mjs.map +1 -1
  107. package/esm/use-did-update/use-did-update.mjs.map +1 -1
  108. package/esm/use-disclosure/use-disclosure.mjs.map +1 -1
  109. package/esm/use-document-title/use-document-title.mjs.map +1 -1
  110. package/esm/use-document-visibility/use-document-visibility.mjs.map +1 -1
  111. package/esm/use-drag/use-drag.mjs +266 -0
  112. package/esm/use-drag/use-drag.mjs.map +1 -0
  113. package/esm/use-event-listener/use-event-listener.mjs.map +1 -1
  114. package/esm/use-eye-dropper/use-eye-dropper.mjs.map +1 -1
  115. package/esm/use-favicon/use-favicon.mjs.map +1 -1
  116. package/esm/use-fetch/use-fetch.mjs.map +1 -1
  117. package/esm/use-file-dialog/use-file-dialog.mjs.map +1 -1
  118. package/esm/use-floating-window/use-floating-window.mjs.map +1 -1
  119. package/esm/use-focus-return/use-focus-return.mjs.map +1 -1
  120. package/esm/use-focus-trap/scope-tab.mjs.map +1 -1
  121. package/esm/use-focus-trap/tabbable.mjs.map +1 -1
  122. package/esm/use-focus-trap/use-focus-trap.mjs.map +1 -1
  123. package/esm/use-focus-within/use-focus-within.mjs.map +1 -1
  124. package/esm/use-force-update/use-force-update.mjs.map +1 -1
  125. package/esm/use-fullscreen/use-fullscreen.mjs.map +1 -1
  126. package/esm/use-hash/use-hash.mjs.map +1 -1
  127. package/esm/use-headroom/use-headroom.mjs.map +1 -1
  128. package/esm/use-hotkeys/parse-hotkey.mjs.map +1 -1
  129. package/esm/use-hotkeys/use-hotkeys.mjs.map +1 -1
  130. package/esm/use-hover/use-hover.mjs.map +1 -1
  131. package/esm/use-id/use-id.mjs.map +1 -1
  132. package/esm/use-idle/use-idle.mjs.map +1 -1
  133. package/esm/use-in-viewport/use-in-viewport.mjs.map +1 -1
  134. package/esm/use-input-state/use-input-state.mjs.map +1 -1
  135. package/esm/use-intersection/use-intersection.mjs.map +1 -1
  136. package/esm/use-interval/use-interval.mjs.map +1 -1
  137. package/esm/use-is-first-render/use-is-first-render.mjs.map +1 -1
  138. package/esm/use-list-state/use-list-state.mjs.map +1 -1
  139. package/esm/use-local-storage/create-storage.mjs.map +1 -1
  140. package/esm/use-local-storage/use-local-storage.mjs.map +1 -1
  141. package/esm/use-logger/use-logger.mjs.map +1 -1
  142. package/esm/use-long-press/use-long-press.mjs.map +1 -1
  143. package/esm/use-map/use-map.mjs.map +1 -1
  144. package/esm/use-mask/use-mask.mjs.map +1 -1
  145. package/esm/use-media-query/use-media-query.mjs.map +1 -1
  146. package/esm/use-merged-ref/use-merged-ref.mjs.map +1 -1
  147. package/esm/use-mounted/use-mounted.mjs.map +1 -1
  148. package/esm/use-mouse/use-mouse.mjs.map +1 -1
  149. package/esm/use-move/use-move.mjs.map +1 -1
  150. package/esm/use-mutation-observer/use-mutation-observer.mjs.map +1 -1
  151. package/esm/use-network/use-network.mjs.map +1 -1
  152. package/esm/use-orientation/use-orientation.mjs.map +1 -1
  153. package/esm/use-os/use-os.mjs.map +1 -1
  154. package/esm/use-page-leave/use-page-leave.mjs.map +1 -1
  155. package/esm/use-pagination/use-pagination.mjs.map +1 -1
  156. package/esm/use-previous/use-previous.mjs.map +1 -1
  157. package/esm/use-queue/use-queue.mjs.map +1 -1
  158. package/esm/use-radial-move/use-radial-move.mjs +13 -5
  159. package/esm/use-radial-move/use-radial-move.mjs.map +1 -1
  160. package/esm/use-reduced-motion/use-reduced-motion.mjs.map +1 -1
  161. package/esm/use-resize-observer/use-resize-observer.mjs.map +1 -1
  162. package/esm/use-roving-index/use-roving-index.mjs.map +1 -1
  163. package/esm/use-scroll-direction/use-scroll-direction.mjs.map +1 -1
  164. package/esm/use-scroll-into-view/use-scroll-into-view.mjs.map +1 -1
  165. package/esm/use-scroll-spy/use-scroll-spy.mjs.map +1 -1
  166. package/esm/use-scroller/use-scroller.mjs.map +1 -1
  167. package/esm/use-selection/use-selection.mjs.map +1 -1
  168. package/esm/use-session-storage/use-session-storage.mjs.map +1 -1
  169. package/esm/use-set/use-set.mjs.map +1 -1
  170. package/esm/use-set-state/use-set-state.mjs.map +1 -1
  171. package/esm/use-shallow-effect/use-shallow-effect.mjs.map +1 -1
  172. package/esm/use-state-history/use-state-history.mjs.map +1 -1
  173. package/esm/use-text-selection/use-text-selection.mjs.map +1 -1
  174. package/esm/use-throttled-callback/use-throttled-callback.mjs.map +1 -1
  175. package/esm/use-throttled-state/use-throttled-state.mjs.map +1 -1
  176. package/esm/use-throttled-value/use-throttled-value.mjs.map +1 -1
  177. package/esm/use-timeout/use-timeout.mjs.map +1 -1
  178. package/esm/use-toggle/use-toggle.mjs.map +1 -1
  179. package/esm/use-uncontrolled/use-uncontrolled.mjs.map +1 -1
  180. package/esm/use-validated-state/use-validated-state.mjs.map +1 -1
  181. package/esm/use-viewport-size/use-viewport-size.mjs.map +1 -1
  182. package/esm/use-window-event/use-window-event.mjs.map +1 -1
  183. package/esm/use-window-scroll/use-window-scroll.mjs.map +1 -1
  184. package/esm/utils/clamp/clamp.mjs.map +1 -1
  185. package/esm/utils/lower-first/lower-first.mjs.map +1 -1
  186. package/esm/utils/random-id/random-id.mjs.map +1 -1
  187. package/esm/utils/range/range.mjs.map +1 -1
  188. package/esm/utils/shallow-equal/shallow-equal.mjs.map +1 -1
  189. package/esm/utils/upper-first/upper-first.mjs.map +1 -1
  190. package/esm/utils/use-callback-ref/use-callback-ref.mjs.map +1 -1
  191. package/lib/index.d.mts +2 -0
  192. package/lib/index.d.ts +2 -0
  193. package/lib/use-drag/use-drag.d.ts +60 -0
  194. package/package.json +1 -1
@@ -0,0 +1,266 @@
1
+ "use client";
2
+ import { useCallback, useRef, useState } from "react";
3
+ //#region packages/@mantine/hooks/src/use-drag/use-drag.ts
4
+ const VELOCITY_DECAY_MS = 100;
5
+ function sign(n) {
6
+ if (n > 0) return 1;
7
+ if (n < 0) return -1;
8
+ return 0;
9
+ }
10
+ function getThresholdVector(threshold) {
11
+ const t = threshold ?? 0;
12
+ if (typeof t === "number") return [t, t];
13
+ return t;
14
+ }
15
+ function createInitialState() {
16
+ return {
17
+ isActive: false,
18
+ pointerId: -1,
19
+ startXY: [0, 0],
20
+ prevXY: [0, 0],
21
+ startTimestamp: 0,
22
+ prevTimestamp: 0,
23
+ thresholdMet: false,
24
+ firstFired: false,
25
+ lockedAxis: null,
26
+ canceled: false,
27
+ lastVelocity: [0, 0]
28
+ };
29
+ }
30
+ function useDrag(handler, options = {}) {
31
+ const [active, setActive] = useState(false);
32
+ const handlerRef = useRef(handler);
33
+ handlerRef.current = handler;
34
+ const optionsRef = useRef(options);
35
+ optionsRef.current = options;
36
+ const stateRef = useRef(createInitialState());
37
+ const documentControllerRef = useRef(null);
38
+ return {
39
+ ref: useCallback((node) => {
40
+ if (!node) return;
41
+ const elementController = new AbortController();
42
+ const applyAxisConstraint = (v) => {
43
+ const opts = optionsRef.current;
44
+ const s = stateRef.current;
45
+ if (opts.axis === "x") return [v[0], 0];
46
+ if (opts.axis === "y") return [0, v[1]];
47
+ if (opts.axis === "lock") {
48
+ if (s.lockedAxis === null) {
49
+ const t = opts.axisThreshold ?? 1;
50
+ if (Math.abs(v[0]) > t || Math.abs(v[1]) > t) s.lockedAxis = Math.abs(v[0]) >= Math.abs(v[1]) ? "x" : "y";
51
+ }
52
+ if (s.lockedAxis === "x") return [v[0], 0];
53
+ if (s.lockedAxis === "y") return [0, v[1]];
54
+ }
55
+ return v;
56
+ };
57
+ const resetDrag = () => {
58
+ const s = stateRef.current;
59
+ s.isActive = false;
60
+ s.pointerId = -1;
61
+ s.thresholdMet = false;
62
+ s.firstFired = false;
63
+ s.lockedAxis = null;
64
+ s.canceled = false;
65
+ setActive(false);
66
+ document.body.style.userSelect = "";
67
+ document.body.style.webkitUserSelect = "";
68
+ documentControllerRef.current?.abort();
69
+ documentControllerRef.current = null;
70
+ };
71
+ const cancel = () => {
72
+ if (stateRef.current.isActive) {
73
+ stateRef.current.canceled = true;
74
+ resetDrag();
75
+ }
76
+ };
77
+ const activateDrag = () => {
78
+ setActive(true);
79
+ document.body.style.userSelect = "none";
80
+ document.body.style.webkitUserSelect = "none";
81
+ };
82
+ const onPointerDown = (event) => {
83
+ if (optionsRef.current.enabled === false) return;
84
+ if (event.button !== 0) return;
85
+ if (stateRef.current.isActive) return;
86
+ const s = stateRef.current;
87
+ s.isActive = true;
88
+ s.pointerId = event.pointerId;
89
+ s.startXY = [event.clientX, event.clientY];
90
+ s.prevXY = [event.clientX, event.clientY];
91
+ s.startTimestamp = event.timeStamp;
92
+ s.prevTimestamp = event.timeStamp;
93
+ s.thresholdMet = false;
94
+ s.firstFired = false;
95
+ s.lockedAxis = null;
96
+ s.canceled = false;
97
+ s.lastVelocity = [0, 0];
98
+ const [tx, ty] = getThresholdVector(optionsRef.current.threshold);
99
+ if (tx === 0 && ty === 0) {
100
+ s.thresholdMet = true;
101
+ s.firstFired = true;
102
+ activateDrag();
103
+ handlerRef.current({
104
+ xy: [event.clientX, event.clientY],
105
+ initial: [event.clientX, event.clientY],
106
+ movement: [0, 0],
107
+ delta: [0, 0],
108
+ distance: [0, 0],
109
+ direction: [0, 0],
110
+ velocity: [0, 0],
111
+ elapsedTime: 0,
112
+ first: true,
113
+ last: false,
114
+ active: true,
115
+ tap: false,
116
+ canceled: false,
117
+ cancel,
118
+ event
119
+ });
120
+ }
121
+ documentControllerRef.current?.abort();
122
+ documentControllerRef.current = new AbortController();
123
+ const sig = documentControllerRef.current.signal;
124
+ document.addEventListener("pointermove", onPointerMove, { signal: sig });
125
+ document.addEventListener("pointerup", onPointerUp, { signal: sig });
126
+ document.addEventListener("pointercancel", onPointerCancel, { signal: sig });
127
+ };
128
+ const onPointerMove = (event) => {
129
+ const s = stateRef.current;
130
+ if (!s.isActive || event.pointerId !== s.pointerId) return;
131
+ const rawMovement = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];
132
+ if (!s.thresholdMet) {
133
+ const [tx, ty] = getThresholdVector(optionsRef.current.threshold);
134
+ if (Math.abs(rawMovement[0]) < tx && Math.abs(rawMovement[1]) < ty) {
135
+ s.prevXY = [event.clientX, event.clientY];
136
+ s.prevTimestamp = event.timeStamp;
137
+ return;
138
+ }
139
+ s.thresholdMet = true;
140
+ activateDrag();
141
+ }
142
+ const movement = applyAxisConstraint(rawMovement);
143
+ const delta = applyAxisConstraint([event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]]);
144
+ const timeDelta = event.timeStamp - s.prevTimestamp;
145
+ const velocity = timeDelta > 0 ? [Math.abs(delta[0]) / timeDelta, Math.abs(delta[1]) / timeDelta] : s.lastVelocity;
146
+ s.lastVelocity = velocity;
147
+ const isFirst = !s.firstFired;
148
+ s.firstFired = true;
149
+ s.prevXY = [event.clientX, event.clientY];
150
+ s.prevTimestamp = event.timeStamp;
151
+ handlerRef.current({
152
+ xy: [event.clientX, event.clientY],
153
+ initial: [...s.startXY],
154
+ movement,
155
+ delta,
156
+ distance: [Math.abs(movement[0]), Math.abs(movement[1])],
157
+ direction: [sign(delta[0]), sign(delta[1])],
158
+ velocity,
159
+ elapsedTime: event.timeStamp - s.startTimestamp,
160
+ first: isFirst,
161
+ last: false,
162
+ active: true,
163
+ tap: false,
164
+ canceled: false,
165
+ cancel,
166
+ event
167
+ });
168
+ };
169
+ const onPointerUp = (event) => {
170
+ const s = stateRef.current;
171
+ if (!s.isActive || event.pointerId !== s.pointerId) return;
172
+ const opts = optionsRef.current;
173
+ if (!s.thresholdMet) {
174
+ if (opts.filterTaps) {
175
+ const mov = applyAxisConstraint([event.clientX - s.startXY[0], event.clientY - s.startXY[1]]);
176
+ const dist = [Math.abs(mov[0]), Math.abs(mov[1])];
177
+ const isTap = Math.max(dist[0], dist[1]) < (opts.tapThreshold ?? 3);
178
+ handlerRef.current({
179
+ xy: [event.clientX, event.clientY],
180
+ initial: [...s.startXY],
181
+ movement: mov,
182
+ delta: mov,
183
+ distance: dist,
184
+ direction: [sign(mov[0]), sign(mov[1])],
185
+ velocity: [0, 0],
186
+ elapsedTime: event.timeStamp - s.startTimestamp,
187
+ first: true,
188
+ last: true,
189
+ active: false,
190
+ tap: isTap,
191
+ canceled: false,
192
+ cancel,
193
+ event
194
+ });
195
+ }
196
+ resetDrag();
197
+ return;
198
+ }
199
+ const movement = applyAxisConstraint([event.clientX - s.startXY[0], event.clientY - s.startXY[1]]);
200
+ const distance = [Math.abs(movement[0]), Math.abs(movement[1])];
201
+ const delta = applyAxisConstraint([event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]]);
202
+ const velocity = event.timeStamp - s.prevTimestamp > VELOCITY_DECAY_MS ? [0, 0] : s.lastVelocity;
203
+ const maxDistance = Math.max(distance[0], distance[1]);
204
+ const tap = opts.filterTaps === true && maxDistance < (opts.tapThreshold ?? 3);
205
+ handlerRef.current({
206
+ xy: [event.clientX, event.clientY],
207
+ initial: [...s.startXY],
208
+ movement,
209
+ delta,
210
+ distance,
211
+ direction: [sign(delta[0]), sign(delta[1])],
212
+ velocity,
213
+ elapsedTime: event.timeStamp - s.startTimestamp,
214
+ first: !s.firstFired,
215
+ last: true,
216
+ active: false,
217
+ tap,
218
+ canceled: false,
219
+ cancel,
220
+ event
221
+ });
222
+ resetDrag();
223
+ };
224
+ const onPointerCancel = (event) => {
225
+ const s = stateRef.current;
226
+ if (!s.isActive || event.pointerId !== s.pointerId) return;
227
+ const movement = applyAxisConstraint([event.clientX - s.startXY[0], event.clientY - s.startXY[1]]);
228
+ handlerRef.current({
229
+ xy: [event.clientX, event.clientY],
230
+ initial: [...s.startXY],
231
+ movement,
232
+ delta: [0, 0],
233
+ distance: [Math.abs(movement[0]), Math.abs(movement[1])],
234
+ direction: [0, 0],
235
+ velocity: [0, 0],
236
+ elapsedTime: event.timeStamp - s.startTimestamp,
237
+ first: !s.firstFired,
238
+ last: true,
239
+ active: false,
240
+ tap: false,
241
+ canceled: true,
242
+ cancel,
243
+ event
244
+ });
245
+ resetDrag();
246
+ };
247
+ node.addEventListener("pointerdown", onPointerDown, { signal: elementController.signal });
248
+ return () => {
249
+ elementController.abort();
250
+ documentControllerRef.current?.abort();
251
+ documentControllerRef.current = null;
252
+ if (stateRef.current.isActive) {
253
+ stateRef.current.isActive = false;
254
+ setActive(false);
255
+ document.body.style.userSelect = "";
256
+ document.body.style.webkitUserSelect = "";
257
+ }
258
+ };
259
+ }, []),
260
+ active
261
+ };
262
+ }
263
+ //#endregion
264
+ export { useDrag };
265
+
266
+ //# sourceMappingURL=use-drag.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-drag.mjs","names":[],"sources":["../../src/use-drag/use-drag.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\n/* eslint-disable @typescript-eslint/no-deprecated */\nimport { useCallback, useRef, useState } from 'react';\n\ntype Vector2 = [number, number];\n\nexport interface UseDragState {\n /** Current pointer position [x, y] */\n xy: Vector2;\n\n /** Position where the gesture started [x, y] */\n initial: Vector2;\n\n /** Displacement from start [x, y], respects axis constraint */\n movement: Vector2;\n\n /** Change since previous event [x, y] */\n delta: Vector2;\n\n /** Absolute distance per axis [x, y] */\n distance: Vector2;\n\n /** Movement direction per axis: -1, 0, or 1 */\n direction: Vector2;\n\n /** Speed per axis in px/ms */\n velocity: Vector2;\n\n /** Time since drag started in ms */\n elapsedTime: number;\n\n /** `true` on the first handler call after the threshold is met */\n first: boolean;\n\n /** `true` on the last handler call (pointer released or canceled) */\n last: boolean;\n\n /** `true` while the gesture is ongoing */\n active: boolean;\n\n /** `true` when the gesture qualifies as a tap (requires `filterTaps: true`) */\n tap: boolean;\n\n /** `true` when the gesture was interrupted by a `pointercancel` event */\n canceled: boolean;\n\n /** Function to programmatically cancel the current gesture */\n cancel: () => void;\n\n /** The source `PointerEvent` */\n event: PointerEvent;\n}\n\nexport interface UseDragOptions {\n /** Constrain movement to a specific axis. `'lock'` locks to whichever axis has more movement after `axisThreshold` is exceeded. */\n axis?: 'x' | 'y' | 'lock';\n\n /** Movement in px required to determine axis when `axis` is `'lock'`, `1` by default */\n axisThreshold?: number;\n\n /** When `true`, the last state includes `tap: true` when total distance is below `tapThreshold`, `false` by default */\n filterTaps?: boolean;\n\n /** Max displacement in px to still be considered a tap, `3` by default */\n tapThreshold?: number;\n\n /** Minimum displacement in px before the drag activates. Can be a number (both axes) or `[x, y]`. `0` by default */\n threshold?: number | Vector2;\n\n /** Enable or disable the hook, `true` by default */\n enabled?: boolean;\n}\n\nexport interface UseDragReturnValue<T extends HTMLElement = any> {\n /** Ref callback to attach to the draggable element */\n ref: React.RefCallback<T | null>;\n\n /** `true` while the element is being dragged */\n active: boolean;\n}\n\nconst VELOCITY_DECAY_MS = 100;\n\nfunction sign(n: number): -1 | 0 | 1 {\n if (n > 0) {\n return 1;\n }\n if (n < 0) {\n return -1;\n }\n return 0;\n}\n\nfunction getThresholdVector(threshold: number | Vector2 | undefined): Vector2 {\n const t = threshold ?? 0;\n if (typeof t === 'number') {\n return [t, t];\n }\n return t;\n}\n\ninterface DragInternalState {\n isActive: boolean;\n pointerId: number;\n startXY: Vector2;\n prevXY: Vector2;\n startTimestamp: number;\n prevTimestamp: number;\n thresholdMet: boolean;\n firstFired: boolean;\n lockedAxis: 'x' | 'y' | null;\n canceled: boolean;\n lastVelocity: Vector2;\n}\n\nfunction createInitialState(): DragInternalState {\n return {\n isActive: false,\n pointerId: -1,\n startXY: [0, 0],\n prevXY: [0, 0],\n startTimestamp: 0,\n prevTimestamp: 0,\n thresholdMet: false,\n firstFired: false,\n lockedAxis: null,\n canceled: false,\n lastVelocity: [0, 0],\n };\n}\n\nexport function useDrag<T extends HTMLElement = any>(\n handler: (state: UseDragState) => void,\n options: UseDragOptions = {}\n): UseDragReturnValue<T> {\n const [active, setActive] = useState(false);\n\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const stateRef = useRef<DragInternalState>(createInitialState());\n const documentControllerRef = useRef<AbortController | null>(null);\n\n const refCallback: React.RefCallback<T | null> = useCallback((node) => {\n if (!node) {\n return undefined;\n }\n\n const elementController = new AbortController();\n\n const applyAxisConstraint = (v: Vector2): Vector2 => {\n const opts = optionsRef.current;\n const s = stateRef.current;\n\n if (opts.axis === 'x') {\n return [v[0], 0];\n }\n if (opts.axis === 'y') {\n return [0, v[1]];\n }\n if (opts.axis === 'lock') {\n if (s.lockedAxis === null) {\n const t = opts.axisThreshold ?? 1;\n if (Math.abs(v[0]) > t || Math.abs(v[1]) > t) {\n s.lockedAxis = Math.abs(v[0]) >= Math.abs(v[1]) ? 'x' : 'y';\n }\n }\n if (s.lockedAxis === 'x') {\n return [v[0], 0];\n }\n if (s.lockedAxis === 'y') {\n return [0, v[1]];\n }\n }\n return v;\n };\n\n const resetDrag = () => {\n const s = stateRef.current;\n s.isActive = false;\n s.pointerId = -1;\n s.thresholdMet = false;\n s.firstFired = false;\n s.lockedAxis = null;\n s.canceled = false;\n setActive(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n documentControllerRef.current?.abort();\n documentControllerRef.current = null;\n };\n\n const cancel = () => {\n if (stateRef.current.isActive) {\n stateRef.current.canceled = true;\n resetDrag();\n }\n };\n\n const activateDrag = () => {\n setActive(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n };\n\n const onPointerDown = (event: PointerEvent) => {\n if (optionsRef.current.enabled === false) {\n return;\n }\n if (event.button !== 0) {\n return;\n }\n if (stateRef.current.isActive) {\n return;\n }\n\n const s = stateRef.current;\n s.isActive = true;\n s.pointerId = event.pointerId;\n s.startXY = [event.clientX, event.clientY];\n s.prevXY = [event.clientX, event.clientY];\n s.startTimestamp = event.timeStamp;\n s.prevTimestamp = event.timeStamp;\n s.thresholdMet = false;\n s.firstFired = false;\n s.lockedAxis = null;\n s.canceled = false;\n s.lastVelocity = [0, 0];\n\n const [tx, ty] = getThresholdVector(optionsRef.current.threshold);\n if (tx === 0 && ty === 0) {\n s.thresholdMet = true;\n s.firstFired = true;\n activateDrag();\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [event.clientX, event.clientY],\n movement: [0, 0],\n delta: [0, 0],\n distance: [0, 0],\n direction: [0, 0],\n velocity: [0, 0],\n elapsedTime: 0,\n first: true,\n last: false,\n active: true,\n tap: false,\n canceled: false,\n cancel,\n event,\n });\n }\n\n documentControllerRef.current?.abort();\n documentControllerRef.current = new AbortController();\n const sig = documentControllerRef.current.signal;\n\n document.addEventListener('pointermove', onPointerMove, { signal: sig });\n document.addEventListener('pointerup', onPointerUp, { signal: sig });\n document.addEventListener('pointercancel', onPointerCancel, { signal: sig });\n };\n\n const onPointerMove = (event: PointerEvent) => {\n const s = stateRef.current;\n if (!s.isActive || event.pointerId !== s.pointerId) {\n return;\n }\n\n const rawMovement: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n\n if (!s.thresholdMet) {\n const [tx, ty] = getThresholdVector(optionsRef.current.threshold);\n if (Math.abs(rawMovement[0]) < tx && Math.abs(rawMovement[1]) < ty) {\n s.prevXY = [event.clientX, event.clientY];\n s.prevTimestamp = event.timeStamp;\n return;\n }\n s.thresholdMet = true;\n activateDrag();\n }\n\n const movement = applyAxisConstraint(rawMovement);\n const rawDelta: Vector2 = [event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]];\n const delta = applyAxisConstraint(rawDelta);\n const timeDelta = event.timeStamp - s.prevTimestamp;\n const velocity: Vector2 =\n timeDelta > 0\n ? [Math.abs(delta[0]) / timeDelta, Math.abs(delta[1]) / timeDelta]\n : s.lastVelocity;\n\n s.lastVelocity = velocity;\n const isFirst = !s.firstFired;\n s.firstFired = true;\n s.prevXY = [event.clientX, event.clientY];\n s.prevTimestamp = event.timeStamp;\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement,\n delta,\n distance: [Math.abs(movement[0]), Math.abs(movement[1])],\n direction: [sign(delta[0]), sign(delta[1])],\n velocity,\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: isFirst,\n last: false,\n active: true,\n tap: false,\n canceled: false,\n cancel,\n event,\n });\n };\n\n const onPointerUp = (event: PointerEvent) => {\n const s = stateRef.current;\n if (!s.isActive || event.pointerId !== s.pointerId) {\n return;\n }\n\n const opts = optionsRef.current;\n\n if (!s.thresholdMet) {\n if (opts.filterTaps) {\n const rawMov: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n const mov = applyAxisConstraint(rawMov);\n const dist: Vector2 = [Math.abs(mov[0]), Math.abs(mov[1])];\n const maxDist = Math.max(dist[0], dist[1]);\n const isTap = maxDist < (opts.tapThreshold ?? 3);\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement: mov,\n delta: mov,\n distance: dist,\n direction: [sign(mov[0]), sign(mov[1])],\n velocity: [0, 0],\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: true,\n last: true,\n active: false,\n tap: isTap,\n canceled: false,\n cancel,\n event,\n });\n }\n resetDrag();\n return;\n }\n\n const rawMovement: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n const movement = applyAxisConstraint(rawMovement);\n const distance: Vector2 = [Math.abs(movement[0]), Math.abs(movement[1])];\n const rawDelta: Vector2 = [event.clientX - s.prevXY[0], event.clientY - s.prevXY[1]];\n const delta = applyAxisConstraint(rawDelta);\n\n const timeSinceLastMove = event.timeStamp - s.prevTimestamp;\n const velocity: Vector2 = timeSinceLastMove > VELOCITY_DECAY_MS ? [0, 0] : s.lastVelocity;\n\n const maxDistance = Math.max(distance[0], distance[1]);\n const tap = opts.filterTaps === true && maxDistance < (opts.tapThreshold ?? 3);\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement,\n delta,\n distance,\n direction: [sign(delta[0]), sign(delta[1])],\n velocity,\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: !s.firstFired,\n last: true,\n active: false,\n tap,\n canceled: false,\n cancel,\n event,\n });\n\n resetDrag();\n };\n\n const onPointerCancel = (event: PointerEvent) => {\n const s = stateRef.current;\n if (!s.isActive || event.pointerId !== s.pointerId) {\n return;\n }\n\n const rawMovement: Vector2 = [event.clientX - s.startXY[0], event.clientY - s.startXY[1]];\n const movement = applyAxisConstraint(rawMovement);\n\n handlerRef.current({\n xy: [event.clientX, event.clientY],\n initial: [...s.startXY],\n movement,\n delta: [0, 0],\n distance: [Math.abs(movement[0]), Math.abs(movement[1])],\n direction: [0, 0],\n velocity: [0, 0],\n elapsedTime: event.timeStamp - s.startTimestamp,\n first: !s.firstFired,\n last: true,\n active: false,\n tap: false,\n canceled: true,\n cancel,\n event,\n });\n\n resetDrag();\n };\n\n node.addEventListener('pointerdown', onPointerDown, {\n signal: elementController.signal,\n });\n\n return () => {\n elementController.abort();\n documentControllerRef.current?.abort();\n documentControllerRef.current = null;\n if (stateRef.current.isActive) {\n stateRef.current.isActive = false;\n setActive(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n }\n };\n }, []);\n\n return { ref: refCallback, active };\n}\n\nexport namespace useDrag {\n export type State = UseDragState;\n export type Options = UseDragOptions;\n export type ReturnValue<T extends HTMLElement = any> = UseDragReturnValue<T>;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-event-listener.mjs","names":[],"sources":["../../src/use-event-listener/use-event-listener.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nexport function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement = any>(\n type: K,\n listener: (this: T, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions\n): React.RefCallback<T | null> {\n const previousListener = useRef<Function | null>(null);\n const previousNode = useRef<T | null>(null);\n\n const callbackRef: React.RefCallback<T | null> = useCallback(\n (node) => {\n if (!node) {\n return;\n }\n\n if (previousNode.current && previousListener.current) {\n previousNode.current.removeEventListener(type, previousListener.current as any, options);\n }\n\n node.addEventListener(type, listener as any, options);\n previousNode.current = node;\n previousListener.current = listener;\n },\n [type, listener, options]\n );\n\n useEffect(\n () => () => {\n if (previousNode.current && previousListener.current) {\n previousNode.current.removeEventListener(type, previousListener.current as any, options);\n }\n },\n [type, options]\n );\n\n return callbackRef;\n}\n"],"mappings":";;;AAEA,SAAgB,iBACd,MACA,UACA,SAC6B;CAC7B,MAAM,mBAAmB,OAAwB,KAAK;CACtD,MAAM,eAAe,OAAiB,KAAK;CAE3C,MAAM,cAA2C,aAC9C,SAAS;AACR,MAAI,CAAC,KACH;AAGF,MAAI,aAAa,WAAW,iBAAiB,QAC3C,cAAa,QAAQ,oBAAoB,MAAM,iBAAiB,SAAgB,QAAQ;AAG1F,OAAK,iBAAiB,MAAM,UAAiB,QAAQ;AACrD,eAAa,UAAU;AACvB,mBAAiB,UAAU;IAE7B;EAAC;EAAM;EAAU;EAAQ,CAC1B;AAED,uBACc;AACV,MAAI,aAAa,WAAW,iBAAiB,QAC3C,cAAa,QAAQ,oBAAoB,MAAM,iBAAiB,SAAgB,QAAQ;IAG5F,CAAC,MAAM,QAAQ,CAChB;AAED,QAAO"}
1
+ {"version":3,"file":"use-event-listener.mjs","names":[],"sources":["../../src/use-event-listener/use-event-listener.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nexport function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement = any>(\n type: K,\n listener: (this: T, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions\n): React.RefCallback<T | null> {\n const previousListener = useRef<Function | null>(null);\n const previousNode = useRef<T | null>(null);\n\n const callbackRef: React.RefCallback<T | null> = useCallback(\n (node) => {\n if (!node) {\n return;\n }\n\n if (previousNode.current && previousListener.current) {\n previousNode.current.removeEventListener(type, previousListener.current as any, options);\n }\n\n node.addEventListener(type, listener as any, options);\n previousNode.current = node;\n previousListener.current = listener;\n },\n [type, listener, options]\n );\n\n useEffect(\n () => () => {\n if (previousNode.current && previousListener.current) {\n previousNode.current.removeEventListener(type, previousListener.current as any, options);\n }\n },\n [type, options]\n );\n\n return callbackRef;\n}\n"],"mappings":";;;AAEA,SAAgB,iBACd,MACA,UACA,SAC6B;CAC7B,MAAM,mBAAmB,OAAwB,IAAI;CACrD,MAAM,eAAe,OAAiB,IAAI;CAE1C,MAAM,cAA2C,aAC9C,SAAS;EACR,IAAI,CAAC,MACH;EAGF,IAAI,aAAa,WAAW,iBAAiB,SAC3C,aAAa,QAAQ,oBAAoB,MAAM,iBAAiB,SAAgB,OAAO;EAGzF,KAAK,iBAAiB,MAAM,UAAiB,OAAO;EACpD,aAAa,UAAU;EACvB,iBAAiB,UAAU;CAC7B,GACA;EAAC;EAAM;EAAU;CAAO,CAC1B;CAEA,sBACc;EACV,IAAI,aAAa,WAAW,iBAAiB,SAC3C,aAAa,QAAQ,oBAAoB,MAAM,iBAAiB,SAAgB,OAAO;CAE3F,GACA,CAAC,MAAM,OAAO,CAChB;CAEA,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-eye-dropper.mjs","names":[],"sources":["../../src/use-eye-dropper/use-eye-dropper.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport interface EyeDropperOpenOptions {\n signal?: AbortSignal;\n}\n\nexport interface EyeDropperOpenReturnType {\n sRGBHex: string;\n}\n\nexport interface UseEyeDropperReturnValue {\n supported: boolean;\n open: (options?: EyeDropperOpenOptions) => Promise<EyeDropperOpenReturnType | undefined>;\n}\n\nexport function useEyeDropper(): UseEyeDropperReturnValue {\n const [supported, setSupported] = useState(false);\n\n useIsomorphicEffect(() => {\n setSupported(typeof window !== 'undefined' && !isOpera() && 'EyeDropper' in window);\n }, []);\n\n const open = useCallback(\n (options: EyeDropperOpenOptions = {}): Promise<EyeDropperOpenReturnType | undefined> => {\n if (supported) {\n const eyeDropper = new (window as any).EyeDropper();\n return eyeDropper.open(options);\n }\n\n return Promise.resolve(undefined);\n },\n [supported]\n );\n\n return { supported, open };\n}\n\nfunction isOpera() {\n return navigator.userAgent.includes('OPR');\n}\n"],"mappings":";;;;AAgBA,SAAgB,gBAA0C;CACxD,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;AAEjD,2BAA0B;AACxB,eAAa,OAAO,WAAW,eAAe,CAAC,SAAS,IAAI,gBAAgB,OAAO;IAClF,EAAE,CAAC;AAcN,QAAO;EAAE;EAAW,MAZP,aACV,UAAiC,EAAE,KAAoD;AACtF,OAAI,UAEF,QADmB,IAAK,OAAe,YAAY,CACjC,KAAK,QAAQ;AAGjC,UAAO,QAAQ,QAAQ,KAAA,EAAU;KAEnC,CAAC,UAAU,CACZ;EAEyB;;AAG5B,SAAS,UAAU;AACjB,QAAO,UAAU,UAAU,SAAS,MAAM"}
1
+ {"version":3,"file":"use-eye-dropper.mjs","names":[],"sources":["../../src/use-eye-dropper/use-eye-dropper.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport interface EyeDropperOpenOptions {\n signal?: AbortSignal;\n}\n\nexport interface EyeDropperOpenReturnType {\n sRGBHex: string;\n}\n\nexport interface UseEyeDropperReturnValue {\n supported: boolean;\n open: (options?: EyeDropperOpenOptions) => Promise<EyeDropperOpenReturnType | undefined>;\n}\n\nexport function useEyeDropper(): UseEyeDropperReturnValue {\n const [supported, setSupported] = useState(false);\n\n useIsomorphicEffect(() => {\n setSupported(typeof window !== 'undefined' && !isOpera() && 'EyeDropper' in window);\n }, []);\n\n const open = useCallback(\n (options: EyeDropperOpenOptions = {}): Promise<EyeDropperOpenReturnType | undefined> => {\n if (supported) {\n const eyeDropper = new (window as any).EyeDropper();\n return eyeDropper.open(options);\n }\n\n return Promise.resolve(undefined);\n },\n [supported]\n );\n\n return { supported, open };\n}\n\nfunction isOpera() {\n return navigator.userAgent.includes('OPR');\n}\n"],"mappings":";;;;AAgBA,SAAgB,gBAA0C;CACxD,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAEhD,0BAA0B;EACxB,aAAa,OAAO,WAAW,eAAe,CAAC,QAAQ,KAAK,gBAAgB,MAAM;CACpF,GAAG,CAAC,CAAC;CAcL,OAAO;EAAE;EAAW,MAZP,aACV,UAAiC,CAAC,MAAqD;GACtF,IAAI,WAEF,OAAO,IADiB,OAAe,WACvB,EAAE,KAAK,OAAO;GAGhC,OAAO,QAAQ,QAAQ,KAAA,CAAS;EAClC,GACA,CAAC,SAAS,CAGW;CAAE;AAC3B;AAEA,SAAS,UAAU;CACjB,OAAO,UAAU,UAAU,SAAS,KAAK;AAC3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-favicon.mjs","names":[],"sources":["../../src/use-favicon/use-favicon.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nconst MIME_TYPES: Record<string, string> = {\n ico: 'image/x-icon',\n png: 'image/png',\n svg: 'image/svg+xml',\n gif: 'image/gif',\n};\n\nexport function useFavicon(url: string) {\n const link = useRef<HTMLLinkElement>(null);\n\n useIsomorphicEffect(() => {\n if (!url) {\n return;\n }\n\n if (!link.current) {\n const existingElements = document.querySelectorAll<HTMLLinkElement>('link[rel*=\"icon\"]');\n existingElements.forEach((element) => document.head.removeChild(element));\n\n const element = document.createElement('link');\n element.rel = 'shortcut icon';\n link.current = element;\n document.querySelector('head')!.appendChild(element);\n }\n\n const splittedUrl = url.split('.');\n const extension = splittedUrl[splittedUrl.length - 1].toLowerCase();\n const mimeType = MIME_TYPES[extension];\n\n if (mimeType) {\n link.current.setAttribute('type', mimeType);\n } else {\n link.current.removeAttribute('type');\n }\n\n link.current.setAttribute('href', url);\n }, [url]);\n}\n"],"mappings":";;;;AAGA,MAAM,aAAqC;CACzC,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;AAED,SAAgB,WAAW,KAAa;CACtC,MAAM,OAAO,OAAwB,KAAK;AAE1C,2BAA0B;AACxB,MAAI,CAAC,IACH;AAGF,MAAI,CAAC,KAAK,SAAS;AACQ,YAAS,iBAAkC,sBAAoB,CACvE,SAAS,YAAY,SAAS,KAAK,YAAY,QAAQ,CAAC;GAEzE,MAAM,UAAU,SAAS,cAAc,OAAO;AAC9C,WAAQ,MAAM;AACd,QAAK,UAAU;AACf,YAAS,cAAc,OAAO,CAAE,YAAY,QAAQ;;EAGtD,MAAM,cAAc,IAAI,MAAM,IAAI;EAElC,MAAM,WAAW,WADC,YAAY,YAAY,SAAS,GAAG,aAAa;AAGnE,MAAI,SACF,MAAK,QAAQ,aAAa,QAAQ,SAAS;MAE3C,MAAK,QAAQ,gBAAgB,OAAO;AAGtC,OAAK,QAAQ,aAAa,QAAQ,IAAI;IACrC,CAAC,IAAI,CAAC"}
1
+ {"version":3,"file":"use-favicon.mjs","names":[],"sources":["../../src/use-favicon/use-favicon.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nconst MIME_TYPES: Record<string, string> = {\n ico: 'image/x-icon',\n png: 'image/png',\n svg: 'image/svg+xml',\n gif: 'image/gif',\n};\n\nexport function useFavicon(url: string) {\n const link = useRef<HTMLLinkElement>(null);\n\n useIsomorphicEffect(() => {\n if (!url) {\n return;\n }\n\n if (!link.current) {\n const existingElements = document.querySelectorAll<HTMLLinkElement>('link[rel*=\"icon\"]');\n existingElements.forEach((element) => document.head.removeChild(element));\n\n const element = document.createElement('link');\n element.rel = 'shortcut icon';\n link.current = element;\n document.querySelector('head')!.appendChild(element);\n }\n\n const splittedUrl = url.split('.');\n const extension = splittedUrl[splittedUrl.length - 1].toLowerCase();\n const mimeType = MIME_TYPES[extension];\n\n if (mimeType) {\n link.current.setAttribute('type', mimeType);\n } else {\n link.current.removeAttribute('type');\n }\n\n link.current.setAttribute('href', url);\n }, [url]);\n}\n"],"mappings":";;;;AAGA,MAAM,aAAqC;CACzC,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAEA,SAAgB,WAAW,KAAa;CACtC,MAAM,OAAO,OAAwB,IAAI;CAEzC,0BAA0B;EACxB,IAAI,CAAC,KACH;EAGF,IAAI,CAAC,KAAK,SAAS;GAEjB,SADkC,iBAAkC,qBACrD,EAAE,SAAS,YAAY,SAAS,KAAK,YAAY,OAAO,CAAC;GAExE,MAAM,UAAU,SAAS,cAAc,MAAM;GAC7C,QAAQ,MAAM;GACd,KAAK,UAAU;GACf,SAAS,cAAc,MAAM,EAAG,YAAY,OAAO;EACrD;EAEA,MAAM,cAAc,IAAI,MAAM,GAAG;EAEjC,MAAM,WAAW,WADC,YAAY,YAAY,SAAS,GAAG,YAClB;EAEpC,IAAI,UACF,KAAK,QAAQ,aAAa,QAAQ,QAAQ;OAE1C,KAAK,QAAQ,gBAAgB,MAAM;EAGrC,KAAK,QAAQ,aAAa,QAAQ,GAAG;CACvC,GAAG,CAAC,GAAG,CAAC;AACV"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-fetch.mjs","names":[],"sources":["../../src/use-fetch/use-fetch.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseFetchOptions extends RequestInit {\n autoInvoke?: boolean;\n}\n\nexport interface UseFetchReturnValue<T> {\n data: T | null;\n loading: boolean;\n error: Error | null;\n refetch: () => Promise<any>;\n abort: () => void;\n}\n\nexport function useFetch<T>(\n url: string,\n { autoInvoke = true, ...options }: UseFetchOptions = {}\n): UseFetchReturnValue<T> {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const controller = useRef<AbortController | null>(null);\n\n const refetch = useCallback(() => {\n if (controller.current) {\n controller.current.abort();\n }\n\n controller.current = new AbortController();\n\n setLoading(true);\n\n return fetch(url, { ...options, signal: controller.current.signal })\n .then((res) => {\n if (!res.ok) {\n throw new Error(`Request failed with status ${res.status}`);\n }\n return res.json();\n })\n .then((res) => {\n setData(res);\n setLoading(false);\n return res as T;\n })\n .catch((err) => {\n setLoading(false);\n\n if (err.name !== 'AbortError') {\n setError(err);\n }\n\n return err;\n });\n }, [url, JSON.stringify(options)]);\n\n const abort = useCallback(() => {\n if (controller.current) {\n controller.current?.abort('');\n }\n }, []);\n\n useEffect(() => {\n if (autoInvoke) {\n refetch();\n }\n\n return () => {\n if (controller.current) {\n controller.current.abort('');\n }\n };\n }, [refetch, autoInvoke]);\n\n return { data, loading, error, refetch, abort };\n}\n\nexport namespace useFetch {\n export type Options = UseFetchOptions;\n export type ReturnValue<T> = UseFetchReturnValue<T>;\n}\n"],"mappings":";;;AAcA,SAAgB,SACd,KACA,EAAE,aAAa,MAAM,GAAG,YAA6B,EAAE,EAC/B;CACxB,MAAM,CAAC,MAAM,WAAW,SAAmB,KAAK;CAChD,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CACtD,MAAM,aAAa,OAA+B,KAAK;CAEvD,MAAM,UAAU,kBAAkB;AAChC,MAAI,WAAW,QACb,YAAW,QAAQ,OAAO;AAG5B,aAAW,UAAU,IAAI,iBAAiB;AAE1C,aAAW,KAAK;AAEhB,SAAO,MAAM,KAAK;GAAE,GAAG;GAAS,QAAQ,WAAW,QAAQ;GAAQ,CAAC,CACjE,MAAM,QAAQ;AACb,OAAI,CAAC,IAAI,GACP,OAAM,IAAI,MAAM,8BAA8B,IAAI,SAAS;AAE7D,UAAO,IAAI,MAAM;IACjB,CACD,MAAM,QAAQ;AACb,WAAQ,IAAI;AACZ,cAAW,MAAM;AACjB,UAAO;IACP,CACD,OAAO,QAAQ;AACd,cAAW,MAAM;AAEjB,OAAI,IAAI,SAAS,aACf,UAAS,IAAI;AAGf,UAAO;IACP;IACH,CAAC,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC;CAElC,MAAM,QAAQ,kBAAkB;AAC9B,MAAI,WAAW,QACb,YAAW,SAAS,MAAM,GAAG;IAE9B,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,WACF,UAAS;AAGX,eAAa;AACX,OAAI,WAAW,QACb,YAAW,QAAQ,MAAM,GAAG;;IAG/B,CAAC,SAAS,WAAW,CAAC;AAEzB,QAAO;EAAE;EAAM;EAAS;EAAO;EAAS;EAAO"}
1
+ {"version":3,"file":"use-fetch.mjs","names":[],"sources":["../../src/use-fetch/use-fetch.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseFetchOptions extends RequestInit {\n autoInvoke?: boolean;\n}\n\nexport interface UseFetchReturnValue<T> {\n data: T | null;\n loading: boolean;\n error: Error | null;\n refetch: () => Promise<any>;\n abort: () => void;\n}\n\nexport function useFetch<T>(\n url: string,\n { autoInvoke = true, ...options }: UseFetchOptions = {}\n): UseFetchReturnValue<T> {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const controller = useRef<AbortController | null>(null);\n\n const refetch = useCallback(() => {\n if (controller.current) {\n controller.current.abort();\n }\n\n controller.current = new AbortController();\n\n setLoading(true);\n\n return fetch(url, { ...options, signal: controller.current.signal })\n .then((res) => {\n if (!res.ok) {\n throw new Error(`Request failed with status ${res.status}`);\n }\n return res.json();\n })\n .then((res) => {\n setData(res);\n setLoading(false);\n return res as T;\n })\n .catch((err) => {\n setLoading(false);\n\n if (err.name !== 'AbortError') {\n setError(err);\n }\n\n return err;\n });\n }, [url, JSON.stringify(options)]);\n\n const abort = useCallback(() => {\n if (controller.current) {\n controller.current?.abort('');\n }\n }, []);\n\n useEffect(() => {\n if (autoInvoke) {\n refetch();\n }\n\n return () => {\n if (controller.current) {\n controller.current.abort('');\n }\n };\n }, [refetch, autoInvoke]);\n\n return { data, loading, error, refetch, abort };\n}\n\nexport namespace useFetch {\n export type Options = UseFetchOptions;\n export type ReturnValue<T> = UseFetchReturnValue<T>;\n}\n"],"mappings":";;;AAcA,SAAgB,SACd,KACA,EAAE,aAAa,MAAM,GAAG,YAA6B,CAAC,GAC9B;CACxB,MAAM,CAAC,MAAM,WAAW,SAAmB,IAAI;CAC/C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAuB,IAAI;CACrD,MAAM,aAAa,OAA+B,IAAI;CAEtD,MAAM,UAAU,kBAAkB;EAChC,IAAI,WAAW,SACb,WAAW,QAAQ,MAAM;EAG3B,WAAW,UAAU,IAAI,gBAAgB;EAEzC,WAAW,IAAI;EAEf,OAAO,MAAM,KAAK;GAAE,GAAG;GAAS,QAAQ,WAAW,QAAQ;EAAO,CAAC,EAChE,MAAM,QAAQ;GACb,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,8BAA8B,IAAI,QAAQ;GAE5D,OAAO,IAAI,KAAK;EAClB,CAAC,EACA,MAAM,QAAQ;GACb,QAAQ,GAAG;GACX,WAAW,KAAK;GAChB,OAAO;EACT,CAAC,EACA,OAAO,QAAQ;GACd,WAAW,KAAK;GAEhB,IAAI,IAAI,SAAS,cACf,SAAS,GAAG;GAGd,OAAO;EACT,CAAC;CACL,GAAG,CAAC,KAAK,KAAK,UAAU,OAAO,CAAC,CAAC;CAEjC,MAAM,QAAQ,kBAAkB;EAC9B,IAAI,WAAW,SACb,WAAW,SAAS,MAAM,EAAE;CAEhC,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,IAAI,YACF,QAAQ;EAGV,aAAa;GACX,IAAI,WAAW,SACb,WAAW,QAAQ,MAAM,EAAE;EAE/B;CACF,GAAG,CAAC,SAAS,UAAU,CAAC;CAExB,OAAO;EAAE;EAAM;EAAS;EAAO;EAAS;CAAM;AAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-file-dialog.mjs","names":[],"sources":["../../src/use-file-dialog/use-file-dialog.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport interface UseFileDialogOptions {\n /** Determines whether multiple files are allowed, `true` by default */\n multiple?: boolean;\n\n /** `accept` attribute of the file input, '*' by default */\n accept?: string;\n\n /** `capture` attribute of the file input */\n capture?: string;\n\n /** Determines whether the user can pick a directory instead of file, `false` by default */\n directory?: boolean;\n\n /** Determines whether the file input state should be reset when the file dialog is opened, `false` by default */\n resetOnOpen?: boolean;\n\n /** Initial selected files */\n initialFiles?: FileList | File[];\n\n /** Called when files are selected */\n onChange?: (files: FileList | null) => void;\n\n /** Called when file dialog is canceled */\n onCancel?: () => void;\n}\n\nconst defaultOptions: UseFileDialogOptions = {\n multiple: true,\n accept: '*',\n};\n\nfunction getInitialFilesList(files: UseFileDialogOptions['initialFiles']): FileList | null {\n if (!files) {\n return null;\n }\n\n if (files instanceof FileList) {\n return files;\n }\n\n const result = new DataTransfer();\n for (const file of files) {\n result.items.add(file);\n }\n\n return result.files;\n}\n\nfunction createInput(options: UseFileDialogOptions) {\n if (typeof document === 'undefined') {\n return null;\n }\n\n const input = document.createElement('input');\n input.type = 'file';\n\n if (options.accept) {\n input.accept = options.accept;\n }\n\n if (options.multiple) {\n input.multiple = options.multiple;\n }\n\n if (options.capture) {\n input.capture = options.capture;\n }\n\n if (options.directory) {\n input.webkitdirectory = options.directory;\n }\n\n input.style.display = 'none';\n return input;\n}\n\nexport interface UseFileDialogReturnValue {\n files: FileList | null;\n open: () => void;\n reset: () => void;\n}\n\nexport function useFileDialog(input: UseFileDialogOptions = {}): UseFileDialogReturnValue {\n const options: UseFileDialogOptions = { ...defaultOptions, ...input };\n const [files, setFiles] = useState<FileList | null>(getInitialFilesList(options.initialFiles));\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const handleChange = useCallback(\n (event: Event) => {\n const target = event.target as HTMLInputElement;\n if (target?.files) {\n setFiles(target.files);\n options.onChange?.(target.files);\n }\n },\n [options.onChange]\n );\n\n const createAndSetupInput = useCallback(() => {\n inputRef.current?.remove();\n inputRef.current = createInput(options);\n\n if (inputRef.current) {\n inputRef.current.addEventListener('change', handleChange, { once: true });\n if (options.onCancel) {\n inputRef.current.addEventListener('cancel', options.onCancel, { once: true });\n }\n document.body.appendChild(inputRef.current);\n }\n }, [options, handleChange]);\n\n useIsomorphicEffect(() => {\n createAndSetupInput();\n return () => inputRef.current?.remove();\n }, []);\n\n const reset = useCallback(() => {\n setFiles(null);\n options.onChange?.(null);\n }, [options.onChange]);\n\n const open = useCallback(() => {\n if (options.resetOnOpen) {\n reset();\n }\n\n createAndSetupInput();\n inputRef.current?.click();\n }, [options.resetOnOpen, reset, createAndSetupInput]);\n\n return { files, open, reset };\n}\n\nexport namespace useFileDialog {\n export type Options = UseFileDialogOptions;\n export type ReturnValue = UseFileDialogReturnValue;\n}\n"],"mappings":";;;;AA6BA,MAAM,iBAAuC;CAC3C,UAAU;CACV,QAAQ;CACT;AAED,SAAS,oBAAoB,OAA8D;AACzF,KAAI,CAAC,MACH,QAAO;AAGT,KAAI,iBAAiB,SACnB,QAAO;CAGT,MAAM,SAAS,IAAI,cAAc;AACjC,MAAK,MAAM,QAAQ,MACjB,QAAO,MAAM,IAAI,KAAK;AAGxB,QAAO,OAAO;;AAGhB,SAAS,YAAY,SAA+B;AAClD,KAAI,OAAO,aAAa,YACtB,QAAO;CAGT,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,OAAO;AAEb,KAAI,QAAQ,OACV,OAAM,SAAS,QAAQ;AAGzB,KAAI,QAAQ,SACV,OAAM,WAAW,QAAQ;AAG3B,KAAI,QAAQ,QACV,OAAM,UAAU,QAAQ;AAG1B,KAAI,QAAQ,UACV,OAAM,kBAAkB,QAAQ;AAGlC,OAAM,MAAM,UAAU;AACtB,QAAO;;AAST,SAAgB,cAAc,QAA8B,EAAE,EAA4B;CACxF,MAAM,UAAgC;EAAE,GAAG;EAAgB,GAAG;EAAO;CACrE,MAAM,CAAC,OAAO,YAAY,SAA0B,oBAAoB,QAAQ,aAAa,CAAC;CAC9F,MAAM,WAAW,OAAgC,KAAK;CAEtD,MAAM,eAAe,aAClB,UAAiB;EAChB,MAAM,SAAS,MAAM;AACrB,MAAI,QAAQ,OAAO;AACjB,YAAS,OAAO,MAAM;AACtB,WAAQ,WAAW,OAAO,MAAM;;IAGpC,CAAC,QAAQ,SAAS,CACnB;CAED,MAAM,sBAAsB,kBAAkB;AAC5C,WAAS,SAAS,QAAQ;AAC1B,WAAS,UAAU,YAAY,QAAQ;AAEvC,MAAI,SAAS,SAAS;AACpB,YAAS,QAAQ,iBAAiB,UAAU,cAAc,EAAE,MAAM,MAAM,CAAC;AACzE,OAAI,QAAQ,SACV,UAAS,QAAQ,iBAAiB,UAAU,QAAQ,UAAU,EAAE,MAAM,MAAM,CAAC;AAE/E,YAAS,KAAK,YAAY,SAAS,QAAQ;;IAE5C,CAAC,SAAS,aAAa,CAAC;AAE3B,2BAA0B;AACxB,uBAAqB;AACrB,eAAa,SAAS,SAAS,QAAQ;IACtC,EAAE,CAAC;CAEN,MAAM,QAAQ,kBAAkB;AAC9B,WAAS,KAAK;AACd,UAAQ,WAAW,KAAK;IACvB,CAAC,QAAQ,SAAS,CAAC;AAWtB,QAAO;EAAE;EAAO,MATH,kBAAkB;AAC7B,OAAI,QAAQ,YACV,QAAO;AAGT,wBAAqB;AACrB,YAAS,SAAS,OAAO;KACxB;GAAC,QAAQ;GAAa;GAAO;GAAoB,CAAC;EAE/B;EAAO"}
1
+ {"version":3,"file":"use-file-dialog.mjs","names":[],"sources":["../../src/use-file-dialog/use-file-dialog.ts"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\n\nexport interface UseFileDialogOptions {\n /** Determines whether multiple files are allowed, `true` by default */\n multiple?: boolean;\n\n /** `accept` attribute of the file input, '*' by default */\n accept?: string;\n\n /** `capture` attribute of the file input */\n capture?: string;\n\n /** Determines whether the user can pick a directory instead of file, `false` by default */\n directory?: boolean;\n\n /** Determines whether the file input state should be reset when the file dialog is opened, `false` by default */\n resetOnOpen?: boolean;\n\n /** Initial selected files */\n initialFiles?: FileList | File[];\n\n /** Called when files are selected */\n onChange?: (files: FileList | null) => void;\n\n /** Called when file dialog is canceled */\n onCancel?: () => void;\n}\n\nconst defaultOptions: UseFileDialogOptions = {\n multiple: true,\n accept: '*',\n};\n\nfunction getInitialFilesList(files: UseFileDialogOptions['initialFiles']): FileList | null {\n if (!files) {\n return null;\n }\n\n if (files instanceof FileList) {\n return files;\n }\n\n const result = new DataTransfer();\n for (const file of files) {\n result.items.add(file);\n }\n\n return result.files;\n}\n\nfunction createInput(options: UseFileDialogOptions) {\n if (typeof document === 'undefined') {\n return null;\n }\n\n const input = document.createElement('input');\n input.type = 'file';\n\n if (options.accept) {\n input.accept = options.accept;\n }\n\n if (options.multiple) {\n input.multiple = options.multiple;\n }\n\n if (options.capture) {\n input.capture = options.capture;\n }\n\n if (options.directory) {\n input.webkitdirectory = options.directory;\n }\n\n input.style.display = 'none';\n return input;\n}\n\nexport interface UseFileDialogReturnValue {\n files: FileList | null;\n open: () => void;\n reset: () => void;\n}\n\nexport function useFileDialog(input: UseFileDialogOptions = {}): UseFileDialogReturnValue {\n const options: UseFileDialogOptions = { ...defaultOptions, ...input };\n const [files, setFiles] = useState<FileList | null>(getInitialFilesList(options.initialFiles));\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const handleChange = useCallback(\n (event: Event) => {\n const target = event.target as HTMLInputElement;\n if (target?.files) {\n setFiles(target.files);\n options.onChange?.(target.files);\n }\n },\n [options.onChange]\n );\n\n const createAndSetupInput = useCallback(() => {\n inputRef.current?.remove();\n inputRef.current = createInput(options);\n\n if (inputRef.current) {\n inputRef.current.addEventListener('change', handleChange, { once: true });\n if (options.onCancel) {\n inputRef.current.addEventListener('cancel', options.onCancel, { once: true });\n }\n document.body.appendChild(inputRef.current);\n }\n }, [options, handleChange]);\n\n useIsomorphicEffect(() => {\n createAndSetupInput();\n return () => inputRef.current?.remove();\n }, []);\n\n const reset = useCallback(() => {\n setFiles(null);\n options.onChange?.(null);\n }, [options.onChange]);\n\n const open = useCallback(() => {\n if (options.resetOnOpen) {\n reset();\n }\n\n createAndSetupInput();\n inputRef.current?.click();\n }, [options.resetOnOpen, reset, createAndSetupInput]);\n\n return { files, open, reset };\n}\n\nexport namespace useFileDialog {\n export type Options = UseFileDialogOptions;\n export type ReturnValue = UseFileDialogReturnValue;\n}\n"],"mappings":";;;;AA6BA,MAAM,iBAAuC;CAC3C,UAAU;CACV,QAAQ;AACV;AAEA,SAAS,oBAAoB,OAA8D;CACzF,IAAI,CAAC,OACH,OAAO;CAGT,IAAI,iBAAiB,UACnB,OAAO;CAGT,MAAM,SAAS,IAAI,aAAa;CAChC,KAAK,MAAM,QAAQ,OACjB,OAAO,MAAM,IAAI,IAAI;CAGvB,OAAO,OAAO;AAChB;AAEA,SAAS,YAAY,SAA+B;CAClD,IAAI,OAAO,aAAa,aACtB,OAAO;CAGT,MAAM,QAAQ,SAAS,cAAc,OAAO;CAC5C,MAAM,OAAO;CAEb,IAAI,QAAQ,QACV,MAAM,SAAS,QAAQ;CAGzB,IAAI,QAAQ,UACV,MAAM,WAAW,QAAQ;CAG3B,IAAI,QAAQ,SACV,MAAM,UAAU,QAAQ;CAG1B,IAAI,QAAQ,WACV,MAAM,kBAAkB,QAAQ;CAGlC,MAAM,MAAM,UAAU;CACtB,OAAO;AACT;AAQA,SAAgB,cAAc,QAA8B,CAAC,GAA6B;CACxF,MAAM,UAAgC;EAAE,GAAG;EAAgB,GAAG;CAAM;CACpE,MAAM,CAAC,OAAO,YAAY,SAA0B,oBAAoB,QAAQ,YAAY,CAAC;CAC7F,MAAM,WAAW,OAAgC,IAAI;CAErD,MAAM,eAAe,aAClB,UAAiB;EAChB,MAAM,SAAS,MAAM;EACrB,IAAI,QAAQ,OAAO;GACjB,SAAS,OAAO,KAAK;GACrB,QAAQ,WAAW,OAAO,KAAK;EACjC;CACF,GACA,CAAC,QAAQ,QAAQ,CACnB;CAEA,MAAM,sBAAsB,kBAAkB;EAC5C,SAAS,SAAS,OAAO;EACzB,SAAS,UAAU,YAAY,OAAO;EAEtC,IAAI,SAAS,SAAS;GACpB,SAAS,QAAQ,iBAAiB,UAAU,cAAc,EAAE,MAAM,KAAK,CAAC;GACxE,IAAI,QAAQ,UACV,SAAS,QAAQ,iBAAiB,UAAU,QAAQ,UAAU,EAAE,MAAM,KAAK,CAAC;GAE9E,SAAS,KAAK,YAAY,SAAS,OAAO;EAC5C;CACF,GAAG,CAAC,SAAS,YAAY,CAAC;CAE1B,0BAA0B;EACxB,oBAAoB;EACpB,aAAa,SAAS,SAAS,OAAO;CACxC,GAAG,CAAC,CAAC;CAEL,MAAM,QAAQ,kBAAkB;EAC9B,SAAS,IAAI;EACb,QAAQ,WAAW,IAAI;CACzB,GAAG,CAAC,QAAQ,QAAQ,CAAC;CAWrB,OAAO;EAAE;EAAO,MATH,kBAAkB;GAC7B,IAAI,QAAQ,aACV,MAAM;GAGR,oBAAoB;GACpB,SAAS,SAAS,MAAM;EAC1B,GAAG;GAAC,QAAQ;GAAa;GAAO;EAAmB,CAEhC;EAAG;CAAM;AAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-floating-window.mjs","names":[],"sources":["../../src/use-floating-window/use-floating-window.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\nimport { RefCallback, useCallback, useEffect, useRef, useState } from 'react';\n\nfunction useRefValue<T>(value: T) {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\ninterface FloatingWindowPositionConfig {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface FloatingWindowPosition {\n /** Element offset from the left side of the viewport */\n x: number;\n\n /** Element offset from the top side of the viewport */\n y: number;\n}\n\nexport interface UseFloatingWindowOptions {\n /** If `false`, the element can not be dragged. */\n enabled?: boolean;\n\n /** If `true`, the element can only move within the current viewport boundaries. */\n constrainToViewport?: boolean;\n\n /** The offset from the viewport edges when constraining the element. Requires `constrainToViewport: true`. */\n constrainOffset?: number;\n\n /** Selector of an element that should be used to drag floating window. If not specified, the entire root element is used as a drag target. */\n dragHandleSelector?: string;\n\n /** Selector of an element within `dragHandleSelector` that should be excluded from the drag event. */\n excludeDragHandleSelector?: string;\n\n /** If set, restricts movement to the specified axis */\n axis?: 'x' | 'y';\n\n /** Initial position. If not set, calculated from element styles. */\n initialPosition?: FloatingWindowPositionConfig;\n\n /** Called when the element position changes */\n onPositionChange?: (pos: FloatingWindowPosition) => void;\n\n /** Called when the drag starts */\n onDragStart?: () => void;\n\n /** Called when the drag stops */\n onDragEnd?: () => void;\n}\n\nexport type SetFloatingWindowPosition = (position: FloatingWindowPositionConfig) => void;\n\nexport interface UseFloatingWindowReturnValue<T extends HTMLElement> {\n /** Ref to the element that should be draggable */\n ref: RefCallback<T | null>;\n\n /** Function to set the position of the element */\n setPosition: SetFloatingWindowPosition;\n\n /** `true` if the element is currently being dragged */\n isDragging: boolean;\n}\n\nexport function useFloatingWindow<T extends HTMLElement>(\n options: UseFloatingWindowOptions = {}\n): UseFloatingWindowReturnValue<T> {\n const [element, setElement] = useState<T | null>(null);\n const ref = useRef<T>(null);\n const pos = useRef({ x: 0, y: 0 });\n const offset = useRef({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const isDraggingRef = useRef(false);\n const initialized = useRef(false);\n const enabledRef = useRefValue(options.enabled);\n\n const setDragging = useCallback((value: boolean) => {\n setIsDragging(value);\n isDraggingRef.current = value;\n }, []);\n\n const assignRef = useCallback((node: T | null) => {\n if (node) {\n ref.current = node;\n setElement(node);\n } else {\n ref.current = null;\n setElement(null);\n }\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!initialized.current && el) {\n initialized.current = true;\n pos.current = calculateInitialPosition(el, options);\n el.style.left = `${pos.current.x}px`;\n el.style.top = `${pos.current.y}px`;\n el.style.right = 'unset';\n el.style.bottom = 'unset';\n }\n\n return () => {\n initialized.current = false;\n };\n }, [\n element,\n options.constrainOffset,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n options.constrainToViewport,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n const onStart = (e: MouseEvent | TouchEvent) => {\n if (enabledRef.current === false) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n\n if ('button' in e && e.button !== 0) {\n return;\n }\n\n if (!getHandle(el, e.target, options)) {\n return;\n }\n\n setDragging(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n\n const rect = el.getBoundingClientRect();\n\n offset.current = {\n x: point.clientX - rect.left,\n y: point.clientY - rect.top,\n };\n\n options.onDragStart?.();\n\n document.addEventListener('mousemove', onMove, { signal });\n document.addEventListener('mouseup', onEnd, { signal });\n document.addEventListener('touchmove', onMove, { signal, passive: false });\n document.addEventListener('touchend', onEnd, { signal });\n };\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!isDraggingRef.current) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n e.preventDefault();\n\n let x = point.clientX - offset.current.x;\n let y = point.clientY - offset.current.y;\n\n const constrained = getConstrainedPosition(el, { x, y }, options);\n if (options.axis === 'x') {\n x = constrained.x;\n y = pos.current.y;\n } else if (options.axis === 'y') {\n x = pos.current.x;\n y = constrained.y;\n } else {\n x = constrained.x;\n y = constrained.y;\n }\n\n pos.current = { x, y };\n\n if (ref.current) {\n ref.current.style.left = `${x}px`;\n ref.current.style.top = `${y}px`;\n }\n\n options.onPositionChange?.({ x, y });\n };\n\n const onEnd = () => {\n if (isDraggingRef.current) {\n setDragging(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n options.onDragEnd?.();\n }\n };\n\n el.addEventListener('mousedown', onStart, { signal });\n el.addEventListener('touchstart', onStart, { signal, passive: false });\n\n return () => {\n controller.abort();\n };\n }, [\n options.constrainToViewport,\n options.constrainOffset,\n options.dragHandleSelector,\n options.axis,\n options.onPositionChange,\n options.onDragStart,\n options.onDragEnd,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n element,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n // Re-clamp current position if element size changes\n const constrained = getConstrainedPosition(el, pos.current, options);\n pos.current = constrained;\n el.style.left = `${constrained.x}px`;\n el.style.top = `${constrained.y}px`;\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [options.constrainToViewport, options.constrainOffset]);\n\n const setPosition = useCallback(\n (position: FloatingWindowPositionConfig) => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const offset = options.constrainOffset ?? 0;\n const rect = el.getBoundingClientRect();\n\n let x: number | undefined;\n let y: number | undefined;\n\n if (position.left != null) {\n x = position.left;\n } else if (position.right != null) {\n x = window.innerWidth - rect.width - position.right;\n }\n\n if (position.top != null) {\n y = position.top;\n } else if (position.bottom != null) {\n y = window.innerHeight - rect.height - position.bottom;\n }\n\n x = x ?? pos.current.x;\n y = y ?? pos.current.y;\n\n if (options.constrainToViewport) {\n const clamped = clampToViewport(x, y, el, offset);\n x = clamped.x;\n y = clamped.y;\n }\n\n pos.current = { x, y };\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n options.onPositionChange?.({ x, y });\n },\n [options.constrainToViewport, options.constrainOffset, options.onPositionChange]\n );\n\n return {\n ref: assignRef,\n setPosition,\n isDragging,\n };\n}\n\n// -------------------------------------------------------\n// Helper functions\n// -------------------------------------------------------\n\nfunction px(v: string) {\n return v.endsWith('px') ? parseFloat(v) : 0;\n}\n\nfunction calculateInitialPosition(\n el: HTMLElement,\n options: UseFloatingWindowOptions\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const winW = window.innerWidth;\n const winH = window.innerHeight;\n const style = window.getComputedStyle(el);\n const top = options.initialPosition?.top;\n const left = options.initialPosition?.left;\n const right = options.initialPosition?.right;\n const bottom = options.initialPosition?.bottom;\n\n let x = offset;\n let y = offset;\n\n if (left != null) {\n x = left;\n } else if (right != null) {\n x = winW - rect.width - right;\n } else {\n x = px(style.left) || winW - rect.width - px(style.right) || offset;\n }\n\n if (top != null) {\n y = top;\n } else if (bottom != null) {\n y = winH - rect.height - bottom;\n } else {\n y = px(style.top) || winH - rect.height - px(style.bottom) || offset;\n }\n\n return options.constrainToViewport\n ? clampToViewport(x, y, el, options.constrainOffset)\n : { x, y };\n}\n\nfunction getConstrainedPosition(\n el: HTMLElement,\n pos: FloatingWindowPosition,\n options: UseFloatingWindowOptions\n) {\n if (!options.constrainToViewport || !el) {\n return pos;\n }\n\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, pos.x), maxX),\n y: Math.min(Math.max(offset, pos.y), maxY),\n };\n}\n\nfunction matchesExcludeSelector(target: Node, excludeSelector?: string): boolean {\n if (!excludeSelector) {\n return false;\n }\n if (!(target instanceof Element)) {\n return false;\n }\n\n return Boolean(target.closest(excludeSelector));\n}\n\nfunction getHandle(\n el: HTMLElement,\n target: EventTarget | null,\n options: UseFloatingWindowOptions\n): boolean {\n if (!(target instanceof Node)) {\n return false;\n }\n\n // If no drag handle selector, allow dragging from entire element\n if (!options.dragHandleSelector) {\n return !matchesExcludeSelector(target, options.excludeDragHandleSelector);\n }\n\n const handles = Array.from(el.querySelectorAll(options.dragHandleSelector));\n return handles.some(\n (handle) =>\n handle.contains(target) && !matchesExcludeSelector(target, options.excludeDragHandleSelector)\n );\n}\n\nfunction clampToViewport(\n x: number,\n y: number,\n el: HTMLElement,\n offset: number = 0\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, x), maxX),\n y: Math.min(Math.max(offset, y), maxY),\n };\n}\n\nexport namespace useFloatingWindow {\n export type Options = UseFloatingWindowOptions;\n export type Position = FloatingWindowPosition;\n export type SetPosition = SetFloatingWindowPosition;\n export type ReturnValue<T extends HTMLElement> = UseFloatingWindowReturnValue<T>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-floating-window.mjs","names":[],"sources":["../../src/use-floating-window/use-floating-window.ts"],"sourcesContent":["// Required to disable for webkit-user-select, although deprecated, it is still required for Safari support\nimport { RefCallback, useCallback, useEffect, useRef, useState } from 'react';\n\nfunction useRefValue<T>(value: T) {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n}\n\ninterface FloatingWindowPositionConfig {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface FloatingWindowPosition {\n /** Element offset from the left side of the viewport */\n x: number;\n\n /** Element offset from the top side of the viewport */\n y: number;\n}\n\nexport interface UseFloatingWindowOptions {\n /** If `false`, the element can not be dragged. */\n enabled?: boolean;\n\n /** If `true`, the element can only move within the current viewport boundaries. */\n constrainToViewport?: boolean;\n\n /** The offset from the viewport edges when constraining the element. Requires `constrainToViewport: true`. */\n constrainOffset?: number;\n\n /** Selector of an element that should be used to drag floating window. If not specified, the entire root element is used as a drag target. */\n dragHandleSelector?: string;\n\n /** Selector of an element within `dragHandleSelector` that should be excluded from the drag event. */\n excludeDragHandleSelector?: string;\n\n /** If set, restricts movement to the specified axis */\n axis?: 'x' | 'y';\n\n /** Initial position. If not set, calculated from element styles. */\n initialPosition?: FloatingWindowPositionConfig;\n\n /** Called when the element position changes */\n onPositionChange?: (pos: FloatingWindowPosition) => void;\n\n /** Called when the drag starts */\n onDragStart?: () => void;\n\n /** Called when the drag stops */\n onDragEnd?: () => void;\n}\n\nexport type SetFloatingWindowPosition = (position: FloatingWindowPositionConfig) => void;\n\nexport interface UseFloatingWindowReturnValue<T extends HTMLElement> {\n /** Ref to the element that should be draggable */\n ref: RefCallback<T | null>;\n\n /** Function to set the position of the element */\n setPosition: SetFloatingWindowPosition;\n\n /** `true` if the element is currently being dragged */\n isDragging: boolean;\n}\n\nexport function useFloatingWindow<T extends HTMLElement>(\n options: UseFloatingWindowOptions = {}\n): UseFloatingWindowReturnValue<T> {\n const [element, setElement] = useState<T | null>(null);\n const ref = useRef<T>(null);\n const pos = useRef({ x: 0, y: 0 });\n const offset = useRef({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const isDraggingRef = useRef(false);\n const initialized = useRef(false);\n const enabledRef = useRefValue(options.enabled);\n\n const setDragging = useCallback((value: boolean) => {\n setIsDragging(value);\n isDraggingRef.current = value;\n }, []);\n\n const assignRef = useCallback((node: T | null) => {\n if (node) {\n ref.current = node;\n setElement(node);\n } else {\n ref.current = null;\n setElement(null);\n }\n }, []);\n\n useEffect(() => {\n const el = ref.current;\n if (!initialized.current && el) {\n initialized.current = true;\n pos.current = calculateInitialPosition(el, options);\n el.style.left = `${pos.current.x}px`;\n el.style.top = `${pos.current.y}px`;\n el.style.right = 'unset';\n el.style.bottom = 'unset';\n }\n\n return () => {\n initialized.current = false;\n };\n }, [\n element,\n options.constrainOffset,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n options.constrainToViewport,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n const onStart = (e: MouseEvent | TouchEvent) => {\n if (enabledRef.current === false) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n\n if ('button' in e && e.button !== 0) {\n return;\n }\n\n if (!getHandle(el, e.target, options)) {\n return;\n }\n\n setDragging(true);\n document.body.style.userSelect = 'none';\n document.body.style.webkitUserSelect = 'none';\n\n const rect = el.getBoundingClientRect();\n\n offset.current = {\n x: point.clientX - rect.left,\n y: point.clientY - rect.top,\n };\n\n options.onDragStart?.();\n\n document.addEventListener('mousemove', onMove, { signal });\n document.addEventListener('mouseup', onEnd, { signal });\n document.addEventListener('touchmove', onMove, { signal, passive: false });\n document.addEventListener('touchend', onEnd, { signal });\n };\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!isDraggingRef.current) {\n return;\n }\n\n const point = 'touches' in e ? e.touches[0] : e;\n e.preventDefault();\n\n let x = point.clientX - offset.current.x;\n let y = point.clientY - offset.current.y;\n\n const constrained = getConstrainedPosition(el, { x, y }, options);\n if (options.axis === 'x') {\n x = constrained.x;\n y = pos.current.y;\n } else if (options.axis === 'y') {\n x = pos.current.x;\n y = constrained.y;\n } else {\n x = constrained.x;\n y = constrained.y;\n }\n\n pos.current = { x, y };\n\n if (ref.current) {\n ref.current.style.left = `${x}px`;\n ref.current.style.top = `${y}px`;\n }\n\n options.onPositionChange?.({ x, y });\n };\n\n const onEnd = () => {\n if (isDraggingRef.current) {\n setDragging(false);\n document.body.style.userSelect = '';\n document.body.style.webkitUserSelect = '';\n options.onDragEnd?.();\n }\n };\n\n el.addEventListener('mousedown', onStart, { signal });\n el.addEventListener('touchstart', onStart, { signal, passive: false });\n\n return () => {\n controller.abort();\n };\n }, [\n options.constrainToViewport,\n options.constrainOffset,\n options.dragHandleSelector,\n options.axis,\n options.onPositionChange,\n options.onDragStart,\n options.onDragEnd,\n options.initialPosition?.top,\n options.initialPosition?.left,\n options.initialPosition?.right,\n options.initialPosition?.bottom,\n element,\n ]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n // Re-clamp current position if element size changes\n const constrained = getConstrainedPosition(el, pos.current, options);\n pos.current = constrained;\n el.style.left = `${constrained.x}px`;\n el.style.top = `${constrained.y}px`;\n });\n\n observer.observe(el);\n\n return () => {\n observer.disconnect();\n };\n }, [options.constrainToViewport, options.constrainOffset]);\n\n const setPosition = useCallback(\n (position: FloatingWindowPositionConfig) => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n const offset = options.constrainOffset ?? 0;\n const rect = el.getBoundingClientRect();\n\n let x: number | undefined;\n let y: number | undefined;\n\n if (position.left != null) {\n x = position.left;\n } else if (position.right != null) {\n x = window.innerWidth - rect.width - position.right;\n }\n\n if (position.top != null) {\n y = position.top;\n } else if (position.bottom != null) {\n y = window.innerHeight - rect.height - position.bottom;\n }\n\n x = x ?? pos.current.x;\n y = y ?? pos.current.y;\n\n if (options.constrainToViewport) {\n const clamped = clampToViewport(x, y, el, offset);\n x = clamped.x;\n y = clamped.y;\n }\n\n pos.current = { x, y };\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n options.onPositionChange?.({ x, y });\n },\n [options.constrainToViewport, options.constrainOffset, options.onPositionChange]\n );\n\n return {\n ref: assignRef,\n setPosition,\n isDragging,\n };\n}\n\n// -------------------------------------------------------\n// Helper functions\n// -------------------------------------------------------\n\nfunction px(v: string) {\n return v.endsWith('px') ? parseFloat(v) : 0;\n}\n\nfunction calculateInitialPosition(\n el: HTMLElement,\n options: UseFloatingWindowOptions\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const winW = window.innerWidth;\n const winH = window.innerHeight;\n const style = window.getComputedStyle(el);\n const top = options.initialPosition?.top;\n const left = options.initialPosition?.left;\n const right = options.initialPosition?.right;\n const bottom = options.initialPosition?.bottom;\n\n let x = offset;\n let y = offset;\n\n if (left != null) {\n x = left;\n } else if (right != null) {\n x = winW - rect.width - right;\n } else {\n x = px(style.left) || winW - rect.width - px(style.right) || offset;\n }\n\n if (top != null) {\n y = top;\n } else if (bottom != null) {\n y = winH - rect.height - bottom;\n } else {\n y = px(style.top) || winH - rect.height - px(style.bottom) || offset;\n }\n\n return options.constrainToViewport\n ? clampToViewport(x, y, el, options.constrainOffset)\n : { x, y };\n}\n\nfunction getConstrainedPosition(\n el: HTMLElement,\n pos: FloatingWindowPosition,\n options: UseFloatingWindowOptions\n) {\n if (!options.constrainToViewport || !el) {\n return pos;\n }\n\n const rect = el.getBoundingClientRect();\n const offset = options.constrainOffset ?? 0;\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, pos.x), maxX),\n y: Math.min(Math.max(offset, pos.y), maxY),\n };\n}\n\nfunction matchesExcludeSelector(target: Node, excludeSelector?: string): boolean {\n if (!excludeSelector) {\n return false;\n }\n if (!(target instanceof Element)) {\n return false;\n }\n\n return Boolean(target.closest(excludeSelector));\n}\n\nfunction getHandle(\n el: HTMLElement,\n target: EventTarget | null,\n options: UseFloatingWindowOptions\n): boolean {\n if (!(target instanceof Node)) {\n return false;\n }\n\n // If no drag handle selector, allow dragging from entire element\n if (!options.dragHandleSelector) {\n return !matchesExcludeSelector(target, options.excludeDragHandleSelector);\n }\n\n const handles = Array.from(el.querySelectorAll(options.dragHandleSelector));\n return handles.some(\n (handle) =>\n handle.contains(target) && !matchesExcludeSelector(target, options.excludeDragHandleSelector)\n );\n}\n\nfunction clampToViewport(\n x: number,\n y: number,\n el: HTMLElement,\n offset: number = 0\n): { x: number; y: number } {\n const rect = el.getBoundingClientRect();\n const maxX = window.innerWidth - rect.width - offset;\n const maxY = window.innerHeight - rect.height - offset;\n\n return {\n x: Math.min(Math.max(offset, x), maxX),\n y: Math.min(Math.max(offset, y), maxY),\n };\n}\n\nexport namespace useFloatingWindow {\n export type Options = UseFloatingWindowOptions;\n export type Position = FloatingWindowPosition;\n export type SetPosition = SetFloatingWindowPosition;\n export type ReturnValue<T extends HTMLElement> = UseFloatingWindowReturnValue<T>;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-focus-return.mjs","names":[],"sources":["../../src/use-focus-return/use-focus-return.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\n\nexport interface UseFocusReturnInput {\n opened: boolean;\n shouldReturnFocus?: boolean;\n}\n\nexport type UseFocusReturnReturnValue = () => void;\n\nexport function useFocusReturn({\n opened,\n shouldReturnFocus = true,\n}: UseFocusReturnInput): UseFocusReturnReturnValue {\n const lastActiveElement = useRef<HTMLElement>(null);\n const returnFocus = () => {\n if (\n lastActiveElement.current &&\n 'focus' in lastActiveElement.current &&\n typeof lastActiveElement.current.focus === 'function'\n ) {\n lastActiveElement.current?.focus({ preventScroll: true });\n }\n };\n\n useDidUpdate(() => {\n let timeout = -1;\n\n const clearFocusTimeout = (event: KeyboardEvent) => {\n if (event.key === 'Tab') {\n window.clearTimeout(timeout);\n }\n };\n\n document.addEventListener('keydown', clearFocusTimeout);\n\n if (opened) {\n lastActiveElement.current = document.activeElement as HTMLElement;\n } else if (shouldReturnFocus) {\n const activeElementAtClose = document.activeElement;\n timeout = window.setTimeout(() => {\n const currentActiveElement = document.activeElement;\n if (\n currentActiveElement === null ||\n currentActiveElement === document.body ||\n currentActiveElement === activeElementAtClose\n ) {\n returnFocus();\n }\n }, 10);\n }\n\n return () => {\n window.clearTimeout(timeout);\n document.removeEventListener('keydown', clearFocusTimeout);\n };\n }, [opened, shouldReturnFocus]);\n\n return returnFocus;\n}\n\nexport namespace useFocusReturn {\n export type Input = UseFocusReturnInput;\n export type ReturnValue = UseFocusReturnReturnValue;\n}\n"],"mappings":";;;;AAUA,SAAgB,eAAe,EAC7B,QACA,oBAAoB,QAC6B;CACjD,MAAM,oBAAoB,OAAoB,KAAK;CACnD,MAAM,oBAAoB;AACxB,MACE,kBAAkB,WAClB,WAAW,kBAAkB,WAC7B,OAAO,kBAAkB,QAAQ,UAAU,WAE3C,mBAAkB,SAAS,MAAM,EAAE,eAAe,MAAM,CAAC;;AAI7D,oBAAmB;EACjB,IAAI,UAAU;EAEd,MAAM,qBAAqB,UAAyB;AAClD,OAAI,MAAM,QAAQ,MAChB,QAAO,aAAa,QAAQ;;AAIhC,WAAS,iBAAiB,WAAW,kBAAkB;AAEvD,MAAI,OACF,mBAAkB,UAAU,SAAS;WAC5B,mBAAmB;GAC5B,MAAM,uBAAuB,SAAS;AACtC,aAAU,OAAO,iBAAiB;IAChC,MAAM,uBAAuB,SAAS;AACtC,QACE,yBAAyB,QACzB,yBAAyB,SAAS,QAClC,yBAAyB,qBAEzB,cAAa;MAEd,GAAG;;AAGR,eAAa;AACX,UAAO,aAAa,QAAQ;AAC5B,YAAS,oBAAoB,WAAW,kBAAkB;;IAE3D,CAAC,QAAQ,kBAAkB,CAAC;AAE/B,QAAO"}
1
+ {"version":3,"file":"use-focus-return.mjs","names":[],"sources":["../../src/use-focus-return/use-focus-return.ts"],"sourcesContent":["import { useRef } from 'react';\nimport { useDidUpdate } from '../use-did-update/use-did-update';\n\nexport interface UseFocusReturnInput {\n opened: boolean;\n shouldReturnFocus?: boolean;\n}\n\nexport type UseFocusReturnReturnValue = () => void;\n\nexport function useFocusReturn({\n opened,\n shouldReturnFocus = true,\n}: UseFocusReturnInput): UseFocusReturnReturnValue {\n const lastActiveElement = useRef<HTMLElement>(null);\n const returnFocus = () => {\n if (\n lastActiveElement.current &&\n 'focus' in lastActiveElement.current &&\n typeof lastActiveElement.current.focus === 'function'\n ) {\n lastActiveElement.current?.focus({ preventScroll: true });\n }\n };\n\n useDidUpdate(() => {\n let timeout = -1;\n\n const clearFocusTimeout = (event: KeyboardEvent) => {\n if (event.key === 'Tab') {\n window.clearTimeout(timeout);\n }\n };\n\n document.addEventListener('keydown', clearFocusTimeout);\n\n if (opened) {\n lastActiveElement.current = document.activeElement as HTMLElement;\n } else if (shouldReturnFocus) {\n const activeElementAtClose = document.activeElement;\n timeout = window.setTimeout(() => {\n const currentActiveElement = document.activeElement;\n if (\n currentActiveElement === null ||\n currentActiveElement === document.body ||\n currentActiveElement === activeElementAtClose\n ) {\n returnFocus();\n }\n }, 10);\n }\n\n return () => {\n window.clearTimeout(timeout);\n document.removeEventListener('keydown', clearFocusTimeout);\n };\n }, [opened, shouldReturnFocus]);\n\n return returnFocus;\n}\n\nexport namespace useFocusReturn {\n export type Input = UseFocusReturnInput;\n export type ReturnValue = UseFocusReturnReturnValue;\n}\n"],"mappings":";;;;AAUA,SAAgB,eAAe,EAC7B,QACA,oBAAoB,QAC6B;CACjD,MAAM,oBAAoB,OAAoB,IAAI;CAClD,MAAM,oBAAoB;EACxB,IACE,kBAAkB,WAClB,WAAW,kBAAkB,WAC7B,OAAO,kBAAkB,QAAQ,UAAU,YAE3C,kBAAkB,SAAS,MAAM,EAAE,eAAe,KAAK,CAAC;CAE5D;CAEA,mBAAmB;EACjB,IAAI,UAAU;EAEd,MAAM,qBAAqB,UAAyB;GAClD,IAAI,MAAM,QAAQ,OAChB,OAAO,aAAa,OAAO;EAE/B;EAEA,SAAS,iBAAiB,WAAW,iBAAiB;EAEtD,IAAI,QACF,kBAAkB,UAAU,SAAS;OAChC,IAAI,mBAAmB;GAC5B,MAAM,uBAAuB,SAAS;GACtC,UAAU,OAAO,iBAAiB;IAChC,MAAM,uBAAuB,SAAS;IACtC,IACE,yBAAyB,QACzB,yBAAyB,SAAS,QAClC,yBAAyB,sBAEzB,YAAY;GAEhB,GAAG,EAAE;EACP;EAEA,aAAa;GACX,OAAO,aAAa,OAAO;GAC3B,SAAS,oBAAoB,WAAW,iBAAiB;EAC3D;CACF,GAAG,CAAC,QAAQ,iBAAiB,CAAC;CAE9B,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"scope-tab.mjs","names":[],"sources":["../../src/use-focus-trap/scope-tab.ts"],"sourcesContent":["import { findTabbableDescendants } from './tabbable';\n\nexport function scopeTab(node: HTMLElement, event: KeyboardEvent) {\n const tabbable = findTabbableDescendants(node);\n if (!tabbable.length) {\n event.preventDefault();\n return;\n }\n const finalTabbable = tabbable[event.shiftKey ? 0 : tabbable.length - 1];\n const root = node.getRootNode() as unknown as DocumentOrShadowRoot;\n let leavingFinalTabbable = finalTabbable === root.activeElement || node === root.activeElement;\n\n const activeElement = root.activeElement as Element;\n const activeElementIsRadio =\n activeElement.tagName === 'INPUT' && activeElement.getAttribute('type') === 'radio';\n if (activeElementIsRadio) {\n const activeRadioGroup = tabbable.filter(\n (element) =>\n element.getAttribute('type') === 'radio' &&\n element.getAttribute('name') === activeElement.getAttribute('name')\n );\n leavingFinalTabbable = activeRadioGroup.includes(finalTabbable);\n }\n\n if (!leavingFinalTabbable) {\n return;\n }\n\n event.preventDefault();\n\n const target = tabbable[event.shiftKey ? tabbable.length - 1 : 0];\n\n if (target) {\n target.focus();\n }\n}\n"],"mappings":";;;AAEA,SAAgB,SAAS,MAAmB,OAAsB;CAChE,MAAM,WAAW,wBAAwB,KAAK;AAC9C,KAAI,CAAC,SAAS,QAAQ;AACpB,QAAM,gBAAgB;AACtB;;CAEF,MAAM,gBAAgB,SAAS,MAAM,WAAW,IAAI,SAAS,SAAS;CACtE,MAAM,OAAO,KAAK,aAAa;CAC/B,IAAI,uBAAuB,kBAAkB,KAAK,iBAAiB,SAAS,KAAK;CAEjF,MAAM,gBAAgB,KAAK;AAG3B,KADE,cAAc,YAAY,WAAW,cAAc,aAAa,OAAO,KAAK,QAO5E,wBALyB,SAAS,QAC/B,YACC,QAAQ,aAAa,OAAO,KAAK,WACjC,QAAQ,aAAa,OAAO,KAAK,cAAc,aAAa,OAAO,CACtE,CACuC,SAAS,cAAc;AAGjE,KAAI,CAAC,qBACH;AAGF,OAAM,gBAAgB;CAEtB,MAAM,SAAS,SAAS,MAAM,WAAW,SAAS,SAAS,IAAI;AAE/D,KAAI,OACF,QAAO,OAAO"}
1
+ {"version":3,"file":"scope-tab.mjs","names":[],"sources":["../../src/use-focus-trap/scope-tab.ts"],"sourcesContent":["import { findTabbableDescendants } from './tabbable';\n\nexport function scopeTab(node: HTMLElement, event: KeyboardEvent) {\n const tabbable = findTabbableDescendants(node);\n if (!tabbable.length) {\n event.preventDefault();\n return;\n }\n const finalTabbable = tabbable[event.shiftKey ? 0 : tabbable.length - 1];\n const root = node.getRootNode() as unknown as DocumentOrShadowRoot;\n let leavingFinalTabbable = finalTabbable === root.activeElement || node === root.activeElement;\n\n const activeElement = root.activeElement as Element;\n const activeElementIsRadio =\n activeElement.tagName === 'INPUT' && activeElement.getAttribute('type') === 'radio';\n if (activeElementIsRadio) {\n const activeRadioGroup = tabbable.filter(\n (element) =>\n element.getAttribute('type') === 'radio' &&\n element.getAttribute('name') === activeElement.getAttribute('name')\n );\n leavingFinalTabbable = activeRadioGroup.includes(finalTabbable);\n }\n\n if (!leavingFinalTabbable) {\n return;\n }\n\n event.preventDefault();\n\n const target = tabbable[event.shiftKey ? tabbable.length - 1 : 0];\n\n if (target) {\n target.focus();\n }\n}\n"],"mappings":";;;AAEA,SAAgB,SAAS,MAAmB,OAAsB;CAChE,MAAM,WAAW,wBAAwB,IAAI;CAC7C,IAAI,CAAC,SAAS,QAAQ;EACpB,MAAM,eAAe;EACrB;CACF;CACA,MAAM,gBAAgB,SAAS,MAAM,WAAW,IAAI,SAAS,SAAS;CACtE,MAAM,OAAO,KAAK,YAAY;CAC9B,IAAI,uBAAuB,kBAAkB,KAAK,iBAAiB,SAAS,KAAK;CAEjF,MAAM,gBAAgB,KAAK;CAG3B,IADE,cAAc,YAAY,WAAW,cAAc,aAAa,MAAM,MAAM,SAO5E,uBALyB,SAAS,QAC/B,YACC,QAAQ,aAAa,MAAM,MAAM,WACjC,QAAQ,aAAa,MAAM,MAAM,cAAc,aAAa,MAAM,CAEhC,EAAE,SAAS,aAAa;CAGhE,IAAI,CAAC,sBACH;CAGF,MAAM,eAAe;CAErB,MAAM,SAAS,SAAS,MAAM,WAAW,SAAS,SAAS,IAAI;CAE/D,IAAI,QACF,OAAO,MAAM;AAEjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"tabbable.mjs","names":[],"sources":["../../src/use-focus-trap/tabbable.ts"],"sourcesContent":["const TABBABLE_NODES = /input|select|textarea|button|object/;\nexport const FOCUS_SELECTOR = 'a, input, select, textarea, button, object, [tabindex]';\n\nfunction hidden(element: HTMLElement) {\n if (process.env.NODE_ENV === 'test') {\n return false;\n }\n\n return element.style.display === 'none';\n}\n\nfunction visible(element: HTMLElement) {\n const isHidden =\n element.getAttribute('aria-hidden') ||\n element.getAttribute('hidden') ||\n element.getAttribute('type') === 'hidden';\n\n if (isHidden) {\n return false;\n }\n\n let parentElement: HTMLElement = element;\n while (parentElement) {\n if (parentElement === document.body || parentElement.nodeType === 11) {\n break;\n }\n\n if (hidden(parentElement)) {\n return false;\n }\n\n parentElement = parentElement.parentNode as HTMLElement;\n }\n\n return true;\n}\n\nfunction getElementTabIndex(element: HTMLElement) {\n let tabIndex: string | null | undefined = element.getAttribute('tabindex');\n if (tabIndex === null) {\n tabIndex = undefined;\n }\n return parseInt(tabIndex as string, 10);\n}\n\nexport function focusable(element: HTMLElement) {\n const nodeName = element.nodeName.toLowerCase();\n const isTabIndexNotNaN = !Number.isNaN(getElementTabIndex(element));\n const res =\n // @ts-expect-error function accepts any html element but if it is a button, it should not be disabled to trigger the condition\n (TABBABLE_NODES.test(nodeName) && !element.disabled) ||\n (element instanceof HTMLAnchorElement ? element.href || isTabIndexNotNaN : isTabIndexNotNaN);\n\n return res && visible(element);\n}\n\nexport function tabbable(element: HTMLElement) {\n const tabIndex = getElementTabIndex(element);\n const isTabIndexNaN = Number.isNaN(tabIndex);\n return (isTabIndexNaN || tabIndex >= 0) && focusable(element);\n}\n\nexport function findTabbableDescendants(element: HTMLElement): HTMLElement[] {\n return Array.from(element.querySelectorAll<HTMLElement>(FOCUS_SELECTOR)).filter(tabbable);\n}\n"],"mappings":";;AAAA,MAAM,iBAAiB;AACvB,MAAa,iBAAiB;AAE9B,SAAS,OAAO,SAAsB;AAKpC,QAAO,QAAQ,MAAM,YAAY;;AAGnC,SAAS,QAAQ,SAAsB;AAMrC,KAJE,QAAQ,aAAa,cAAc,IACnC,QAAQ,aAAa,SAAS,IAC9B,QAAQ,aAAa,OAAO,KAAK,SAGjC,QAAO;CAGT,IAAI,gBAA6B;AACjC,QAAO,eAAe;AACpB,MAAI,kBAAkB,SAAS,QAAQ,cAAc,aAAa,GAChE;AAGF,MAAI,OAAO,cAAc,CACvB,QAAO;AAGT,kBAAgB,cAAc;;AAGhC,QAAO;;AAGT,SAAS,mBAAmB,SAAsB;CAChD,IAAI,WAAsC,QAAQ,aAAa,WAAW;AAC1E,KAAI,aAAa,KACf,YAAW,KAAA;AAEb,QAAO,SAAS,UAAoB,GAAG;;AAGzC,SAAgB,UAAU,SAAsB;CAC9C,MAAM,WAAW,QAAQ,SAAS,aAAa;CAC/C,MAAM,mBAAmB,CAAC,OAAO,MAAM,mBAAmB,QAAQ,CAAC;AAMnE,SAHG,eAAe,KAAK,SAAS,IAAI,CAAC,QAAQ,aAC1C,mBAAmB,oBAAoB,QAAQ,QAAQ,mBAAmB,sBAE/D,QAAQ,QAAQ;;AAGhC,SAAgB,SAAS,SAAsB;CAC7C,MAAM,WAAW,mBAAmB,QAAQ;AAE5C,SADsB,OAAO,MAAM,SAAS,IACnB,YAAY,MAAM,UAAU,QAAQ;;AAG/D,SAAgB,wBAAwB,SAAqC;AAC3E,QAAO,MAAM,KAAK,QAAQ,iBAA8B,eAAe,CAAC,CAAC,OAAO,SAAS"}
1
+ {"version":3,"file":"tabbable.mjs","names":[],"sources":["../../src/use-focus-trap/tabbable.ts"],"sourcesContent":["const TABBABLE_NODES = /input|select|textarea|button|object/;\nexport const FOCUS_SELECTOR = 'a, input, select, textarea, button, object, [tabindex]';\n\nfunction hidden(element: HTMLElement) {\n if (process.env.NODE_ENV === 'test') {\n return false;\n }\n\n return element.style.display === 'none';\n}\n\nfunction visible(element: HTMLElement) {\n const isHidden =\n element.getAttribute('aria-hidden') ||\n element.getAttribute('hidden') ||\n element.getAttribute('type') === 'hidden';\n\n if (isHidden) {\n return false;\n }\n\n let parentElement: HTMLElement = element;\n while (parentElement) {\n if (parentElement === document.body || parentElement.nodeType === 11) {\n break;\n }\n\n if (hidden(parentElement)) {\n return false;\n }\n\n parentElement = parentElement.parentNode as HTMLElement;\n }\n\n return true;\n}\n\nfunction getElementTabIndex(element: HTMLElement) {\n let tabIndex: string | null | undefined = element.getAttribute('tabindex');\n if (tabIndex === null) {\n tabIndex = undefined;\n }\n return parseInt(tabIndex as string, 10);\n}\n\nexport function focusable(element: HTMLElement) {\n const nodeName = element.nodeName.toLowerCase();\n const isTabIndexNotNaN = !Number.isNaN(getElementTabIndex(element));\n const res =\n // @ts-expect-error function accepts any html element but if it is a button, it should not be disabled to trigger the condition\n (TABBABLE_NODES.test(nodeName) && !element.disabled) ||\n (element instanceof HTMLAnchorElement ? element.href || isTabIndexNotNaN : isTabIndexNotNaN);\n\n return res && visible(element);\n}\n\nexport function tabbable(element: HTMLElement) {\n const tabIndex = getElementTabIndex(element);\n const isTabIndexNaN = Number.isNaN(tabIndex);\n return (isTabIndexNaN || tabIndex >= 0) && focusable(element);\n}\n\nexport function findTabbableDescendants(element: HTMLElement): HTMLElement[] {\n return Array.from(element.querySelectorAll<HTMLElement>(FOCUS_SELECTOR)).filter(tabbable);\n}\n"],"mappings":";;AAAA,MAAM,iBAAiB;AACvB,MAAa,iBAAiB;AAE9B,SAAS,OAAO,SAAsB;CAKpC,OAAO,QAAQ,MAAM,YAAY;AACnC;AAEA,SAAS,QAAQ,SAAsB;CAMrC,IAJE,QAAQ,aAAa,aAAa,KAClC,QAAQ,aAAa,QAAQ,KAC7B,QAAQ,aAAa,MAAM,MAAM,UAGjC,OAAO;CAGT,IAAI,gBAA6B;CACjC,OAAO,eAAe;EACpB,IAAI,kBAAkB,SAAS,QAAQ,cAAc,aAAa,IAChE;EAGF,IAAI,OAAO,aAAa,GACtB,OAAO;EAGT,gBAAgB,cAAc;CAChC;CAEA,OAAO;AACT;AAEA,SAAS,mBAAmB,SAAsB;CAChD,IAAI,WAAsC,QAAQ,aAAa,UAAU;CACzE,IAAI,aAAa,MACf,WAAW,KAAA;CAEb,OAAO,SAAS,UAAoB,EAAE;AACxC;AAEA,SAAgB,UAAU,SAAsB;CAC9C,MAAM,WAAW,QAAQ,SAAS,YAAY;CAC9C,MAAM,mBAAmB,CAAC,OAAO,MAAM,mBAAmB,OAAO,CAAC;CAMlE,QAHG,eAAe,KAAK,QAAQ,KAAK,CAAC,QAAQ,aAC1C,mBAAmB,oBAAoB,QAAQ,QAAQ,mBAAmB,sBAE/D,QAAQ,OAAO;AAC/B;AAEA,SAAgB,SAAS,SAAsB;CAC7C,MAAM,WAAW,mBAAmB,OAAO;CAE3C,QADsB,OAAO,MAAM,QACf,KAAK,YAAY,MAAM,UAAU,OAAO;AAC9D;AAEA,SAAgB,wBAAwB,SAAqC;CAC3E,OAAO,MAAM,KAAK,QAAQ,iBAA8B,cAAc,CAAC,EAAE,OAAO,QAAQ;AAC1F"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-focus-trap.mjs","names":[],"sources":["../../src/use-focus-trap/use-focus-trap.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { scopeTab } from './scope-tab';\nimport { FOCUS_SELECTOR, focusable, tabbable } from './tabbable';\n\nexport function useFocusTrap(active = true): React.RefCallback<HTMLElement | null> {\n const ref = useRef<HTMLElement>(null);\n\n const focusNode = (node: HTMLElement) => {\n let focusElement: HTMLElement | null = node.querySelector('[data-autofocus]');\n\n if (!focusElement) {\n const children = Array.from<HTMLElement>(node.querySelectorAll(FOCUS_SELECTOR));\n focusElement = children.find(tabbable) || children.find(focusable) || null;\n if (!focusElement && focusable(node)) {\n focusElement = node;\n }\n }\n\n if (focusElement) {\n focusElement.focus({ preventScroll: true });\n } else if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.warn(\n '[@mantine/hooks/use-focus-trap] Failed to find focusable element within provided node',\n node\n );\n }\n };\n\n const setRef = useCallback(\n (node: HTMLElement | null) => {\n if (!active) {\n return;\n }\n\n if (node === null) {\n ref.current = null;\n return;\n }\n\n if (ref.current === node) {\n return;\n }\n\n // Delay processing the HTML node by a frame. This ensures focus is assigned correctly.\n setTimeout(() => {\n if (node.getRootNode()) {\n focusNode(node);\n } else if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.warn('[@mantine/hooks/use-focus-trap] Ref node is not part of the dom', node);\n }\n });\n\n ref.current = node;\n },\n [active]\n );\n\n useEffect(() => {\n if (!active) {\n return undefined;\n }\n\n if (ref.current) {\n setTimeout(() => {\n if (ref.current) {\n focusNode(ref.current);\n }\n });\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Tab' && ref.current) {\n scopeTab(ref.current, event);\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [active]);\n\n return setRef;\n}\n"],"mappings":";;;;;AAIA,SAAgB,aAAa,SAAS,MAA6C;CACjF,MAAM,MAAM,OAAoB,KAAK;CAErC,MAAM,aAAa,SAAsB;EACvC,IAAI,eAAmC,KAAK,cAAc,mBAAmB;AAE7E,MAAI,CAAC,cAAc;GACjB,MAAM,WAAW,MAAM,KAAkB,KAAK,iBAAiB,eAAe,CAAC;AAC/E,kBAAe,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,UAAU,IAAI;AACtE,OAAI,CAAC,gBAAgB,UAAU,KAAK,CAClC,gBAAe;;AAInB,MAAI,aACF,cAAa,MAAM,EAAE,eAAe,MAAM,CAAC;MAG3C,SAAQ,KACN,yFACA,KACD;;CAIL,MAAM,SAAS,aACZ,SAA6B;AAC5B,MAAI,CAAC,OACH;AAGF,MAAI,SAAS,MAAM;AACjB,OAAI,UAAU;AACd;;AAGF,MAAI,IAAI,YAAY,KAClB;AAIF,mBAAiB;AACf,OAAI,KAAK,aAAa,CACpB,WAAU,KAAK;OAGf,SAAQ,KAAK,mEAAmE,KAAK;IAEvF;AAEF,MAAI,UAAU;IAEhB,CAAC,OAAO,CACT;AAED,iBAAgB;AACd,MAAI,CAAC,OACH;AAGF,MAAI,IAAI,QACN,kBAAiB;AACf,OAAI,IAAI,QACN,WAAU,IAAI,QAAQ;IAExB;EAGJ,MAAM,iBAAiB,UAAyB;AAC9C,OAAI,MAAM,QAAQ,SAAS,IAAI,QAC7B,UAAS,IAAI,SAAS,MAAM;;AAIhC,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa,SAAS,oBAAoB,WAAW,cAAc;IAClE,CAAC,OAAO,CAAC;AAEZ,QAAO"}
1
+ {"version":3,"file":"use-focus-trap.mjs","names":[],"sources":["../../src/use-focus-trap/use-focus-trap.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { scopeTab } from './scope-tab';\nimport { FOCUS_SELECTOR, focusable, tabbable } from './tabbable';\n\nexport function useFocusTrap(active = true): React.RefCallback<HTMLElement | null> {\n const ref = useRef<HTMLElement>(null);\n\n const focusNode = (node: HTMLElement) => {\n let focusElement: HTMLElement | null = node.querySelector('[data-autofocus]');\n\n if (!focusElement) {\n const children = Array.from<HTMLElement>(node.querySelectorAll(FOCUS_SELECTOR));\n focusElement = children.find(tabbable) || children.find(focusable) || null;\n if (!focusElement && focusable(node)) {\n focusElement = node;\n }\n }\n\n if (focusElement) {\n focusElement.focus({ preventScroll: true });\n } else if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.warn(\n '[@mantine/hooks/use-focus-trap] Failed to find focusable element within provided node',\n node\n );\n }\n };\n\n const setRef = useCallback(\n (node: HTMLElement | null) => {\n if (!active) {\n return;\n }\n\n if (node === null) {\n ref.current = null;\n return;\n }\n\n if (ref.current === node) {\n return;\n }\n\n // Delay processing the HTML node by a frame. This ensures focus is assigned correctly.\n setTimeout(() => {\n if (node.getRootNode()) {\n focusNode(node);\n } else if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.warn('[@mantine/hooks/use-focus-trap] Ref node is not part of the dom', node);\n }\n });\n\n ref.current = node;\n },\n [active]\n );\n\n useEffect(() => {\n if (!active) {\n return undefined;\n }\n\n if (ref.current) {\n setTimeout(() => {\n if (ref.current) {\n focusNode(ref.current);\n }\n });\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Tab' && ref.current) {\n scopeTab(ref.current, event);\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [active]);\n\n return setRef;\n}\n"],"mappings":";;;;;AAIA,SAAgB,aAAa,SAAS,MAA6C;CACjF,MAAM,MAAM,OAAoB,IAAI;CAEpC,MAAM,aAAa,SAAsB;EACvC,IAAI,eAAmC,KAAK,cAAc,kBAAkB;EAE5E,IAAI,CAAC,cAAc;GACjB,MAAM,WAAW,MAAM,KAAkB,KAAK,iBAAiB,cAAc,CAAC;GAC9E,eAAe,SAAS,KAAK,QAAQ,KAAK,SAAS,KAAK,SAAS,KAAK;GACtE,IAAI,CAAC,gBAAgB,UAAU,IAAI,GACjC,eAAe;EAEnB;EAEA,IAAI,cACF,aAAa,MAAM,EAAE,eAAe,KAAK,CAAC;OAG1C,QAAQ,KACN,yFACA,IACF;CAEJ;CAEA,MAAM,SAAS,aACZ,SAA6B;EAC5B,IAAI,CAAC,QACH;EAGF,IAAI,SAAS,MAAM;GACjB,IAAI,UAAU;GACd;EACF;EAEA,IAAI,IAAI,YAAY,MAClB;EAIF,iBAAiB;GACf,IAAI,KAAK,YAAY,GACnB,UAAU,IAAI;QAGd,QAAQ,KAAK,mEAAmE,IAAI;EAExF,CAAC;EAED,IAAI,UAAU;CAChB,GACA,CAAC,MAAM,CACT;CAEA,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,IAAI,IAAI,SACN,iBAAiB;GACf,IAAI,IAAI,SACN,UAAU,IAAI,OAAO;EAEzB,CAAC;EAGH,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,SAAS,IAAI,SAC7B,SAAS,IAAI,SAAS,KAAK;EAE/B;EAEA,SAAS,iBAAiB,WAAW,aAAa;EAClD,aAAa,SAAS,oBAAoB,WAAW,aAAa;CACpE,GAAG,CAAC,MAAM,CAAC;CAEX,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-focus-within.mjs","names":[],"sources":["../../src/use-focus-within/use-focus-within.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useCallbackRef } from '../utils';\n\nfunction containsRelatedTarget(event: FocusEvent) {\n if (event.currentTarget instanceof HTMLElement && event.relatedTarget instanceof HTMLElement) {\n return event.currentTarget.contains(event.relatedTarget);\n }\n\n return false;\n}\n\nexport interface UseFocusWithinOptions {\n onFocus?: (event: FocusEvent) => void;\n onBlur?: (event: FocusEvent) => void;\n}\n\nexport interface UseFocusWithinReturnValue<T extends HTMLElement = any> {\n ref: React.RefCallback<T | null>;\n focused: boolean;\n}\n\nexport function useFocusWithin<T extends HTMLElement = any>({\n onBlur,\n onFocus,\n}: UseFocusWithinOptions = {}): UseFocusWithinReturnValue<T> {\n const [focused, setFocused] = useState(false);\n const focusedRef = useRef(false);\n const previousNode = useRef<T | null>(null);\n\n const onFocusRef = useCallbackRef(onFocus);\n const onBlurRef = useCallbackRef(onBlur);\n\n const _setFocused = useCallback((value: boolean) => {\n setFocused(value);\n focusedRef.current = value;\n }, []);\n\n const handleFocusIn = useCallback((event: FocusEvent) => {\n if (!focusedRef.current) {\n _setFocused(true);\n onFocusRef(event);\n }\n }, []);\n\n const handleFocusOut = useCallback((event: FocusEvent) => {\n if (focusedRef.current && !containsRelatedTarget(event)) {\n _setFocused(false);\n onBlurRef(event);\n }\n }, []);\n\n const callbackRef: React.RefCallback<T | null> = useCallback(\n (node) => {\n if (!node) {\n return;\n }\n\n if (previousNode.current) {\n previousNode.current.removeEventListener('focusin', handleFocusIn);\n previousNode.current.removeEventListener('focusout', handleFocusOut);\n }\n\n node.addEventListener('focusin', handleFocusIn);\n node.addEventListener('focusout', handleFocusOut);\n previousNode.current = node;\n },\n [handleFocusIn, handleFocusOut]\n );\n\n useEffect(\n () => () => {\n if (previousNode.current) {\n previousNode.current.removeEventListener('focusin', handleFocusIn);\n previousNode.current.removeEventListener('focusout', handleFocusOut);\n }\n },\n []\n );\n\n return { ref: callbackRef, focused };\n}\n\nexport namespace useFocusWithin {\n export type Options = UseFocusWithinOptions;\n export type ReturnValue<T extends HTMLElement> = UseFocusWithinReturnValue<T>;\n}\n"],"mappings":";;;;AAGA,SAAS,sBAAsB,OAAmB;AAChD,KAAI,MAAM,yBAAyB,eAAe,MAAM,yBAAyB,YAC/E,QAAO,MAAM,cAAc,SAAS,MAAM,cAAc;AAG1D,QAAO;;AAaT,SAAgB,eAA4C,EAC1D,QACA,YACyB,EAAE,EAAgC;CAC3D,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,aAAa,OAAO,MAAM;CAChC,MAAM,eAAe,OAAiB,KAAK;CAE3C,MAAM,aAAa,eAAe,QAAQ;CAC1C,MAAM,YAAY,eAAe,OAAO;CAExC,MAAM,cAAc,aAAa,UAAmB;AAClD,aAAW,MAAM;AACjB,aAAW,UAAU;IACpB,EAAE,CAAC;CAEN,MAAM,gBAAgB,aAAa,UAAsB;AACvD,MAAI,CAAC,WAAW,SAAS;AACvB,eAAY,KAAK;AACjB,cAAW,MAAM;;IAElB,EAAE,CAAC;CAEN,MAAM,iBAAiB,aAAa,UAAsB;AACxD,MAAI,WAAW,WAAW,CAAC,sBAAsB,MAAM,EAAE;AACvD,eAAY,MAAM;AAClB,aAAU,MAAM;;IAEjB,EAAE,CAAC;CAEN,MAAM,cAA2C,aAC9C,SAAS;AACR,MAAI,CAAC,KACH;AAGF,MAAI,aAAa,SAAS;AACxB,gBAAa,QAAQ,oBAAoB,WAAW,cAAc;AAClE,gBAAa,QAAQ,oBAAoB,YAAY,eAAe;;AAGtE,OAAK,iBAAiB,WAAW,cAAc;AAC/C,OAAK,iBAAiB,YAAY,eAAe;AACjD,eAAa,UAAU;IAEzB,CAAC,eAAe,eAAe,CAChC;AAED,uBACc;AACV,MAAI,aAAa,SAAS;AACxB,gBAAa,QAAQ,oBAAoB,WAAW,cAAc;AAClE,gBAAa,QAAQ,oBAAoB,YAAY,eAAe;;IAGxE,EAAE,CACH;AAED,QAAO;EAAE,KAAK;EAAa;EAAS"}
1
+ {"version":3,"file":"use-focus-within.mjs","names":[],"sources":["../../src/use-focus-within/use-focus-within.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useCallbackRef } from '../utils';\n\nfunction containsRelatedTarget(event: FocusEvent) {\n if (event.currentTarget instanceof HTMLElement && event.relatedTarget instanceof HTMLElement) {\n return event.currentTarget.contains(event.relatedTarget);\n }\n\n return false;\n}\n\nexport interface UseFocusWithinOptions {\n onFocus?: (event: FocusEvent) => void;\n onBlur?: (event: FocusEvent) => void;\n}\n\nexport interface UseFocusWithinReturnValue<T extends HTMLElement = any> {\n ref: React.RefCallback<T | null>;\n focused: boolean;\n}\n\nexport function useFocusWithin<T extends HTMLElement = any>({\n onBlur,\n onFocus,\n}: UseFocusWithinOptions = {}): UseFocusWithinReturnValue<T> {\n const [focused, setFocused] = useState(false);\n const focusedRef = useRef(false);\n const previousNode = useRef<T | null>(null);\n\n const onFocusRef = useCallbackRef(onFocus);\n const onBlurRef = useCallbackRef(onBlur);\n\n const _setFocused = useCallback((value: boolean) => {\n setFocused(value);\n focusedRef.current = value;\n }, []);\n\n const handleFocusIn = useCallback((event: FocusEvent) => {\n if (!focusedRef.current) {\n _setFocused(true);\n onFocusRef(event);\n }\n }, []);\n\n const handleFocusOut = useCallback((event: FocusEvent) => {\n if (focusedRef.current && !containsRelatedTarget(event)) {\n _setFocused(false);\n onBlurRef(event);\n }\n }, []);\n\n const callbackRef: React.RefCallback<T | null> = useCallback(\n (node) => {\n if (!node) {\n return;\n }\n\n if (previousNode.current) {\n previousNode.current.removeEventListener('focusin', handleFocusIn);\n previousNode.current.removeEventListener('focusout', handleFocusOut);\n }\n\n node.addEventListener('focusin', handleFocusIn);\n node.addEventListener('focusout', handleFocusOut);\n previousNode.current = node;\n },\n [handleFocusIn, handleFocusOut]\n );\n\n useEffect(\n () => () => {\n if (previousNode.current) {\n previousNode.current.removeEventListener('focusin', handleFocusIn);\n previousNode.current.removeEventListener('focusout', handleFocusOut);\n }\n },\n []\n );\n\n return { ref: callbackRef, focused };\n}\n\nexport namespace useFocusWithin {\n export type Options = UseFocusWithinOptions;\n export type ReturnValue<T extends HTMLElement> = UseFocusWithinReturnValue<T>;\n}\n"],"mappings":";;;;AAGA,SAAS,sBAAsB,OAAmB;CAChD,IAAI,MAAM,yBAAyB,eAAe,MAAM,yBAAyB,aAC/E,OAAO,MAAM,cAAc,SAAS,MAAM,aAAa;CAGzD,OAAO;AACT;AAYA,SAAgB,eAA4C,EAC1D,QACA,YACyB,CAAC,GAAiC;CAC3D,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,aAAa,OAAO,KAAK;CAC/B,MAAM,eAAe,OAAiB,IAAI;CAE1C,MAAM,aAAa,eAAe,OAAO;CACzC,MAAM,YAAY,eAAe,MAAM;CAEvC,MAAM,cAAc,aAAa,UAAmB;EAClD,WAAW,KAAK;EAChB,WAAW,UAAU;CACvB,GAAG,CAAC,CAAC;CAEL,MAAM,gBAAgB,aAAa,UAAsB;EACvD,IAAI,CAAC,WAAW,SAAS;GACvB,YAAY,IAAI;GAChB,WAAW,KAAK;EAClB;CACF,GAAG,CAAC,CAAC;CAEL,MAAM,iBAAiB,aAAa,UAAsB;EACxD,IAAI,WAAW,WAAW,CAAC,sBAAsB,KAAK,GAAG;GACvD,YAAY,KAAK;GACjB,UAAU,KAAK;EACjB;CACF,GAAG,CAAC,CAAC;CAEL,MAAM,cAA2C,aAC9C,SAAS;EACR,IAAI,CAAC,MACH;EAGF,IAAI,aAAa,SAAS;GACxB,aAAa,QAAQ,oBAAoB,WAAW,aAAa;GACjE,aAAa,QAAQ,oBAAoB,YAAY,cAAc;EACrE;EAEA,KAAK,iBAAiB,WAAW,aAAa;EAC9C,KAAK,iBAAiB,YAAY,cAAc;EAChD,aAAa,UAAU;CACzB,GACA,CAAC,eAAe,cAAc,CAChC;CAEA,sBACc;EACV,IAAI,aAAa,SAAS;GACxB,aAAa,QAAQ,oBAAoB,WAAW,aAAa;GACjE,aAAa,QAAQ,oBAAoB,YAAY,cAAc;EACrE;CACF,GACA,CAAC,CACH;CAEA,OAAO;EAAE,KAAK;EAAa;CAAQ;AACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-force-update.mjs","names":[],"sources":["../../src/use-force-update/use-force-update.ts"],"sourcesContent":["import { useReducer } from 'react';\n\nconst reducer = (value: number) => (value + 1) % 1000000;\n\nexport function useForceUpdate(): () => void {\n const [, update] = useReducer(reducer, 0);\n return update;\n}\n"],"mappings":";;;AAEA,MAAM,WAAW,WAAmB,QAAQ,KAAK;AAEjD,SAAgB,iBAA6B;CAC3C,MAAM,GAAG,UAAU,WAAW,SAAS,EAAE;AACzC,QAAO"}
1
+ {"version":3,"file":"use-force-update.mjs","names":[],"sources":["../../src/use-force-update/use-force-update.ts"],"sourcesContent":["import { useReducer } from 'react';\n\nconst reducer = (value: number) => (value + 1) % 1000000;\n\nexport function useForceUpdate(): () => void {\n const [, update] = useReducer(reducer, 0);\n return update;\n}\n"],"mappings":";;;AAEA,MAAM,WAAW,WAAmB,QAAQ,KAAK;AAEjD,SAAgB,iBAA6B;CAC3C,MAAM,GAAG,UAAU,WAAW,SAAS,CAAC;CACxC,OAAO;AACT"}