@t007/utils 0.0.29 → 0.0.31

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/README.md CHANGED
@@ -151,10 +151,10 @@ All defaults are exposed as root-level, `t007`-prefixed variables:
151
151
  /** default scroll assist values */
152
152
  --t007-scroll-assist-color: rgb(0 0 0 / 1);
153
153
  --t007-scroll-assist-opacity: 0.07;
154
- --t007-scroll-assist-min-width: 2rem;
154
+ --t007-scroll-assist-width: 2rem;
155
155
  --t007-scroll-assist-height: 2rem;
156
- --t007-scroll-assist-inline-offset: -0.35rem;
157
- --t007-scroll-assist-block-offset: 0;
156
+ --t007-scroll-assist-inset-x: -0.35rem;
157
+ --t007-scroll-assist-inset-y: 0;
158
158
  }
159
159
 
160
160
  /** custom pre-requisites */
@@ -4,20 +4,21 @@ import {
4
4
  createEl,
5
5
  getActiveEl,
6
6
  isInteractive
7
- } from "./chunk-N5KX6IW4.js";
7
+ } from "./chunk-Y5YJMRXD.js";
8
8
 
9
- // src/hooks/vanilla/outsideClick.ts
9
+ // src/ts/hooks/vanilla/outsideClick.ts
10
10
  import { NIL, NOOP } from "sia-reactor";
11
- function initOutsideClick(el, { enabled = false, onOutsideClick = NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowInputs = false, root = window, scoped = true, capture = true } = NIL) {
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
12
  const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), 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 stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
16
- if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
17
- return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
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);
15
+ const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
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
+ return (!scoped ? true : root.contains(t)) && onOutside(e);
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
- root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
20
+ root.addEventListener("keydown", handleEscape, capture);
21
+ el.addEventListener("focusout", handleFocusOut, capture);
21
22
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
22
23
  const destroy = () => {
23
24
  root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
@@ -29,7 +30,7 @@ function initOutsideClick(el, { enabled = false, onOutsideClick = NOOP, outOnCli
29
30
  }
30
31
  var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
31
32
 
32
- // src/hooks/vanilla/focusTrap.ts
33
+ // src/ts/hooks/vanilla/focusTrap.ts
33
34
  import { NIL as NIL2 } from "sia-reactor";
34
35
  function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = NIL2) {
35
36
  const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
@@ -40,9 +41,9 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
40
41
  if (rt.hasAttribute("tabindex")) return rt.focus();
41
42
  const items = getFocusable();
42
43
  if (!items.length) return resetFocus(0, null);
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);
44
+ const ceil = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
45
+ let p = rt.parentElement || ceil, all = getFocusable(p);
46
+ while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
46
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();
47
48
  (pre ? first : last).blur();
48
49
  }, handleFocusIn = () => {
@@ -52,7 +53,7 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
52
53
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
53
54
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
54
55
  root.addEventListener("focusin", handleFocusIn, capture);
55
- if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
56
+ if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
56
57
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
57
58
  const destroy = () => {
58
59
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
@@ -64,7 +65,7 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
64
65
  }
65
66
  var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
66
67
 
67
- // src/hooks/vanilla/ripple.ts
68
+ // src/ts/hooks/vanilla/ripple.ts
68
69
  import { NIL as NIL3 } from "sia-reactor";
69
70
  function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade" } = NIL3) {
70
71
  const el = target || e.currentTarget;
@@ -83,7 +84,7 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
83
84
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
84
85
  }
85
86
 
86
- // src/hooks/react/useArrowNavigation/utils.ts
87
+ // src/ts/hooks/react/useArrowNavigation/utils.ts
87
88
  var getTargetIndex = ({ key, currIndex, length, gridX, gridY, vGridY, loop, ctrlKey = false, rtl }) => {
88
89
  const rowStart = currIndex - currIndex % gridX, rowEnd = Math.min(rowStart + gridX - 1, length - 1), colStart = currIndex % gridX, colEnd = Math.min(colStart + gridX * (gridY - 1), length - 1), canX = gridX > 1, canY = gridY > 1, horizontalMove = rtl ? { ArrowRight: canX ? -1 : 0, ArrowLeft: canX ? 1 : 0 } : { ArrowRight: canX ? 1 : 0, ArrowLeft: canX ? -1 : 0 }, move = { ...horizontalMove, ArrowDown: canY ? gridX : 0, ArrowUp: canY ? -gridX : 0, Home: ctrlKey ? 0 : rowStart, End: ctrlKey ? length - 1 : rowEnd, PageDown: (vGridY - 1) * gridX, PageUp: -(vGridY - 1) * gridX }[key] ?? 0;
89
90
  let targetIndex = key === "Home" || key === "End" ? move : currIndex + move;
@@ -127,7 +128,7 @@ var getGrid = (all, x = true, y = true, vY = true) => {
127
128
  return grid;
128
129
  };
129
130
 
130
- // src/hooks/react/useArrowNavigation/consts.ts
131
+ // src/ts/hooks/react/useArrowNavigation/consts.ts
131
132
  import { NOOP as NOOP2 } from "sia-reactor";
132
133
  var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
133
134
  var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
@@ -1,4 +1,4 @@
1
- // src/hooks/react/useHighlight.ts
1
+ // src/ts/hooks/react/useHighlight.ts
2
2
  import { useMemo } from "react";
3
3
  function useHighlight(text, query, ignoreCase = true, options) {
4
4
  const { trimQuery = true, wholeWord = false } = options ?? {};
@@ -1,4 +1,4 @@
1
- // src/core/dom.ts
1
+ // src/ts/core/dom.ts
2
2
  import { createEl, assignEl } from "sia-reactor/utils";
3
3
  import { getActiveEl } from "sia-reactor/utils";
4
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] *)";
@@ -31,11 +31,14 @@ function loadResource(req, type = "style", { module, media, crossOrigin, integri
31
31
  });
32
32
  return w.t007._resourceCache[src];
33
33
  }
34
+ function getWindow(el = window) {
35
+ return (el instanceof Window ? el : el instanceof Document ? el?.defaultView : el?.ownerDocument?.defaultView) ?? void 0;
36
+ }
34
37
 
35
- // src/index.ts
38
+ // src/ts/index.ts
36
39
  import { NIL, NOOP } from "sia-reactor";
