UNPKG

react-roving-focus

Version:

Flexible roving focus for React with support for any fixed or responsive layout.

302 lines (301 loc) 8.49 kB
import { useRef as R, createContext as oe, useContext as ie, useState as se, useEffect as U, useCallback as g, useMemo as Y } from "react"; import { jsx as ce } from "react/jsx-runtime"; function ue(e) { const t = R(null); return e !== void 0 ? e : t; } const Q = oe( null ); function fe() { const e = ie(Q); if (!e) throw new Error("useRovingFocus must be used within a RovingFocusGroup"); return e; } const je = ({ ref: e, disabled: t = !1 } = {}) => { const n = fe(), [o, i] = se(-1), a = ue(e); U(() => { const s = a.current; if (!(!s || t)) return n.registerElement(s, { onTabIndexChange: i }), () => { n.unregisterElement(s); }; }, [a, n, t]); const m = g( (s) => { switch (s.key) { case "ArrowLeft": n.focusNextElement("left"); break; case "ArrowRight": n.focusNextElement("right"); break; case "ArrowUp": n.focusNextElement("up"); break; case "ArrowDown": n.focusNextElement("down"); break; case "Home": n.focusFirstElement(); break; case "End": n.focusLastElement(); break; default: return; } s.preventDefault(), s.stopPropagation(); }, [n] ), f = g(() => { const s = a.current; !s || t || n.setFocusedElement(s); }, [a, n, t]); return U(() => { const s = a.current; if (!(!s || t)) return s.addEventListener("focus", f), s.addEventListener("keydown", m), () => { s.removeEventListener("focus", f), s.removeEventListener("keydown", m); }; }, [a, n, t, f, m]), { ref: a, tabIndex: o }; }; var A = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {}; function le(e) { return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e; } var G, z; function ae() { if (z) return G; z = 1; var e = "Expected a function", t = NaN, n = "[object Symbol]", o = /^\s+|\s+$/g, i = /^[-+]0x[0-9a-f]+$/i, a = /^0b[01]+$/i, m = /^0o[0-7]+$/i, f = parseInt, s = typeof A == "object" && A && A.Object === Object && A, O = typeof self == "object" && self && self.Object === Object && self, C = s || O || Function("return this")(), y = Object.prototype, b = y.toString, L = Math.max, M = Math.min, w = function() { return C.Date.now(); }; function P(r, l, p) { var T, D, N, k, d, x, j = 0, q = !1, F = !1, _ = !0; if (typeof r != "function") throw new TypeError(e); l = $(l) || 0, c(p) && (q = !!p.leading, F = "maxWait" in p, N = F ? L($(p.maxWait) || 0, l) : N, _ = "trailing" in p ? !!p.trailing : _); function B(u) { var v = T, I = D; return T = D = void 0, j = u, k = r.apply(I, v), k; } function ee(u) { return j = u, d = setTimeout(S, l), q ? B(u) : k; } function te(u) { var v = u - x, I = u - j, V = l - v; return F ? M(V, N - I) : V; } function H(u) { var v = u - x, I = u - j; return x === void 0 || v >= l || v < 0 || F && I >= N; } function S() { var u = w(); if (H(u)) return K(u); d = setTimeout(S, te(u)); } function K(u) { return d = void 0, _ && T ? B(u) : (T = D = void 0, k); } function ne() { d !== void 0 && clearTimeout(d), j = 0, T = x = D = d = void 0; } function re() { return d === void 0 ? k : K(w()); } function X() { var u = w(), v = H(u); if (T = arguments, D = this, x = u, v) { if (d === void 0) return ee(x); if (F) return d = setTimeout(S, l), B(x); } return d === void 0 && (d = setTimeout(S, l)), k; } return X.cancel = ne, X.flush = re, X; } function c(r) { var l = typeof r; return !!r && (l == "object" || l == "function"); } function h(r) { return !!r && typeof r == "object"; } function E(r) { return typeof r == "symbol" || h(r) && b.call(r) == n; } function $(r) { if (typeof r == "number") return r; if (E(r)) return t; if (c(r)) { var l = typeof r.valueOf == "function" ? r.valueOf() : r; r = c(l) ? l + "" : l; } if (typeof r != "string") return r === 0 ? r : +r; r = r.replace(o, ""); var p = a.test(r); return p || m.test(r) ? f(r.slice(2), p ? 2 : 8) : i.test(r) ? t : +r; } return G = P, G; } var me = ae(); const de = /* @__PURE__ */ le(me), ge = (e) => { U(() => e, []); }; function Z(e) { const t = e.getBoundingClientRect(); return { left: t.left, right: t.right, top: t.top, bottom: t.bottom, centerX: t.left + t.width / 2, centerY: t.top + t.height / 2 }; } function W(e, t, n) { return n === "row" ? Math.min(e.bottom, t.bottom) - Math.max(e.top, t.top) > 1 : Math.min(e.right, t.right) - Math.max(e.left, t.left) > 1; } function pe(e, t, n) { switch (n) { case "left": return e.centerX < t.left; case "right": return e.centerX > t.right; case "up": return e.centerY < t.top; case "down": return e.centerY > t.bottom; } } function be(e) { var n; return ((n = e.sort((o, i) => W(o.position, i.position, "row") ? o.position.left - i.position.left : o.position.top - i.position.top)[0]) == null ? void 0 : n.element) ?? null; } function he(e) { var n; return ((n = e.sort((o, i) => W(o.position, i.position, "row") ? i.position.right - o.position.right : i.position.bottom - o.position.bottom)[0]) == null ? void 0 : n.element) ?? null; } function Ee(e, t) { return e.right <= t.left ? t.left - e.right : t.right <= e.left ? e.left - t.right : e.bottom <= t.top ? t.top - e.bottom : t.bottom <= e.top ? e.top - t.bottom : 0; } function ve(e, t) { const n = e.centerX - t.centerX, o = e.centerY - t.centerY; return Math.sqrt(n * n + o * o); } function J(e, t) { const n = e.map(({ element: o, position: i }) => ({ element: o, edgeDistance: Ee(i, t), centerDistance: ve(i, t) })).sort((o, i) => o.edgeDistance !== i.edgeDistance ? o.edgeDistance - i.edgeDistance : o.centerDistance - i.centerDistance); return n.length === 0 ? null : n[0].element; } function xe(e) { return e === "left" || e === "right" ? "row" : "column"; } function ye(e, t, n) { const o = Z(e), i = n.filter(({ element: f, position: s }) => f !== e && pe(s, o, t)); if (i.length === 0) return null; const a = xe(t), m = i.filter( ({ position: f }) => W(f, o, a) ); return m.length > 0 ? J(m, o) : J(i, o); } function we(e) { return !Te(e); } function Te(e) { return "disabled" in e && e.disabled === !0 || e.ariaDisabled === "true"; } function Fe({ children: e }) { const t = R(null), n = R(null), o = R(null), i = R(/* @__PURE__ */ new Map()), a = () => Array.from(i.current.keys()).filter(we).map((c) => ({ element: c, position: Z(c) })), m = Y( () => de(() => { const c = a(); n.current = be(c), o.current = he(c), t.current && !i.current.has(t.current) && (t.current = null); for (const [h, E] of i.current.entries()) t.current === h || n.current === h && !t.current ? E.onTabIndexChange(0) : E.onTabIndexChange(-1); }), [] ), f = g(m, [m]), s = Y( () => new MutationObserver(f), [f] ); ge(() => { m.cancel(), s.disconnect(); }); const O = g( (c, h) => { i.current.set(c, h), f(), s.observe(c, { attributes: !0, attributeFilter: ["disabled", "aria-disabled"] }); }, [f, s] ), C = g( (c) => { i.current.delete(c), f(); }, [f] ), y = g( (c) => { t.current = c, f(); }, [f] ), b = g( (c) => { y(c), c.focus(); }, [y] ), L = g( (c) => { if (!t.current) return; const h = a(), E = ye( t.current, c, h ); E && b(E); }, [b] ), M = g(() => { n.current && b(n.current); }, [b]), w = g(() => { o.current && b(o.current); }, [b]), P = Y( () => ({ registerElement: O, unregisterElement: C, setFocusedElement: y, focusNextElement: L, focusFirstElement: M, focusLastElement: w }), [ O, C, y, L, M, w ] ); return /* @__PURE__ */ ce(Q.Provider, { value: P, children: e }); } export { Fe as RovingFocusGroup, je as useRovingFocus };