@t007/utils 0.0.26 → 0.0.27

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.
@@ -1,6 +1,7 @@
1
1
  // src/core/dom.ts
2
2
  import { createEl, assignEl } from "sia-reactor/utils";
3
- var INTERACTIVE_SELECTOR = 'button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex="-1"])';
3
+ import { getActiveEl } from "sia-reactor/utils";
4
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
4
5
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
5
6
  var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
6
7
  function loadResource(req, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
@@ -30,10 +31,6 @@ function loadResource(req, type = "style", { module, media, crossOrigin, integri
30
31
  });
31
32
  return w.t007._resourceCache[src];
32
33
  }
33
- function getActiveElement(root = document) {
34
- const activeEl = root.activeElement;
35
- return !activeEl ? null : activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl;
36
- }
37
34
 
38
35
  // src/index.ts
39
36
  import { NIL, NOOP } from "sia-reactor";
@@ -78,6 +75,18 @@ import { clamp } from "sia-reactor/utils";
78
75
  function uid(prefix = "") {
79
76
  return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
80
77
  }
78
+ function remToPx(rem, el = document.documentElement) {
79
+ return rem * parseFloat(getComputedStyle(el).fontSize);
80
+ }
81
+ function pxToRem(px, el = document.documentElement) {
82
+ return px / parseFloat(getComputedStyle(el).fontSize);
83
+ }
84
+ function parseCSSTime(time) {
85
+ return time?.endsWith?.("ms") ? parseFloat(time) : parseFloat(time) * 1e3;
86
+ }
87
+ function parseCSSSize(size, el) {
88
+ return size?.endsWith?.("px") ? parseFloat(size) : remToPx(parseFloat(size), el);
89
+ }
81
90
  function isSameURL(src1, src2) {
82
91
  if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
83
92
  try {
@@ -143,7 +152,7 @@ export {
143
152
  isInteractive,
144
153
  VIRTUAL_RESOURCE,
145
154
  loadResource,
146
- getActiveElement,
155
+ getActiveEl,
147
156
  isObj,
148
157
  isDef,
149
158
  isSym,
@@ -157,6 +166,10 @@ export {
157
166
  inBoolArrOpt,
158
167
  clamp,
159
168
  uid,
169
+ remToPx,
170
+ pxToRem,
171
+ parseCSSTime,
172
+ parseCSSSize,
160
173
  isSameURL,
161
174
  limited,
162
175
  mockAsync,
@@ -2,21 +2,20 @@ import {
2
2
  INTERACTIVE_SELECTOR,
3
3
  clamp,
4
4
  createEl,
5
- getActiveElement,
5
+ getActiveEl,
6
6
  isInteractive
7
- } from "./chunk-XVFFZZJA.js";
7
+ } from "./chunk-N5KX6IW4.js";
8
8
 
9
9
  // src/hooks/vanilla/outsideClick.ts
10
10
  import { NIL, NOOP } from "sia-reactor";
11
- var stacks = /* @__PURE__ */ new WeakMap();
12
- function initOutsideClick(el, { enabled = false, onOutsideClick = NOOP, clickOnClick = true, clickOnEscape = true, clickOnFocusOut = false, allowInputs = true, root = window, scoped = true, capture = true } = NIL) {
13
- const existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
11
+ function initOutsideClick(el, { enabled = false, onOutsideClick = NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowInputs = false, root = window, scoped = true, capture = true } = NIL) {
12
+ const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
14
13
  if (!enabled || existing) return existing ? existing : void 0;
15
14
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
16
15
  const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
17
16
  if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
18
17
  return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
19
- }, handleClick = ((e) => clickOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => clickOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutsideClick(e)), handleFocusOut = (e) => clickOnFocusOut && onScopedOut(e, e.relatedTarget);
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 && onOutsideClick(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
20
19
  root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
21
20
  root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
22
21
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
@@ -32,25 +31,29 @@ var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
32
31
 
33
32
  // src/hooks/vanilla/focusTrap.ts
34
33
  import { NIL as NIL2 } from "sia-reactor";
35
- var stacks2 = /* @__PURE__ */ new WeakMap();
36
34
  function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = NIL2) {
37
- const existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
35
+ const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
38
36
  if (!enabled || existing) return existing ? existing : void 0;
39
37
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
40
- const stack = stacks2.get(root) ?? [], 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) => Array.prototype.filter.call(c.querySelectorAll(INTERACTIVE_SELECTOR), (el2) => !el2.hasAttribute("disabled") && !el2.hasAttribute("aria-hidden") && !el2.hasAttribute("data-focus-guard")), resetFocus = (i = 0, els = getFocusable()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false) => {
38
+ const stack = stacks.get(root) ?? [], 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()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false, rt = root) => {
41
39
  if (!scoped) return resetFocus(pre ? -1 : 0);
42
- else if (root.hasAttribute("tabindex")) return root.focus();
40
+ if (rt.hasAttribute("tabindex")) return rt.focus();
43
41
  const items = getFocusable();
44
42
  if (!items.length) return resetFocus(0, null);
45
- const all = getFocusable(root.parentElement?.closest(`:has(${INTERACTIVE_SELECTOR})`) || document.body);
46
- 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 (!root.contains(target = all[i])) return target.focus();
43
+ const ceiling = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
44
+ let p = rt.parentElement || ceiling, all = getFocusable(p);
45
+ while (p !== ceiling && (!all.length || rt.contains(all[0]) && rt.contains(all.at(-1)))) all = getFocusable(p = p.parentElement || ceiling);
46
+ 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();
47
47
  (pre ? first : last).blur();
48
- }, handleFocusIn = () => stack.at(-1) === el && getActiveElement() !== root && !el.contains(getActiveElement()) && setTimeout(resetFocus, 0, 0), handleInitialBlur = () => initial.classList.remove(ringClassName);
48
+ }, handleFocusIn = () => {
49
+ if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
50
+ stack.at(-1) === el && getActiveEl(el.ownerDocument) !== root && !el.contains(getActiveEl(el.ownerDocument)) && resetFocus();
51
+ }, handleInitialBlur = () => initial.classList.remove(ringClassName);
49
52
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
50
53
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
51
54
  root.addEventListener("focusin", handleFocusIn, capture);
52
- if (!el.querySelector(":focus")) !initial ? resetFocus() : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
53
- if (!stack.includes(el)) stack.push(el), stacks2.set(root, stack);
55
+ if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
56
+ if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
54
57
  const destroy = () => {
55
58
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
56
59
  root.removeEventListener("focusin", handleFocusIn, capture);
@@ -37,12 +37,9 @@ var import_utils = require("sia-reactor/utils");
37
37
 
38
38
  // src/core/dom.ts
39
39
  var import_utils2 = require("sia-reactor/utils");
40
- var INTERACTIVE_SELECTOR = 'button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex="-1"])';
40
+ var import_utils3 = require("sia-reactor/utils");
41
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
41
42
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
42
- function getActiveElement(root = document) {
43
- const activeEl = root.activeElement;
44
- return !activeEl ? null : activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl;
45
- }
46
43
 
47
44
  // src/hooks/react/useScrollAssist.ts
48
45
  var import_sia_reactor = require("sia-reactor");
@@ -128,15 +125,14 @@ var import_react2 = require("react");
128
125
 
129
126
  // src/hooks/vanilla/outsideClick.ts
130
127
  var import_sia_reactor2 = require("sia-reactor");
131
- var stacks = /* @__PURE__ */ new WeakMap();
132
- function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_reactor2.NOOP, clickOnClick = true, clickOnEscape = true, clickOnFocusOut = false, allowInputs = true, root = window, scoped = true, capture = true } = import_sia_reactor2.NIL) {
133
- const existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
128
+ function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_reactor2.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor2.NIL) {
129
+ const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
134
130
  if (!enabled || existing) return existing ? existing : void 0;
135
131
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
136
132
  const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
137
133
  if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
138
134
  return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
139
- }, handleClick = ((e) => clickOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => clickOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutsideClick(e)), handleFocusOut = (e) => clickOnFocusOut && onScopedOut(e, e.relatedTarget);
135
+ }, 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 && onOutsideClick(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
140
136
  root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
141
137
  root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
142
138
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
@@ -152,7 +148,7 @@ function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_rea
152
148
  // src/hooks/react/useOutsideClick.ts
153
149
  var import_sia_reactor3 = require("sia-reactor");
154
150
  function useOutsideClick(ref, config = import_sia_reactor3.NIL) {
155
- (0, import_react2.useEffect)(() => ref.current ? initOutsideClick(ref.current, config) : void 0, [ref, config.enabled, config.onOutsideClick, config.clickOnEscape, config.clickOnClick, config.clickOnFocusOut, config.allowInputs, config.root, config.scoped, config.capture]);
151
+ (0, import_react2.useEffect)(() => ref.current ? initOutsideClick(ref.current, config) : void 0, [ref, config.enabled, config.onOutsideClick, config.outOnEscape, config.outOnClick, config.outOnFocusOut, config.allowInputs, config.root, config.scoped, config.capture]);
156
152
  }
157
153
 
158
154
  // src/hooks/react/useArrowNavigation/index.ts
@@ -290,15 +286,16 @@ function useArrowNavigation(containerRef, config = {}) {
290
286
  );
291
287
  const simulateKey = (0, import_react3.useCallback)(
292
288
  (e) => {
293
- if (shouldSnub() || getActiveElement()?.matches("option")) return;
289
+ const t = e.target;
290
+ if (shouldSnub() || (0, import_utils3.getActiveEl)(t?.ownerDocument)?.matches("option")) return;
294
291
  const all = itemsRef.current, { key } = e;
295
292
  if (!all.length) return;
296
293
  if (virtual && (key === " " || key === "Enter")) return all[activeIndex]?.click();
297
- if (e.target?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
294
+ if (t?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
298
295
  if (typeahead && key.length === 1 && /^[a-z0-9]$/i.test(key)) return typeAhead(key);
299
296
  if (!NAV_KEYS.includes(key)) return;
300
297
  if (!(e.currentTarget?.matches(DEFAULT_CONFIG.inputSelector) && gridX <= 1 && H_NAV_KEYS.includes(key))) e.preventDefault?.(), e.stopPropagation?.();
301
- const currIndex = virtual ? activeIndex : all.indexOf(getActiveElement()), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: all.length, loop, rtl, key, ctrlKey: e.ctrlKey });
298
+ const currIndex = virtual ? activeIndex : all.indexOf((0, import_utils3.getActiveEl)(t?.ownerDocument)), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: all.length, loop, rtl, key, ctrlKey: e.ctrlKey });
302
299
  goToIndex(targetIndex, e);
303
300
  },
304
301
  [shouldSnub, virtual, activeIndex, gridX, gridY, vGridY, loop, rtl, goToIndex, typeahead, typeAhead]
@@ -381,25 +378,29 @@ var import_react4 = require("react");
381
378
 
382
379
  // src/hooks/vanilla/focusTrap.ts
383
380
  var import_sia_reactor5 = require("sia-reactor");
384
- var stacks2 = /* @__PURE__ */ new WeakMap();
385
381
  function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor5.NIL) {
386
- const existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
382
+ const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
387
383
  if (!enabled || existing) return existing ? existing : void 0;
388
384
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
389
- const stack = stacks2.get(root) ?? [], 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) => Array.prototype.filter.call(c.querySelectorAll(INTERACTIVE_SELECTOR), (el2) => !el2.hasAttribute("disabled") && !el2.hasAttribute("aria-hidden") && !el2.hasAttribute("data-focus-guard")), resetFocus = (i = 0, els = getFocusable()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false) => {
385
+ const stack = stacks.get(root) ?? [], 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()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false, rt = root) => {
390
386
  if (!scoped) return resetFocus(pre ? -1 : 0);
391
- else if (root.hasAttribute("tabindex")) return root.focus();
387
+ if (rt.hasAttribute("tabindex")) return rt.focus();
392
388
  const items = getFocusable();
393
389
  if (!items.length) return resetFocus(0, null);
394
- const all = getFocusable(root.parentElement?.closest(`:has(${INTERACTIVE_SELECTOR})`) || document.body);
395
- 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 (!root.contains(target = all[i])) return target.focus();
390
+ const ceiling = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
391
+ let p = rt.parentElement || ceiling, all = getFocusable(p);
392
+ while (p !== ceiling && (!all.length || rt.contains(all[0]) && rt.contains(all.at(-1)))) all = getFocusable(p = p.parentElement || ceiling);
393
+ 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
394
  (pre ? first : last).blur();
397
- }, handleFocusIn = () => stack.at(-1) === el && getActiveElement() !== root && !el.contains(getActiveElement()) && setTimeout(resetFocus, 0, 0), handleInitialBlur = () => initial.classList.remove(ringClassName);
395
+ }, handleFocusIn = () => {
396
+ if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
397
+ stack.at(-1) === el && (0, import_utils3.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils3.getActiveEl)(el.ownerDocument)) && resetFocus();
398
+ }, handleInitialBlur = () => initial.classList.remove(ringClassName);
398
399
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
399
400
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
400
401
  root.addEventListener("focusin", handleFocusIn, capture);