37
40
 
38
- // src/core/obj.ts
41
+ // src/ts/core/obj.ts
39
42
  import { isObj } from "sia-reactor/utils";
40
43
  function isDef(val) {
41
44
  return "undefined" !== typeof val;
@@ -68,10 +71,10 @@ function inBoolArrOpt(opt, str) {
68
71
  return opt?.includes?.(str) ?? opt;
69
72
  }
70
73
 
71
- // src/core/num.ts
74
+ // src/ts/core/num.ts
72
75
  import { clamp } from "sia-reactor/utils";
73
76
 
74
- // src/core/str.ts
77
+ // src/ts/core/str.ts
75
78
  function uid(prefix = "") {
76
79
  return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
77
80
  }
@@ -97,7 +100,7 @@ function isSameURL(src1, src2) {
97
100
  }
98
101
  }
99
102
 
100
- // src/core/fn.ts
103
+ // src/ts/core/fn.ts
101
104
  import { setTimeout as setTimeout2, setInterval, requestAnimationFrame } from "sia-reactor/utils";
102
105
  function limited(FN_KEY, fn, opts = {}) {
103
106
  let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
@@ -121,20 +124,20 @@ function bindCleanupToSignal(cleanup, signal) {
121
124
  return cleanup;
122
125
  }
123
126
 
124
- // src/core/keys.ts
127
+ // src/ts/core/keys.ts
125
128
  import { parseKeyCombo, stringifyKeyEvent, cleanKeyCombo, matchKeys, getTermsForKey, keyEventAllowed, formatKeyForDisplay, formatKeyShortcutsForDisplay, parseForARIAKS } from "sia-reactor/utils";
126
129
 
127
- // src/core/file.ts
130
+ // src/ts/core/file.ts
128
131
  function formatSize(bytes, decimals = 3, base = 1e3) {
129
132
  if (bytes < base) return `${bytes} byte${bytes == 1 ? "" : "s"}`;
130
133
  const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(base)), units.length - 1);
131
134
  return `${(bytes / Math.pow(base, exponent)).toFixed(decimals).replace(/\.0+$/, "")} ${units[exponent]}`;
132
135
  }
133
136
 
134
- // src/mixins/methd.ts
137
+ // src/ts/mixins/methd.ts
135
138
  import { onAllMethods, bindAllMethods, guardAllMethods, guardMethod } from "sia-reactor/utils";
136
139
 
137
- // src/index.ts
140
+ // src/ts/index.ts
138
141
  if ("undefined" !== typeof window) {
139
142
  (window.t007 ??= {}).VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
140
143
  window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
@@ -152,6 +155,7 @@ export {
152
155
  isInteractive,
153
156
  VIRTUAL_RESOURCE,
154
157
  loadResource,
158
+ getWindow,
155
159
  getActiveEl,
156
160
  isObj,
157
161
  isDef,
@@ -17,14 +17,14 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/components/react.ts
20
+ // src/ts/components/react.ts
21
21
  var react_exports = {};
22
22
  __export(react_exports, {
23
23
  HighlightText: () => HighlightText
24
24
  });
25
25
  module.exports = __toCommonJS(react_exports);
26
26
 
27
- // src/hooks/react/useHighlight.ts
27
+ // src/ts/hooks/react/useHighlight.ts
28
28
  var import_react = require("react");
29
29
  function useHighlight(text, query, ignoreCase = true, options) {
30
30
  const { trimQuery = true, wholeWord = false } = options ?? {};
@@ -37,7 +37,7 @@ function useHighlight(text, query, ignoreCase = true, options) {
37
37
  }, [text, query, ignoreCase, trimQuery, wholeWord]);
38
38
  }
39
39
 
40
- // src/components/react/HighlightText.tsx
40
+ // src/ts/components/react/HighlightText.tsx
41
41
  var import_jsx_runtime = require("react/jsx-runtime");
42
42
  var HighlightText = ({ children = "", query = "", className = "highlight", ignoreCase = true, trimQuery = true, wholeWord = false }) => {
43
43
  const chunks = useHighlight(children, query, ignoreCase, { trimQuery, wholeWord });
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  useHighlight
3
- } from "../chunk-OGFBI6PS.js";
3
+ } from "../chunk-RXMKMD3E.js";
4
4
 
5
- // src/components/react/HighlightText.tsx
5
+ // src/ts/components/react/HighlightText.tsx
6
6
  import { Fragment, jsx } from "react/jsx-runtime";
7
7
  var HighlightText = ({ children = "", query = "", className = "highlight", ignoreCase = true, trimQuery = true, wholeWord = false }) => {
8
8
  const chunks = useHighlight(children, query, ignoreCase, { trimQuery, wholeWord });
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/hooks/react.ts
20
+ // src/ts/hooks/react.ts
21
21
  var react_exports = {};
22
22
  __export(react_exports, {
23
23
  useArrowNavigation: () => useArrowNavigation,
@@ -29,19 +29,19 @@ __export(react_exports, {
29
29
  });
30
30
  module.exports = __toCommonJS(react_exports);
31
31
 
32
- // src/hooks/react/useScrollAssist.ts
32
+ // src/ts/hooks/react/useScrollAssist.ts
33
33
  var import_react = require("react");
34
34
 
35
- // src/core/num.ts
35
+ // src/ts/core/num.ts
36
36
  var import_utils = require("sia-reactor/utils");
37
37
 
38
- // src/core/dom.ts
38
+ // src/ts/core/dom.ts
39
39
  var import_utils2 = require("sia-reactor/utils");
40
40
  var import_utils3 = require("sia-reactor/utils");
41
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] *)";
42
42
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
43
43
 
44
- // src/hooks/react/useScrollAssist.ts
44
+ // src/ts/hooks/react/useScrollAssist.ts
45
45
  var import_sia_reactor = require("sia-reactor");
46
46
  function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor.NIL) {
47
47
  const scrollId = (0, import_react.useRef)(null), last = (0, import_react.useRef)(performance.now()), assists = (0, import_react.useRef)({}), assistWidth = (0, import_react.useRef)(20), assistHeight = (0, import_react.useRef)(20);
@@ -120,21 +120,22 @@ function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassNam
120
120
  return { update };
121
121
  }
122
122
 
