@vitus-labs/hooks 2.0.0-beta.0 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +3 -0
- package/lib/index.js +28 -15
- package/package.json +8 -9
- package/LICENSE +0 -21
package/lib/index.d.ts
CHANGED
|
@@ -108,6 +108,9 @@ type UseFocusTrap = (ref: {
|
|
|
108
108
|
* Traps keyboard focus within the referenced container.
|
|
109
109
|
* Tab and Shift+Tab cycle through focusable elements inside.
|
|
110
110
|
* Useful for modals, dialogs, and dropdown menus.
|
|
111
|
+
*
|
|
112
|
+
* Focusable elements are cached and only re-queried when DOM mutations
|
|
113
|
+
* inside the container add/remove nodes — not on every Tab keypress.
|
|
111
114
|
*/
|
|
112
115
|
declare const useFocusTrap: UseFocusTrap;
|
|
113
116
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -27,24 +27,21 @@ const useBreakpoint = () => {
|
|
|
27
27
|
});
|
|
28
28
|
useEffect(() => {
|
|
29
29
|
if (sorted.length === 0) return void 0;
|
|
30
|
-
|
|
30
|
+
let raf = 0;
|
|
31
31
|
const update = () => {
|
|
32
|
+
raf = 0;
|
|
32
33
|
const width = window.innerWidth;
|
|
33
34
|
let match = sorted[0]?.[0];
|
|
34
35
|
for (const [name, min] of sorted) if (width >= min) match = name;
|
|
35
36
|
setCurrent(match);
|
|
36
37
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
mqls.push({
|
|
42
|
-
mql,
|
|
43
|
-
handler
|
|
44
|
-
});
|
|
45
|
-
}
|
|
38
|
+
const onResize = () => {
|
|
39
|
+
if (raf === 0) raf = requestAnimationFrame(update);
|
|
40
|
+
};
|
|
41
|
+
window.addEventListener("resize", onResize, { passive: true });
|
|
46
42
|
return () => {
|
|
47
|
-
|
|
43
|
+
if (raf !== 0) cancelAnimationFrame(raf);
|
|
44
|
+
window.removeEventListener("resize", onResize);
|
|
48
45
|
};
|
|
49
46
|
}, [sorted]);
|
|
50
47
|
return current;
|
|
@@ -254,14 +251,27 @@ const wrapFocus = (e, target) => {
|
|
|
254
251
|
* Traps keyboard focus within the referenced container.
|
|
255
252
|
* Tab and Shift+Tab cycle through focusable elements inside.
|
|
256
253
|
* Useful for modals, dialogs, and dropdown menus.
|
|
254
|
+
*
|
|
255
|
+
* Focusable elements are cached and only re-queried when DOM mutations
|
|
256
|
+
* inside the container add/remove nodes — not on every Tab keypress.
|
|
257
257
|
*/
|
|
258
258
|
const useFocusTrap = (ref, enabled = true) => {
|
|
259
259
|
useEffect(() => {
|
|
260
260
|
if (!enabled) return void 0;
|
|
261
|
+
const container = ref.current;
|
|
262
|
+
if (!container) return void 0;
|
|
263
|
+
let focusable = null;
|
|
264
|
+
const refresh = () => {
|
|
265
|
+
focusable = container.querySelectorAll(FOCUSABLE);
|
|
266
|
+
};
|
|
267
|
+
refresh();
|
|
268
|
+
const observer = new MutationObserver(refresh);
|
|
269
|
+
observer.observe(container, {
|
|
270
|
+
childList: true,
|
|
271
|
+
subtree: true
|
|
272
|
+
});
|
|
261
273
|
const handler = (e) => {
|
|
262
|
-
if (e.key !== "Tab" || !
|
|
263
|
-
const focusable = ref.current.querySelectorAll(FOCUSABLE);
|
|
264
|
-
if (focusable.length === 0) return;
|
|
274
|
+
if (e.key !== "Tab" || !focusable || focusable.length === 0) return;
|
|
265
275
|
const first = focusable[0];
|
|
266
276
|
const last = focusable[focusable.length - 1];
|
|
267
277
|
const active = document.activeElement;
|
|
@@ -269,7 +279,10 @@ const useFocusTrap = (ref, enabled = true) => {
|
|
|
269
279
|
else if (!e.shiftKey && active === last) wrapFocus(e, first);
|
|
270
280
|
};
|
|
271
281
|
document.addEventListener("keydown", handler);
|
|
272
|
-
return () =>
|
|
282
|
+
return () => {
|
|
283
|
+
observer.disconnect();
|
|
284
|
+
document.removeEventListener("keydown", handler);
|
|
285
|
+
};
|
|
273
286
|
}, [ref, enabled]);
|
|
274
287
|
};
|
|
275
288
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitus-labs/hooks",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Vit Bokisch <vit@bokisch.cz>",
|
|
6
6
|
"maintainers": [
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"node": ">= 18"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@vitus-labs/core": "2.0.0-
|
|
55
|
+
"@vitus-labs/core": "2.0.0-beta.2",
|
|
56
56
|
"react": ">= 19",
|
|
57
57
|
"react-native": ">= 0.76"
|
|
58
58
|
},
|
|
@@ -62,11 +62,10 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@vitus-labs/core": "
|
|
66
|
-
"@vitus-labs/tools-rolldown": "
|
|
67
|
-
"@vitus-labs/tools-storybook": "
|
|
68
|
-
"@vitus-labs/tools-typescript": "1.
|
|
69
|
-
"react-native": ">=0.
|
|
70
|
-
}
|
|
71
|
-
"gitHead": "dd8b9f356086ecd8bfb69c87fcad1e8bfa9ab1f4"
|
|
65
|
+
"@vitus-labs/core": "workspace:*",
|
|
66
|
+
"@vitus-labs/tools-rolldown": "2.2.0",
|
|
67
|
+
"@vitus-labs/tools-storybook": "2.2.0",
|
|
68
|
+
"@vitus-labs/tools-typescript": "2.1.0",
|
|
69
|
+
"react-native": ">=0.84.1"
|
|
70
|
+
}
|
|
72
71
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023-present Vit Bokisch
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|