401
- if (!el.querySelector(":focus")) !initial ? resetFocus() : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
402
- if (!stack.includes(el)) stack.push(el), stacks2.set(root, stack);
402
+ if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
403
+ if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
403
404
  const destroy = () => {
404
405
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
405
406
  root.removeEventListener("focusin", handleFocusIn, capture);
@@ -1,6 +1,6 @@
1
1
  import { RefObject } from 'react';
2
2
  import { a as ScrollAssistConfig, C as Config, K as KeyEvent } from '../scrollAssist-y9wFmYgt.cjs';
3
- import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-DcMWw_AP.cjs';
3
+ import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-CVQx46Xq.cjs';
4
4
  export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DMpDCILK.cjs';
5
5
 
6
6
  /** Configuration options for the `useScrollAssist` hook. */
@@ -1,6 +1,6 @@
1
1
  import { RefObject } from 'react';
2
2
  import { a as ScrollAssistConfig, C as Config, K as KeyEvent } from '../scrollAssist-y9wFmYgt.js';
3
- import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-DcMWw_AP.js';
3
+ import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-CVQx46Xq.js';
4
4
  export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DMpDCILK.js';
5
5
 
6
6
  /** Configuration options for the `useScrollAssist` hook. */
@@ -11,11 +11,11 @@ import {
11
11
  initFocusTrap,
12
12
  initOutsideClick,
13
13
  rippleHandler
14
- } from "../chunk-AI5O3OGE.js";
14
+ } from "../chunk-NLR4ANGT.js";
15
15
  import {
16
16
  INTERACTIVE_SELECTOR,
17
- getActiveElement
18
- } from "../chunk-XVFFZZJA.js";
17
+ getActiveEl
18
+ } from "../chunk-N5KX6IW4.js";
19
19
 