123
- // src/hooks/react/useOutsideClick.ts
123
+ // src/ts/hooks/react/useOutsideClick.ts
124
124
  var import_react2 = require("react");
125
125
 
126
- // src/hooks/vanilla/outsideClick.ts
126
+ // src/ts/hooks/vanilla/outsideClick.ts
127
127
  var import_sia_reactor2 = require("sia-reactor");
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) {
128
+ function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor2.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowBounds = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor2.NIL) {
129
129
  const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
130
130
  if (!enabled || existing) return existing ? existing : void 0;
131
131
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
132
- const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
133
- if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
134
- return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
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);
132
+ const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
133
+ 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;
134
+ return (!scoped ? true : root.contains(t)) && onOutside(e);
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 && onOutside(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
136
136
  root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
137
- root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
137
+ root.addEventListener("keydown", handleEscape, capture);
138
+ el.addEventListener("focusout", handleFocusOut, capture);
138
139
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
139
140
  const destroy = () => {
140
141
  root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
@@ -145,16 +146,16 @@ function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_rea
145
146
  return t007._outsiders.set(el, destroy), destroy;
146
147
  }
147
148
 
148
- // src/hooks/react/useOutsideClick.ts
149
+ // src/ts/hooks/react/useOutsideClick.ts
149
150
  var import_sia_reactor3 = require("sia-reactor");
150
151
  function useOutsideClick(ref, config = import_sia_reactor3.NIL) {
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]);
152
+ (0, import_react2.useEffect)(() => ref.current ? initOutsideClick(ref.current, config) : void 0, [ref, config.enabled, config.onOutside, config.outOnEscape, config.outOnClick, config.outOnFocusOut, config.allowBounds, config.allowInputs, config.root, config.scoped, config.capture]);
152
153
  }
153
154
 
154
- // src/hooks/react/useArrowNavigation/index.ts
155
+ // src/ts/hooks/react/useArrowNavigation/index.ts
155
156
  var import_react3 = require("react");
156
157
 
157
- // src/hooks/react/useArrowNavigation/utils.ts
158
+ // src/ts/hooks/react/useArrowNavigation/utils.ts
158
159
  var getTargetIndex = ({ key, currIndex, length, gridX, gridY, vGridY, loop, ctrlKey = false, rtl }) => {
159
160
  const rowStart = currIndex - currIndex % gridX, rowEnd = Math.min(rowStart + gridX - 1, length - 1), colStart = currIndex % gridX, colEnd = Math.min(colStart + gridX * (gridY - 1), length - 1), canX = gridX > 1, canY = gridY > 1, horizontalMove = rtl ? { ArrowRight: canX ? -1 : 0, ArrowLeft: canX ? 1 : 0 } : { ArrowRight: canX ? 1 : 0, ArrowLeft: canX ? -1 : 0 }, move = { ...horizontalMove, ArrowDown: canY ? gridX : 0, ArrowUp: canY ? -gridX : 0, Home: ctrlKey ? 0 : rowStart, End: ctrlKey ? length - 1 : rowEnd, PageDown: (vGridY - 1) * gridX, PageUp: -(vGridY - 1) * gridX }[key] ?? 0;
160
161
  let targetIndex = key === "Home" || key === "End" ? move : currIndex + move;
@@ -198,7 +199,7 @@ var getGrid = (all, x = true, y = true, vY = true) => {
198
199
  return grid;
199
200
  };
200
201
 
201
- // src/hooks/react/useArrowNavigation/consts.ts
202
+ // src/ts/hooks/react/useArrowNavigation/consts.ts
202
203
  var import_sia_reactor4 = require("sia-reactor");
203
204
  var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
204
205
  var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
@@ -224,7 +225,7 @@ var DEFAULT_CONFIG = {
224
225
  onFocusOut: import_sia_reactor4.NOOP
225
226
  };
226
227
 
227
- // src/hooks/react/useArrowNavigation/index.ts
228
+ // src/ts/hooks/react/useArrowNavigation/index.ts
228
229
  function useArrowNavigation(containerRef, config = {}) {
229
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 ? true : el.hasAttribute("disabled") || el.hasAttribute("aria-disabled"), []), getItems = (0, import_react3.useCallback)(() => itemsRef.current = Array.from(containerRef.current?.querySelectorAll(selector) || []), [containerRef, selector]);
230
231
  const getAbleIndex = (0, import_react3.useCallback)(
@@ -373,10 +374,10 @@ function useArrowNavigation(containerRef, config = {}) {
373
374
  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 };
374
375
  }
375
376
 
376
- // src/hooks/react/useFocusTrap.ts
377
+ // src/ts/hooks/react/useFocusTrap.ts
377
378
  var import_react4 = require("react");
378
379
 
379
- // src/hooks/vanilla/focusTrap.ts
380
+ // src/ts/hooks/vanilla/focusTrap.ts
380
381
  var import_sia_reactor5 = require("sia-reactor");
381
382
  function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor5.NIL) {
382
383
  const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
@@ -387,9 +388,9 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
387
388
  if (rt.hasAttribute("tabindex")) return rt.focus();
388
389
  const items = getFocusable();
389
390
  if (!items.length) return resetFocus(0, null);
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);
391
+ const ceil = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
392
+ let p = rt.parentElement || ceil, all = getFocusable(p);
393
+ while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
393
394
  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();
394
395
  (pre ? first : last).blur();
395
396
  }, handleFocusIn = () => {
@@ -399,7 +400,7 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
399
400
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
400
401
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
401
402
  root.addEventListener("focusin", handleFocusIn, capture);
402
- if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
403
+ if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
403
404
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
404
405
  const destroy = () => {
405
406
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
@@ -410,13 +411,13 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
410
411
  return t007._ftrappers.set(el, destroy), destroy;
411
412
  }
412
413
 
413
- // src/hooks/react/useFocusTrap.ts
414
+ // src/ts/hooks/react/useFocusTrap.ts
414
415
  var import_sia_reactor6 = require("sia-reactor");
415
416
  function useFocusTrap(ref, config = import_sia_reactor6.NIL) {
416
417
  (0, import_react4.useEffect)(() => ref.current ? initFocusTrap(ref.current, config) : void 0, [ref, config.enabled, config.initialSelector, config.ringClassName, config.root, config.scoped, config.capture]);
417
418
  }
418
419
 
419
- // src/hooks/react/useHighlight.ts
420
+ // src/ts/hooks/react/useHighlight.ts
420
421
  var import_react5 = require("react");
421
422
  function useHighlight(text, query, ignoreCase = true, options) {
422
423
  const { trimQuery = true, wholeWord = false } = options ?? {};
@@ -429,10 +430,10 @@ function useHighlight(text, query, ignoreCase = true, options) {
429
430
  }, [text, query, ignoreCase, trimQuery, wholeWord]);
430
431
  }
