@t007/utils 0.0.34 → 0.0.35
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/dist/{arrowNavigation-DF7ul3A3.d.ts → arrowNavigation-B9bEdoqR.d.ts} +5 -2
- package/dist/{arrowNavigation-IaWiNTa6.d.cts → arrowNavigation-zEJbo8Er.d.cts} +5 -2
- package/dist/{chunk-ZRCBB5P4.js → chunk-NX2LWYD6.js} +14 -12
- package/dist/{chunk-HGUYOV3P.js → chunk-ZALPHCSJ.js} +8 -3
- package/dist/hooks/react.cjs +41 -31
- package/dist/hooks/react.d.cts +7 -4
- package/dist/hooks/react.d.ts +7 -4
- package/dist/hooks/react.js +34 -24
- package/dist/hooks/vanilla.cjs +36 -23
- package/dist/hooks/vanilla.d.cts +3 -3
- package/dist/hooks/vanilla.d.ts +3 -3
- package/dist/hooks/vanilla.js +25 -14
- package/dist/index.cjs +8 -3
- package/dist/index.d.cts +19 -11
- package/dist/index.d.ts +19 -11
- package/dist/index.js +1 -1
- package/dist/{ripple-87VO-U7A.d.cts → ripple-D1kGErTn.d.cts} +1 -20
- package/dist/{ripple-87VO-U7A.d.ts → ripple-D1kGErTn.d.ts} +1 -20
- package/dist/{scrollAssist-l0V2H-jx.d.cts → scrollAssist-BNvPnsJq.d.cts} +26 -1
- package/dist/{scrollAssist-l0V2H-jx.d.ts → scrollAssist-BNvPnsJq.d.ts} +26 -1
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as KeyEvent, C as Config } from './scrollAssist-
|
|
1
|
+
import { K as KeyEvent, C as Config } from './scrollAssist-BNvPnsJq.js';
|
|
2
2
|
|
|
3
3
|
type ArrowNavigationHandle = {
|
|
4
4
|
/** Current computed horizontal grid size. */
|
|
@@ -21,11 +21,14 @@ type ArrowNavigationHandle = {
|
|
|
21
21
|
goToIndex: (index: number, e?: KeyEvent) => void;
|
|
22
22
|
/** Simulate directional key navigation with a keyboard-like event. */
|
|
23
23
|
simulateKey: (e: KeyEvent) => void;
|
|
24
|
+
/** Synchronize the navigation items list with the DOM. */
|
|
25
|
+
sync: () => void;
|
|
24
26
|
/** Remove listeners/observers and release resources. */
|
|
25
27
|
destroy: () => void;
|
|
26
28
|
};
|
|
27
29
|
/** A vanilla JavaScript utility for managing robust arrow-key roving focus navigation. */
|
|
28
30
|
declare function initArrowNavigation(container: HTMLElement, config?: Config): ArrowNavigationHandle | void;
|
|
29
31
|
declare const removeArrowNavigation: (container: HTMLElement) => void | undefined;
|
|
32
|
+
declare const syncArrowNavigation: (container: HTMLElement) => void | undefined;
|
|
30
33
|
|
|
31
|
-
export { type ArrowNavigationHandle as A, initArrowNavigation as i, removeArrowNavigation as r };
|
|
34
|
+
export { type ArrowNavigationHandle as A, initArrowNavigation as i, removeArrowNavigation as r, syncArrowNavigation as s };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as KeyEvent, C as Config } from './scrollAssist-
|
|
1
|
+
import { K as KeyEvent, C as Config } from './scrollAssist-BNvPnsJq.cjs';
|
|
2
2
|
|
|
3
3
|
type ArrowNavigationHandle = {
|
|
4
4
|
/** Current computed horizontal grid size. */
|
|
@@ -21,11 +21,14 @@ type ArrowNavigationHandle = {
|
|
|
21
21
|
goToIndex: (index: number, e?: KeyEvent) => void;
|
|
22
22
|
/** Simulate directional key navigation with a keyboard-like event. */
|
|
23
23
|
simulateKey: (e: KeyEvent) => void;
|
|
24
|
+
/** Synchronize the navigation items list with the DOM. */
|
|
25
|
+
sync: () => void;
|
|
24
26
|
/** Remove listeners/observers and release resources. */
|
|
25
27
|
destroy: () => void;
|
|
26
28
|
};
|
|
27
29
|
/** A vanilla JavaScript utility for managing robust arrow-key roving focus navigation. */
|
|
28
30
|
declare function initArrowNavigation(container: HTMLElement, config?: Config): ArrowNavigationHandle | void;
|
|
29
31
|
declare const removeArrowNavigation: (container: HTMLElement) => void | undefined;
|
|
32
|
+
declare const syncArrowNavigation: (container: HTMLElement) => void | undefined;
|
|
30
33
|
|
|
31
|
-
export { type ArrowNavigationHandle as A, initArrowNavigation as i, removeArrowNavigation as r };
|
|
34
|
+
export { type ArrowNavigationHandle as A, initArrowNavigation as i, removeArrowNavigation as r, syncArrowNavigation as s };
|
|
@@ -4,39 +4,39 @@ import {
|
|
|
4
4
|
createEl,
|
|
5
5
|
getActiveEl,
|
|
6
6
|
isInteractive
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZALPHCSJ.js";
|
|
8
8
|
|
|
9
9
|
// src/ts/hooks/vanilla/outsideClick.ts
|
|
10
10
|
import { NIL, NOOP } from "sia-reactor";
|
|
11
11
|
function initOutsideClick(el, { enabled = false, onOutside = NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowBounds = false, allowInputs = false, root = window, scoped = true, capture = true } = NIL) {
|
|
12
|
-
const
|
|
12
|
+
const stack = t007._outsiders_stack ??= [], existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
13
13
|
if (!enabled || existing) return existing ? existing : void 0;
|
|
14
14
|
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
15
|
-
const
|
|
15
|
+
const onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
|
|
16
16
|
if (stack.at(-1) !== el || (allowBounds ? p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom : el.contains(t))) return false;
|
|
17
17
|
return (!scoped ? true : root.contains(t)) && onOutside(e);
|
|
18
18
|
}, handleClick = ((e) => outOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => outOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutside(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
|
|
19
19
|
root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
|
|
20
20
|
root.addEventListener("keydown", handleEscape, capture);
|
|
21
21
|
el.addEventListener("focusout", handleFocusOut, capture);
|
|
22
|
-
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
23
22
|
const destroy = () => {
|
|
24
23
|
root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
|
|
25
24
|
root.removeEventListener("keydown", handleEscape, capture);
|
|
26
25
|
el.removeEventListener("focusout", handleFocusOut, capture);
|
|
27
26
|
t007._outsiders.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
28
27
|
};
|
|
29
|
-
return t007._outsiders.set(el, destroy), destroy;
|
|
28
|
+
return !stack.includes(el) && stack.push(el), t007._outsiders.set(el, destroy), destroy;
|
|
30
29
|
}
|
|
31
30
|
var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
|
|
32
31
|
|
|
33
32
|
// src/ts/hooks/vanilla/focusTrap.ts
|
|
34
33
|
import { NIL as NIL2 } from "sia-reactor";
|
|
35
34
|
function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = NIL2) {
|
|
36
|
-
const
|
|
35
|
+
const stack = t007._ftrappers_stack ??= [], existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
37
36
|
if (!enabled || existing) return existing ? existing : void 0;
|
|
38
37
|
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
39
|
-
|
|
38
|
+
let recovering = false;
|
|
39
|
+
const focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = createEl("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = createEl("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => [...c.querySelectorAll(INTERACTIVE_SELECTOR)], resetFocus = (i = 0, els = getFocusable()) => !recovering && (recovering = true, els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), recovering = false), edgeFocus = (pre = false, rt = root) => {
|
|
40
40
|
if (!scoped) return resetFocus(pre ? -1 : 0);
|
|
41
41
|
if (rt.hasAttribute("tabindex")) return rt.focus();
|
|
42
42
|
const items = getFocusable();
|
|
@@ -46,24 +46,25 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
|
|
|
46
46
|
while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
|
|
47
47
|
for (let target, len = all.length, i = all.indexOf(items[pre ? 0 : items.length - 1]) + (pre ? -1 : 1); pre ? i >= 0 : i < len; pre ? i-- : i++) if (!rt.contains(target = all[i])) return target.focus();
|
|
48
48
|
(pre ? first : last).blur();
|
|
49
|
-
}, handleFocusIn = () => {
|
|
49
|
+
}, handleFocusIn = (e) => {
|
|
50
50
|
if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
|
|
51
|
-
stack.at(-1) === el && getActiveEl(el.ownerDocument) !== root && !el.contains(getActiveEl(el.ownerDocument)) && resetFocus();
|
|
51
|
+
stack.at(-1) === el && getActiveEl(el.ownerDocument) !== root && !el.contains(getActiveEl(el.ownerDocument)) && !e.composedPath().includes(el) && resetFocus();
|
|
52
52
|
}, handleInitialBlur = () => initial.classList.remove(ringClassName);
|
|
53
53
|
first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
|
|
54
54
|
last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
|
|
55
55
|
root.addEventListener("focusin", handleFocusIn, capture);
|
|
56
56
|
if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
|
|
57
|
-
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
58
57
|
const destroy = () => {
|
|
59
58
|
focused?.isConnected && focused.focus(), first.remove(), last.remove();
|
|
60
59
|
root.removeEventListener("focusin", handleFocusIn, capture);
|
|
61
60
|
initial?.removeEventListener("blur", handleInitialBlur, capture);
|
|
62
61
|
t007._ftrappers.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
63
62
|
};
|
|
64
|
-
|
|
63
|
+
const handle = { destroy, sync: () => (el.prepend(first), el.append(last)) };
|
|
64
|
+
return !stack.includes(el) && stack.push(el), t007._ftrappers.set(el, handle), handle;
|
|
65
65
|
}
|
|
66
|
-
var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
|
|
66
|
+
var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.destroy();
|
|
67
|
+
var syncFocusTrap = (el) => t007._ftrappers?.get(el)?.sync();
|
|
67
68
|
|
|
68
69
|
// src/ts/hooks/vanilla/ripple.ts
|
|
69
70
|
import { NIL as NIL3 } from "sia-reactor";
|
|
@@ -168,5 +169,6 @@ export {
|
|
|
168
169
|
DEFAULT_CONFIG,
|
|
169
170
|
initFocusTrap,
|
|
170
171
|
removeFocusTrap,
|
|
172
|
+
syncFocusTrap,
|
|
171
173
|
rippleHandler
|
|
172
174
|
};
|
|
@@ -35,8 +35,12 @@ function loadResource(req, type = "style", { module, media, crossOrigin, integri
|
|
|
35
35
|
function getWindow(el = window) {
|
|
36
36
|
return el?.ownerDocument?.defaultView ?? el?.defaultView ?? window;
|
|
37
37
|
}
|
|
38
|
-
function createListRenderer({ container, getKey, createNode, updateNode = NOOP, destroyNode = NOOP }) {
|
|
38
|
+
function createListRenderer({ container, getKey, createNode, updateNode = NOOP, destroyNode = NOOP, initNode }) {
|
|
39
39
|
let nodeRegistry = /* @__PURE__ */ new Map();
|
|
40
|
+
if (initNode) {
|
|
41
|
+
const children = Array.from(container.children);
|
|
42
|
+
for (let i = 0, len = children.length; i < len; i++) initNode(children[i], (key) => nodeRegistry.set(key, children[i]));
|
|
43
|
+
}
|
|
40
44
|
return function syncDOM(array, strict = true) {
|
|
41
45
|
const newRegistry = /* @__PURE__ */ new Map(), seenKeys = /* @__PURE__ */ new Set(), oldPositions = /* @__PURE__ */ new WeakMap(), children = Array.from(container.children);
|
|
42
46
|
for (let i = 0, len = children.length; i < len; i++) oldPositions.set(children[i], i);
|
|
@@ -202,12 +206,13 @@ function RAFLoop(key, fn, signal, win) {
|
|
|
202
206
|
}
|
|
203
207
|
var cancelRAFLoop = (key) => t007._RAFLoopers ? t007._RAFLoopers.delete(key) : false;
|
|
204
208
|
function limited(FN_KEY, fn, opts = {}) {
|
|
205
|
-
let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
|
|
209
|
+
let count = 0, { key, maxTimes: max = 1, oncePerSession = true } = isStr(opts) ? { key: opts } : opts;
|
|
206
210
|
const getReg = () => JSON.parse(localStorage.getItem(FN_KEY) || "{}"), setReg = (r) => localStorage.setItem(FN_KEY, JSON.stringify(r));
|
|
207
211
|
const handle = (...args) => {
|
|
212
|
+
if (oncePerSession && count > 0) return void 0;
|
|
208
213
|
if (!key) return count++ < max ? fn(...args) : void 0;
|
|
209
214
|
const r = getReg(), c = r[key] || 0;
|
|
210
|
-
return c < max ? (r[key] = c + 1, setReg(r), fn(...args)) : void 0;
|
|
215
|
+
return c < max ? (count++, r[key] = c + 1, setReg(r), fn(...args)) : void 0;
|
|
211
216
|
};
|
|
212
217
|
handle.left = max - (handle.count = count);
|
|
213
218
|
handle.reset = () => (count = 0, key && ((r) => (delete r[key], setReg(r)))(getReg()));
|
package/dist/hooks/react.cjs
CHANGED
|
@@ -127,24 +127,23 @@ var import_react2 = require("react");
|
|
|
127
127
|
// src/ts/hooks/vanilla/outsideClick.ts
|
|
128
128
|
var import_sia_reactor3 = require("sia-reactor");
|
|
129
129
|
function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor3.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowBounds = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor3.NIL) {
|
|
130
|
-
const
|
|
130
|
+
const stack = t007._outsiders_stack ??= [], existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
131
131
|
if (!enabled || existing) return existing ? existing : void 0;
|
|
132
132
|
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
133
|
-
const
|
|
133
|
+
const onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
|
|
134
134
|
if (stack.at(-1) !== el || (allowBounds ? p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom : el.contains(t))) return false;
|
|
135
135
|
return (!scoped ? true : root.contains(t)) && onOutside(e);
|
|
136
136
|
}, handleClick = ((e) => outOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => outOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutside(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
|
|
137
137
|
root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
|
|
138
138
|
root.addEventListener("keydown", handleEscape, capture);
|
|
139
139
|
el.addEventListener("focusout", handleFocusOut, capture);
|
|
140
|
-
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
141
140
|
const destroy = () => {
|
|
142
141
|
root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
|
|
143
142
|
root.removeEventListener("keydown", handleEscape, capture);
|
|
144
143
|
el.removeEventListener("focusout", handleFocusOut, capture);
|
|
145
144
|
t007._outsiders.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
146
145
|
};
|
|
147
|
-
return t007._outsiders.set(el, destroy), destroy;
|
|
146
|
+
return !stack.includes(el) && stack.push(el), t007._outsiders.set(el, destroy), destroy;
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
// src/ts/hooks/react/useOutsideClick.ts
|
|
@@ -228,7 +227,7 @@ var DEFAULT_CONFIG = {
|
|
|
228
227
|
|
|
229
228
|
// src/ts/hooks/react/useArrowNavigation/index.ts
|
|
230
229
|
function useArrowNavigation(containerRef, config = {}) {
|
|
231
|
-
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, rovingTab, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut } = { ...DEFAULT_CONFIG, ...config }, [gridX, setGridX] = (0, import_react3.useState)(grid.x || 1), [gridY, setGridY] = (0, import_react3.useState)(grid.y || 1), [vGridY, setVGridY] = (0, import_react3.useState)(grid.vY || 1), [activeIndex, setActiveIndex] = (0, import_react3.useState)(-1), buffer = (0, import_react3.useRef)(""), timeout = (0, import_react3.useRef)(null), itemsRef = (0, import_react3.useRef)([]), enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = (0, import_react3.useMemo)(() => isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(containerRef.current || document.body).direction === "rtl"), [containerRef, isRtl]), mutationObserverRef = (0, import_react3.useRef)(null), shouldSnub = (0, import_react3.useCallback)(() => !enabled || !containerRef.current, [enabled, containerRef]), isItemDisabled = (0, import_react3.useCallback)((el) => !el
|
|
230
|
+
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, rovingTab, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut } = { ...DEFAULT_CONFIG, ...config }, [gridX, setGridX] = (0, import_react3.useState)(grid.x || 1), [gridY, setGridY] = (0, import_react3.useState)(grid.y || 1), [vGridY, setVGridY] = (0, import_react3.useState)(grid.vY || 1), [activeIndex, setActiveIndex] = (0, import_react3.useState)(-1), buffer = (0, import_react3.useRef)(""), timeout = (0, import_react3.useRef)(null), itemsRef = (0, import_react3.useRef)([]), enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = (0, import_react3.useMemo)(() => isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(containerRef.current || document.body).direction === "rtl"), [containerRef, isRtl]), mutationObserverRef = (0, import_react3.useRef)(null), shouldSnub = (0, import_react3.useCallback)(() => !enabled || !containerRef.current, [enabled, containerRef]), isItemDisabled = (0, import_react3.useCallback)((el) => !el || el.matches('[disabled],[aria-disabled="true"],[inert],:disabled'), []), getItems = (0, import_react3.useCallback)(() => itemsRef.current = Array.from(containerRef.current?.querySelectorAll(selector) || []), [containerRef, selector]);
|
|
232
231
|
const getAbleIndex = (0, import_react3.useCallback)(
|
|
233
232
|
(targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
|
|
234
233
|
if (shouldSnub()) return;
|
|
@@ -302,8 +301,9 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
302
301
|
},
|
|
303
302
|
[shouldSnub, virtual, activeIndex, gridX, gridY, vGridY, loop, rtl, goToIndex, typeahead, typeAhead]
|
|
304
303
|
);
|
|
305
|
-
const latest = (0, import_react3.useRef)({ getItems, updateDOM, activeIndex
|
|
306
|
-
|
|
304
|
+
const latest = (0, import_react3.useRef)({ getItems, updateDOM, activeIndex, calcGrid: () => {
|
|
305
|
+
} });
|
|
306
|
+
(0, import_react3.useEffect)(() => void (latest.current = { ...latest.current, getItems, updateDOM, activeIndex }), [getItems, updateDOM, activeIndex]);
|
|
307
307
|
(0, import_react3.useEffect)(() => void getItems(), [getItems, enabled]);
|
|
308
308
|
(0, import_react3.useEffect)(() => void (!enabled && setActiveIndex(-1)), [enabled]);
|
|
309
309
|
(0, import_react3.useEffect)(() => updateDOM(), [activeIndex, updateDOM]);
|
|
@@ -332,8 +332,7 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
332
332
|
const all = itemsRef.current;
|
|
333
333
|
const handleHover = (e) => {
|
|
334
334
|
if (!focusOnHover) return;
|
|
335
|
-
const el = e.currentTarget;
|
|
336
|
-
const i = itemsRef.current?.indexOf(el);
|
|
335
|
+
const el = e.currentTarget, i = itemsRef.current?.indexOf(el);
|
|
337
336
|
if (i !== -1) goToIndex(i);
|
|
338
337
|
};
|
|
339
338
|
for (const el of all) el.addEventListener("mouseenter", handleHover);
|
|
@@ -341,23 +340,23 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
341
340
|
for (const el of all) el.removeEventListener("mouseenter", handleHover);
|
|
342
341
|
};
|
|
343
342
|
}, [enabled, focusOnHover, goToIndex]);
|
|
343
|
+
const sync = (0, import_react3.useCallback)(() => {
|
|
344
|
+
const { getItems: getItems2, updateDOM: updateDOM2, activeIndex: activeIndex2 } = latest.current;
|
|
345
|
+
const oldEl = itemsRef.current[activeIndex2];
|
|
346
|
+
getItems2();
|
|
347
|
+
const newEl = itemsRef.current[activeIndex2];
|
|
348
|
+
updateDOM2();
|
|
349
|
+
if (oldEl && newEl && oldEl === newEl) return;
|
|
350
|
+
setActiveIndex(-1);
|
|
351
|
+
}, []);
|
|
344
352
|
(0, import_react3.useEffect)(() => {
|
|
345
353
|
if (shouldSnub()) return;
|
|
346
|
-
const observer = mutationObserverRef.current = new MutationObserver(
|
|
347
|
-
const { getItems: getItems2, updateDOM: updateDOM2, activeIndex: activeIndex2 } = latest.current;
|
|
348
|
-
const oldEl = itemsRef.current[activeIndex2];
|
|
349
|
-
getItems2();
|
|
350
|
-
const newEl = itemsRef.current[activeIndex2];
|
|
351
|
-
updateDOM2();
|
|
352
|
-
if (oldEl && newEl && oldEl === newEl) return;
|
|
353
|
-
setActiveIndex(-1);
|
|
354
|
-
});
|
|
354
|
+
const observer = mutationObserverRef.current = new MutationObserver(sync);
|
|
355
355
|
observer.observe(containerRef.current, { childList: true, subtree: true });
|
|
356
356
|
return () => observer.disconnect();
|
|
357
|
-
}, [shouldSnub, containerRef]);
|
|
357
|
+
}, [shouldSnub, containerRef, sync]);
|
|
358
358
|
(0, import_react3.useEffect)(() => {
|
|
359
359
|
if (shouldSnub()) return;
|
|
360
|
-
const all = itemsRef.current;
|
|
361
360
|
const setGrid = (g) => {
|
|
362
361
|
if (g.x) setGridX(g.x);
|
|
363
362
|
if (g.y) setGridY(g.y);
|
|
@@ -365,14 +364,19 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
365
364
|
};
|
|
366
365
|
setGrid(grid);
|
|
367
366
|
if (grid.x && grid.y && grid.vY) return;
|
|
368
|
-
|
|
369
|
-
calcGrid();
|
|
367
|
+
let ancestor = null;
|
|
370
368
|
const resizeObserver = new ResizeObserver(() => calcGrid());
|
|
371
|
-
|
|
369
|
+
const calcGrid = () => {
|
|
370
|
+
setGrid(getGrid(itemsRef.current, !grid.x, !grid.y, !grid.vY));
|
|
371
|
+
const next = itemsRef.current.length > 1 ? getCommonAncestor(itemsRef.current[0], itemsRef.current[1]) : containerRef.current;
|
|
372
|
+
if (next && next !== ancestor) ancestor && resizeObserver.unobserve(ancestor), ancestor = next, resizeObserver.observe(ancestor);
|
|
373
|
+
};
|
|
374
|
+
latest.current.calcGrid = calcGrid;
|
|
375
|
+
calcGrid();
|
|
372
376
|
return () => resizeObserver.disconnect();
|
|
373
377
|
}, [shouldSnub, containerRef, grid]);
|
|
374
378
|
(0, import_react3.useEffect)(() => void (timeout.current && clearTimeout(timeout.current)), []);
|
|
375
|
-
return { gridX, gridY, vGridY, activeIndex, activeItem: (0, import_react3.useCallback)(() => itemsRef.current[activeIndex] ?? null, [activeIndex]), items: (0, import_react3.useCallback)(() => itemsRef.current, []), getAbleIndex, typeAhead, goToIndex, simulateKey };
|
|
379
|
+
return { gridX, gridY, vGridY, activeIndex, activeItem: (0, import_react3.useCallback)(() => itemsRef.current[activeIndex] ?? null, [activeIndex]), items: (0, import_react3.useCallback)(() => itemsRef.current, []), getAbleIndex, typeAhead, goToIndex, simulateKey, sync };
|
|
376
380
|
}
|
|
377
381
|
|
|
378
382
|
// src/ts/hooks/react/useFocusTrap.ts
|
|
@@ -381,10 +385,11 @@ var import_react4 = require("react");
|
|
|
381
385
|
// src/ts/hooks/vanilla/focusTrap.ts
|
|
382
386
|
var import_sia_reactor6 = require("sia-reactor");
|
|
383
387
|
function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor6.NIL) {
|
|
384
|
-
const
|
|
388
|
+
const stack = t007._ftrappers_stack ??= [], existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
385
389
|
if (!enabled || existing) return existing ? existing : void 0;
|
|
386
390
|
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
387
|
-
|
|
391
|
+
let recovering = false;
|
|
392
|
+
const focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = (0, import_utils2.createEl)("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = (0, import_utils2.createEl)("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => [...c.querySelectorAll(INTERACTIVE_SELECTOR)], resetFocus = (i = 0, els = getFocusable()) => !recovering && (recovering = true, els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), recovering = false), edgeFocus = (pre = false, rt = root) => {
|
|
388
393
|
if (!scoped) return resetFocus(pre ? -1 : 0);
|
|
389
394
|
if (rt.hasAttribute("tabindex")) return rt.focus();
|
|
390
395
|
const items = getFocusable();
|
|
@@ -394,28 +399,33 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
|
|
|
394
399
|
while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
|
|
395
400
|
for (let target, len = all.length, i = all.indexOf(items[pre ? 0 : items.length - 1]) + (pre ? -1 : 1); pre ? i >= 0 : i < len; pre ? i-- : i++) if (!rt.contains(target = all[i])) return target.focus();
|
|
396
401
|
(pre ? first : last).blur();
|
|
397
|
-
}, handleFocusIn = () => {
|
|
402
|
+
}, handleFocusIn = (e) => {
|
|
398
403
|
if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
|
|
399
|
-
stack.at(-1) === el && (0, import_utils3.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils3.getActiveEl)(el.ownerDocument)) && resetFocus();
|
|
404
|
+
stack.at(-1) === el && (0, import_utils3.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils3.getActiveEl)(el.ownerDocument)) && !e.composedPath().includes(el) && resetFocus();
|
|
400
405
|
}, handleInitialBlur = () => initial.classList.remove(ringClassName);
|
|
401
406
|
first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
|
|
402
407
|
last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
|
|
403
408
|
root.addEventListener("focusin", handleFocusIn, capture);
|
|
404
409
|
if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
|
|
405
|
-
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
406
410
|
const destroy = () => {
|
|
407
411
|
focused?.isConnected && focused.focus(), first.remove(), last.remove();
|
|
408
412
|
root.removeEventListener("focusin", handleFocusIn, capture);
|
|
409
413
|
initial?.removeEventListener("blur", handleInitialBlur, capture);
|
|
410
414
|
t007._ftrappers.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
411
415
|
};
|
|
412
|
-
|
|
416
|
+
const handle = { destroy, sync: () => (el.prepend(first), el.append(last)) };
|
|
417
|
+
return !stack.includes(el) && stack.push(el), t007._ftrappers.set(el, handle), handle;
|
|
413
418
|
}
|
|
414
419
|
|
|
415
420
|
// src/ts/hooks/react/useFocusTrap.ts
|
|
416
421
|
var import_sia_reactor7 = require("sia-reactor");
|
|
417
422
|
function useFocusTrap(ref, config = import_sia_reactor7.NIL) {
|
|
418
|
-
(0, import_react4.
|
|
423
|
+
const handle = (0, import_react4.useRef)(void 0);
|
|
424
|
+
(0, import_react4.useEffect)(() => {
|
|
425
|
+
if (!ref.current) return;
|
|
426
|
+
return handle.current = initFocusTrap(ref.current, config), () => handle.current?.destroy();
|
|
427
|
+
}, [ref, config.enabled, config.initialSelector, config.ringClassName, config.root, config.scoped, config.capture]);
|
|
428
|
+
return { sync: () => handle.current?.sync() };
|
|
419
429
|
}
|
|
420
430
|
|
|
421
431
|
// src/ts/hooks/react/useHighlight.ts
|
package/dist/hooks/react.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
-
import { a as ScrollAssistConfig, C as Config, K as KeyEvent } from '../scrollAssist-
|
|
3
|
-
import { O as OutsideClickConfig,
|
|
2
|
+
import { a as ScrollAssistConfig, C as Config, K as KeyEvent, b as FocusTrapConfig } from '../scrollAssist-BNvPnsJq.cjs';
|
|
3
|
+
import { O as OutsideClickConfig, R as RippleConfig } from '../ripple-D1kGErTn.cjs';
|
|
4
4
|
export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DYc_GF0R.cjs';
|
|
5
5
|
|
|
6
6
|
/** Configuration options for the `useScrollAssist` hook. */
|
|
@@ -33,10 +33,13 @@ declare function useArrowNavigation(containerRef: React.RefObject<HTMLElement>,
|
|
|
33
33
|
typeAhead: (key: string) => void;
|
|
34
34
|
goToIndex: (targetIndex: number, e?: KeyEvent) => void;
|
|
35
35
|
simulateKey: (e: KeyEvent) => void;
|
|
36
|
+
sync: () => void;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
/** React hook
|
|
39
|
-
declare function useFocusTrap(ref: RefObject<HTMLElement>, config?: FocusTrapConfig):
|
|
39
|
+
/** React hook to keep focus trapped inside an element until disabled. */
|
|
40
|
+
declare function useFocusTrap(ref: RefObject<HTMLElement>, config?: FocusTrapConfig): {
|
|
41
|
+
sync: () => void | undefined;
|
|
42
|
+
};
|
|
40
43
|
|
|
41
44
|
/** React hook to render and control a material-style ripple animation on an element.
|
|
42
45
|
* @returns Stable pointer handler for clickable UI surfaces.
|
package/dist/hooks/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
-
import { a as ScrollAssistConfig, C as Config, K as KeyEvent } from '../scrollAssist-
|
|
3
|
-
import { O as OutsideClickConfig,
|
|
2
|
+
import { a as ScrollAssistConfig, C as Config, K as KeyEvent, b as FocusTrapConfig } from '../scrollAssist-BNvPnsJq.js';
|
|
3
|
+
import { O as OutsideClickConfig, R as RippleConfig } from '../ripple-D1kGErTn.js';
|
|
4
4
|
export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DYc_GF0R.js';
|
|
5
5
|
|
|
6
6
|
/** Configuration options for the `useScrollAssist` hook. */
|
|
@@ -33,10 +33,13 @@ declare function useArrowNavigation(containerRef: React.RefObject<HTMLElement>,
|
|
|
33
33
|
typeAhead: (key: string) => void;
|
|
34
34
|
goToIndex: (targetIndex: number, e?: KeyEvent) => void;
|
|
35
35
|
simulateKey: (e: KeyEvent) => void;
|
|
36
|
+
sync: () => void;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
/** React hook
|
|
39
|
-
declare function useFocusTrap(ref: RefObject<HTMLElement>, config?: FocusTrapConfig):
|
|
39
|
+
/** React hook to keep focus trapped inside an element until disabled. */
|
|
40
|
+
declare function useFocusTrap(ref: RefObject<HTMLElement>, config?: FocusTrapConfig): {
|
|
41
|
+
sync: () => void | undefined;
|
|
42
|
+
};
|
|
40
43
|
|
|
41
44
|
/** React hook to render and control a material-style ripple animation on an element.
|
|
42
45
|
* @returns Stable pointer handler for clickable UI surfaces.
|
package/dist/hooks/react.js
CHANGED
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
initFocusTrap,
|
|
12
12
|
initOutsideClick,
|
|
13
13
|
rippleHandler
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-NX2LWYD6.js";
|
|
15
15
|
import {
|
|
16
16
|
INTERACTIVE_SELECTOR,
|
|
17
17
|
getActiveEl
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-ZALPHCSJ.js";
|
|
19
19
|
|
|
20
20
|
// src/ts/hooks/react/useScrollAssist.ts
|
|
21
21
|
import { useEffect, useRef, useCallback } from "react";
|
|
@@ -107,7 +107,7 @@ function useOutsideClick(ref, config = NIL2) {
|
|
|
107
107
|
// src/ts/hooks/react/useArrowNavigation/index.ts
|
|
108
108
|
import { useEffect as useEffect3, useState, useCallback as useCallback2, useRef as useRef2, useMemo } from "react";
|
|
109
109
|
function useArrowNavigation(containerRef, config = {}) {
|
|
110
|
-
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, rovingTab, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut } = { ...DEFAULT_CONFIG, ...config }, [gridX, setGridX] = useState(grid.x || 1), [gridY, setGridY] = useState(grid.y || 1), [vGridY, setVGridY] = useState(grid.vY || 1), [activeIndex, setActiveIndex] = useState(-1), buffer = useRef2(""), timeout = useRef2(null), itemsRef = useRef2([]), enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = useMemo(() => isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(containerRef.current || document.body).direction === "rtl"), [containerRef, isRtl]), mutationObserverRef = useRef2(null), shouldSnub = useCallback2(() => !enabled || !containerRef.current, [enabled, containerRef]), isItemDisabled = useCallback2((el) => !el
|
|
110
|
+
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, rovingTab, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut } = { ...DEFAULT_CONFIG, ...config }, [gridX, setGridX] = useState(grid.x || 1), [gridY, setGridY] = useState(grid.y || 1), [vGridY, setVGridY] = useState(grid.vY || 1), [activeIndex, setActiveIndex] = useState(-1), buffer = useRef2(""), timeout = useRef2(null), itemsRef = useRef2([]), enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = useMemo(() => isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(containerRef.current || document.body).direction === "rtl"), [containerRef, isRtl]), mutationObserverRef = useRef2(null), shouldSnub = useCallback2(() => !enabled || !containerRef.current, [enabled, containerRef]), isItemDisabled = useCallback2((el) => !el || el.matches('[disabled],[aria-disabled="true"],[inert],:disabled'), []), getItems = useCallback2(() => itemsRef.current = Array.from(containerRef.current?.querySelectorAll(selector) || []), [containerRef, selector]);
|
|
111
111
|
const getAbleIndex = useCallback2(
|
|
112
112
|
(targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
|
|
113
113
|
if (shouldSnub()) return;
|
|
@@ -181,8 +181,9 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
181
181
|
},
|
|
182
182
|
[shouldSnub, virtual, activeIndex, gridX, gridY, vGridY, loop, rtl, goToIndex, typeahead, typeAhead]
|
|
183
183
|
);
|
|
184
|
-
const latest = useRef2({ getItems, updateDOM, activeIndex
|
|
185
|
-
|
|
184
|
+
const latest = useRef2({ getItems, updateDOM, activeIndex, calcGrid: () => {
|
|
185
|
+
} });
|
|
186
|
+
useEffect3(() => void (latest.current = { ...latest.current, getItems, updateDOM, activeIndex }), [getItems, updateDOM, activeIndex]);
|
|
186
187
|
useEffect3(() => void getItems(), [getItems, enabled]);
|
|
187
188
|
useEffect3(() => void (!enabled && setActiveIndex(-1)), [enabled]);
|
|
188
189
|
useEffect3(() => updateDOM(), [activeIndex, updateDOM]);
|
|
@@ -211,8 +212,7 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
211
212
|
const all = itemsRef.current;
|
|
212
213
|
const handleHover = (e) => {
|
|
213
214
|
if (!focusOnHover) return;
|
|
214
|
-
const el = e.currentTarget;
|
|
215
|
-
const i = itemsRef.current?.indexOf(el);
|
|
215
|
+
const el = e.currentTarget, i = itemsRef.current?.indexOf(el);
|
|
216
216
|
if (i !== -1) goToIndex(i);
|
|
217
217
|
};
|
|
218
218
|
for (const el of all) el.addEventListener("mouseenter", handleHover);
|
|
@@ -220,23 +220,23 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
220
220
|
for (const el of all) el.removeEventListener("mouseenter", handleHover);
|
|
221
221
|
};
|
|
222
222
|
}, [enabled, focusOnHover, goToIndex]);
|
|
223
|
+
const sync = useCallback2(() => {
|
|
224
|
+
const { getItems: getItems2, updateDOM: updateDOM2, activeIndex: activeIndex2 } = latest.current;
|
|
225
|
+
const oldEl = itemsRef.current[activeIndex2];
|
|
226
|
+
getItems2();
|
|
227
|
+
const newEl = itemsRef.current[activeIndex2];
|
|
228
|
+
updateDOM2();
|
|
229
|
+
if (oldEl && newEl && oldEl === newEl) return;
|
|
230
|
+
setActiveIndex(-1);
|
|
231
|
+
}, []);
|
|
223
232
|
useEffect3(() => {
|
|
224
233
|
if (shouldSnub()) return;
|
|
225
|
-
const observer = mutationObserverRef.current = new MutationObserver(
|
|
226
|
-
const { getItems: getItems2, updateDOM: updateDOM2, activeIndex: activeIndex2 } = latest.current;
|
|
227
|
-
const oldEl = itemsRef.current[activeIndex2];
|
|
228
|
-
getItems2();
|
|
229
|
-
const newEl = itemsRef.current[activeIndex2];
|
|
230
|
-
updateDOM2();
|
|
231
|
-
if (oldEl && newEl && oldEl === newEl) return;
|
|
232
|
-
setActiveIndex(-1);
|
|
233
|
-
});
|
|
234
|
+
const observer = mutationObserverRef.current = new MutationObserver(sync);
|
|
234
235
|
observer.observe(containerRef.current, { childList: true, subtree: true });
|
|
235
236
|
return () => observer.disconnect();
|
|
236
|
-
}, [shouldSnub, containerRef]);
|
|
237
|
+
}, [shouldSnub, containerRef, sync]);
|
|
237
238
|
useEffect3(() => {
|
|
238
239
|
if (shouldSnub()) return;
|
|
239
|
-
const all = itemsRef.current;
|
|
240
240
|
const setGrid = (g) => {
|
|
241
241
|
if (g.x) setGridX(g.x);
|
|
242
242
|
if (g.y) setGridY(g.y);
|
|
@@ -244,21 +244,31 @@ function useArrowNavigation(containerRef, config = {}) {
|
|
|
244
244
|
};
|
|
245
245
|
setGrid(grid);
|
|
246
246
|
if (grid.x && grid.y && grid.vY) return;
|
|
247
|
-
|
|
248
|
-
calcGrid();
|
|
247
|
+
let ancestor = null;
|
|
249
248
|
const resizeObserver = new ResizeObserver(() => calcGrid());
|
|
250
|
-
|
|
249
|
+
const calcGrid = () => {
|
|
250
|
+
setGrid(getGrid(itemsRef.current, !grid.x, !grid.y, !grid.vY));
|
|
251
|
+
const next = itemsRef.current.length > 1 ? getCommonAncestor(itemsRef.current[0], itemsRef.current[1]) : containerRef.current;
|
|
252
|
+
if (next && next !== ancestor) ancestor && resizeObserver.unobserve(ancestor), ancestor = next, resizeObserver.observe(ancestor);
|
|
253
|
+
};
|
|
254
|
+
latest.current.calcGrid = calcGrid;
|
|
255
|
+
calcGrid();
|
|
251
256
|
return () => resizeObserver.disconnect();
|
|
252
257
|
}, [shouldSnub, containerRef, grid]);
|
|
253
258
|
useEffect3(() => void (timeout.current && clearTimeout(timeout.current)), []);
|
|
254
|
-
return { gridX, gridY, vGridY, activeIndex, activeItem: useCallback2(() => itemsRef.current[activeIndex] ?? null, [activeIndex]), items: useCallback2(() => itemsRef.current, []), getAbleIndex, typeAhead, goToIndex, simulateKey };
|
|
259
|
+
return { gridX, gridY, vGridY, activeIndex, activeItem: useCallback2(() => itemsRef.current[activeIndex] ?? null, [activeIndex]), items: useCallback2(() => itemsRef.current, []), getAbleIndex, typeAhead, goToIndex, simulateKey, sync };
|
|
255
260
|
}
|
|
256
261
|
|
|
257
262
|
// src/ts/hooks/react/useFocusTrap.ts
|
|
258
|
-
import { useEffect as useEffect4 } from "react";
|
|
263
|
+
import { useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
259
264
|
import { NIL as NIL3 } from "sia-reactor";
|
|
260
265
|
function useFocusTrap(ref, config = NIL3) {
|
|
261
|
-
|
|
266
|
+
const handle = useRef3(void 0);
|
|
267
|
+
useEffect4(() => {
|
|
268
|
+
if (!ref.current) return;
|
|
269
|
+
return handle.current = initFocusTrap(ref.current, config), () => handle.current?.destroy();
|
|
270
|
+
}, [ref, config.enabled, config.initialSelector, config.ringClassName, config.root, config.scoped, config.capture]);
|
|
271
|
+
return { sync: () => handle.current?.sync() };
|
|
262
272
|
}
|
|
263
273
|
|
|
264
274
|
// src/ts/hooks/react/useRipple.ts
|
package/dist/hooks/vanilla.cjs
CHANGED
|
@@ -29,7 +29,9 @@ __export(vanilla_exports, {
|
|
|
29
29
|
removeFocusTrap: () => removeFocusTrap,
|
|
30
30
|
removeOutsideClick: () => removeOutsideClick,
|
|
31
31
|
removeScrollAssist: () => removeScrollAssist,
|
|
32
|
-
rippleHandler: () => rippleHandler
|
|
32
|
+
rippleHandler: () => rippleHandler,
|
|
33
|
+
syncArrowNavigation: () => syncArrowNavigation,
|
|
34
|
+
syncFocusTrap: () => syncFocusTrap
|
|
33
35
|
});
|
|
34
36
|
module.exports = __toCommonJS(vanilla_exports);
|
|
35
37
|
|
|
@@ -138,34 +140,34 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
|
|
|
138
140
|
// src/ts/hooks/vanilla/outsideClick.ts
|
|
139
141
|
var import_sia_reactor4 = require("sia-reactor");
|
|
140
142
|
function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor4.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowBounds = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor4.NIL) {
|
|
141
|
-
const
|
|
143
|
+
const stack = t007._outsiders_stack ??= [], existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
142
144
|
if (!enabled || existing) return existing ? existing : void 0;
|
|
143
145
|
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
144
|
-
const
|
|
146
|
+
const onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
|
|
145
147
|
if (stack.at(-1) !== el || (allowBounds ? p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom : el.contains(t))) return false;
|
|
146
148
|
return (!scoped ? true : root.contains(t)) && onOutside(e);
|
|
147
149
|
}, handleClick = ((e) => outOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => outOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutside(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
|
|
148
150
|
root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
|
|
149
151
|
root.addEventListener("keydown", handleEscape, capture);
|
|
150
152
|
el.addEventListener("focusout", handleFocusOut, capture);
|
|
151
|
-
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
152
153
|
const destroy = () => {
|
|
153
154
|
root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
|
|
154
155
|
root.removeEventListener("keydown", handleEscape, capture);
|
|
155
156
|
el.removeEventListener("focusout", handleFocusOut, capture);
|
|
156
157
|
t007._outsiders.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
157
158
|
};
|
|
158
|
-
return t007._outsiders.set(el, destroy), destroy;
|
|
159
|
+
return !stack.includes(el) && stack.push(el), t007._outsiders.set(el, destroy), destroy;
|
|
159
160
|
}
|
|
160
161
|
var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
|
|
161
162
|
|
|
162
163
|
// src/ts/hooks/vanilla/focusTrap.ts
|
|
163
164
|
var import_sia_reactor5 = require("sia-reactor");
|
|
164
165
|
function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor5.NIL) {
|
|
165
|
-
const
|
|
166
|
+
const stack = t007._ftrappers_stack ??= [], existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
166
167
|
if (!enabled || existing) return existing ? existing : void 0;
|
|
167
168
|
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
168
|
-
|
|
169
|
+
let recovering = false;
|
|
170
|
+
const focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = (0, import_utils.createEl)("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = (0, import_utils.createEl)("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => [...c.querySelectorAll(INTERACTIVE_SELECTOR)], resetFocus = (i = 0, els = getFocusable()) => !recovering && (recovering = true, els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), recovering = false), edgeFocus = (pre = false, rt = root) => {
|
|
169
171
|
if (!scoped) return resetFocus(pre ? -1 : 0);
|
|
170
172
|
if (rt.hasAttribute("tabindex")) return rt.focus();
|
|
171
173
|
const items = getFocusable();
|
|
@@ -175,24 +177,25 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
|
|
|
175
177
|
while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
|
|
176
178
|
for (let target, len = all.length, i = all.indexOf(items[pre ? 0 : items.length - 1]) + (pre ? -1 : 1); pre ? i >= 0 : i < len; pre ? i-- : i++) if (!rt.contains(target = all[i])) return target.focus();
|
|
177
179
|
(pre ? first : last).blur();
|
|
178
|
-
}, handleFocusIn = () => {
|
|
180
|
+
}, handleFocusIn = (e) => {
|
|
179
181
|
if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
|
|
180
|
-
stack.at(-1) === el && (0, import_utils2.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils2.getActiveEl)(el.ownerDocument)) && resetFocus();
|
|
182
|
+
stack.at(-1) === el && (0, import_utils2.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils2.getActiveEl)(el.ownerDocument)) && !e.composedPath().includes(el) && resetFocus();
|
|
181
183
|
}, handleInitialBlur = () => initial.classList.remove(ringClassName);
|
|
182
184
|
first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
|
|
183
185
|
last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
|
|
184
186
|
root.addEventListener("focusin", handleFocusIn, capture);
|
|
185
187
|
if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
|
|
186
|
-
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
187
188
|
const destroy = () => {
|
|
188
189
|
focused?.isConnected && focused.focus(), first.remove(), last.remove();
|
|
189
190
|
root.removeEventListener("focusin", handleFocusIn, capture);
|
|
190
191
|
initial?.removeEventListener("blur", handleInitialBlur, capture);
|
|
191
192
|
t007._ftrappers.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
192
193
|
};
|
|
193
|
-
|
|
194
|
+
const handle = { destroy, sync: () => (el.prepend(first), el.append(last)) };
|
|
195
|
+
return !stack.includes(el) && stack.push(el), t007._ftrappers.set(el, handle), handle;
|
|
194
196
|
}
|
|
195
|
-
var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
|
|
197
|
+
var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.destroy();
|
|
198
|
+
var syncFocusTrap = (el) => t007._ftrappers?.get(el)?.sync();
|
|
196
199
|
|
|
197
200
|
// src/ts/hooks/react/useArrowNavigation/consts.ts
|
|
198
201
|
var import_sia_reactor6 = require("sia-reactor");
|
|
@@ -270,7 +273,7 @@ function initArrowNavigation(container, config = {}) {
|
|
|
270
273
|
if (!config.enabled || existing) return existing ? existing : void 0;
|
|
271
274
|
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut, rovingTab } = { ...DEFAULT_CONFIG, ...config };
|
|
272
275
|
let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
|
|
273
|
-
const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.
|
|
276
|
+
const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.matches('[disabled],[aria-disabled="true"],[inert],:disabled'), getItems = () => items = Array.from(container.querySelectorAll(selector));
|
|
274
277
|
const getAbleIndex = (targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
|
|
275
278
|
if (shouldSnub() || !items.length) return null;
|
|
276
279
|
let index = targetIndex;
|
|
@@ -345,37 +348,45 @@ function initArrowNavigation(container, config = {}) {
|
|
|
345
348
|
if (idx !== -1) goToIndex(idx);
|
|
346
349
|
};
|
|
347
350
|
for (const el of items) el.addEventListener("mouseenter", handleHover);
|
|
348
|
-
const
|
|
351
|
+
const sync = () => {
|
|
349
352
|
const oldEl = items[activeIndex];
|
|
353
|
+
for (const el of items) el.removeEventListener("mouseenter", handleHover);
|
|
350
354
|
getItems();
|
|
355
|
+
for (const el of items) el.addEventListener("mouseenter", handleHover);
|
|
356
|
+
calcGrid();
|
|
351
357
|
const newEl = items[activeIndex];
|
|
352
358
|
updateDOM();
|
|
353
359
|
if (oldEl && newEl && oldEl === newEl) return;
|
|
354
|
-
resetActiveIndex();
|
|
355
|
-
|
|
356
|
-
|
|
360
|
+
resetActiveIndex(), updateDOM();
|
|
361
|
+
};
|
|
362
|
+
const mutationObserver = new MutationObserver(sync);
|
|
357
363
|
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
358
364
|
const setGrid = (g) => {
|
|
359
365
|
if (g.x !== void 0) gridX = g.x;
|
|
360
366
|
if (g.y !== void 0) gridY = g.y;
|
|
361
367
|
if (g.vY !== void 0) vGridY = g.vY;
|
|
362
368
|
};
|
|
363
|
-
|
|
369
|
+
let ancestor = null;
|
|
370
|
+
const resizeObserver = new ResizeObserver(() => calcGrid());
|
|
371
|
+
const calcGrid = () => {
|
|
372
|
+
setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
|
|
373
|
+
const next = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
|
|
374
|
+
if (next && next !== ancestor) ancestor && resizeObserver.unobserve(ancestor), ancestor = next, resizeObserver.observe(ancestor);
|
|
375
|
+
};
|
|
364
376
|
setGrid(grid), calcGrid();
|
|
365
|
-
const ancestor = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
|
|
366
|
-
const resizeObserver = new ResizeObserver(calcGrid);
|
|
367
|
-
if (ancestor) resizeObserver.observe(ancestor);
|
|
368
377
|
const destroy = () => {
|
|
369
378
|
for (const el of interactiveEls) el?.removeEventListener("keydown", simulateKey);
|
|
370
379
|
container.removeEventListener("focusout", handleFocusOut);
|
|
371
380
|
for (const el of items) el.removeEventListener("mouseenter", handleHover);
|
|
372
381
|
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
373
382
|
if (timeout) clearTimeout(timeout);
|
|
383
|
+
t007._arrownavs.delete(container);
|
|
374
384
|
};
|
|
375
|
-
const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
|
|
385
|
+
const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, sync, destroy };
|
|
376
386
|
return t007._arrownavs.set(container, handle), handle;
|
|
377
387
|
}
|
|
378
388
|
var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
|
|
389
|
+
var syncArrowNavigation = (container) => t007._arrownavs?.get(container)?.sync();
|
|
379
390
|
|
|
380
391
|
// src/ts/hooks/vanilla/ripple.ts
|
|
381
392
|
var import_sia_reactor7 = require("sia-reactor");
|
|
@@ -409,5 +420,7 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
|
|
|
409
420
|
removeFocusTrap,
|
|
410
421
|
removeOutsideClick,
|
|
411
422
|
removeScrollAssist,
|
|
412
|
-
rippleHandler
|
|
423
|
+
rippleHandler,
|
|
424
|
+
syncArrowNavigation,
|
|
425
|
+
syncFocusTrap
|
|
413
426
|
});
|
package/dist/hooks/vanilla.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { a as ScrollAssistConfig, S as ScrollAssistHandle,
|
|
2
|
-
export {
|
|
3
|
-
export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-
|
|
1
|
+
export { b as FocusTrapConfig, F as FocusTrapHandle, a as ScrollAssistConfig, S as ScrollAssistHandle, c as ScrollDir, i as initFocusTrap, d as initScrollAssist, r as removeFocusTrap, e as removeScrollAssist, s as syncFocusTrap } from '../scrollAssist-BNvPnsJq.cjs';
|
|
2
|
+
export { O as OutsideClickConfig, R as RippleConfig, i as initOutsideClick, r as removeOutsideClick, a as rippleHandler } from '../ripple-D1kGErTn.cjs';
|
|
3
|
+
export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation, s as syncArrowNavigation } from '../arrowNavigation-zEJbo8Er.cjs';
|
|
4
4
|
|
|
5
5
|
/** Configuration for the vertical edge-scrolling helper. */
|
|
6
6
|
interface ScrolleratorOptions {
|
package/dist/hooks/vanilla.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { a as ScrollAssistConfig, S as ScrollAssistHandle,
|
|
2
|
-
export {
|
|
3
|
-
export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-
|
|
1
|
+
export { b as FocusTrapConfig, F as FocusTrapHandle, a as ScrollAssistConfig, S as ScrollAssistHandle, c as ScrollDir, i as initFocusTrap, d as initScrollAssist, r as removeFocusTrap, e as removeScrollAssist, s as syncFocusTrap } from '../scrollAssist-BNvPnsJq.js';
|
|
2
|
+
export { O as OutsideClickConfig, R as RippleConfig, i as initOutsideClick, r as removeOutsideClick, a as rippleHandler } from '../ripple-D1kGErTn.js';
|
|
3
|
+
export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation, s as syncArrowNavigation } from '../arrowNavigation-B9bEdoqR.js';
|
|
4
4
|
|
|
5
5
|
/** Configuration for the vertical edge-scrolling helper. */
|
|
6
6
|
interface ScrolleratorOptions {
|
package/dist/hooks/vanilla.js
CHANGED
|
@@ -9,13 +9,14 @@ import {
|
|
|
9
9
|
initOutsideClick,
|
|
10
10
|
removeFocusTrap,
|
|
11
11
|
removeOutsideClick,
|
|
12
|
-
rippleHandler
|
|
13
|
-
|
|
12
|
+
rippleHandler,
|
|
13
|
+
syncFocusTrap
|
|
14
|
+
} from "../chunk-NX2LWYD6.js";
|
|
14
15
|
import {
|
|
15
16
|
INTERACTIVE_SELECTOR,
|
|
16
17
|
createEl,
|
|
17
18
|
getActiveEl
|
|
18
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-ZALPHCSJ.js";
|
|
19
20
|
|
|
20
21
|
// src/ts/hooks/vanilla/scrollAssist.ts
|
|
21
22
|
import { NIL } from "sia-reactor";
|
|
@@ -113,7 +114,7 @@ function initArrowNavigation(container, config = {}) {
|
|
|
113
114
|
if (!config.enabled || existing) return existing ? existing : void 0;
|
|
114
115
|
const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut, rovingTab } = { ...DEFAULT_CONFIG, ...config };
|
|
115
116
|
let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
|
|
116
|
-
const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.
|
|
117
|
+
const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.matches('[disabled],[aria-disabled="true"],[inert],:disabled'), getItems = () => items = Array.from(container.querySelectorAll(selector));
|
|
117
118
|
const getAbleIndex = (targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
|
|
118
119
|
if (shouldSnub() || !items.length) return null;
|
|
119
120
|
let index = targetIndex;
|
|
@@ -188,37 +189,45 @@ function initArrowNavigation(container, config = {}) {
|
|
|
188
189
|
if (idx !== -1) goToIndex(idx);
|
|
189
190
|
};
|
|
190
191
|
for (const el of items) el.addEventListener("mouseenter", handleHover);
|
|
191
|
-
const
|
|
192
|
+
const sync = () => {
|
|
192
193
|
const oldEl = items[activeIndex];
|
|
194
|
+
for (const el of items) el.removeEventListener("mouseenter", handleHover);
|
|
193
195
|
getItems();
|
|
196
|
+
for (const el of items) el.addEventListener("mouseenter", handleHover);
|
|
197
|
+
calcGrid();
|
|
194
198
|
const newEl = items[activeIndex];
|
|
195
199
|
updateDOM();
|
|
196
200
|
if (oldEl && newEl && oldEl === newEl) return;
|
|
197
|
-
resetActiveIndex();
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
resetActiveIndex(), updateDOM();
|
|
202
|
+
};
|
|
203
|
+
const mutationObserver = new MutationObserver(sync);
|
|
200
204
|
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
201
205
|
const setGrid = (g) => {
|
|
202
206
|
if (g.x !== void 0) gridX = g.x;
|
|
203
207
|
if (g.y !== void 0) gridY = g.y;
|
|
204
208
|
if (g.vY !== void 0) vGridY = g.vY;
|
|
205
209
|
};
|
|
206
|
-
|
|
210
|
+
let ancestor = null;
|
|
211
|
+
const resizeObserver = new ResizeObserver(() => calcGrid());
|
|
212
|
+
const calcGrid = () => {
|
|
213
|
+
setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
|
|
214
|
+
const next = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
|
|
215
|
+
if (next && next !== ancestor) ancestor && resizeObserver.unobserve(ancestor), ancestor = next, resizeObserver.observe(ancestor);
|
|
216
|
+
};
|
|
207
217
|
setGrid(grid), calcGrid();
|
|
208
|
-
const ancestor = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
|
|
209
|
-
const resizeObserver = new ResizeObserver(calcGrid);
|
|
210
|
-
if (ancestor) resizeObserver.observe(ancestor);
|
|
211
218
|
const destroy = () => {
|
|
212
219
|
for (const el of interactiveEls) el?.removeEventListener("keydown", simulateKey);
|
|
213
220
|
container.removeEventListener("focusout", handleFocusOut);
|
|
214
221
|
for (const el of items) el.removeEventListener("mouseenter", handleHover);
|
|
215
222
|
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
216
223
|
if (timeout) clearTimeout(timeout);
|
|
224
|
+
t007._arrownavs.delete(container);
|
|
217
225
|
};
|
|
218
|
-
const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
|
|
226
|
+
const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, sync, destroy };
|
|
219
227
|
return t007._arrownavs.set(container, handle), handle;
|
|
220
228
|
}
|
|
221
229
|
var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
|
|
230
|
+
var syncArrowNavigation = (container) => t007._arrownavs?.get(container)?.sync();
|
|
222
231
|
export {
|
|
223
232
|
initArrowNavigation,
|
|
224
233
|
initFocusTrap,
|
|
@@ -229,5 +238,7 @@ export {
|
|
|
229
238
|
removeFocusTrap,
|
|
230
239
|
removeOutsideClick,
|
|
231
240
|
removeScrollAssist,
|
|
232
|
-
rippleHandler
|
|
241
|
+
rippleHandler,
|
|
242
|
+
syncArrowNavigation,
|
|
243
|
+
syncFocusTrap
|
|
233
244
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -121,8 +121,12 @@ function loadResource(req, type = "style", { module: module2, media, crossOrigin
|
|
|
121
121
|
function getWindow(el = window) {
|
|
122
122
|
return el?.ownerDocument?.defaultView ?? el?.defaultView ?? window;
|
|
123
123
|
}
|
|
124
|
-
function createListRenderer({ container, getKey, createNode, updateNode = import_sia_reactor.NOOP, destroyNode = import_sia_reactor.NOOP }) {
|
|
124
|
+
function createListRenderer({ container, getKey, createNode, updateNode = import_sia_reactor.NOOP, destroyNode = import_sia_reactor.NOOP, initNode }) {
|
|
125
125
|
let nodeRegistry = /* @__PURE__ */ new Map();
|
|
126
|
+
if (initNode) {
|
|
127
|
+
const children = Array.from(container.children);
|
|
128
|
+
for (let i = 0, len = children.length; i < len; i++) initNode(children[i], (key) => nodeRegistry.set(key, children[i]));
|
|
129
|
+
}
|
|
126
130
|
return function syncDOM(array, strict = true) {
|
|
127
131
|
const newRegistry = /* @__PURE__ */ new Map(), seenKeys = /* @__PURE__ */ new Set(), oldPositions = /* @__PURE__ */ new WeakMap(), children = Array.from(container.children);
|
|
128
132
|
for (let i = 0, len = children.length; i < len; i++) oldPositions.set(children[i], i);
|
|
@@ -288,12 +292,13 @@ function RAFLoop(key, fn, signal, win) {
|
|
|
288
292
|
}
|
|
289
293
|
var cancelRAFLoop = (key) => t007._RAFLoopers ? t007._RAFLoopers.delete(key) : false;
|
|
290
294
|
function limited(FN_KEY, fn, opts = {}) {
|
|
291
|
-
let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
|
|
295
|
+
let count = 0, { key, maxTimes: max = 1, oncePerSession = true } = isStr(opts) ? { key: opts } : opts;
|
|
292
296
|
const getReg = () => JSON.parse(localStorage.getItem(FN_KEY) || "{}"), setReg = (r) => localStorage.setItem(FN_KEY, JSON.stringify(r));
|
|
293
297
|
const handle = (...args) => {
|
|
298
|
+
if (oncePerSession && count > 0) return void 0;
|
|
294
299
|
if (!key) return count++ < max ? fn(...args) : void 0;
|
|
295
300
|
const r = getReg(), c = r[key] || 0;
|
|
296
|
-
return c < max ? (r[key] = c + 1, setReg(r), fn(...args)) : void 0;
|
|
301
|
+
return c < max ? (count++, r[key] = c + 1, setReg(r), fn(...args)) : void 0;
|
|
297
302
|
};
|
|
298
303
|
handle.left = max - (handle.count = count);
|
|
299
304
|
handle.reset = () => (count = 0, key && ((r) => (delete r[key], setReg(r)))(getReg()));
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ArrowNavigationHandle } from './arrowNavigation-
|
|
2
|
-
import { S as ScrollAssistHandle } from './scrollAssist-
|
|
1
|
+
import { A as ArrowNavigationHandle } from './arrowNavigation-zEJbo8Er.cjs';
|
|
2
|
+
import { F as FocusTrapHandle, S as ScrollAssistHandle } from './scrollAssist-BNvPnsJq.cjs';
|
|
3
3
|
export { NIL, NOOP } from 'sia-reactor';
|
|
4
4
|
export { KEYS_BLOCKS, KeyStruct, KeysSettings, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getActiveEl, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
|
|
5
5
|
|
|
@@ -11,12 +11,12 @@ declare global {
|
|
|
11
11
|
_throttlers?: Map<string, number>;
|
|
12
12
|
_debouncers?: Map<string, number>;
|
|
13
13
|
_RAFLoopers?: Map<string, Function>;
|
|
14
|
-
_ftrappers?: WeakMap<HTMLElement,
|
|
14
|
+
_ftrappers?: WeakMap<HTMLElement, FocusTrapHandle>;
|
|
15
15
|
_outsiders?: WeakMap<HTMLElement, () => void>;
|
|
16
16
|
_arrownavs?: WeakMap<HTMLElement, ArrowNavigationHandle>;
|
|
17
17
|
_scrollers?: WeakMap<HTMLElement, ScrollAssistHandle>;
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
_ftrappers_stack?: HTMLElement[];
|
|
19
|
+
_outsiders_stack?: HTMLElement[];
|
|
20
20
|
_scrollers_r_observer?: ResizeObserver;
|
|
21
21
|
_scrollers_m_observer?: MutationObserver;
|
|
22
22
|
}
|
|
@@ -180,8 +180,10 @@ declare const cancelRAFLoop: (key: string) => boolean;
|
|
|
180
180
|
interface LimitedOptions {
|
|
181
181
|
/** Storage key used to persist call counts. */
|
|
182
182
|
key?: string;
|
|
183
|
-
/** Maximum number of allowed calls. */
|
|
183
|
+
/** Maximum number of allowed calls. @default 1 */
|
|
184
184
|
maxTimes?: number;
|
|
185
|
+
/** Only allow calling once per session, regardless of maxTimes. @default true */
|
|
186
|
+
oncePerSession?: boolean;
|
|
185
187
|
}
|
|
186
188
|
interface LimitedHandle<T extends (...args: any[]) => any> {
|
|
187
189
|
/** Call the wrapped function with the original arguments. */
|
|
@@ -268,7 +270,7 @@ declare function loadResource(req: string | symbol, type?: ResourceType, { modul
|
|
|
268
270
|
*/
|
|
269
271
|
declare function getWindow(el?: any): Window & typeof globalThis;
|
|
270
272
|
/** Options for configuring a list renderer */
|
|
271
|
-
type ListRendererOptions<T> = {
|
|
273
|
+
type ListRendererOptions<T, El extends HTMLElement = HTMLElement> = {
|
|
272
274
|
/** The container element to render the list into */
|
|
273
275
|
container: HTMLElement;
|
|
274
276
|
/** Function to extract a unique key from each item.
|
|
@@ -280,24 +282,30 @@ type ListRendererOptions<T> = {
|
|
|
280
282
|
* @param item The item to create a node for
|
|
281
283
|
* @returns An HTMLElement representing the item, or null/undefined to skip rendering
|
|
282
284
|
*/
|
|
283
|
-
createNode: (item: T) =>
|
|
285
|
+
createNode: (item: T) => El | null | undefined;
|
|
284
286
|
/** Optional function to update an existing node with new item data, called when an item is reused.
|
|
285
287
|
* @param node The existing DOM node for the item
|
|
286
288
|
* @param item The new item data to update the node with
|
|
287
289
|
*/
|
|
288
|
-
updateNode?: (node:
|
|
290
|
+
updateNode?: (node: El, item: T) => void;
|
|
289
291
|
/** Optional function to clean up a DOM node when an item is removed, called before the node is removed from the DOM.
|
|
290
292
|
* @param node The DOM node to be removed
|
|
291
293
|
* @param key The unique key of the item associated with the node
|
|
292
294
|
*/
|
|
293
|
-
destroyNode?: (node:
|
|
295
|
+
destroyNode?: (node: El, key: string) => void;
|
|
296
|
+
/** Optional function called once during initialization for each existing child node in the container.
|
|
297
|
+
* Allows seeding the registry with pre-existing markup so nodes are reused instead of destroyed.
|
|
298
|
+
* @param node The pre-existing DOM node
|
|
299
|
+
* @param register A callback to register the node with its corresponding item key
|
|
300
|
+
*/
|
|
301
|
+
initNode?: (node: El, register: (key: string) => void) => void;
|
|
294
302
|
};
|
|
295
303
|
/**
|
|
296
304
|
* Creates a list renderer function for efficiently updating a DOM list based on a new array of items using the L.I.S(Longest Increasing Subsequence) algorithm.
|
|
297
305
|
* @param param0 The options for configuring the list renderer
|
|
298
306
|
* @returns A function that synchronizes the DOM with the new array of items
|
|
299
307
|
*/
|
|
300
|
-
declare function createListRenderer<T>({ container, getKey, createNode, updateNode, destroyNode }: ListRendererOptions<T>): (array: T[], strict?: boolean) => void;
|
|
308
|
+
declare function createListRenderer<T, El extends HTMLElement = HTMLElement>({ container, getKey, createNode, updateNode, destroyNode, initNode }: ListRendererOptions<T, El>): (array: T[], strict?: boolean) => void;
|
|
301
309
|
|
|
302
310
|
/** Format a file size for display.
|
|
303
311
|
* @param size Size in bytes.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ArrowNavigationHandle } from './arrowNavigation-
|
|
2
|
-
import { S as ScrollAssistHandle } from './scrollAssist-
|
|
1
|
+
import { A as ArrowNavigationHandle } from './arrowNavigation-B9bEdoqR.js';
|
|
2
|
+
import { F as FocusTrapHandle, S as ScrollAssistHandle } from './scrollAssist-BNvPnsJq.js';
|
|
3
3
|
export { NIL, NOOP } from 'sia-reactor';
|
|
4
4
|
export { KEYS_BLOCKS, KeyStruct, KeysSettings, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getActiveEl, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
|
|
5
5
|
|
|
@@ -11,12 +11,12 @@ declare global {
|
|
|
11
11
|
_throttlers?: Map<string, number>;
|
|
12
12
|
_debouncers?: Map<string, number>;
|
|
13
13
|
_RAFLoopers?: Map<string, Function>;
|
|
14
|
-
_ftrappers?: WeakMap<HTMLElement,
|
|
14
|
+
_ftrappers?: WeakMap<HTMLElement, FocusTrapHandle>;
|
|
15
15
|
_outsiders?: WeakMap<HTMLElement, () => void>;
|
|
16
16
|
_arrownavs?: WeakMap<HTMLElement, ArrowNavigationHandle>;
|
|
17
17
|
_scrollers?: WeakMap<HTMLElement, ScrollAssistHandle>;
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
_ftrappers_stack?: HTMLElement[];
|
|
19
|
+
_outsiders_stack?: HTMLElement[];
|
|
20
20
|
_scrollers_r_observer?: ResizeObserver;
|
|
21
21
|
_scrollers_m_observer?: MutationObserver;
|
|
22
22
|
}
|
|
@@ -180,8 +180,10 @@ declare const cancelRAFLoop: (key: string) => boolean;
|
|
|
180
180
|
interface LimitedOptions {
|
|
181
181
|
/** Storage key used to persist call counts. */
|
|
182
182
|
key?: string;
|
|
183
|
-
/** Maximum number of allowed calls. */
|
|
183
|
+
/** Maximum number of allowed calls. @default 1 */
|
|
184
184
|
maxTimes?: number;
|
|
185
|
+
/** Only allow calling once per session, regardless of maxTimes. @default true */
|
|
186
|
+
oncePerSession?: boolean;
|
|
185
187
|
}
|
|
186
188
|
interface LimitedHandle<T extends (...args: any[]) => any> {
|
|
187
189
|
/** Call the wrapped function with the original arguments. */
|
|
@@ -268,7 +270,7 @@ declare function loadResource(req: string | symbol, type?: ResourceType, { modul
|
|
|
268
270
|
*/
|
|
269
271
|
declare function getWindow(el?: any): Window & typeof globalThis;
|
|
270
272
|
/** Options for configuring a list renderer */
|
|
271
|
-
type ListRendererOptions<T> = {
|
|
273
|
+
type ListRendererOptions<T, El extends HTMLElement = HTMLElement> = {
|
|
272
274
|
/** The container element to render the list into */
|
|
273
275
|
container: HTMLElement;
|
|
274
276
|
/** Function to extract a unique key from each item.
|
|
@@ -280,24 +282,30 @@ type ListRendererOptions<T> = {
|
|
|
280
282
|
* @param item The item to create a node for
|
|
281
283
|
* @returns An HTMLElement representing the item, or null/undefined to skip rendering
|
|
282
284
|
*/
|
|
283
|
-
createNode: (item: T) =>
|
|
285
|
+
createNode: (item: T) => El | null | undefined;
|
|
284
286
|
/** Optional function to update an existing node with new item data, called when an item is reused.
|
|
285
287
|
* @param node The existing DOM node for the item
|
|
286
288
|
* @param item The new item data to update the node with
|
|
287
289
|
*/
|
|
288
|
-
updateNode?: (node:
|
|
290
|
+
updateNode?: (node: El, item: T) => void;
|
|
289
291
|
/** Optional function to clean up a DOM node when an item is removed, called before the node is removed from the DOM.
|
|
290
292
|
* @param node The DOM node to be removed
|
|
291
293
|
* @param key The unique key of the item associated with the node
|
|
292
294
|
*/
|
|
293
|
-
destroyNode?: (node:
|
|
295
|
+
destroyNode?: (node: El, key: string) => void;
|
|
296
|
+
/** Optional function called once during initialization for each existing child node in the container.
|
|
297
|
+
* Allows seeding the registry with pre-existing markup so nodes are reused instead of destroyed.
|
|
298
|
+
* @param node The pre-existing DOM node
|
|
299
|
+
* @param register A callback to register the node with its corresponding item key
|
|
300
|
+
*/
|
|
301
|
+
initNode?: (node: El, register: (key: string) => void) => void;
|
|
294
302
|
};
|
|
295
303
|
/**
|
|
296
304
|
* Creates a list renderer function for efficiently updating a DOM list based on a new array of items using the L.I.S(Longest Increasing Subsequence) algorithm.
|
|
297
305
|
* @param param0 The options for configuring the list renderer
|
|
298
306
|
* @returns A function that synchronizes the DOM with the new array of items
|
|
299
307
|
*/
|
|
300
|
-
declare function createListRenderer<T>({ container, getKey, createNode, updateNode, destroyNode }: ListRendererOptions<T>): (array: T[], strict?: boolean) => void;
|
|
308
|
+
declare function createListRenderer<T, El extends HTMLElement = HTMLElement>({ container, getKey, createNode, updateNode, destroyNode, initNode }: ListRendererOptions<T, El>): (array: T[], strict?: boolean) => void;
|
|
301
309
|
|
|
302
310
|
/** Format a file size for display.
|
|
303
311
|
* @param size Size in bytes.
|
package/dist/index.js
CHANGED
|
@@ -25,25 +25,6 @@ declare function initOutsideClick(el: HTMLElement, { enabled, onOutside, outOnCl
|
|
|
25
25
|
/** Remove outside-click handling from an element. */
|
|
26
26
|
declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
|
|
27
27
|
|
|
28
|
-
interface FocusTrapConfig {
|
|
29
|
-
/** Enables or disables the focus trap. @default `false`. */
|
|
30
|
-
enabled?: boolean;
|
|
31
|
-
/** The preferred initial focus target selector within the element, overrides whatever was focused. Try `autofocus` attribute if working with dialogs before this. @default `[data-autofocus]`. */
|
|
32
|
-
initialSelector?: string;
|
|
33
|
-
/** The class name for the initial focus ring since programmatic focus is not always visible, `autofocus` attribute in dialogs might work fine as an alternative. @default `"focus-outline"`. */
|
|
34
|
-
ringClassName?: string;
|
|
35
|
-
/** Optional root used to scope focus listeners to an element instead of the window. @default `window`. */
|
|
36
|
-
root?: HTMLElement | Document | Window;
|
|
37
|
-
/** Whether the focus trap is scoped to the root provided it is an HTMLElement. @default `true`. */
|
|
38
|
-
scoped?: boolean;
|
|
39
|
-
/** Passed down to all event listeners used. @default `true`. */
|
|
40
|
-
capture?: boolean;
|
|
41
|
-
}
|
|
42
|
-
/** Hook to keep focus trapped inside an element until disabled. */
|
|
43
|
-
declare function initFocusTrap(el: HTMLElement, { enabled, initialSelector, ringClassName, root, scoped, capture }?: FocusTrapConfig): (() => void) | void;
|
|
44
|
-
/** Remove the focus trap guard from an element. */
|
|
45
|
-
declare const removeFocusTrap: (el: HTMLElement) => void | undefined;
|
|
46
|
-
|
|
47
28
|
interface RippleConfig {
|
|
48
29
|
/** Optional explicit ripple host element. @default event currentTarget. */
|
|
49
30
|
target?: HTMLElement | null;
|
|
@@ -71,4 +52,4 @@ interface RippleConfig {
|
|
|
71
52
|
*/
|
|
72
53
|
declare function rippleHandler(e: Pick<PointerEvent, "target" | "currentTarget" | "button" | "clientX" | "clientY" | "stopPropagation">, { target, forceCenter, wrapperClassName, className, holdClassName, fadeClassName, maxDuration }?: RippleConfig): void;
|
|
73
54
|
|
|
74
|
-
export { type
|
|
55
|
+
export { type OutsideClickConfig as O, type RippleConfig as R, rippleHandler as a, initOutsideClick as i, removeOutsideClick as r };
|
|
@@ -25,25 +25,6 @@ declare function initOutsideClick(el: HTMLElement, { enabled, onOutside, outOnCl
|
|
|
25
25
|
/** Remove outside-click handling from an element. */
|
|
26
26
|
declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
|
|
27
27
|
|
|
28
|
-
interface FocusTrapConfig {
|
|
29
|
-
/** Enables or disables the focus trap. @default `false`. */
|
|
30
|
-
enabled?: boolean;
|
|
31
|
-
/** The preferred initial focus target selector within the element, overrides whatever was focused. Try `autofocus` attribute if working with dialogs before this. @default `[data-autofocus]`. */
|
|
32
|
-
initialSelector?: string;
|
|
33
|
-
/** The class name for the initial focus ring since programmatic focus is not always visible, `autofocus` attribute in dialogs might work fine as an alternative. @default `"focus-outline"`. */
|
|
34
|
-
ringClassName?: string;
|
|
35
|
-
/** Optional root used to scope focus listeners to an element instead of the window. @default `window`. */
|
|
36
|
-
root?: HTMLElement | Document | Window;
|
|
37
|
-
/** Whether the focus trap is scoped to the root provided it is an HTMLElement. @default `true`. */
|
|
38
|
-
scoped?: boolean;
|
|
39
|
-
/** Passed down to all event listeners used. @default `true`. */
|
|
40
|
-
capture?: boolean;
|
|
41
|
-
}
|
|
42
|
-
/** Hook to keep focus trapped inside an element until disabled. */
|
|
43
|
-
declare function initFocusTrap(el: HTMLElement, { enabled, initialSelector, ringClassName, root, scoped, capture }?: FocusTrapConfig): (() => void) | void;
|
|
44
|
-
/** Remove the focus trap guard from an element. */
|
|
45
|
-
declare const removeFocusTrap: (el: HTMLElement) => void | undefined;
|
|
46
|
-
|
|
47
28
|
interface RippleConfig {
|
|
48
29
|
/** Optional explicit ripple host element. @default event currentTarget. */
|
|
49
30
|
target?: HTMLElement | null;
|
|
@@ -71,4 +52,4 @@ interface RippleConfig {
|
|
|
71
52
|
*/
|
|
72
53
|
declare function rippleHandler(e: Pick<PointerEvent, "target" | "currentTarget" | "button" | "clientX" | "clientY" | "stopPropagation">, { target, forceCenter, wrapperClassName, className, holdClassName, fadeClassName, maxDuration }?: RippleConfig): void;
|
|
73
54
|
|
|
74
|
-
export { type
|
|
55
|
+
export { type OutsideClickConfig as O, type RippleConfig as R, rippleHandler as a, initOutsideClick as i, removeOutsideClick as r };
|
|
@@ -38,6 +38,31 @@ type Config = {
|
|
|
38
38
|
onFocusOut?: (e: FocusEvent) => void;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
interface FocusTrapConfig {
|
|
42
|
+
/** Enables or disables the focus trap. @default `false`. */
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
/** The preferred initial focus target selector within the element, overrides whatever was focused. Try `autofocus` attribute if working with dialogs before this. @default `[data-autofocus]`. */
|
|
45
|
+
initialSelector?: string;
|
|
46
|
+
/** The class name for the initial focus ring since programmatic focus is not always visible, `autofocus` attribute in dialogs might work fine as an alternative. @default `"focus-outline"`. */
|
|
47
|
+
ringClassName?: string;
|
|
48
|
+
/** Optional root used to scope focus listeners to an element instead of the window. @default `window`. */
|
|
49
|
+
root?: HTMLElement | Document | Window;
|
|
50
|
+
/** Whether the focus trap is scoped to the root provided it is an HTMLElement. @default `true`. */
|
|
51
|
+
scoped?: boolean;
|
|
52
|
+
/** Passed down to all event listeners used. @default `true`. */
|
|
53
|
+
capture?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface FocusTrapHandle {
|
|
56
|
+
destroy: () => void;
|
|
57
|
+
sync: () => void;
|
|
58
|
+
}
|
|
59
|
+
/** Hook to keep focus trapped inside an element until disabled. */
|
|
60
|
+
declare function initFocusTrap(el: HTMLElement, { enabled, initialSelector, ringClassName, root, scoped, capture }?: FocusTrapConfig): FocusTrapHandle | void;
|
|
61
|
+
/** Remove the focus trap guard from an element. */
|
|
62
|
+
declare const removeFocusTrap: (el: HTMLElement) => void | undefined;
|
|
63
|
+
/** Synchronize the focus trap guards. */
|
|
64
|
+
declare const syncFocusTrap: (el: HTMLElement) => void | undefined;
|
|
65
|
+
|
|
41
66
|
type ScrollDir = "left" | "right" | "up" | "down";
|
|
42
67
|
/** Scroll assist control object returned by initScrollAssist. */
|
|
43
68
|
interface ScrollAssistHandle {
|
|
@@ -69,4 +94,4 @@ declare function initScrollAssist(el: HTMLElement, { pxPerSecond, assistClassNam
|
|
|
69
94
|
*/
|
|
70
95
|
declare const removeScrollAssist: (el: HTMLElement) => void | undefined;
|
|
71
96
|
|
|
72
|
-
export { type Config as C, type KeyEvent as K, type ScrollAssistHandle as S, type ScrollAssistConfig as a, type
|
|
97
|
+
export { type Config as C, type FocusTrapHandle as F, type KeyEvent as K, type ScrollAssistHandle as S, type ScrollAssistConfig as a, type FocusTrapConfig as b, type ScrollDir as c, initScrollAssist as d, removeScrollAssist as e, initFocusTrap as i, removeFocusTrap as r, syncFocusTrap as s };
|
|
@@ -38,6 +38,31 @@ type Config = {
|
|
|
38
38
|
onFocusOut?: (e: FocusEvent) => void;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
interface FocusTrapConfig {
|
|
42
|
+
/** Enables or disables the focus trap. @default `false`. */
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
/** The preferred initial focus target selector within the element, overrides whatever was focused. Try `autofocus` attribute if working with dialogs before this. @default `[data-autofocus]`. */
|
|
45
|
+
initialSelector?: string;
|
|
46
|
+
/** The class name for the initial focus ring since programmatic focus is not always visible, `autofocus` attribute in dialogs might work fine as an alternative. @default `"focus-outline"`. */
|
|
47
|
+
ringClassName?: string;
|
|
48
|
+
/** Optional root used to scope focus listeners to an element instead of the window. @default `window`. */
|
|
49
|
+
root?: HTMLElement | Document | Window;
|
|
50
|
+
/** Whether the focus trap is scoped to the root provided it is an HTMLElement. @default `true`. */
|
|
51
|
+
scoped?: boolean;
|
|
52
|
+
/** Passed down to all event listeners used. @default `true`. */
|
|
53
|
+
capture?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface FocusTrapHandle {
|
|
56
|
+
destroy: () => void;
|
|
57
|
+
sync: () => void;
|
|
58
|
+
}
|
|
59
|
+
/** Hook to keep focus trapped inside an element until disabled. */
|
|
60
|
+
declare function initFocusTrap(el: HTMLElement, { enabled, initialSelector, ringClassName, root, scoped, capture }?: FocusTrapConfig): FocusTrapHandle | void;
|
|
61
|
+
/** Remove the focus trap guard from an element. */
|
|
62
|
+
declare const removeFocusTrap: (el: HTMLElement) => void | undefined;
|
|
63
|
+
/** Synchronize the focus trap guards. */
|
|
64
|
+
declare const syncFocusTrap: (el: HTMLElement) => void | undefined;
|
|
65
|
+
|
|
41
66
|
type ScrollDir = "left" | "right" | "up" | "down";
|
|
42
67
|
/** Scroll assist control object returned by initScrollAssist. */
|
|
43
68
|
interface ScrollAssistHandle {
|
|
@@ -69,4 +94,4 @@ declare function initScrollAssist(el: HTMLElement, { pxPerSecond, assistClassNam
|
|
|
69
94
|
*/
|
|
70
95
|
declare const removeScrollAssist: (el: HTMLElement) => void | undefined;
|
|
71
96
|
|
|
72
|
-
export { type Config as C, type KeyEvent as K, type ScrollAssistHandle as S, type ScrollAssistConfig as a, type
|
|
97
|
+
export { type Config as C, type FocusTrapHandle as F, type KeyEvent as K, type ScrollAssistHandle as S, type ScrollAssistConfig as a, type FocusTrapConfig as b, type ScrollDir as c, initScrollAssist as d, removeScrollAssist as e, initFocusTrap as i, removeFocusTrap as r, syncFocusTrap as s };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t007/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
4
4
|
"description": "High-performance utilities for the t007 ecosystem.",
|
|
5
5
|
"author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"react-dom": "^18.3.1"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"sia-reactor": "^0.0.
|
|
79
|
+
"sia-reactor": "^0.0.36"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"react": "^18.0.0"
|