20
20
  // src/hooks/react/useScrollAssist.ts
21
21
  import { useEffect, useRef, useCallback } from "react";
@@ -101,7 +101,7 @@ function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassNam
101
101
  import { useEffect as useEffect2 } from "react";
102
102
  import { NIL as NIL2 } from "sia-reactor";
103
103
  function useOutsideClick(ref, config = NIL2) {
104
- useEffect2(() => ref.current ? initOutsideClick(ref.current, config) : void 0, [ref, config.enabled, config.onOutsideClick, config.clickOnEscape, config.clickOnClick, config.clickOnFocusOut, config.allowInputs, config.root, config.scoped, config.capture]);
104
+ useEffect2(() => ref.current ? initOutsideClick(ref.current, config) : void 0, [ref, config.enabled, config.onOutsideClick, config.outOnEscape, config.outOnClick, config.outOnFocusOut, config.allowInputs, config.root, config.scoped, config.capture]);
105
105
  }
106
106
 
107
107
  // src/hooks/react/useArrowNavigation/index.ts
@@ -167,15 +167,16 @@ function useArrowNavigation(containerRef, config = {}) {
167
167
  );
168
168
  const simulateKey = useCallback2(
169
169
  (e) => {
170
- if (shouldSnub() || getActiveElement()?.matches("option")) return;
170
+ const t = e.target;
171
+ if (shouldSnub() || getActiveEl(t?.ownerDocument)?.matches("option")) return;
171
172
  const all = itemsRef.current, { key } = e;
172
173
  if (!all.length) return;
173
174
  if (virtual && (key === " " || key === "Enter")) return all[activeIndex]?.click();
174
- if (e.target?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
175
+ if (t?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
175
176
  if (typeahead && key.length === 1 && /^[a-z0-9]$/i.test(key)) return typeAhead(key);
176
177
  if (!NAV_KEYS.includes(key)) return;
177
178
  if (!(e.currentTarget?.matches(DEFAULT_CONFIG.inputSelector) && gridX <= 1 && H_NAV_KEYS.includes(key))) e.preventDefault?.(), e.stopPropagation?.();
178
- const currIndex = virtual ? activeIndex : all.indexOf(getActiveElement()), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: all.length, loop, rtl, key, ctrlKey: e.ctrlKey });
179
+ const currIndex = virtual ? activeIndex : all.indexOf(getActiveEl(t?.ownerDocument)), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: all.length, loop, rtl, key, ctrlKey: e.ctrlKey });
179
180
  goToIndex(targetIndex, e);
180
181
  },
181
182
  [shouldSnub, virtual, activeIndex, gridX, gridY, vGridY, loop, rtl, goToIndex, typeahead, typeAhead]
@@ -38,24 +38,21 @@ var import_sia_reactor = require("sia-reactor");
38
38
 
39
39
  // src/core/dom.ts
40
40
  var import_utils = require("sia-reactor/utils");
41
- var INTERACTIVE_SELECTOR = 'button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex="-1"])';
41
+ var import_utils2 = require("sia-reactor/utils");
42
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
42
43
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
43
- function getActiveElement(root = document) {
44
- const activeEl = root.activeElement;
45
- return !activeEl ? null : activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl;
46
- }
47
44
 
48
45
  // src/core/num.ts
49
- var import_utils2 = require("sia-reactor/utils");
46
+ var import_utils3 = require("sia-reactor/utils");
50
47
 
51
48
  // src/hooks/vanilla/scrollAssist.ts