431
432
 
432
- // src/hooks/react/useRipple.ts
433
+ // src/ts/hooks/react/useRipple.ts
433
434
  var import_react6 = require("react");
434
435
 
435
- // src/hooks/vanilla/ripple.ts
436
+ // src/ts/hooks/vanilla/ripple.ts
436
437
  var import_sia_reactor7 = require("sia-reactor");
437
438
  function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade" } = import_sia_reactor7.NIL) {
438
439
  const el = target || e.currentTarget;
@@ -451,7 +452,7 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
451
452
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
452
453
  }
453
454
 
454
- // src/hooks/react/useRipple.ts
455
+ // src/ts/hooks/react/useRipple.ts
455
456
  var import_sia_reactor8 = require("sia-reactor");
456
457
  function useRipple() {
457
458
  return (0, import_react6.useCallback)((e, config = import_sia_reactor8.NIL) => rippleHandler(e.nativeEvent, config), []);
@@ -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-CVQx46Xq.cjs';
3
+ import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-C5MfEErC.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-CVQx46Xq.js';
3
+ import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-C5MfEErC.js';
4
4
  export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DMpDCILK.js';
5
5
 
6
6
  /** Configuration options for the `useScrollAssist` hook. */
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useHighlight
3
- } from "../chunk-OGFBI6PS.js";
3
+ } from "../chunk-RXMKMD3E.js";
4
4
  import {
5
5
  DEFAULT_CONFIG,
6
6
  H_NAV_KEYS,
@@ -11,13 +11,13 @@ import {
11
11
  initFocusTrap,
12
12
  initOutsideClick,
13
13
  rippleHandler
14
- } from "../chunk-NLR4ANGT.js";
14
+ } from "../chunk-LBDWVTYF.js";
15
15
  import {
16
16
  INTERACTIVE_SELECTOR,
17
17
  getActiveEl
18
- } from "../chunk-N5KX6IW4.js";
18
+ } from "../chunk-Y5YJMRXD.js";
19
19
 
20
- // src/hooks/react/useScrollAssist.ts
20
+ // src/ts/hooks/react/useScrollAssist.ts
21
21
  import { useEffect, useRef, useCallback } from "react";
22
22
  import { NIL } from "sia-reactor";
23
23
  function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = NIL) {
@@ -97,14 +97,14 @@ function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassNam
97
97
  return { update };
98
98
  }
99
99
 
100
- // src/hooks/react/useOutsideClick.ts
100
+ // src/ts/hooks/react/useOutsideClick.ts
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.outOnEscape, config.outOnClick, config.outOnFocusOut, config.allowInputs, config.root, config.scoped, config.capture]);
104
+ useEffect2(() => ref.current ? initOutsideClick(ref.current, config) : void 0, [ref, config.enabled, config.onOutside, config.outOnEscape, config.outOnClick, config.outOnFocusOut, config.allowBounds, config.allowInputs, config.root, config.scoped, config.capture]);
105
105
  }
106
106
 
107
- // src/hooks/react/useArrowNavigation/index.ts
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
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 ? true : el.hasAttribute("disabled") || el.hasAttribute("aria-disabled"), []), getItems = useCallback2(() => itemsRef.current = Array.from(containerRef.current?.querySelectorAll(selector) || []), [containerRef, selector]);
@@ -254,14 +254,14 @@ function useArrowNavigation(containerRef, config = {}) {
254
254
  return { gridX, gridY, vGridY, activeIndex, activeItem: useCallback2(() => itemsRef.current[activeIndex] ?? null, [activeIndex]), items: useCallback2(() => itemsRef.current, []), getAbleIndex, typeAhead, goToIndex, simulateKey };
255
255
  }
256
256
 
257
- // src/hooks/react/useFocusTrap.ts
257
+ // src/ts/hooks/react/useFocusTrap.ts
258
258
  import { useEffect as useEffect4 } from "react";
259
259
  import { NIL as NIL3 } from "sia-reactor";
260
260
  function useFocusTrap(ref, config = NIL3) {
261
261
  useEffect4(() => ref.current ? initFocusTrap(ref.current, config) : void 0, [ref, config.enabled, config.initialSelector, config.ringClassName, config.root, config.scoped, config.capture]);
262
262
  }
263
263
 
264
- // src/hooks/react/useRipple.ts
264
+ // src/ts/hooks/react/useRipple.ts
265
265
  import { useCallback as useCallback3 } from "react";
266
266
  import { NIL as NIL4 } from "sia-reactor";
267
267
  function useRipple() {
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/hooks/vanilla.ts
20
+ // src/ts/hooks/vanilla.ts
21
21
  var vanilla_exports = {};
22
22
  __export(vanilla_exports, {
23
23
  initArrowNavigation: () => initArrowNavigation,
@@ -33,19 +33,19 @@ __export(vanilla_exports, {
33
33
  });
34
34
  module.exports = __toCommonJS(vanilla_exports);
35
35
 
36
- // src/hooks/vanilla/scrollAssist.ts
36
+ // src/ts/hooks/vanilla/scrollAssist.ts
37
37
  var import_sia_reactor = require("sia-reactor");
38
38
 
39
- // src/core/dom.ts
39
+ // src/ts/core/dom.ts
40
40
  var import_utils = require("sia-reactor/utils");
41
41
  var import_utils2 = require("sia-reactor/utils");
42
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] *)";
43
43
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
44
44
 
45
- // src/core/num.ts
45
+ // src/ts/core/num.ts
46
46
  var import_utils3 = require("sia-reactor/utils");
47
47
 
48
- // src/hooks/vanilla/scrollAssist.ts
48
+ // src/ts/hooks/vanilla/scrollAssist.ts
49
49
  function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor.NIL) {
50
50
  const parent = el?.parentElement, existing = (t007._scrollers ??= /* @__PURE__ */ new WeakMap()).get(el);
51
51
  if (!parent || existing) return existing ? existing : void 0;
@@ -117,7 +117,7 @@ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll
117
117
  }
