@t007/utils 0.0.30 → 0.0.32

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.
@@ -90,15 +90,18 @@ function parseCSSTime(time) {
90
90
  function parseCSSSize(size, el) {
91
91
  return size?.endsWith?.("px") ? parseFloat(size) : remToPx(parseFloat(size), el);
92
92
  }
93
- function isSameURL(src1, src2) {
94
- if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
93
+ function cleanURL(url) {
95
94
  try {
96
- const u1 = new URL(src1, window.location.href), u2 = new URL(src2, window.location.href);
97
- return decodeURIComponent(u1.origin + u1.pathname) === decodeURIComponent(u2.origin + u2.pathname);
95
+ const u = new URL(url, window.location.href);
96
+ return decodeURIComponent(u.origin + u.pathname);
98
97
  } catch {
99
- return src1.replace(/\\/g, "/").split("?")[0].trim() === src2.replace(/\\/g, "/").split("?")[0].trim();
98
+ return url.replace(/\\/g, "/").split("?")[0].trim();
100
99
  }
101
100
  }
101
+ function isSameURL(url1, url2) {
102
+ if (!isStr(url1) || !isStr(url2) || !url1 || !url2) return false;
103
+ return cleanURL(url1) === cleanURL(url2);
104
+ }
102
105
 
103
106
  // src/ts/core/fn.ts
104
107
  import { setTimeout as setTimeout2, setInterval, requestAnimationFrame } from "sia-reactor/utils";
@@ -174,6 +177,7 @@ export {
174
177
  pxToRem,
175
178
  parseCSSTime,
176
179
  parseCSSSize,
180
+ cleanURL,
177
181
  isSameURL,
178
182
  limited,
179
183
  mockAsync,
@@ -4,20 +4,21 @@ import {
4
4
  createEl,
5
5
  getActiveEl,
6
6
  isInteractive
7
- } from "./chunk-Y5YJMRXD.js";
7
+ } from "./chunk-4O76EZJ4.js";
8
8
 
9
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);
@@ -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();
@@ -125,16 +125,17 @@ var import_react2 = require("react");
125
125
 
126
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);
@@ -148,7 +149,7 @@ function initOutsideClick(el, { enabled = false, onOutsideClick = import_sia_rea
148
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
155
  // src/ts/hooks/react/useArrowNavigation/index.ts
@@ -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();
@@ -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. */
@@ -11,11 +11,11 @@ import {
11
11
  initFocusTrap,
12
12
  initOutsideClick,
13
13
  rippleHandler
14
- } from "../chunk-YDARUYLO.js";
14
+ } from "../chunk-QJ72EQCJ.js";
15
15
  import {
16
16
  INTERACTIVE_SELECTOR,
17
17
  getActiveEl
18
- } from "../chunk-Y5YJMRXD.js";
18
+ } from "../chunk-4O76EZJ4.js";
19
19
 
20
20
  // src/ts/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.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
107
  // src/ts/hooks/react/useArrowNavigation/index.ts
@@ -136,16 +136,17 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
136
136
 
137
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);
@@ -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();
@@ -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,12 +10,12 @@ import {
10
10
  removeFocusTrap,
11
11
  removeOutsideClick,
12
12
  rippleHandler
13
- } from "../chunk-YDARUYLO.js";
13
+ } from "../chunk-QJ72EQCJ.js";
14
14
  import {
15
15
  INTERACTIVE_SELECTOR,
16
16
  createEl,
17
17
  getActiveEl
18
- } from "../chunk-Y5YJMRXD.js";
18
+ } from "../chunk-4O76EZJ4.js";
19
19
 
20
20
  // src/ts/hooks/vanilla/scrollAssist.ts
21
21
  import { NIL } from "sia-reactor";
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  breath: () => breath,
31
31
  clamp: () => import_utils4.clamp,
32
32
  cleanKeyCombo: () => import_utils6.cleanKeyCombo,
33
+ cleanURL: () => cleanURL,
33
34
  createEl: () => import_utils.createEl,
34
35
  deepBreath: () => deepBreath,
35
36
  formatKeyForDisplay: () => import_utils6.formatKeyForDisplay,