52
49
  function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor.NIL) {
53
50
  const parent = el?.parentElement, existing = (t007._scrollers ??= /* @__PURE__ */ new WeakMap()).get(el);
54
51
  if (!parent || existing) return existing ? existing : void 0;
55
- t007._scroller_r_observer ??= new ResizeObserver((entries) => {
52
+ t007._scrollers_r_observer ??= new ResizeObserver((entries) => {
56
53
  for (const { target } of entries) t007._scrollers.get(target)?.update();
57
54
  });
58
- t007._scroller_m_observer ??= new MutationObserver((entries) => {
55
+ t007._scrollers_m_observer ??= new MutationObserver((entries) => {
59
56
  const els = /* @__PURE__ */ new Set();
60
57
  for (const entry of entries) {
61
58
  let node = entry.target instanceof Element ? entry.target : null;
@@ -110,12 +107,12 @@ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll
110
107
  update,
111
108
  destroy() {
112
109
  stop(), el.removeEventListener("scroll", update);
113
- t007._scroller_r_observer.unobserve(el), t007._scrollers.delete(el);
110
+ t007._scrollers_r_observer.unobserve(el), t007._scrollers.delete(el);
114
111
  for (const a of Object.values(assist)) a.remove();
115
112
  }
116
113
  };
117
114
  update(), el.addEventListener("scroll", update);
118
- t007._scroller_r_observer.observe(el), t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
115
+ t007._scrollers_r_observer.observe(el), t007._scrollers_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
119
116
  return t007._scrollers.set(el, handle), handle;
120
117
  }
121
118
  var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
@@ -139,15 +136,14 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
139
136
 
140
137
  // src/hooks/vanilla/outsideClick.ts
141
138
  var import_sia_reactor3 = require("sia-reactor");
142
- var stacks = /* @__PURE__ */ new WeakMap();
143
- function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_reactor3.NOOP, clickOnClick = true, clickOnEscape = true, clickOnFocusOut = false, allowInputs = true, root = window, scoped = true, capture = true } = import_sia_reactor3.NIL) {
144
- const existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
139
+ function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_reactor3.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor3.NIL) {
140
+ const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
145
141
  if (!enabled || existing) return existing ? existing : void 0;
146
142
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
147
143
  const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
148
144
  if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
149
145
  return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
150
- }, handleClick = ((e) => clickOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => clickOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutsideClick(e)), handleFocusOut = (e) => clickOnFocusOut && onScopedOut(e, e.relatedTarget);
146
+ }, 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 && onOutsideClick(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
151
147
  root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
152
148
  root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
153
149
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
@@ -163,25 +159,29 @@ var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
163
159
 
164
160
  // src/hooks/vanilla/focusTrap.ts
165
161
  var import_sia_reactor4 = require("sia-reactor");
166
- var stacks2 = /* @__PURE__ */ new WeakMap();
167
162
  function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor4.NIL) {
168
- const existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
163
+ const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
169
164
  if (!enabled || existing) return existing ? existing : void 0;
170
165
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
171
- const stack = stacks2.get(root) ?? [], 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) => Array.prototype.filter.call(c.querySelectorAll(INTERACTIVE_SELECTOR), (el2) => !el2.hasAttribute("disabled") && !el2.hasAttribute("aria-hidden") && !el2.hasAttribute("data-focus-guard")), resetFocus = (i = 0, els = getFocusable()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false) => {
166
+ const stack = stacks.get(root) ?? [], 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()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false, rt = root) => {
172
167
  if (!scoped) return resetFocus(pre ? -1 : 0);
173
- else if (root.hasAttribute("tabindex")) return root.focus();
168
+ if (rt.hasAttribute("tabindex")) return rt.focus();
174
169
  const items = getFocusable();
175
170
  if (!items.length) return resetFocus(0, null);
176
- const all = getFocusable(root.parentElement?.closest(`:has(${INTERACTIVE_SELECTOR})`) || document.body);
177
- 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 (!root.contains(target = all[i])) return target.focus();
171
+ const ceiling = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
172
+ let p = rt.parentElement || ceiling, all = getFocusable(p);
173
+ while (p !== ceiling && (!all.length || rt.contains(all[0]) && rt.contains(all.at(-1)))) all = getFocusable(p = p.parentElement || ceiling);
174
+ 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();
178
175
  (pre ? first : last).blur();
179
- }, handleFocusIn = () => stack.at(-1) === el && getActiveElement() !== root && !el.contains(getActiveElement()) && setTimeout(resetFocus, 0, 0), handleInitialBlur = () => initial.classList.remove(ringClassName);
176
+ }, handleFocusIn = () => {
177
+ if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
178
+ stack.at(-1) === el && (0, import_utils2.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils2.getActiveEl)(el.ownerDocument)) && resetFocus();
179
+ }, handleInitialBlur = () => initial.classList.remove(ringClassName);
180
180
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
181
181
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
182
182
  root.addEventListener("focusin", handleFocusIn, capture);
183
- if (!el.querySelector(":focus")) !initial ? resetFocus() : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
184
- if (!stack.includes(el)) stack.push(el), stacks2.set(root, stack);
183
+ if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
184
+ if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
185
185
  const destroy = () => {
186
186
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
187
187
  root.removeEventListener("focusin", handleFocusIn, capture);
@@ -231,7 +231,7 @@ var getTargetIndex = ({ key, currIndex, length, gridX, gridY, vGridY, loop, ctrl
231
231
  } else if (key === "PageUp") {
232
232
  if (!loop && targetIndex < 0) targetIndex = colStart;
233
233
  }
234
- return loop ? (targetIndex + length) % length : (0, import_utils2.clamp)(0, targetIndex, length - 1);
234
+ return loop ? (targetIndex + length) % length : (0, import_utils3.clamp)(0, targetIndex, length - 1);
235
235
  };
236
236
  var getCommonAncestor = (first, second) => {
237
237
  if (!first) return null;
@@ -256,7 +256,7 @@ var getGrid = (all, x = true, y = true, vY = true) => {
256
256
  if (y) grid.y = rows;
257
257
  if (vY) {
258
258
  const itemHeight = all[0].offsetHeight ?? 0, containerHeight = getCommonAncestor(all[0], all[1])?.clientHeight ?? 0;
259
- rows = (0, import_utils2.clamp)(1, Math.floor(containerHeight / itemHeight), rows) || rows;
259
+ rows = (0, import_utils3.clamp)(1, Math.floor(containerHeight / itemHeight), rows) || rows;
260
260
  grid.vY = rows;
261
261
  }
262
262
  return grid;
@@ -264,7 +264,7 @@ var getGrid = (all, x = true, y = true, vY = true) => {
264
264
 
265
265
  // src/hooks/vanilla/arrowNavigation.ts
266
266
  function initArrowNavigation(container, config = {}) {
267
- const existing = (t007._ashooters ??= /* @__PURE__ */ new WeakMap()).get(container);
267
+ const existing = (t007._arrownavs ??= /* @__PURE__ */ new WeakMap()).get(container);
268
268
  if (!config.enabled || existing) return existing ? existing : void 0;
269
269
  const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut, rovingTab } = { ...DEFAULT_CONFIG, ...config };
270
270
  let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
@@ -315,15 +315,16 @@ function initArrowNavigation(container, config = {}) {
315
315
  }
316
316
  };
317
317
  const simulateKey = (e) => {
318
- if (shouldSnub() || getActiveElement()?.matches("option")) return;
318
+ const t = e.target;
319
+ if (shouldSnub() || (0, import_utils2.getActiveEl)(t?.ownerDocument)?.matches("option")) return;
319
320
  const { key } = e;
320
321
  if (!items.length) return;
321
322
  if (virtual && (key === " " || key === "Enter")) return items[activeIndex]?.click();
322
- if (e.target?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
323
+ if (t?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
323
324
  if (typeahead && key.length === 1 && /^[a-z0-9]$/i.test(key)) return typeAhead(key);
324
325
  if (!NAV_KEYS.includes(key)) return;
325
326
  if (!(e.currentTarget?.matches(DEFAULT_CONFIG.inputSelector) && gridX <= 1 && H_NAV_KEYS.includes(key))) e.preventDefault?.(), e.stopPropagation?.();
326
- const currIndex = virtual ? activeIndex : items.indexOf(getActiveElement()), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: items.length, loop, rtl, key, ctrlKey: e.ctrlKey });
327
+ const currIndex = virtual ? activeIndex : items.indexOf((0, import_utils2.getActiveEl)(t?.ownerDocument)), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: items.length, loop, rtl, key, ctrlKey: e.ctrlKey });
327
328
  goToIndex(targetIndex, e);
328
329
  };
329
330
  getItems(), updateDOM();
@@ -370,9 +371,9 @@ function initArrowNavigation(container, config = {}) {
370
371
  if (timeout) clearTimeout(timeout);
371
372
  };
372
373
  const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
373
- return t007._ashooters.set(container, handle), handle;
374
+ return t007._arrownavs.set(container, handle), handle;
374
375
  }
375
- var removeArrowNavigation = (container) => t007._ashooters?.get(container)?.destroy();
376
+ var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
376
377
 
377
378
  // src/hooks/vanilla/ripple.ts
378
379
  var import_sia_reactor6 = require("sia-reactor");
@@ -1,5 +1,5 @@
1
1
  export { a as ScrollAssistConfig, S as ScrollAssistHandle, b as ScrollDir, i as initScrollAssist, r as removeScrollAssist } from '../scrollAssist-y9wFmYgt.cjs';
2
- export { F as FocusTrapConfig, O as OutsideClickConfig, R as RippleConfig, i as initFocusTrap, a as initOutsideClick, r as removeFocusTrap, b as removeOutsideClick, c as rippleHandler } from '../ripple-DcMWw_AP.cjs';
2
+ export { F as FocusTrapConfig, O as OutsideClickConfig, R as RippleConfig, i as initFocusTrap, a as initOutsideClick, r as removeFocusTrap, b as removeOutsideClick, c as rippleHandler } from '../ripple-CVQx46Xq.cjs';
3
3
  export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-DK8mqVOk.cjs';
4
4
 
5
5
  /** Configuration for the vertical edge-scrolling helper. */
@@ -1,5 +1,5 @@
1
1
  export { a as ScrollAssistConfig, S as ScrollAssistHandle, b as ScrollDir, i as initScrollAssist, r as removeScrollAssist } from '../scrollAssist-y9wFmYgt.js';
2
- export { F as FocusTrapConfig, O as OutsideClickConfig, R as RippleConfig, i as initFocusTrap, a as initOutsideClick, r as removeFocusTrap, b as removeOutsideClick, c as rippleHandler } from '../ripple-DcMWw_AP.js';
2
+ export { F as FocusTrapConfig, O as OutsideClickConfig, R as RippleConfig, i as initFocusTrap, a as initOutsideClick, r as removeFocusTrap, b as removeOutsideClick, c as rippleHandler } from '../ripple-CVQx46Xq.js';
3
3
  export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-VenvPI4H.js';
4
4
 
5
5
  /** Configuration for the vertical edge-scrolling helper. */
@@ -10,22 +10,22 @@ import {
10
10
  removeFocusTrap,
11
11
  removeOutsideClick,
12
12
  rippleHandler
13
- } from "../chunk-AI5O3OGE.js";
13
+ } from "../chunk-NLR4ANGT.js";
14
14
  import {
15
15
  INTERACTIVE_SELECTOR,
16
16
  createEl,
17
- getActiveElement
18
- } from "../chunk-XVFFZZJA.js";
17
+ getActiveEl
18
+ } from "../chunk-N5KX6IW4.js";
19
19
 