118
118
  var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
119
119
 
120
- // src/hooks/vanilla/scrollerator.ts
120
+ // src/ts/hooks/vanilla/scrollerator.ts
121
121
  var import_sia_reactor2 = require("sia-reactor");
122
122
  function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = import_sia_reactor2.NIL) {
123
123
  let linesPerSec = baseSpeed, accelId = null, lastTime = null;
@@ -134,18 +134,19 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
134
134
  return { drive, reset };
135
135
  }
136
136
 
137
- // src/hooks/vanilla/outsideClick.ts
137
+ // src/ts/hooks/vanilla/outsideClick.ts
138
138
  var import_sia_reactor3 = require("sia-reactor");
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) {
139
+ 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) {
140
140
  const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
141
141
  if (!enabled || existing) return existing ? existing : void 0;
142
142
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
143
- const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
144
- if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
145
- return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
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);
143
+ const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
144
+ 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;
145
+ return (!scoped ? true : root.contains(t)) && onOutside(e);
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 && onOutside(e)), handleFocusOut = (e) => outOnFocusOut && !el.contains(e.relatedTarget) && onScopedOut(e, e.relatedTarget);
147
147
  root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
148
- root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
148
+ root.addEventListener("keydown", handleEscape, capture);
149
+ el.addEventListener("focusout", handleFocusOut, capture);
149
150
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
150
151
  const destroy = () => {
151
152
  root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
@@ -157,7 +158,7 @@ function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_rea
157
158
  }
158
159
  var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
159
160
 
160
- // src/hooks/vanilla/focusTrap.ts
161
+ // src/ts/hooks/vanilla/focusTrap.ts
161
162
  var import_sia_reactor4 = require("sia-reactor");
162
163
  function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor4.NIL) {
163
164
  const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
@@ -168,9 +169,9 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
168
169
  if (rt.hasAttribute("tabindex")) return rt.focus();
169
170
  const items = getFocusable();
170
171
  if (!items.length) return resetFocus(0, null);
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);
172
+ const ceil = document.fullscreenElement || document.querySelector("dialog:modal") || document.body;
173
+ let p = rt.parentElement || ceil, all = getFocusable(p);
174
+ while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
174
175
  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();
175
176
  (pre ? first : last).blur();
176
177
  }, handleFocusIn = () => {
@@ -180,7 +181,7 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
180
181
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
181
182
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
182
183
  root.addEventListener("focusin", handleFocusIn, capture);
183
- if (!el.querySelector(":focus")) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
184
+ if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
184
185
  if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
185
186
  const destroy = () => {
186
187
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
@@ -192,7 +193,7 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
192
193
  }
193
194
  var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
194
195
 
195
- // src/hooks/react/useArrowNavigation/consts.ts
196
+ // src/ts/hooks/react/useArrowNavigation/consts.ts
196
197
  var import_sia_reactor5 = require("sia-reactor");
197
198
  var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
198
199
  var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
@@ -218,7 +219,7 @@ var DEFAULT_CONFIG = {
218
219
  onFocusOut: import_sia_reactor5.NOOP
219
220
  };
220
221
 
221
- // src/hooks/react/useArrowNavigation/utils.ts
222
+ // src/ts/hooks/react/useArrowNavigation/utils.ts
222
223
  var getTargetIndex = ({ key, currIndex, length, gridX, gridY, vGridY, loop, ctrlKey = false, rtl }) => {
223
224
  const rowStart = currIndex - currIndex % gridX, rowEnd = Math.min(rowStart + gridX - 1, length - 1), colStart = currIndex % gridX, colEnd = Math.min(colStart + gridX * (gridY - 1), length - 1), canX = gridX > 1, canY = gridY > 1, horizontalMove = rtl ? { ArrowRight: canX ? -1 : 0, ArrowLeft: canX ? 1 : 0 } : { ArrowRight: canX ? 1 : 0, ArrowLeft: canX ? -1 : 0 }, move = { ...horizontalMove, ArrowDown: canY ? gridX : 0, ArrowUp: canY ? -gridX : 0, Home: ctrlKey ? 0 : rowStart, End: ctrlKey ? length - 1 : rowEnd, PageDown: (vGridY - 1) * gridX, PageUp: -(vGridY - 1) * gridX }[key] ?? 0;
224
225
  let targetIndex = key === "Home" || key === "End" ? move : currIndex + move;
@@ -262,7 +263,7 @@ var getGrid = (all, x = true, y = true, vY = true) => {
262
263
  return grid;
263
264
  };
264
265
 
265
- // src/hooks/vanilla/arrowNavigation.ts
266
+ // src/ts/hooks/vanilla/arrowNavigation.ts
266
267
  function initArrowNavigation(container, config = {}) {
267
268
  const existing = (t007._arrownavs ??= /* @__PURE__ */ new WeakMap()).get(container);
268
269
  if (!config.enabled || existing) return existing ? existing : void 0;
@@ -375,7 +376,7 @@ function initArrowNavigation(container, config = {}) {
375
376
  }
376
377
  var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
377
378
 
378
- // src/hooks/vanilla/ripple.ts
379
+ // src/ts/hooks/vanilla/ripple.ts
379
380
  var import_sia_reactor6 = require("sia-reactor");
380
381
  function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade" } = import_sia_reactor6.NIL) {
381
382
  const el = target || e.currentTarget;
@@ -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-CVQx46Xq.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-C5MfEErC.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-CVQx46Xq.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-C5MfEErC.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,14 +10,14 @@ import {
10
10
  removeFocusTrap,
11
11
  removeOutsideClick,
12
12
  rippleHandler
13
- } from "../chunk-NLR4ANGT.js";
13
+ } from "../chunk-LBDWVTYF.js";
14
14
  import {
15
15
  INTERACTIVE_SELECTOR,
16
16
  createEl,
17
17
  getActiveEl
18
- } from "../chunk-N5KX6IW4.js";
18
+ } from "../chunk-Y5YJMRXD.js";
19
19
 
20
- // src/hooks/vanilla/scrollAssist.ts
20
+ // src/ts/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);
@@ -90,7 +90,7 @@ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll
90
90
  }