@@ -165,15 +166,18 @@ function parseCSSTime(time) {
165
166
  function parseCSSSize(size, el) {
166
167
  return size?.endsWith?.("px") ? parseFloat(size) : remToPx(parseFloat(size), el);
167
168
  }
168
- function isSameURL(src1, src2) {
169
- if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
169
+ function cleanURL(url) {
170
170
  try {
171
- const u1 = new URL(src1, window.location.href), u2 = new URL(src2, window.location.href);
172
- return decodeURIComponent(u1.origin + u1.pathname) === decodeURIComponent(u2.origin + u2.pathname);
171
+ const u = new URL(url, window.location.href);
172
+ return decodeURIComponent(u.origin + u.pathname);
173
173
  } catch {
174
- return src1.replace(/\\/g, "/").split("?")[0].trim() === src2.replace(/\\/g, "/").split("?")[0].trim();
174
+ return url.replace(/\\/g, "/").split("?")[0].trim();
175
175
  }
176
176
  }
177
+ function isSameURL(url1, url2) {
178
+ if (!isStr(url1) || !isStr(url2) || !url1 || !url2) return false;
179
+ return cleanURL(url1) === cleanURL(url2);
180
+ }
177
181
 
178
182
  // src/ts/core/fn.ts
179
183
  var import_utils5 = require("sia-reactor/utils");
@@ -234,6 +238,7 @@ if ("undefined" !== typeof window) {
234
238
  breath,
235
239
  clamp,
236
240
  cleanKeyCombo,
241
+ cleanURL,
237
242
  createEl,
238
243
  deepBreath,
239
244
  formatKeyForDisplay,
package/dist/index.d.cts CHANGED
@@ -20,18 +20,18 @@ declare global {
20
20
  interface Window {
21
21
  /** Shared T007 namespace. */
22
22
  t007: T007Namespace;
23
- /** CDN entrypoint for @t007/toast. */
24
- T007_TOAST_JS_SRC?: string;
25
- /** CDN entrypoint for @t007/input. */
26
- T007_INPUT_JS_SRC?: string;
27
- /** CDN entrypoint for @t007/dialog. */
28
- T007_DIALOG_JS_SRC?: string;
29
- /** CDN stylesheet for @t007/toast. */
30
- T007_TOAST_CSS_SRC?: string;
31
- /** CDN stylesheet for @t007/input. */
32
- T007_INPUT_CSS_SRC?: string;
33
- /** CDN stylesheet for @t007/dialog. */
34
- T007_DIALOG_CSS_SRC?: string;
23
+ /** CDN js entrypoint for `@t007/toast`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
24
+ T007_TOAST_JS_SRC?: string | symbol;
25
+ /** CDN js entrypoint for `@t007/input`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
26
+ T007_INPUT_JS_SRC?: string | symbol;
27
+ /** CDN js entrypoint for `@t007/dialog`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
28
+ T007_DIALOG_JS_SRC?: string | symbol;
29
+ /** CDN stylesheet for `@t007/toast`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
30
+ T007_TOAST_CSS_SRC?: string | symbol;
31
+ /** CDN stylesheet for `@t007/input`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
32
+ T007_INPUT_CSS_SRC?: string | symbol;
33
+ /** CDN stylesheet for `@t007/dialog`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
34
+ T007_DIALOG_CSS_SRC?: string | symbol;
35
35
  }
36
36
  /** Shared T007 namespace on the global object. */
37
37
  var t007: T007Namespace;
@@ -77,12 +77,17 @@ declare function parseCSSTime(time: any): number;
77
77
  * @returns The equivalent value in pixels.
78
78
  */
79
79
  declare function parseCSSSize(size: any, el?: HTMLElement): number;
80
+ /** Normalize a URL by decoding it and removing query parameters and hash fragments.
81
+ * @param url The URL string to clean.
82
+ * @returns A normalized URL string for comparison.
83
+ */
84
+ declare function cleanURL(url: string): string;
80
85
  /** Compare two URLs after normalizing origin, pathname, and separators.
81
- * @param src1 First URL or path.
82
- * @param src2 Second URL or path.
86
+ * @param url1 First URL or path.
87
+ * @param url2 Second URL or path.
83
88
  * @returns True when both references point to the same resource.
84
89
  */
85
- declare function isSameURL(src1: unknown, src2: unknown): boolean;
90
+ declare function isSameURL(url1: unknown, url2: unknown): boolean;
86
91
 
87
92
  interface LimitedOptions {
88
93
  /** Storage key used to persist call counts. */
@@ -134,7 +139,7 @@ declare function bindCleanupToSignal<Cb extends () => any>(cleanup: Cb, signal?:
134
139
  /** Exhaustive Selector used for interactive, tabbable UI controls. */
135
140
  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] *)";
136
141
  /** Check whether an event target points to an interactive element. */
137
- declare const isInteractive: (target: EventTarget | null) => target is HTMLElement;
142
+ declare const isInteractive: (target: EventTarget | null) => boolean;
138
143
  /** Resource type accepted by loadResource. */
139
144
  type ResourceType = "style" | "script" | string;
140
145
  /** Options used when loading a script or stylesheet resource. */
@@ -183,4 +188,4 @@ declare function getWindow(el?: any): (Window & typeof globalThis) | undefined;
183
188
  */
184
189
  declare function formatSize(bytes: number, decimals?: number, base?: number): string;
185
190
 
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 };
191
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, cleanURL, 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
@@ -20,18 +20,18 @@ declare global {
20
20
  interface Window {
21
21
  /** Shared T007 namespace. */
22
22
  t007: T007Namespace;
23
- /** CDN entrypoint for @t007/toast. */
24
- T007_TOAST_JS_SRC?: string;
25
- /** CDN entrypoint for @t007/input. */
26
- T007_INPUT_JS_SRC?: string;
27
- /** CDN entrypoint for @t007/dialog. */
28
- T007_DIALOG_JS_SRC?: string;
29
- /** CDN stylesheet for @t007/toast. */
30
- T007_TOAST_CSS_SRC?: string;
31
- /** CDN stylesheet for @t007/input. */
32
- T007_INPUT_CSS_SRC?: string;
33
- /** CDN stylesheet for @t007/dialog. */
34
- T007_DIALOG_CSS_SRC?: string;
23
+ /** CDN js entrypoint for `@t007/toast`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
24
+ T007_TOAST_JS_SRC?: string | symbol;
25
+ /** CDN js entrypoint for `@t007/input`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
26
+ T007_INPUT_JS_SRC?: string | symbol;
27
+ /** CDN js entrypoint for `@t007/dialog`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
28
+ T007_DIALOG_JS_SRC?: string | symbol;
29
+ /** CDN stylesheet for `@t007/toast`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
30
+ T007_TOAST_CSS_SRC?: string | symbol;
31
+ /** CDN stylesheet for `@t007/input`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
32
+ T007_INPUT_CSS_SRC?: string | symbol;
33
+ /** CDN stylesheet for `@t007/dialog`, assign a symbol if bundling, e.g. `VIRTUAL_RESOURCE` from `@t007/utils`. */
34
+ T007_DIALOG_CSS_SRC?: string | symbol;
35
35
  }
36
36
  /** Shared T007 namespace on the global object. */
37
37
  var t007: T007Namespace;
@@ -77,12 +77,17 @@ declare function parseCSSTime(time: any): number;
77
77
  * @returns The equivalent value in pixels.
78
78
  */
79
79
  declare function parseCSSSize(size: any, el?: HTMLElement): number;
80
+ /** Normalize a URL by decoding it and removing query parameters and hash fragments.
81
+ * @param url The URL string to clean.
82
+ * @returns A normalized URL string for comparison.
83
+ */
84
+ declare function cleanURL(url: string): string;
80
85
  /** Compare two URLs after normalizing origin, pathname, and separators.
81
- * @param src1 First URL or path.
82
- * @param src2 Second URL or path.
86
+ * @param url1 First URL or path.
87
+ * @param url2 Second URL or path.
83
88
  * @returns True when both references point to the same resource.
84
89
  */
85
- declare function isSameURL(src1: unknown, src2: unknown): boolean;
90
+ declare function isSameURL(url1: unknown, url2: unknown): boolean;
86
91
 
87
92
  interface LimitedOptions {
88
93
  /** Storage key used to persist call counts. */
@@ -134,7 +139,7 @@ declare function bindCleanupToSignal<Cb extends () => any>(cleanup: Cb, signal?:
134
139
  /** Exhaustive Selector used for interactive, tabbable UI controls. */
135
140
  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] *)";
136
141
  /** Check whether an event target points to an interactive element. */
137
- declare const isInteractive: (target: EventTarget | null) => target is HTMLElement;
142
+ declare const isInteractive: (target: EventTarget | null) => boolean;
138
143
  /** Resource type accepted by loadResource. */
139
144
  type ResourceType = "style" | "script" | string;
140
145
  /** Options used when loading a script or stylesheet resource. */
@@ -183,4 +188,4 @@ declare function getWindow(el?: any): (Window & typeof globalThis) | undefined;
183
188
  */
184
189
  declare function formatSize(bytes: number, decimals?: number, base?: number): string;
185
190
 
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 };
191
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, cleanURL, 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
@@ -9,6 +9,7 @@ import {
9
9
  breath,
10
10
  clamp,
11
11
  cleanKeyCombo,
12
+ cleanURL,
12
13
  createEl,
13
14
  deepBreath,
14
15
  formatKeyForDisplay,
@@ -49,7 +50,7 @@ import {
49
50
  setTimeout,
50
51
  stringifyKeyEvent,
51
52
  uid
52
- } from "./chunk-Y5YJMRXD.js";
53
+ } from "./chunk-4O76EZJ4.js";
53
54
  export {
54
55
  INTERACTIVE_SELECTOR,
55
56
  NIL,
@@ -61,6 +62,7 @@ export {
61
62
  breath,
62
63
  clamp,
63
64
  cleanKeyCombo,
65
+ cleanURL,
64
66
  createEl,
65
67
  deepBreath,
66
68
  formatKeyForDisplay,
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t007/utils",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "High-performance utilities for the t007 ecosystem.",
5
5
  "author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
6
6
  "license": "MIT",