@t007/utils 0.0.33 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { K as KeyEvent, C as Config } from './scrollAssist-y9wFmYgt.js';
1
+ import { K as KeyEvent, C as Config } from './scrollAssist-l0V2H-jx.js';
2
2
 
3
3
  type ArrowNavigationHandle = {
4
4
  /** Current computed horizontal grid size. */
@@ -1,4 +1,4 @@
1
- import { K as KeyEvent, C as Config } from './scrollAssist-y9wFmYgt.cjs';
1
+ import { K as KeyEvent, C as Config } from './scrollAssist-l0V2H-jx.cjs';
2
2
 
3
3
  type ArrowNavigationHandle = {
4
4
  /** Current computed horizontal grid size. */
@@ -1,42 +1,77 @@
1
1
  // src/ts/core/dom.ts
2
2
  import { createEl, assignEl } from "sia-reactor/utils";
3
3
  import { getActiveEl } from "sia-reactor/utils";
4
- var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
4
+ import { NOOP } from "sia-reactor";
5
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
5
6
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
6
7
  var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
7
- function loadResource(req, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
8
- w.t007 ??= {}, w.t007._resourceCache ??= {};
8
+ function loadResource(req, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, win = window) {
9
+ win.t007 ??= {}, win.t007._resourceCache ??= {};
9
10
  if (req === VIRTUAL_RESOURCE || isSym(req)) return Promise.resolve();
10
11
  const src = req;
11
- if (w.t007._resourceCache[src]) return w.t007._resourceCache[src];
12
- const existing = type === "script" ? Array.prototype.find.call(w.document.scripts, (s) => isSameURL(s.src, src)) : type === "style" ? Array.prototype.find.call(w.document.styleSheets, (s) => isSameURL(s.href, src)) : null;
13
- if (existing) return w.t007._resourceCache[src] = Promise.resolve(existing);
14
- w.t007._resourceCache[src] = new Promise((resolve, reject) => {
12
+ if (win.t007._resourceCache[src]) return win.t007._resourceCache[src];
13
+ const existing = type === "script" ? Array.prototype.find.call(win.document.scripts, (s) => isSameURL(s.src, src)) : type === "style" ? Array.prototype.find.call(win.document.styleSheets, (s) => isSameURL(s.href, src)) : null;
14
+ if (existing) return win.t007._resourceCache[src] = Promise.resolve(existing);
15
+ win.t007._resourceCache[src] = new Promise((resolve, reject) => {
15
16
  (function tryLoad(remaining, el) {
16
17
  const onerror = () => {
17
18
  el?.remove?.();
18
19
  if (remaining > 1) {
19
20
  setTimeout(tryLoad, 1e3, remaining - 1);
20
- console.warn(`Retrying ${type} load (${attempts - remaining + 1}): ${src}...`);
21
+ console.warn(`Retrying ${type} load for "${src}" (${attempts - remaining + 1})...`);
21
22
  } else {
22
- delete w.t007._resourceCache[src];
23
- reject(new Error(`${type} load failed after ${attempts} attempts: ${src}`));
23
+ delete win.t007._resourceCache[src];
24
+ reject(new Error(`${capitalize(type)} load failed for "${src}" after ${attempts - 1} attempts`));
24
25
  }
25
26
  };
26
27
  const url = retryKey && remaining < attempts ? `${src}${src.includes("?") ? "&" : "?"}_${retryKey}=${Date.now()}` : src;
27
- if (type === "script") w.document.body.append(el = createEl("script", { src: url, type: module ? "module" : "text/javascript", crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
28
- else if (type === "style") w.document.head.append(el = createEl("link", { rel: "stylesheet", href: url, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
28
+ if (type === "script") win.document.body.append(el = createEl("script", { src: url, type: module ? "module" : "text/javascript", crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
29
+ else if (type === "style") win.document.head.append(el = createEl("link", { rel: "stylesheet", href: url, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
29
30
  else reject(new Error(`Unsupported resource type: ${type}`));
30
31
  })(attempts);
31
32
  });
32
- return w.t007._resourceCache[src];
33
+ return win.t007._resourceCache[src];
33
34
  }
34
35
  function getWindow(el = window) {
35
- return (el instanceof Window ? el : el instanceof Document ? el?.defaultView : el?.ownerDocument?.defaultView) ?? void 0;
36
+ return el?.ownerDocument?.defaultView ?? el?.defaultView ?? window;
37
+ }
38
+ function createListRenderer({ container, getKey, createNode, updateNode = NOOP, destroyNode = NOOP }) {
39
+ let nodeRegistry = /* @__PURE__ */ new Map();
40
+ return function syncDOM(array, strict = true) {
41
+ const newRegistry = /* @__PURE__ */ new Map(), seenKeys = /* @__PURE__ */ new Set(), oldPositions = /* @__PURE__ */ new WeakMap(), children = Array.from(container.children);
42
+ for (let i = 0, len = children.length; i < len; i++) oldPositions.set(children[i], i);
43
+ const futureNodes = [], oldIndices = [];
44
+ for (let i = 0, len = array.length; i < len; i++) {
45
+ const item = array[i], key = getKey(item);
46
+ if (seenKeys.has(key)) throw new Error(`[List Renderer] Duplicate key "${key}" detected`);
47
+ let node = nodeRegistry.get(key);
48
+ if (!node) {
49
+ node = createNode(item);
50
+ if (!node) {
51
+ if (strict) throw new Error(`[List Renderer] No HTMLElement for key "${key}"`);
52
+ continue;
53
+ }
54
+ oldIndices.push(-1);
55
+ } else updateNode(node, item), oldIndices.push(oldPositions.get(node) ?? -1);
56
+ seenKeys.add(key), futureNodes.push(node), newRegistry.set(key, node);
57
+ }
58
+ for (const [key, node] of nodeRegistry.entries()) !seenKeys.has(key) && node.parentElement === container && (destroyNode(node, key), node.remove());
59
+ const sequence = longestIncreasingSubsequence(oldIndices), stable = new Set(sequence);
60
+ let anchor = null;
61
+ for (let i = futureNodes.length - 1; i >= 0; i--) {
62
+ const node = futureNodes[i];
63
+ if (oldIndices[i] !== -1 && stable.has(i)) {
64
+ anchor = node;
65
+ continue;
66
+ }
67
+ container.insertBefore(node, anchor), anchor = node;
68
+ }
69
+ nodeRegistry = newRegistry;
70
+ };
36
71
  }
37
72
 
38
73
  // src/ts/index.ts
39
- import { NIL, NOOP } from "sia-reactor";
74
+ import { NIL, NOOP as NOOP2 } from "sia-reactor";
40
75
 
41
76
  // src/ts/core/obj.ts
42
77
  import { isObj } from "sia-reactor/utils";
@@ -73,11 +108,42 @@ function inBoolArrOpt(opt, str) {
73
108
 
74
109
  // src/ts/core/num.ts
75
110
  import { clamp } from "sia-reactor/utils";
111
+ function longestIncreasingSubsequence(array) {
112
+ const n = array.length, parent = new Array(n), tails = [];
113
+ for (let i = 0; i < n; i++) {
114
+ const value = array[i];
115
+ if (value === -1) continue;
116
+ let left = 0;
117
+ let right = tails.length;
118
+ while (left < right) {
119
+ const mid = left + right >> 1;
120
+ if (array[tails[mid]] < value) left = mid + 1;
121
+ else right = mid;
122
+ }
123
+ if (left > 0) parent[i] = tails[left - 1];
124
+ else parent[i] = -1;
125
+ tails[left] = i;
126
+ }
127
+ if (!tails.length) return [];
128
+ const result = [];
129
+ let k = tails[tails.length - 1];
130
+ while (k >= 0) result.push(k), k = parent[k];
131
+ return result.reverse(), result;
132
+ }
76
133
 
77
134
  // src/ts/core/str.ts
78
135
  function uid(prefix = "") {
79
136
  return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
80
137
  }
138
+ function capitalize(word = "") {
139
+ return word.replace(/^(\s*)([a-z])/i, (_, s, l) => s + l.toUpperCase());
140
+ }
141
+ function camelize(str = "", { source } = /[\s_-]+/, { preserveInnerCase: pIC = true, upperFirst: uF = false } = {}) {
142
+ return (pIC ? str : str.toLowerCase()).replace(new RegExp(source + "(\\w)", "g"), (_, c) => c.toUpperCase()).replace(/^\w/, (c) => c[uF ? "toUpperCase" : "toLowerCase"]());
143
+ }
144
+ function uncamelize(str, separator = " ") {
145
+ return str.replace(/([a-z])([A-Z])/g, `$1${separator}$2`).toLowerCase();
146
+ }
81
147
  function remToPx(rem, el = document.documentElement) {
82
148
  return rem * parseFloat(getComputedStyle(el).fontSize);
83
149
  }
@@ -99,12 +165,42 @@ function cleanURL(url) {
99
165
  }
100
166
  }
101
167
  function isSameURL(url1, url2) {
168
+ if (url1 === url2) return true;
102
169
  if (!isStr(url1) || !isStr(url2) || !url1 || !url2) return false;
103
170
  return cleanURL(url1) === cleanURL(url2);
104
171
  }
105
172
 
106
173
  // src/ts/core/fn.ts
107
174
  import { setTimeout as setTimeout2, setInterval, requestAnimationFrame } from "sia-reactor/utils";
175
+ function throttle(key, fn, delay = 30, strict = true, signal, win) {
176
+ const throttleMap = t007._throttlers ??= /* @__PURE__ */ new Map();
177
+ if (strict === true) {
178
+ const now = performance.now();
179
+ return now - (throttleMap.get(key) ?? 0) < delay ? void 0 : (throttleMap.set(key, now), fn());
180
+ }
181
+ if (throttleMap.has(key)) return;
182
+ const id = strict === false ? setTimeout2(() => throttleMap.delete(key), delay, signal, win) : strict(() => throttleMap.delete(key));
183
+ return throttleMap.set(key, id), fn();
184
+ }
185
+ function debounce(key, fn, delay = 30, strict = true, signal, win) {
186
+ const debounceMap = t007._debouncers ??= /* @__PURE__ */ new Map();
187
+ if (strict) {
188
+ const now = performance.now(), prev = debounceMap.get(key) ?? 0;
189
+ return debounceMap.set(key, now), now - prev < delay ? void 0 : fn();
190
+ }
191
+ const prevId = debounceMap.get(key);
192
+ prevId !== void 0 && (win ?? window).clearTimeout(prevId);
193
+ const id = setTimeout2(() => (debounceMap.delete(key), fn()), delay, signal, win);
194
+ return void debounceMap.set(key, id);
195
+ }
196
+ function RAFLoop(key, fn, signal, win) {
197
+ const rafLoopMap = t007._RAFLoopers ??= /* @__PURE__ */ new Map();
198
+ if (rafLoopMap.has(key)) return void rafLoopMap.set(key, fn);
199
+ rafLoopMap.set(key, fn);
200
+ const loop = (_ = 0, fn2 = rafLoopMap.get(key)) => fn2 && (fn2(), requestAnimationFrame(loop, signal, win));
201
+ loop();
202
+ }
203
+ var cancelRAFLoop = (key) => t007._RAFLoopers ? t007._RAFLoopers.delete(key) : false;
108
204
  function limited(FN_KEY, fn, opts = {}) {
109
205
  let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
110
206
  const getReg = () => JSON.parse(localStorage.getItem(FN_KEY) || "{}"), setReg = (r) => localStorage.setItem(FN_KEY, JSON.stringify(r));
@@ -118,7 +214,7 @@ function limited(FN_KEY, fn, opts = {}) {
118
214
  handle.block = () => (count = max, key && ((r) => (r[key] = max, setReg(r)))(getReg()));
119
215
  return handle;
120
216
  }
121
- var mockAsync = (timeout = 250) => new Promise((resolve) => setTimeout(resolve, timeout));
217
+ var mockAsync = (timeout = 250) => new Promise((resolve) => setTimeout2(resolve, timeout));
122
218
  var breath = (w = window) => new Promise((res) => w.requestAnimationFrame(res));
123
219
  var deepBreath = (w = window) => new Promise((res) => w.requestAnimationFrame(() => w.requestAnimationFrame(res)));
124
220
  function bindCleanupToSignal(cleanup, signal) {
@@ -128,7 +224,7 @@ function bindCleanupToSignal(cleanup, signal) {
128
224
  }
129
225
 
130
226
  // src/ts/core/keys.ts
131
- import { parseKeyCombo, stringifyKeyEvent, cleanKeyCombo, matchKeys, getTermsForKey, keyEventAllowed, formatKeyForDisplay, formatKeyShortcutsForDisplay, parseForARIAKS } from "sia-reactor/utils";
227
+ import { KEYS_BLOCKS, parseKeyCombo, stringifyKeyEvent, cleanKeyCombo, matchKeys, getTermsForKey, keyEventAllowed, formatKeyForDisplay, formatKeyShortcutsForDisplay, parseForARIAKS } from "sia-reactor/utils";
132
228
 
133
229
  // src/ts/core/file.ts
134
230
  function formatSize(bytes, decimals = 3, base = 1e3) {
@@ -159,6 +255,7 @@ export {
159
255
  VIRTUAL_RESOURCE,
160
256
  loadResource,
161
257
  getWindow,
258
+ createListRenderer,
162
259
  getActiveEl,
163
260
  isObj,
164
261
  isDef,
@@ -171,22 +268,31 @@ export {
171
268
  isIter,
172
269
  isFunc,
173
270
  inBoolArrOpt,
271
+ longestIncreasingSubsequence,
174
272
  clamp,
175
273
  uid,
274
+ capitalize,
275
+ camelize,
276
+ uncamelize,
176
277
  remToPx,
177
278
  pxToRem,
178
279
  parseCSSTime,
179
280
  parseCSSSize,
180
281
  cleanURL,
181
282
  isSameURL,
283
+ setTimeout2 as setTimeout,
284
+ setInterval,
285
+ requestAnimationFrame,
286
+ throttle,
287
+ debounce,
288
+ RAFLoop,
289
+ cancelRAFLoop,
182
290
  limited,
183
291
  mockAsync,
184
292
  breath,
185
293
  deepBreath,
186
294
  bindCleanupToSignal,
187
- setTimeout2 as setTimeout,
188
- setInterval,
189
- requestAnimationFrame,
295
+ KEYS_BLOCKS,
190
296
  parseKeyCombo,
191
297
  stringifyKeyEvent,
192
298
  cleanKeyCombo,
@@ -202,5 +308,5 @@ export {
202
308
  guardAllMethods,
203
309
  guardMethod,
204
310
  NIL,
205
- NOOP
311
+ NOOP2 as NOOP
206
312
  };
@@ -4,7 +4,7 @@ import {
4
4
  createEl,
5
5
  getActiveEl,
6
6
  isInteractive
7
- } from "./chunk-4O76EZJ4.js";
7
+ } from "./chunk-HGUYOV3P.js";
8
8
 
9
9
  // src/ts/hooks/vanilla/outsideClick.ts
10
10
  import { NIL, NOOP } from "sia-reactor";
@@ -67,7 +67,7 @@ var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
67
67
 
68
68
  // src/ts/hooks/vanilla/ripple.ts
69
69
  import { NIL as NIL3 } from "sia-reactor";
70
- function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade" } = NIL3) {
70
+ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade", maxDuration = 1e3 } = NIL3) {
71
71
  const el = target || e.currentTarget;
72
72
  if (!el || e.target !== e.currentTarget && isInteractive(e.target) || el.hasAttribute("disabled") || e.pointerType === "mouse" && e.button !== 0) return;
73
73
  e.stopPropagation?.();
@@ -75,10 +75,13 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
75
75
  let canRelease = false;
76
76
  ripple.addEventListener("animationend", () => canRelease = true, { once: true });
77
77
  el.append(wrapper.appendChild(ripple).parentElement);
78
- const release = () => {
79
- if (!canRelease) return ripple.addEventListener("animationend", release, { once: true });
78
+ let released = false;
79
+ const release = (force) => {
80
+ if (released) return;
81
+ if (!canRelease && force !== true) return ripple.addEventListener("animationend", release, { once: true }), void setTimeout(release, maxDuration, true);
82
+ released = true;
80
83
  ripple.classList.replace(holdClassName, fadeClassName);
81
- ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove()));
84
+ ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove())), setTimeout(() => wrapper.remove(), maxDuration);
82
85
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).removeEventListener(evt, release);
83
86
  };
84
87
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
@@ -147,7 +150,7 @@ var DEFAULT_CONFIG = {
147
150
  rtl: null,
148
151
  grid: {},
149
152
  activeClass: "focus-outlined",
150
- inputSelector: "input,textarea,[contenteditable='true']",
153
+ inputSelector: "input,textarea,[contenteditable]",
151
154
  focusOptions: { preventScroll: false },
152
155
  scrollIntoView: { block: "nearest", inline: "nearest" },
153
156
  onSelect: NOOP2,
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { H as HighlightOptions } from '../useHighlight-DMpDCILK.cjs';
2
+ import { H as HighlightOptions } from '../useHighlight-DYc_GF0R.cjs';
3
3
 
4
4
  interface HighlightTextProps extends HighlightOptions {
5
5
  /** The text to be processed and displayed. */
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { H as HighlightOptions } from '../useHighlight-DMpDCILK.js';
2
+ import { H as HighlightOptions } from '../useHighlight-DYc_GF0R.js';
3
3
 
4
4
  interface HighlightTextProps extends HighlightOptions {
5
5
  /** The text to be processed and displayed. */
@@ -38,12 +38,13 @@ var import_utils = require("sia-reactor/utils");
38
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
- var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable='true'],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
41
+ var import_sia_reactor = require("sia-reactor");
42
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
42
43
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
43
44
 
44
45
  // src/ts/hooks/react/useScrollAssist.ts
45
- var import_sia_reactor = require("sia-reactor");
46
- function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor.NIL) {
46
+ var import_sia_reactor2 = require("sia-reactor");
47
+ function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor2.NIL) {
47
48
  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);
48
49
  const update = (0, import_react.useCallback)(() => {
49
50
  const el = ref.current;
@@ -124,8 +125,8 @@ function useScrollAssist(ref, { enabled = true, pxPerSecond = 80, assistClassNam
124
125
  var import_react2 = require("react");
125
126
 
126
127
  // src/ts/hooks/vanilla/outsideClick.ts
127
- var import_sia_reactor2 = require("sia-reactor");
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) {
128
+ var import_sia_reactor3 = require("sia-reactor");
129
+ function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor3.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowBounds = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor3.NIL) {
129
130
  const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
130
131
  if (!enabled || existing) return existing ? existing : void 0;
131
132
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
@@ -147,8 +148,8 @@ function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor2
147
148
  }
148
149
 
149
150
  // src/ts/hooks/react/useOutsideClick.ts
150
- var import_sia_reactor3 = require("sia-reactor");
151
- function useOutsideClick(ref, config = import_sia_reactor3.NIL) {
151
+ var import_sia_reactor4 = require("sia-reactor");
152
+ function useOutsideClick(ref, config = import_sia_reactor4.NIL) {
152
153
  (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]);
153
154
  }
154
155
 
@@ -200,7 +201,7 @@ var getGrid = (all, x = true, y = true, vY = true) => {
200
201
  };
201
202
 
202
203
  // src/ts/hooks/react/useArrowNavigation/consts.ts
203
- var import_sia_reactor4 = require("sia-reactor");
204
+ var import_sia_reactor5 = require("sia-reactor");
204
205
  var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
205
206
  var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
206
207
  var NAV_KEYS = [...H_NAV_KEYS, ...V_NAV_KEYS];
@@ -218,11 +219,11 @@ var DEFAULT_CONFIG = {
218
219
  rtl: null,
219
220
  grid: {},
220
221
  activeClass: "focus-outlined",
221
- inputSelector: "input,textarea,[contenteditable='true']",
222
+ inputSelector: "input,textarea,[contenteditable]",
222
223
  focusOptions: { preventScroll: false },
223
224
  scrollIntoView: { block: "nearest", inline: "nearest" },
224
- onSelect: import_sia_reactor4.NOOP,
225
- onFocusOut: import_sia_reactor4.NOOP
225
+ onSelect: import_sia_reactor5.NOOP,
226
+ onFocusOut: import_sia_reactor5.NOOP
226
227
  };
227
228
 
228
229
  // src/ts/hooks/react/useArrowNavigation/index.ts
@@ -378,8 +379,8 @@ function useArrowNavigation(containerRef, config = {}) {
378
379
  var import_react4 = require("react");
379
380
 
380
381
  // src/ts/hooks/vanilla/focusTrap.ts
381
- var import_sia_reactor5 = require("sia-reactor");
382
- function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor5.NIL) {
382
+ var import_sia_reactor6 = require("sia-reactor");
383
+ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor6.NIL) {
383
384
  const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
384
385
  if (!enabled || existing) return existing ? existing : void 0;
385
386
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
@@ -412,8 +413,8 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
412
413
  }
413
414
 
414
415
  // src/ts/hooks/react/useFocusTrap.ts
415
- var import_sia_reactor6 = require("sia-reactor");
416
- function useFocusTrap(ref, config = import_sia_reactor6.NIL) {
416
+ var import_sia_reactor7 = require("sia-reactor");
417
+ function useFocusTrap(ref, config = import_sia_reactor7.NIL) {
417
418
  (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]);
418
419
  }
419
420
 
@@ -434,8 +435,8 @@ function useHighlight(text, query, ignoreCase = true, options) {
434
435
  var import_react6 = require("react");
435
436
 
436
437
  // src/ts/hooks/vanilla/ripple.ts
437
- var import_sia_reactor7 = require("sia-reactor");
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
+ var import_sia_reactor8 = require("sia-reactor");
439
+ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade", maxDuration = 1e3 } = import_sia_reactor8.NIL) {
439
440
  const el = target || e.currentTarget;
440
441
  if (!el || e.target !== e.currentTarget && isInteractive(e.target) || el.hasAttribute("disabled") || e.pointerType === "mouse" && e.button !== 0) return;
441
442
  e.stopPropagation?.();
@@ -443,19 +444,22 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
443
444
  let canRelease = false;
444
445
  ripple.addEventListener("animationend", () => canRelease = true, { once: true });
445
446
  el.append(wrapper.appendChild(ripple).parentElement);
446
- const release = () => {
447
- if (!canRelease) return ripple.addEventListener("animationend", release, { once: true });
447
+ let released = false;
448
+ const release = (force) => {
449
+ if (released) return;
450
+ if (!canRelease && force !== true) return ripple.addEventListener("animationend", release, { once: true }), void setTimeout(release, maxDuration, true);
451
+ released = true;
448
452
  ripple.classList.replace(holdClassName, fadeClassName);
449
- ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove()));
453
+ ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove())), setTimeout(() => wrapper.remove(), maxDuration);
450
454
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).removeEventListener(evt, release);
451
455
  };
452
456
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
453
457
  }
454
458
 
455
459
  // src/ts/hooks/react/useRipple.ts
456
- var import_sia_reactor8 = require("sia-reactor");
460
+ var import_sia_reactor9 = require("sia-reactor");
457
461
  function useRipple() {
458
- return (0, import_react6.useCallback)((e, config = import_sia_reactor8.NIL) => rippleHandler(e.nativeEvent, config), []);
462
+ return (0, import_react6.useCallback)((e, config = import_sia_reactor9.NIL) => rippleHandler(e.nativeEvent, config), []);
459
463
  }
460
464
  // Annotate the CommonJS export names for ESM import in node:
461
465
  0 && (module.exports = {
@@ -1,11 +1,11 @@
1
1
  import { RefObject } from 'react';
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-C5MfEErC.cjs';
4
- export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DMpDCILK.cjs';
2
+ import { a as ScrollAssistConfig, C as Config, K as KeyEvent } from '../scrollAssist-l0V2H-jx.cjs';
3
+ import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-87VO-U7A.cjs';
4
+ export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DYc_GF0R.cjs';
5
5
 
6
6
  /** Configuration options for the `useScrollAssist` hook. */
7
7
  interface UseScrollAssistConfig extends ScrollAssistConfig {
8
- /** Enables or disables the scroll assist. Defaults to `true`. */
8
+ /** Enables or disables the scroll assist. @default `true`. */
9
9
  enabled?: boolean;
10
10
  }
11
11
  /** React hook to add edge-scrolling assist to an element. It creates invisible "hot zones" at the edges of the element that, when hovered or dragged into, will scroll the element in that direction.
@@ -1,11 +1,11 @@
1
1
  import { RefObject } from 'react';
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-C5MfEErC.js';
4
- export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DMpDCILK.js';
2
+ import { a as ScrollAssistConfig, C as Config, K as KeyEvent } from '../scrollAssist-l0V2H-jx.js';
3
+ import { O as OutsideClickConfig, F as FocusTrapConfig, R as RippleConfig } from '../ripple-87VO-U7A.js';
4
+ export { H as HighlightOptions, u as useHighlight } from '../useHighlight-DYc_GF0R.js';
5
5
 
6
6
  /** Configuration options for the `useScrollAssist` hook. */
7
7
  interface UseScrollAssistConfig extends ScrollAssistConfig {
8
- /** Enables or disables the scroll assist. Defaults to `true`. */
8
+ /** Enables or disables the scroll assist. @default `true`. */
9
9
  enabled?: boolean;
10
10
  }
11
11
  /** React hook to add edge-scrolling assist to an element. It creates invisible "hot zones" at the edges of the element that, when hovered or dragged into, will scroll the element in that direction.
@@ -11,11 +11,11 @@ import {
11
11
  initFocusTrap,
12
12
  initOutsideClick,
13
13
  rippleHandler
14
- } from "../chunk-QJ72EQCJ.js";
14
+ } from "../chunk-ZRCBB5P4.js";
15
15
  import {
16
16
  INTERACTIVE_SELECTOR,
17
17
  getActiveEl
18
- } from "../chunk-4O76EZJ4.js";
18
+ } from "../chunk-HGUYOV3P.js";
19
19
 
20
20
  // src/ts/hooks/react/useScrollAssist.ts
21
21
  import { useEffect, useRef, useCallback } from "react";
@@ -34,19 +34,20 @@ __export(vanilla_exports, {
34
34
  module.exports = __toCommonJS(vanilla_exports);
35
35
 
36
36
  // src/ts/hooks/vanilla/scrollAssist.ts
37
- var import_sia_reactor = require("sia-reactor");
37
+ var import_sia_reactor2 = require("sia-reactor");
38
38
 
39
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
- 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
+ var import_sia_reactor = require("sia-reactor");
43
+ var INTERACTIVE_SELECTOR = ":is(button,[href],input:not([type='hidden']),select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]):not([disabled],[tabindex='-1'],[data-focus-guard],[inert],[inert] *)";
43
44
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
44
45
 
45
46
  // src/ts/core/num.ts
46
47
  var import_utils3 = require("sia-reactor/utils");
47
48
 
48
49
  // src/ts/hooks/vanilla/scrollAssist.ts
49
- function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor.NIL) {
50
+ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor2.NIL) {
50
51
  const parent = el?.parentElement, existing = (t007._scrollers ??= /* @__PURE__ */ new WeakMap()).get(el);
51
52
  if (!parent || existing) return existing ? existing : void 0;
52
53
  t007._scrollers_r_observer ??= new ResizeObserver((entries) => {
@@ -118,8 +119,8 @@ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll
118
119
  var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
119
120
 
120
121
  // src/ts/hooks/vanilla/scrollerator.ts
121
- var import_sia_reactor2 = require("sia-reactor");
122
- function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = import_sia_reactor2.NIL) {
122
+ var import_sia_reactor3 = require("sia-reactor");
123
+ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = import_sia_reactor3.NIL) {
123
124
  let linesPerSec = baseSpeed, accelId = null, lastTime = null;
124
125
  const drive = (clientY, brake = false, offsetY = 0) => {
125
126
  if (car !== window) clientY -= offsetY;
@@ -135,8 +136,8 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
135
136
  }
136
137
 
137
138
  // src/ts/hooks/vanilla/outsideClick.ts
138
- var import_sia_reactor3 = require("sia-reactor");
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) {
139
+ var import_sia_reactor4 = require("sia-reactor");
140
+ function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor4.NOOP, outOnClick = true, outOnEscape = true, outOnFocusOut = false, allowBounds = false, allowInputs = false, root = window, scoped = true, capture = true } = import_sia_reactor4.NIL) {
140
141
  const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
141
142
  if (!enabled || existing) return existing ? existing : void 0;
142
143
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
@@ -159,8 +160,8 @@ function initOutsideClick(el, { enabled = false, onOutside = import_sia_reactor3
159
160
  var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
160
161
 
161
162
  // src/ts/hooks/vanilla/focusTrap.ts
162
- var import_sia_reactor4 = require("sia-reactor");
163
- function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor4.NIL) {
163
+ var import_sia_reactor5 = require("sia-reactor");
164
+ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor5.NIL) {
164
165
  const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
165
166
  if (!enabled || existing) return existing ? existing : void 0;
166
167
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
@@ -194,7 +195,7 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
194
195
  var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
195
196
 
196
197
  // src/ts/hooks/react/useArrowNavigation/consts.ts
197
- var import_sia_reactor5 = require("sia-reactor");
198
+ var import_sia_reactor6 = require("sia-reactor");
198
199
  var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
199
200
  var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
200
201
  var NAV_KEYS = [...H_NAV_KEYS, ...V_NAV_KEYS];
@@ -212,11 +213,11 @@ var DEFAULT_CONFIG = {
212
213
  rtl: null,
213
214
  grid: {},
214
215
  activeClass: "focus-outlined",
215
- inputSelector: "input,textarea,[contenteditable='true']",
216
+ inputSelector: "input,textarea,[contenteditable]",
216
217
  focusOptions: { preventScroll: false },
217
218
  scrollIntoView: { block: "nearest", inline: "nearest" },
218
- onSelect: import_sia_reactor5.NOOP,
219
- onFocusOut: import_sia_reactor5.NOOP
219
+ onSelect: import_sia_reactor6.NOOP,
220
+ onFocusOut: import_sia_reactor6.NOOP
220
221
  };
221
222
 
222
223
  // src/ts/hooks/react/useArrowNavigation/utils.ts
@@ -377,8 +378,8 @@ function initArrowNavigation(container, config = {}) {
377
378
  var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
378
379
 
379
380
  // src/ts/hooks/vanilla/ripple.ts
380
- var import_sia_reactor6 = require("sia-reactor");
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
+ var import_sia_reactor7 = require("sia-reactor");
382
+ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade", maxDuration = 1e3 } = import_sia_reactor7.NIL) {
382
383
  const el = target || e.currentTarget;
383
384
  if (!el || e.target !== e.currentTarget && isInteractive(e.target) || el.hasAttribute("disabled") || e.pointerType === "mouse" && e.button !== 0) return;
384
385
  e.stopPropagation?.();
@@ -386,10 +387,13 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
386
387
  let canRelease = false;
387
388
  ripple.addEventListener("animationend", () => canRelease = true, { once: true });
388
389
  el.append(wrapper.appendChild(ripple).parentElement);
389
- const release = () => {
390
- if (!canRelease) return ripple.addEventListener("animationend", release, { once: true });
390
+ let released = false;
391
+ const release = (force) => {
392
+ if (released) return;
393
+ if (!canRelease && force !== true) return ripple.addEventListener("animationend", release, { once: true }), void setTimeout(release, maxDuration, true);
394
+ released = true;
391
395
  ripple.classList.replace(holdClassName, fadeClassName);
392
- ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove()));
396
+ ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove())), setTimeout(() => wrapper.remove(), maxDuration);
393
397
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).removeEventListener(evt, release);
394
398
  };
395
399
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
@@ -1,22 +1,22 @@
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-C5MfEErC.cjs';
3
- export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-DK8mqVOk.cjs';
1
+ export { a as ScrollAssistConfig, S as ScrollAssistHandle, b as ScrollDir, i as initScrollAssist, r as removeScrollAssist } from '../scrollAssist-l0V2H-jx.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-87VO-U7A.cjs';
3
+ export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-IaWiNTa6.cjs';
4
4
 
5
5
  /** Configuration for the vertical edge-scrolling helper. */
6
6
  interface ScrolleratorOptions {
7
- /** Starting lines-per-second speed. Defaults to `3`. */
7
+ /** Starting lines-per-second speed. @default `3`. */
8
8
  baseSpeed?: number;
9
- /** Maximum accelerated speed. Defaults to `10`. */
9
+ /** Maximum accelerated speed. @default `10`. */
10
10
  maxSpeed?: number;
11
- /** Delay before acceleration kicks in. Defaults to `2000`. */
11
+ /** Delay before acceleration kicks in. @default `2000`. */
12
12
  stepDelay?: number;
13
- /** Base frame rate used to estimate movement. Defaults to `16`. */
13
+ /** Base frame rate used to estimate movement. @default `16`. */
14
14
  baseRate?: number;
15
- /** Approximate line height in pixels. Defaults to `80`. */
15
+ /** Approximate line height in pixels. @default `80`. */
16
16
  lineHeight?: number;
17
- /** Edge margin that triggers scrolling. Defaults to `80`. */
17
+ /** Edge margin that triggers scrolling. @default `80`. */
18
18
  margin?: number;
19
- /** Scroll container or window target. Defaults to `window`. */
19
+ /** Scroll container or window target. @default `window`. */
20
20
  car?: Window | HTMLElement;
21
21
  }
22
22
  /** Scrolling controls returned by initVScrollerator. */