91
91
  var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
92
92
 
93
- // src/hooks/vanilla/scrollerator.ts
93
+ // src/ts/hooks/vanilla/scrollerator.ts
94
94
  import { NIL as NIL2 } from "sia-reactor";
95
95
  function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = NIL2) {
96
96
  let linesPerSec = baseSpeed, accelId = null, lastTime = null;
@@ -107,7 +107,7 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
107
107
  return { drive, reset };
108
108
  }
109
109
 
110
- // src/hooks/vanilla/arrowNavigation.ts
110
+ // src/ts/hooks/vanilla/arrowNavigation.ts
111
111
  function initArrowNavigation(container, config = {}) {
112
112
  const existing = (t007._arrownavs ??= /* @__PURE__ */ new WeakMap()).get(container);
113
113
  if (!config.enabled || existing) return existing ? existing : void 0;
package/dist/index.cjs CHANGED
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/index.ts
20
+ // src/ts/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  INTERACTIVE_SELECTOR: () => INTERACTIVE_SELECTOR,
@@ -37,6 +37,7 @@ __export(index_exports, {
37
37
  formatSize: () => formatSize,
38
38
  getActiveEl: () => import_utils2.getActiveEl,
39
39
  getTermsForKey: () => import_utils6.getTermsForKey,
40
+ getWindow: () => getWindow,
40
41
  guardAllMethods: () => import_utils7.guardAllMethods,
41
42
  guardMethod: () => import_utils7.guardMethod,
42
43
  inBoolArrOpt: () => inBoolArrOpt,
@@ -72,7 +73,7 @@ __export(index_exports, {
72
73
  });
73
74
  module.exports = __toCommonJS(index_exports);
74
75
 
75
- // src/core/dom.ts
76
+ // src/ts/core/dom.ts
76
77
  var import_utils = require("sia-reactor/utils");
77
78
  var import_utils2 = require("sia-reactor/utils");
78
79
  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] *)";
@@ -105,11 +106,14 @@ function loadResource(req, type = "style", { module: module2, media, crossOrigin
105
106
  });
106
107
  return w.t007._resourceCache[src];
107
108
  }
109
+ function getWindow(el = window) {
110
+ return (el instanceof Window ? el : el instanceof Document ? el?.defaultView : el?.ownerDocument?.defaultView) ?? void 0;
111
+ }
108
112
 
109
- // src/index.ts
113
+ // src/ts/index.ts
110
114
  var import_sia_reactor = require("sia-reactor");
111
115
 
112
- // src/core/obj.ts
116
+ // src/ts/core/obj.ts
113
117
  var import_utils3 = require("sia-reactor/utils");
114
118
  function isDef(val) {
115
119
  return "undefined" !== typeof val;
@@ -142,10 +146,10 @@ function inBoolArrOpt(opt, str) {
142
146
  return opt?.includes?.(str) ?? opt;
143
147
  }
144
148
 
145
- // src/core/num.ts
149
+ // src/ts/core/num.ts
146
150
  var import_utils4 = require("sia-reactor/utils");
147
151
 
148
- // src/core/str.ts
152
+ // src/ts/core/str.ts
149
153
  function uid(prefix = "") {
150
154
  return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
151
155
  }
@@ -171,7 +175,7 @@ function isSameURL(src1, src2) {
171
175
  }
172
176
  }
173
177
 
174
- // src/core/fn.ts
178
+ // src/ts/core/fn.ts
175
179
  var import_utils5 = require("sia-reactor/utils");
176
180
  function limited(FN_KEY, fn, opts = {}) {
177
181
  let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
@@ -195,20 +199,20 @@ function bindCleanupToSignal(cleanup, signal) {
195
199
  return cleanup;
196
200
  }
197
201
 
198
- // src/core/keys.ts
202
+ // src/ts/core/keys.ts
199
203
  var import_utils6 = require("sia-reactor/utils");
200
204
 
201
- // src/core/file.ts
205
+ // src/ts/core/file.ts
202
206
  function formatSize(bytes, decimals = 3, base = 1e3) {
203
207
  if (bytes < base) return `${bytes} byte${bytes == 1 ? "" : "s"}`;
204
208
  const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(base)), units.length - 1);
205
209
  return `${(bytes / Math.pow(base, exponent)).toFixed(decimals).replace(/\.0+$/, "")} ${units[exponent]}`;
206
210
  }
207
211
 
208
- // src/mixins/methd.ts
212
+ // src/ts/mixins/methd.ts
209
213
  var import_utils7 = require("sia-reactor/utils");
210
214
 