20
20
  // src/hooks/vanilla/scrollAssist.ts
21
21
  import { NIL } from "sia-reactor";
22
22
  function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = NIL) {
23
23
  const parent = el?.parentElement, existing = (t007._scrollers ??= /* @__PURE__ */ new WeakMap()).get(el);
24
24
  if (!parent || existing) return existing ? existing : void 0;
25
- t007._scroller_r_observer ??= new ResizeObserver((entries) => {
25
+ t007._scrollers_r_observer ??= new ResizeObserver((entries) => {
26
26
  for (const { target } of entries) t007._scrollers.get(target)?.update();
27
27
  });
28
- t007._scroller_m_observer ??= new MutationObserver((entries) => {
28
+ t007._scrollers_m_observer ??= new MutationObserver((entries) => {
29
29
  const els = /* @__PURE__ */ new Set();
30
30
  for (const entry of entries) {
31
31
  let node = entry.target instanceof Element ? entry.target : null;
@@ -80,12 +80,12 @@ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll
80
80
  update,
81
81
  destroy() {
82
82
  stop(), el.removeEventListener("scroll", update);
83
- t007._scroller_r_observer.unobserve(el), t007._scrollers.delete(el);
83
+ t007._scrollers_r_observer.unobserve(el), t007._scrollers.delete(el);
84
84
  for (const a of Object.values(assist)) a.remove();
85
85
  }
86
86
  };
87
87
  update(), el.addEventListener("scroll", update);
88
- t007._scroller_r_observer.observe(el), t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
88
+ t007._scrollers_r_observer.observe(el), t007._scrollers_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
89
89
  return t007._scrollers.set(el, handle), handle;
90
90
  }
91
91
  var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
@@ -109,7 +109,7 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
109
109
 
110
110
  // src/hooks/vanilla/arrowNavigation.ts
111
111
  function initArrowNavigation(container, config = {}) {
112
- const existing = (t007._ashooters ??= /* @__PURE__ */ new WeakMap()).get(container);
112
+ const existing = (t007._arrownavs ??= /* @__PURE__ */ new WeakMap()).get(container);
113
113
  if (!config.enabled || existing) return existing ? existing : void 0;
114
114
  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
115
  let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
@@ -160,15 +160,16 @@ function initArrowNavigation(container, config = {}) {
160
160
  }
161
161
  };
162
162
  const simulateKey = (e) => {
163
- if (shouldSnub() || getActiveElement()?.matches("option")) return;
163
+ const t = e.target;
164
+ if (shouldSnub() || getActiveEl(t?.ownerDocument)?.matches("option")) return;
164
165
  const { key } = e;
165
166
  if (!items.length) return;
166
167
  if (virtual && (key === " " || key === "Enter")) return items[activeIndex]?.click();
167
- if (e.target?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
168
+ if (t?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
168
169
  if (typeahead && key.length === 1 && /^[a-z0-9]$/i.test(key)) return typeAhead(key);
169
170
  if (!NAV_KEYS.includes(key)) return;
170
171
  if (!(e.currentTarget?.matches(DEFAULT_CONFIG.inputSelector) && gridX <= 1 && H_NAV_KEYS.includes(key))) e.preventDefault?.(), e.stopPropagation?.();
171
- const currIndex = virtual ? activeIndex : items.indexOf(getActiveElement()), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: items.length, loop, rtl, key, ctrlKey: e.ctrlKey });
172
+ const currIndex = virtual ? activeIndex : items.indexOf(getActiveEl(t?.ownerDocument)), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: items.length, loop, rtl, key, ctrlKey: e.ctrlKey });
172
173
  goToIndex(targetIndex, e);
173
174
  };
174
175
  getItems(), updateDOM();
@@ -215,9 +216,9 @@ function initArrowNavigation(container, config = {}) {
215
216
  if (timeout) clearTimeout(timeout);
216
217
  };
217
218
  const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
218
- return t007._ashooters.set(container, handle), handle;
219
+ return t007._arrownavs.set(container, handle), handle;
219
220
  }
220
- var removeArrowNavigation = (container) => t007._ashooters?.get(container)?.destroy();
221
+ var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
221
222
  export {
222
223
  initArrowNavigation,
223
224
  initFocusTrap,
package/dist/index.cjs CHANGED
@@ -25,20 +25,20 @@ __export(index_exports, {
25
25
  NOOP: () => import_sia_reactor.NOOP,
26
26
  VIRTUAL_RESOURCE: () => VIRTUAL_RESOURCE,
27
27
  assignEl: () => import_utils.assignEl,
28
- bindAllMethods: () => import_utils6.bindAllMethods,
28
+ bindAllMethods: () => import_utils7.bindAllMethods,
29
29
  bindCleanupToSignal: () => bindCleanupToSignal,
30
30
  breath: () => breath,
31
- clamp: () => import_utils3.clamp,
32
- cleanKeyCombo: () => import_utils5.cleanKeyCombo,
31
+ clamp: () => import_utils4.clamp,
32
+ cleanKeyCombo: () => import_utils6.cleanKeyCombo,
33
33
  createEl: () => import_utils.createEl,
34
34
  deepBreath: () => deepBreath,
35
- formatKeyForDisplay: () => import_utils5.formatKeyForDisplay,
36
- formatKeyShortcutsForDisplay: () => import_utils5.formatKeyShortcutsForDisplay,
35
+ formatKeyForDisplay: () => import_utils6.formatKeyForDisplay,
36
+ formatKeyShortcutsForDisplay: () => import_utils6.formatKeyShortcutsForDisplay,
37
37
  formatSize: () => formatSize,
38
- getActiveElement: () => getActiveElement,
39
- getTermsForKey: () => import_utils5.getTermsForKey,
40
- guardAllMethods: () => import_utils6.guardAllMethods,
41
- guardMethod: () => import_utils6.guardMethod,
38
+ getActiveEl: () => import_utils2.getActiveEl,
39
+ getTermsForKey: () => import_utils6.getTermsForKey,
40
+ guardAllMethods: () => import_utils7.guardAllMethods,
41
+ guardMethod: () => import_utils7.guardMethod,
42
42
  inBoolArrOpt: () => inBoolArrOpt,
43
43
  isArr: () => isArr,
44
44
  isBool: () => isBool,
@@ -47,30 +47,35 @@ __export(index_exports, {
47
47
  isInteractive: () => isInteractive,
48
48
  isIter: () => isIter,
49
49
  isNum: () => isNum,
50
- isObj: () => import_utils2.isObj,
50
+ isObj: () => import_utils3.isObj,
51
51
  isPOJO: () => isPOJO,
52
52
  isSameURL: () => isSameURL,
53
53
  isStr: () => isStr,
54
54
  isSym: () => isSym,
55
- keyEventAllowed: () => import_utils5.keyEventAllowed,
55
+ keyEventAllowed: () => import_utils6.keyEventAllowed,
56
56
  limited: () => limited,
57
57
  loadResource: () => loadResource,
58
- matchKeys: () => import_utils5.matchKeys,
58
+ matchKeys: () => import_utils6.matchKeys,
59
59
  mockAsync: () => mockAsync,
60
- onAllMethods: () => import_utils6.onAllMethods,
61
- parseForARIAKS: () => import_utils5.parseForARIAKS,
62
- parseKeyCombo: () => import_utils5.parseKeyCombo,
63
- requestAnimationFrame: () => import_utils4.requestAnimationFrame,
64
- setInterval: () => import_utils4.setInterval,
65
- setTimeout: () => import_utils4.setTimeout,
66
- stringifyKeyEvent: () => import_utils5.stringifyKeyEvent,
60
+ onAllMethods: () => import_utils7.onAllMethods,
61
+ parseCSSSize: () => parseCSSSize,
62
+ parseCSSTime: () => parseCSSTime,
63
+ parseForARIAKS: () => import_utils6.parseForARIAKS,
64
+ parseKeyCombo: () => import_utils6.parseKeyCombo,
65
+ pxToRem: () => pxToRem,
66
+ remToPx: () => remToPx,
67
+ requestAnimationFrame: () => import_utils5.requestAnimationFrame,
68
+ setInterval: () => import_utils5.setInterval,
69
+ setTimeout: () => import_utils5.setTimeout,
70
+ stringifyKeyEvent: () => import_utils6.stringifyKeyEvent,
67
71
  uid: () => uid
68
72
  });
69
73
  module.exports = __toCommonJS(index_exports);
70
74
 
71
75
  // src/core/dom.ts
72
76
  var import_utils = require("sia-reactor/utils");
73
- var INTERACTIVE_SELECTOR = 'button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex="-1"])';
77
+ var import_utils2 = require("sia-reactor/utils");
78
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
74
79
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
75
80
  var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
76
81
  function loadResource(req, type = "style", { module: module2, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
@@ -100,16 +105,12 @@ function loadResource(req, type = "style", { module: module2, media, crossOrigin
100
105
  });
101
106
  return w.t007._resourceCache[src];
102
107
  }
103
- function getActiveElement(root = document) {
104
- const activeEl = root.activeElement;
105
- return !activeEl ? null : activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl;
106
- }
107
108
 
108
109
  // src/index.ts
109
110
  var import_sia_reactor = require("sia-reactor");
110
111
 
111
112
  // src/core/obj.ts
112
- var import_utils2 = require("sia-reactor/utils");
113
+ var import_utils3 = require("sia-reactor/utils");
113
114
  function isDef(val) {
114
115
  return "undefined" !== typeof val;
115
116
  }
@@ -129,7 +130,7 @@ function isArr(obj) {
129
130
  return Array.isArray(obj);
130
131
  }
131
132
  function isPOJO(obj, crossRealms = false, typecheck = true) {
132
- return (typecheck ? (0, import_utils2.isObj)(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
133
+ return (typecheck ? (0, import_utils3.isObj)(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
133
134
  }
134
135
  function isIter(obj) {
135
136
  return obj != null && "function" === typeof obj[Symbol.iterator];
@@ -142,12 +143,24 @@ function inBoolArrOpt(opt, str) {
142
143
  }
143
144
 
144
145
  // src/core/num.ts
145
- var import_utils3 = require("sia-reactor/utils");
146
+ var import_utils4 = require("sia-reactor/utils");
146
147
 
147
148
  // src/core/str.ts
148
149
  function uid(prefix = "") {
149
150
  return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
150
151
  }
152
+ function remToPx(rem, el = document.documentElement) {
153
+ return rem * parseFloat(getComputedStyle(el).fontSize);
154
+ }
155
+ function pxToRem(px, el = document.documentElement) {
156
+ return px / parseFloat(getComputedStyle(el).fontSize);
157
+ }
158
+ function parseCSSTime(time) {
159
+ return time?.endsWith?.("ms") ? parseFloat(time) : parseFloat(time) * 1e3;
160
+ }
161
+ function parseCSSSize(size, el) {
162
+ return size?.endsWith?.("px") ? parseFloat(size) : remToPx(parseFloat(size), el);
163
+ }
151
164
  function isSameURL(src1, src2) {
152
165
  if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
153
166
  try {
@@ -159,7 +172,7 @@ function isSameURL(src1, src2) {
159
172
  }
160
173
 
161
174
  // src/core/fn.ts
162
- var import_utils4 = require("sia-reactor/utils");
175
+ var import_utils5 = require("sia-reactor/utils");
163
176
  function limited(FN_KEY, fn, opts = {}) {
164
177
  let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
165
178
  const getReg = () => JSON.parse(localStorage.getItem(FN_KEY) || "{}"), setReg = (r) => localStorage.setItem(FN_KEY, JSON.stringify(r));
@@ -183,7 +196,7 @@ function bindCleanupToSignal(cleanup, signal) {
183
196
  }
184
197
 
185
198
  // src/core/keys.ts
186
- var import_utils5 = require("sia-reactor/utils");
199
+ var import_utils6 = require("sia-reactor/utils");
187
200
 
188
201
  // src/core/file.ts
189
202
  function formatSize(bytes, decimals = 3, base = 1e3) {
@@ -193,7 +206,7 @@ function formatSize(bytes, decimals = 3, base = 1e3) {
193
206
  }
194
207
 
195
208
  // src/mixins/methd.ts
196
- var import_utils6 = require("sia-reactor/utils");
209
+ var import_utils7 = require("sia-reactor/utils");
197
210
 
198
211
  // src/index.ts
199
212
  if ("undefined" !== typeof window) {
@@ -222,7 +235,7 @@ if ("undefined" !== typeof window) {
222
235
  formatKeyForDisplay,
223
236
  formatKeyShortcutsForDisplay,
224
237
  formatSize,
225
- getActiveElement,
238
+ getActiveEl,
226
239
  getTermsForKey,
227
240
  guardAllMethods,
228
241
  guardMethod,
@@ -245,8 +258,12 @@ if ("undefined" !== typeof window) {
245
258
  matchKeys,
246
259
  mockAsync,
247
260
  onAllMethods,
261
+ parseCSSSize,
262
+ parseCSSTime,
248
263
  parseForARIAKS,
249
264
  parseKeyCombo,
265
+ pxToRem,
266
+ remToPx,
250
267
  requestAnimationFrame,
251
268
  setInterval,
252
269
  setTimeout,
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { A as ArrowNavigationHandle } from './arrowNavigation-DK8mqVOk.cjs';
2
2
  import { S as ScrollAssistHandle } from './scrollAssist-y9wFmYgt.cjs';
3
3
  export { NIL, NOOP } from 'sia-reactor';
4
- export { KeyStruct, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, keysSettings, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
4
+ export { KeyStruct, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getActiveEl, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, keysSettings, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
5
5
 
6
6
  declare global {
7
7
  interface T007Namespace {
@@ -10,10 +10,12 @@ declare global {
10
10
  _resourceCache: Partial<Record<string, Promise<HTMLElement | void>>>;
11
11
  _ftrappers?: WeakMap<HTMLElement, () => void>;
12
12
  _outsiders?: WeakMap<HTMLElement, () => void>;
13
- _ashooters?: WeakMap<HTMLElement, ArrowNavigationHandle>;
13
+ _arrownavs?: WeakMap<HTMLElement, ArrowNavigationHandle>;
14
14
  _scrollers?: WeakMap<HTMLElement, ScrollAssistHandle>;
15
- _scroller_r_observer?: ResizeObserver;
16
- _scroller_m_observer?: MutationObserver;
15
+ _ftrappers_stacks?: WeakMap<EventTarget, HTMLElement[]>;
16
+ _outsiders_stacks?: WeakMap<EventTarget, HTMLElement[]>;
17
+ _scrollers_r_observer?: ResizeObserver;
18
+ _scrollers_m_observer?: MutationObserver;
17
19
  }
18
20
  interface Window {
19
21
  /** Shared T007 namespace. */
@@ -52,6 +54,29 @@ declare function inBoolArrOpt(opt: any, str: string): boolean;
52
54
  * @returns A browser-safe unique id string.
53
55
  */
54
56
  declare function uid(prefix?: string): string;
57
+ /** Convert a rem value to pixels based on the font size of a given element.
58
+ * @param rem The rem value to convert.
59
+ * @param el The element to use for font size reference. Defaults to the root element.
60
+ * @returns The equivalent pixel value.
61
+ */
62
+ declare function remToPx(rem: number, el?: HTMLElement): number;
63
+ /** Convert a pixel value to rem based on the font size of a given element.
64
+ * @param px The pixel value to convert.
65
+ * @param el The element to use for font size reference. Defaults to the root element.
66
+ * @returns The equivalent rem value.
67
+ */
68
+ declare function pxToRem(px: number, el?: HTMLElement): number;
69
+ /** Parse a CSS time value (e.g. "200ms", "0.5s") into milliseconds.
70
+ * @param time The CSS time string to parse.
71
+ * @returns The equivalent time in milliseconds.
72
+ */
73
+ declare function parseCSSTime(time: string): number;
74
+ /** Parse a CSS size value (i.e. "16px" or "1.5rem") into pixels.
75
+ * @param size The CSS size string to parse.
76
+ * @param el The element to use for rem reference if needed. Defaults to the root element.
77
+ * @returns The equivalent value in pixels.
78
+ */
79
+ declare function parseCSSSize(size: string, el?: HTMLElement): number;
55
80
  /** Compare two URLs after normalizing origin, pathname, and separators.
56
81
  * @param src1 First URL or path.
57
82
  * @param src2 Second URL or path.
@@ -107,7 +132,7 @@ declare const deepBreath: (w?: Window & typeof globalThis) => Promise<unknown>;
107
132
  declare function bindCleanupToSignal<Cb extends () => any>(cleanup: Cb, signal?: AbortSignal): Cb;
108
133
 
109
134
  /** Exhaustive Selector used for interactive, tabbable UI controls. */
110
- declare const INTERACTIVE_SELECTOR = "button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex=\"-1\"])";
135
+ declare const INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
111
136
  /** Check whether an event target points to an interactive element. */
112
137
  declare const isInteractive: (target: EventTarget | null) => target is HTMLElement;
113
138
  /** Resource type accepted by loadResource. */
@@ -143,11 +168,6 @@ declare const VIRTUAL_RESOURCE: symbol;
143
168
  * @returns Promise resolving to the created element or void.
144
169
  */
145
170
  declare function loadResource(req: string | symbol, type?: ResourceType, { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts, retryKey }?: LoadResourceOptions, w?: Window & typeof globalThis): Promise<HTMLElement | void>;
146
- /** Get the currently active element, traversing into shadow roots if necessary.
147
- * @param root Root node to start searching from, defaults to the main document.
148
- * @returns The active element or null if none found.
149
- */
150
- declare function getActiveElement(root?: Document | ShadowRoot): Element | null;
151
171
 
152
172
  /** Format a file size for display.
153
173
  * @param size Size in bytes.
@@ -157,4 +177,4 @@ declare function getActiveElement(root?: Document | ShadowRoot): Element | null;
157
177
  */
158
178
  declare function formatSize(bytes: number, decimals?: number, base?: number): string;
159
179
 
160
- export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, getActiveElement, inBoolArrOpt, isArr, isBool, isDef, isFunc, isInteractive, isIter, isNum, isPOJO, isSameURL, isStr, isSym, limited, loadResource, mockAsync, uid };
180
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, inBoolArrOpt, isArr, isBool, isDef, isFunc, isInteractive, isIter, isNum, isPOJO, isSameURL, isStr, isSym, limited, loadResource, mockAsync, parseCSSSize, parseCSSTime, pxToRem, remToPx, uid };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { A as ArrowNavigationHandle } from './arrowNavigation-VenvPI4H.js';
2
2
  import { S as ScrollAssistHandle } from './scrollAssist-y9wFmYgt.js';
3
3
  export { NIL, NOOP } from 'sia-reactor';
4
- export { KeyStruct, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, keysSettings, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
4
+ export { KeyStruct, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getActiveEl, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, keysSettings, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
5
5
 
6
6
  declare global {
7
7
  interface T007Namespace {
@@ -10,10 +10,12 @@ declare global {
10
10
  _resourceCache: Partial<Record<string, Promise<HTMLElement | void>>>;
11
11
  _ftrappers?: WeakMap<HTMLElement, () => void>;
12
12
  _outsiders?: WeakMap<HTMLElement, () => void>;
13
- _ashooters?: WeakMap<HTMLElement, ArrowNavigationHandle>;
13
+ _arrownavs?: WeakMap<HTMLElement, ArrowNavigationHandle>;
14
14
  _scrollers?: WeakMap<HTMLElement, ScrollAssistHandle>;
15
- _scroller_r_observer?: ResizeObserver;
16
- _scroller_m_observer?: MutationObserver;
15
+ _ftrappers_stacks?: WeakMap<EventTarget, HTMLElement[]>;
16
+ _outsiders_stacks?: WeakMap<EventTarget, HTMLElement[]>;
17
+ _scrollers_r_observer?: ResizeObserver;
18
+ _scrollers_m_observer?: MutationObserver;
17
19
  }
18
20
  interface Window {
19
21
  /** Shared T007 namespace. */
@@ -52,6 +54,29 @@ declare function inBoolArrOpt(opt: any, str: string): boolean;
52
54
  * @returns A browser-safe unique id string.
53
55
  */
54
56
  declare function uid(prefix?: string): string;
57
+ /** Convert a rem value to pixels based on the font size of a given element.
58
+ * @param rem The rem value to convert.
59
+ * @param el The element to use for font size reference. Defaults to the root element.
60
+ * @returns The equivalent pixel value.
61
+ */
62
+ declare function remToPx(rem: number, el?: HTMLElement): number;
63
+ /** Convert a pixel value to rem based on the font size of a given element.
64
+ * @param px The pixel value to convert.
65
+ * @param el The element to use for font size reference. Defaults to the root element.
66
+ * @returns The equivalent rem value.
67
+ */
68
+ declare function pxToRem(px: number, el?: HTMLElement): number;
69
+ /** Parse a CSS time value (e.g. "200ms", "0.5s") into milliseconds.
70
+ * @param time The CSS time string to parse.
71
+ * @returns The equivalent time in milliseconds.
72
+ */
73
+ declare function parseCSSTime(time: string): number;
74
+ /** Parse a CSS size value (i.e. "16px" or "1.5rem") into pixels.
75
+ * @param size The CSS size string to parse.
76
+ * @param el The element to use for rem reference if needed. Defaults to the root element.
77
+ * @returns The equivalent value in pixels.
78
+ */
79
+ declare function parseCSSSize(size: string, el?: HTMLElement): number;
55
80
  /** Compare two URLs after normalizing origin, pathname, and separators.
56
81
  * @param src1 First URL or path.
57
82
  * @param src2 Second URL or path.
@@ -107,7 +132,7 @@ declare const deepBreath: (w?: Window & typeof globalThis) => Promise<unknown>;
107
132
  declare function bindCleanupToSignal<Cb extends () => any>(cleanup: Cb, signal?: AbortSignal): Cb;
108
133
 
109
134
  /** Exhaustive Selector used for interactive, tabbable UI controls. */
110
- declare const INTERACTIVE_SELECTOR = "button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex=\"-1\"])";
135
+ declare const INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
111
136
  /** Check whether an event target points to an interactive element. */
112
137
  declare const isInteractive: (target: EventTarget | null) => target is HTMLElement;
113
138
  /** Resource type accepted by loadResource. */
@@ -143,11 +168,6 @@ declare const VIRTUAL_RESOURCE: symbol;
143
168
  * @returns Promise resolving to the created element or void.
144
169
  */
145
170
  declare function loadResource(req: string | symbol, type?: ResourceType, { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts, retryKey }?: LoadResourceOptions, w?: Window & typeof globalThis): Promise<HTMLElement | void>;
146
- /** Get the currently active element, traversing into shadow roots if necessary.
147
- * @param root Root node to start searching from, defaults to the main document.
148
- * @returns The active element or null if none found.
149
- */
150
- declare function getActiveElement(root?: Document | ShadowRoot): Element | null;
151
171
 
152
172
  /** Format a file size for display.
153
173
  * @param size Size in bytes.
@@ -157,4 +177,4 @@ declare function getActiveElement(root?: Document | ShadowRoot): Element | null;
157
177
  */
158
178
  declare function formatSize(bytes: number, decimals?: number, base?: number): string;
159
179
 
160
- export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, getActiveElement, inBoolArrOpt, isArr, isBool, isDef, isFunc, isInteractive, isIter, isNum, isPOJO, isSameURL, isStr, isSym, limited, loadResource, mockAsync, uid };
180
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, inBoolArrOpt, isArr, isBool, isDef, isFunc, isInteractive, isIter, isNum, isPOJO, isSameURL, isStr, isSym, limited, loadResource, mockAsync, parseCSSSize, parseCSSTime, pxToRem, remToPx, uid };
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  formatKeyForDisplay,
15
15
  formatKeyShortcutsForDisplay,
16
16
  formatSize,
17
- getActiveElement,
17
+ getActiveEl,
18
18
  getTermsForKey,
19
19
  guardAllMethods,
20
20
  guardMethod,
@@ -37,14 +37,18 @@ import {
37
37
  matchKeys,
38
38
  mockAsync,
39
39
  onAllMethods,
40
+ parseCSSSize,
41
+ parseCSSTime,
40
42
  parseForARIAKS,
41
43
  parseKeyCombo,
44
+ pxToRem,
45
+ remToPx,
42
46
  requestAnimationFrame,
43
47
  setInterval,
44
48
  setTimeout,
45
49
  stringifyKeyEvent,
46
50
  uid
47
- } from "./chunk-XVFFZZJA.js";
51
+ } from "./chunk-N5KX6IW4.js";
48
52
  export {
49
53
  INTERACTIVE_SELECTOR,
50
54
  NIL,
@@ -61,7 +65,7 @@ export {
61
65
  formatKeyForDisplay,
62
66
  formatKeyShortcutsForDisplay,
63
67
  formatSize,
64
- getActiveElement,
68
+ getActiveEl,
65
69
  getTermsForKey,
66
70
  guardAllMethods,
67
71
  guardMethod,
@@ -84,8 +88,12 @@ export {
84
88
  matchKeys,
85
89
  mockAsync,
86
90
  onAllMethods,
91
+ parseCSSSize,
92
+ parseCSSTime,
87
93
  parseForARIAKS,
88
94
  parseKeyCombo,
95
+ pxToRem,
96
+ remToPx,
89
97
  requestAnimationFrame,
90
98
  setInterval,
91
99
  setTimeout,
@@ -4,12 +4,12 @@ interface OutsideClickConfig {
4
4
  /** Callback invoked when an outside interaction is detected. Defaults to `()=>{}`. */
5
5
  onOutsideClick?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
6
6
  /** Whether pointer/touch outside interactions should trigger callback. Defaults to `true`. */
7
- clickOnClick?: boolean;
7
+ outOnClick?: boolean;
8
8
  /** Whether Escape key should trigger callback. Defaults to `true`. */
9
- clickOnEscape?: boolean;
9
+ outOnEscape?: boolean;
10
10
  /** Whether focus leaving the container should trigger callback. Defaults to `false`. */
11
- clickOnFocusOut?: boolean;
12
- /** Allow interactive elements like outsiders to bypass click callback. Defaults to `true`. */
11
+ outOnFocusOut?: boolean;
12
+ /** Allow interactive elements including outsiders to bypass click callback. Defaults to `false`. */
13
13
  allowInputs?: boolean;
14
14
  /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
15
15
  root?: HTMLElement | Document | Window;
@@ -19,7 +19,7 @@ interface OutsideClickConfig {
19
19
  capture?: boolean;
20
20
  }
21
21
  /** Hook to attach outside-click, escape, and optional focus-out handling to an element. */
22
- declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, clickOnClick, clickOnEscape, clickOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
22
+ declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, outOnClick, outOnEscape, outOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
23
23
  /** Remove outside-click handling from an element. */
24
24
  declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
25
25
 
@@ -4,12 +4,12 @@ interface OutsideClickConfig {
4
4
  /** Callback invoked when an outside interaction is detected. Defaults to `()=>{}`. */
5
5
  onOutsideClick?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
6
6
  /** Whether pointer/touch outside interactions should trigger callback. Defaults to `true`. */
7
- clickOnClick?: boolean;
7
+ outOnClick?: boolean;
8
8
  /** Whether Escape key should trigger callback. Defaults to `true`. */
9
- clickOnEscape?: boolean;
9
+ outOnEscape?: boolean;
10
10
  /** Whether focus leaving the container should trigger callback. Defaults to `false`. */
11
- clickOnFocusOut?: boolean;
12
- /** Allow interactive elements like outsiders to bypass click callback. Defaults to `true`. */
11
+ outOnFocusOut?: boolean;
12
+ /** Allow interactive elements including outsiders to bypass click callback. Defaults to `false`. */
13
13
  allowInputs?: boolean;
14
14
  /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
15
15
  root?: HTMLElement | Document | Window;
@@ -19,7 +19,7 @@ interface OutsideClickConfig {
19
19
  capture?: boolean;
20
20
  }
21
21
  /** Hook to attach outside-click, escape, and optional focus-out handling to an element. */
22
- declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, clickOnClick, clickOnEscape, clickOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
22
+ declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, outOnClick, outOnEscape, outOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
23
23
  /** Remove outside-click handling from an element. */
24
24
  declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t007/utils",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "description": "High-performance, zero-dependency utility functions for the t007 ecosystem.",
5
5
  "author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
6
6
  "license": "MIT",
@@ -74,8 +74,8 @@
74
74
  "react-dom": "^18.3.1"
75
75
  },
76
76
  "dependencies": {
77
- "@t007/input": "^0.0.24",
78
- "sia-reactor": "^0.0.30"
77
+ "@t007/input": "^0.0.25",
78
+ "sia-reactor": "^0.0.31"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "react": "^18.0.0"