211
- // src/index.ts
215
+ // src/ts/index.ts
212
216
  if ("undefined" !== typeof window) {
213
217
  (window.t007 ??= {}).VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
214
218
  window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
@@ -237,6 +241,7 @@ if ("undefined" !== typeof window) {
237
241
  formatSize,
238
242
  getActiveEl,
239
243
  getTermsForKey,
244
+ getWindow,
240
245
  guardAllMethods,
241
246
  guardMethod,
242
247
  inBoolArrOpt,
package/dist/index.d.cts CHANGED
@@ -70,13 +70,13 @@ declare function pxToRem(px: number, el?: HTMLElement): number;
70
70
  * @param time The CSS time string to parse.
71
71
  * @returns The equivalent time in milliseconds.
72
72
  */
73
- declare function parseCSSTime(time: string): number;
73
+ declare function parseCSSTime(time: any): number;
74
74
  /** Parse a CSS size value (i.e. "16px" or "1.5rem") into pixels.
75
75
  * @param size The CSS size string to parse.
76
76
  * @param el The element to use for rem reference if needed. Defaults to the root element.
77
77
  * @returns The equivalent value in pixels.
78
78
  */
79
- declare function parseCSSSize(size: string, el?: HTMLElement): number;
79
+ declare function parseCSSSize(size: any, el?: HTMLElement): number;
80
80
  /** Compare two URLs after normalizing origin, pathname, and separators.
81
81
  * @param src1 First URL or path.
82
82
  * @param src2 Second URL or path.
@@ -169,6 +169,12 @@ declare const VIRTUAL_RESOURCE: symbol;
169
169
  */
170
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>;
171
171
 
172
+ /** Get the window object associated with a given element.
173
+ * @param el The element to get the window for, defaults to the main window.
174
+ * @returns The window object or undefined if none found.
175
+ */
176
+ declare function getWindow(el?: any): (Window & typeof globalThis) | undefined;
177
+
172
178
  /** Format a file size for display.
173
179
  * @param size Size in bytes.
174
180
  * @param decimals Decimal precision.
@@ -177,4 +183,4 @@ declare function loadResource(req: string | symbol, type?: ResourceType, { modul
177
183
  */
178
184
  declare function formatSize(bytes: number, decimals?: number, base?: number): string;
179
185
 
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 };
186
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, getWindow, 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
@@ -70,13 +70,13 @@ declare function pxToRem(px: number, el?: HTMLElement): number;
70
70
  * @param time The CSS time string to parse.
71
71
  * @returns The equivalent time in milliseconds.
72
72
  */
73
- declare function parseCSSTime(time: string): number;
73
+ declare function parseCSSTime(time: any): number;
74
74
  /** Parse a CSS size value (i.e. "16px" or "1.5rem") into pixels.
75
75
  * @param size The CSS size string to parse.
76
76
  * @param el The element to use for rem reference if needed. Defaults to the root element.
77
77
  * @returns The equivalent value in pixels.
78
78
  */
79
- declare function parseCSSSize(size: string, el?: HTMLElement): number;
79
+ declare function parseCSSSize(size: any, el?: HTMLElement): number;
80
80
  /** Compare two URLs after normalizing origin, pathname, and separators.
81
81
  * @param src1 First URL or path.
82
82
  * @param src2 Second URL or path.
@@ -169,6 +169,12 @@ declare const VIRTUAL_RESOURCE: symbol;
169
169
  */
170
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>;
171
171
 
172
+ /** Get the window object associated with a given element.
173
+ * @param el The element to get the window for, defaults to the main window.
174
+ * @returns The window object or undefined if none found.
175
+ */
176
+ declare function getWindow(el?: any): (Window & typeof globalThis) | undefined;
177
+
172
178
  /** Format a file size for display.
173
179
  * @param size Size in bytes.
174
180
  * @param decimals Decimal precision.
@@ -177,4 +183,4 @@ declare function loadResource(req: string | symbol, type?: ResourceType, { modul
177
183
  */
178
184
  declare function formatSize(bytes: number, decimals?: number, base?: number): string;
179
185
 
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 };
186
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, getWindow, 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
@@ -16,6 +16,7 @@ import {
16
16
  formatSize,
17
17
  getActiveEl,
18
18
  getTermsForKey,
19
+ getWindow,
19
20
  guardAllMethods,
20
21
  guardMethod,
21
22
  inBoolArrOpt,
@@ -48,7 +49,7 @@ import {
48
49
  setTimeout,
49
50
  stringifyKeyEvent,
50
51
  uid
51
- } from "./chunk-N5KX6IW4.js";
52
+ } from "./chunk-Y5YJMRXD.js";
52
53
  export {
53
54
  INTERACTIVE_SELECTOR,
54
55
  NIL,
@@ -67,6 +68,7 @@ export {
67
68
  formatSize,
68
69
  getActiveEl,
69
70
  getTermsForKey,
71
+ getWindow,
70
72
  guardAllMethods,
71
73
  guardMethod,
72
74
  inBoolArrOpt,
@@ -2,13 +2,15 @@ interface OutsideClickConfig {
2
2
  /** Enables or disables outside-click handling. Defaults to `false`. */
3
3
  enabled?: boolean;
4
4
  /** Callback invoked when an outside interaction is detected. Defaults to `()=>{}`. */
5
- onOutsideClick?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
5
+ onOutside?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
6
6
  /** Whether pointer/touch outside interactions should trigger callback. Defaults to `true`. */
7
7
  outOnClick?: boolean;
8
8
  /** Whether Escape key should trigger callback. Defaults to `true`. */
9
9
  outOnEscape?: boolean;
10
10
  /** Whether focus leaving the container should trigger callback. Defaults to `false`. */
11
11
  outOnFocusOut?: boolean;
12
+ /** Allow only clicks inside `el` bounding client rectangle to be considered valid, otherwise uses `el.contains(target)`. Defaults to `false . */
13
+ allowBounds?: boolean;
12
14
  /** Allow interactive elements including outsiders to bypass click callback. Defaults to `false`. */
13
15
  allowInputs?: boolean;
14
16
  /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
@@ -19,16 +21,16 @@ interface OutsideClickConfig {
19
21
  capture?: boolean;
20
22
  }
21
23
  /** Hook to attach outside-click, escape, and optional focus-out handling to an element. */
22
- declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, outOnClick, outOnEscape, outOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
24
+ declare function initOutsideClick(el: HTMLElement, { enabled, onOutside, outOnClick, outOnEscape, outOnFocusOut, allowBounds, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
23
25
  /** Remove outside-click handling from an element. */
24
26
  declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
25
27
 
26
28
  interface FocusTrapConfig {
27
29
  /** Enables or disables the focus trap. Defaults to `false`. */
28
30
  enabled?: boolean;
29
- /** The preferred initial focus target selector within the element. Defaults to `[data-autofocus]`. */
31
+ /** The preferred initial focus target selector within the element, overrides whatever was focused. Try `autofocus` attribute if working with dialogs before this. Defaults to `[data-autofocus]`. */
30
32
  initialSelector?: string;
31
- /** The class name for the initial focus ring since programmatic focus is not always visible. Defaults to `"focus-outline"`. */
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. Defaults to `"focus-outline"`. */
32
34
  ringClassName?: string;
33
35
  /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
34
36
  root?: HTMLElement | Document | Window;
@@ -2,13 +2,15 @@ interface OutsideClickConfig {
2
2
  /** Enables or disables outside-click handling. Defaults to `false`. */
3
3
  enabled?: boolean;
4
4
  /** Callback invoked when an outside interaction is detected. Defaults to `()=>{}`. */
5
- onOutsideClick?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
5
+ onOutside?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
6
6
  /** Whether pointer/touch outside interactions should trigger callback. Defaults to `true`. */
7
7
  outOnClick?: boolean;
8
8
  /** Whether Escape key should trigger callback. Defaults to `true`. */
9
9
  outOnEscape?: boolean;
10
10
  /** Whether focus leaving the container should trigger callback. Defaults to `false`. */
11
11
  outOnFocusOut?: boolean;
12
+ /** Allow only clicks inside `el` bounding client rectangle to be considered valid, otherwise uses `el.contains(target)`. Defaults to `false . */
13
+ allowBounds?: boolean;
12
14
  /** Allow interactive elements including outsiders to bypass click callback. Defaults to `false`. */
13
15
  allowInputs?: boolean;
14
16
  /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
@@ -19,16 +21,16 @@ interface OutsideClickConfig {
19
21
  capture?: boolean;
20
22
  }
21
23
  /** Hook to attach outside-click, escape, and optional focus-out handling to an element. */
22
- declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, outOnClick, outOnEscape, outOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
24
+ declare function initOutsideClick(el: HTMLElement, { enabled, onOutside, outOnClick, outOnEscape, outOnFocusOut, allowBounds, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
23
25
  /** Remove outside-click handling from an element. */
24
26
  declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
25
27
 
26
28
  interface FocusTrapConfig {
27
29
  /** Enables or disables the focus trap. Defaults to `false`. */
28
30
  enabled?: boolean;
29
- /** The preferred initial focus target selector within the element. Defaults to `[data-autofocus]`. */
31
+ /** The preferred initial focus target selector within the element, overrides whatever was focused. Try `autofocus` attribute if working with dialogs before this. Defaults to `[data-autofocus]`. */
30
32
  initialSelector?: string;
31
- /** The class name for the initial focus ring since programmatic focus is not always visible. Defaults to `"focus-outline"`. */
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. Defaults to `"focus-outline"`. */
32
34
  ringClassName?: string;
33
35
  /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
34
36
  root?: HTMLElement | Document | Window;
@@ -1,44 +1,44 @@
1
1
  :root {
2
- --t007-scroll-assist-color: rgb(0 0 0 / 1);
2
+ --t007-scroll-assist-color: black;
3
3
  --t007-scroll-assist-opacity: 0.5;
4
- --t007-scroll-assist-min-width: 2rem;
5
- --t007-scroll-assist-height: 2rem;
6
- --t007-scroll-assist-inline-offset: -0.35rem;
7
- --t007-scroll-assist-block-offset: 0;
4
+ --t007-scroll-assist-width: 2rem;
5
+ --t007-scroll-assist-height: var(--t007-scroll-assist-width);
6
+ --t007-scroll-assist-inset-x: calc(var(--t007-scroll-assist-width) * -0.25);
7
+ --t007-scroll-assist-inset-y: 0;
8
8
  }
9
9
 
10
10
  .t007-scroll-assist {
11
- --t007-scroll-assist-gradient-dir: to right;
11
+ --t007-gradient-dir: to right;
12
12
  position: absolute;
13
- min-width: var(--t007-scroll-assist-min-width);
13
+ min-width: var(--t007-scroll-assist-width);
14
14
  height: 100%;
15
15
  opacity: var(--t007-scroll-assist-opacity);
16
- background: linear-gradient(var(--t007-scroll-assist-gradient-dir), transparent 0, var(--t007-scroll-assist-color) 30%, transparent);
16
+ background: linear-gradient(var(--t007-gradient-dir), transparent 0, var(--t007-scroll-assist-color) 30%, transparent);
17
17
  z-index: 2;
18
18
  }
19
19
 
20
20
  .t007-scroll-assist[data-scroll-direction="left"] {
21
- --t007-scroll-assist-gradient-dir: to right;
21
+ --t007-gradient-dir: to right;
22
22
  top: 0;
23
- left: var(--t007-scroll-assist-inline-offset);
23
+ left: var(--t007-scroll-assist-inset-x);
24
24
  }
25
25
 
26
26
  .t007-scroll-assist[data-scroll-direction="right"] {
27
- --t007-scroll-assist-gradient-dir: to left;
27
+ --t007-gradient-dir: to left;
28
28
  top: 0;
29
- right: var(--t007-scroll-assist-inline-offset);
29
+ right: var(--t007-scroll-assist-inset-x);
30
30
  }
31
31
 
32
32
  .t007-scroll-assist[data-scroll-direction="up"] {
33
- --t007-scroll-assist-gradient-dir: to bottom;
34
- top: var(--t007-scroll-assist-block-offset);
33
+ --t007-gradient-dir: to bottom;
34
+ top: var(--t007-scroll-assist-inset-y);
35
35
  height: var(--t007-scroll-assist-height);
36
36
  width: 100%;
37
37
  }
38
38
 
39
39
  .t007-scroll-assist[data-scroll-direction="down"] {
40
- --t007-scroll-assist-gradient-dir: to top;
41
- bottom: var(--t007-scroll-assist-block-offset);
40
+ --t007-gradient-dir: to top;
41
+ bottom: var(--t007-scroll-assist-inset-y);
42
42
  height: var(--t007-scroll-assist-height);
43
43
  width: 100%;
44
44
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@t007/utils",
3
- "version": "0.0.29",
4
- "description": "High-performance, zero-dependency utility functions for the t007 ecosystem.",
3
+ "version": "0.0.31",
4
+ "description": "High-performance utilities for the t007 ecosystem.",
5
5
  "author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -50,7 +50,7 @@
50
50
  "access": "public"
51
51
  },
52
52
  "scripts": {
53
- "build": "tsup --config tsup.config.ts && shx cp src/styles/ripple.css dist/styles/ripple.css && shx cp src/styles/scroll-assist.css dist/styles/scroll-assist.css",
53
+ "build": "tsup --config tsup.config.ts",
54
54
  "prepublishOnly": "shx cp ../../LICENSE ."
55
55
  },
56
56
  "files": [
@@ -70,12 +70,12 @@
70
70
  "devDependencies": {
71
71
  "@types/react": "^18.0.0",
72
72
  "@types/react-dom": "^18.0.0",
73
+ "esbuild-sass-plugin": "^3.7.0",
73
74
  "react": "^18.3.1",
74
75
  "react-dom": "^18.3.1"
75
76
  },
76
77
  "dependencies": {
77
- "@t007/input": "^0.0.27",
78
- "sia-reactor": "^0.0.32"
78
+ "sia-reactor": "^0.0.33"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "react": "^18.0.0"