@t007/utils 0.0.33 → 0.0.35

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.
@@ -29,24 +29,27 @@ __export(vanilla_exports, {
29
29
  removeFocusTrap: () => removeFocusTrap,
30
30
  removeOutsideClick: () => removeOutsideClick,
31
31
  removeScrollAssist: () => removeScrollAssist,
32
- rippleHandler: () => rippleHandler
32
+ rippleHandler: () => rippleHandler,
33
+ syncArrowNavigation: () => syncArrowNavigation,
34
+ syncFocusTrap: () => syncFocusTrap
33
35
  });
34
36
  module.exports = __toCommonJS(vanilla_exports);
35
37
 
36
38
  // src/ts/hooks/vanilla/scrollAssist.ts
37
- var import_sia_reactor = require("sia-reactor");
39
+ var import_sia_reactor2 = require("sia-reactor");
38
40
 
39
41
  // src/ts/core/dom.ts
40
42
  var import_utils = require("sia-reactor/utils");
41
43
  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] *)";
44
+ var import_sia_reactor = require("sia-reactor");
45
+ 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
46
  var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
44
47
 
45
48
  // src/ts/core/num.ts
46
49
  var import_utils3 = require("sia-reactor/utils");
47
50
 
48
51
  // src/ts/hooks/vanilla/scrollAssist.ts
49
- function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor.NIL) {
52
+ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = import_sia_reactor2.NIL) {
50
53
  const parent = el?.parentElement, existing = (t007._scrollers ??= /* @__PURE__ */ new WeakMap()).get(el);
51
54
  if (!parent || existing) return existing ? existing : void 0;
52
55
  t007._scrollers_r_observer ??= new ResizeObserver((entries) => {
@@ -118,8 +121,8 @@ function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll
118
121
  var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
119
122
 
120
123
  // 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) {
124
+ var import_sia_reactor3 = require("sia-reactor");
125
+ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = import_sia_reactor3.NIL) {
123
126
  let linesPerSec = baseSpeed, accelId = null, lastTime = null;
124
127
  const drive = (clientY, brake = false, offsetY = 0) => {
125
128
  if (car !== window) clientY -= offsetY;
@@ -135,36 +138,36 @@ function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, base
135
138
  }
136
139
 
137
140
  // 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) {
140
- const stacks = t007._outsiders_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
141
+ var import_sia_reactor4 = require("sia-reactor");
142
+ 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) {
143
+ const stack = t007._outsiders_stack ??= [], existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
141
144
  if (!enabled || existing) return existing ? existing : void 0;
142
145
  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 = allowBounds ? el.getBoundingClientRect() : null) => {
146
+ const onScopedOut = (e, t, p = e.touches?.[0] || e, rect = allowBounds ? el.getBoundingClientRect() : null) => {
144
147
  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
148
  return (!scoped ? true : root.contains(t)) && onOutside(e);
146
149
  }, 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
150
  root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
148
151
  root.addEventListener("keydown", handleEscape, capture);
149
152
  el.addEventListener("focusout", handleFocusOut, capture);
150
- if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
151
153
  const destroy = () => {
152
154
  root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
153
155
  root.removeEventListener("keydown", handleEscape, capture);
154
156
  el.removeEventListener("focusout", handleFocusOut, capture);
155
157
  t007._outsiders.delete(el), stack.splice(stack.indexOf(el), 1);
156
158
  };
157
- return t007._outsiders.set(el, destroy), destroy;
159
+ return !stack.includes(el) && stack.push(el), t007._outsiders.set(el, destroy), destroy;
158
160
  }
159
161
  var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
160
162
 
161
163
  // 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) {
164
- const stacks = t007._ftrappers_stacks ??= /* @__PURE__ */ new WeakMap(), existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
164
+ var import_sia_reactor5 = require("sia-reactor");
165
+ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = import_sia_reactor5.NIL) {
166
+ const stack = t007._ftrappers_stack ??= [], existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
165
167
  if (!enabled || existing) return existing ? existing : void 0;
166
168
  scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
167
- const stack = stacks.get(root) ?? [], focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = (0, import_utils.createEl)("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = (0, import_utils.createEl)("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => [...c.querySelectorAll(INTERACTIVE_SELECTOR)], resetFocus = (i = 0, els = getFocusable()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false, rt = root) => {
169
+ let recovering = false;
170
+ const focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = (0, import_utils.createEl)("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = (0, import_utils.createEl)("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => [...c.querySelectorAll(INTERACTIVE_SELECTOR)], resetFocus = (i = 0, els = getFocusable()) => !recovering && (recovering = true, els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), recovering = false), edgeFocus = (pre = false, rt = root) => {
168
171
  if (!scoped) return resetFocus(pre ? -1 : 0);
169
172
  if (rt.hasAttribute("tabindex")) return rt.focus();
170
173
  const items = getFocusable();
@@ -174,27 +177,28 @@ function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus
174
177
  while (p !== ceil && (!all.length || (pre ? rt.contains(all[0]) : rt.contains(all.at(-1))))) all = getFocusable(p = p.parentElement || ceil);
175
178
  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();
176
179
  (pre ? first : last).blur();
177
- }, handleFocusIn = () => {
180
+ }, handleFocusIn = (e) => {
178
181
  if (document.querySelector("dialog:modal") && !el.matches("dialog:modal")) return;
179
- stack.at(-1) === el && (0, import_utils2.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils2.getActiveEl)(el.ownerDocument)) && resetFocus();
182
+ stack.at(-1) === el && (0, import_utils2.getActiveEl)(el.ownerDocument) !== root && !el.contains((0, import_utils2.getActiveEl)(el.ownerDocument)) && !e.composedPath().includes(el) && resetFocus();
180
183
  }, handleInitialBlur = () => initial.classList.remove(ringClassName);
181
184
  first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
182
185
  last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
183
186
  root.addEventListener("focusin", handleFocusIn, capture);
184
187
  if (initial || !el.contains(focused)) !initial ? setTimeout(resetFocus) : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
185
- if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
186
188
  const destroy = () => {
187
189
  focused?.isConnected && focused.focus(), first.remove(), last.remove();
188
190
  root.removeEventListener("focusin", handleFocusIn, capture);
189
191
  initial?.removeEventListener("blur", handleInitialBlur, capture);
190
192
  t007._ftrappers.delete(el), stack.splice(stack.indexOf(el), 1);
191
193
  };
192
- return t007._ftrappers.set(el, destroy), destroy;
194
+ const handle = { destroy, sync: () => (el.prepend(first), el.append(last)) };
195
+ return !stack.includes(el) && stack.push(el), t007._ftrappers.set(el, handle), handle;
193
196
  }
194
- var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
197
+ var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.destroy();
198
+ var syncFocusTrap = (el) => t007._ftrappers?.get(el)?.sync();
195
199
 
196
200
  // src/ts/hooks/react/useArrowNavigation/consts.ts
197
- var import_sia_reactor5 = require("sia-reactor");
201
+ var import_sia_reactor6 = require("sia-reactor");
198
202
  var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
199
203
  var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
200
204
  var NAV_KEYS = [...H_NAV_KEYS, ...V_NAV_KEYS];
@@ -212,11 +216,11 @@ var DEFAULT_CONFIG = {
212
216
  rtl: null,
213
217
  grid: {},
214
218
  activeClass: "focus-outlined",
215
- inputSelector: "input,textarea,[contenteditable='true']",
219
+ inputSelector: "input,textarea,[contenteditable]",
216
220
  focusOptions: { preventScroll: false },
217
221
  scrollIntoView: { block: "nearest", inline: "nearest" },
218
- onSelect: import_sia_reactor5.NOOP,
219
- onFocusOut: import_sia_reactor5.NOOP
222
+ onSelect: import_sia_reactor6.NOOP,
223
+ onFocusOut: import_sia_reactor6.NOOP
220
224
  };
221
225
 
222
226
  // src/ts/hooks/react/useArrowNavigation/utils.ts
@@ -269,7 +273,7 @@ function initArrowNavigation(container, config = {}) {
269
273
  if (!config.enabled || existing) return existing ? existing : void 0;
270
274
  const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut, rovingTab } = { ...DEFAULT_CONFIG, ...config };
271
275
  let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
272
- const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.hasAttribute("disabled") || el.hasAttribute("aria-disabled"), getItems = () => items = Array.from(container.querySelectorAll(selector));
276
+ const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.matches('[disabled],[aria-disabled="true"],[inert],:disabled'), getItems = () => items = Array.from(container.querySelectorAll(selector));
273
277
  const getAbleIndex = (targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
274
278
  if (shouldSnub() || !items.length) return null;
275
279
  let index = targetIndex;
@@ -344,41 +348,49 @@ function initArrowNavigation(container, config = {}) {
344
348
  if (idx !== -1) goToIndex(idx);
345
349
  };
346
350
  for (const el of items) el.addEventListener("mouseenter", handleHover);
347
- const mutationObserver = new MutationObserver(() => {
351
+ const sync = () => {
348
352
  const oldEl = items[activeIndex];
353
+ for (const el of items) el.removeEventListener("mouseenter", handleHover);
349
354
  getItems();
355
+ for (const el of items) el.addEventListener("mouseenter", handleHover);
356
+ calcGrid();
350
357
  const newEl = items[activeIndex];
351
358
  updateDOM();
352
359
  if (oldEl && newEl && oldEl === newEl) return;
353
- resetActiveIndex();
354
- updateDOM();
355
- });
360
+ resetActiveIndex(), updateDOM();
361
+ };
362
+ const mutationObserver = new MutationObserver(sync);
356
363
  mutationObserver.observe(container, { childList: true, subtree: true });
357
364
  const setGrid = (g) => {
358
365
  if (g.x !== void 0) gridX = g.x;
359
366
  if (g.y !== void 0) gridY = g.y;
360
367
  if (g.vY !== void 0) vGridY = g.vY;
361
368
  };
362
- const calcGrid = () => setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
369
+ let ancestor = null;
370
+ const resizeObserver = new ResizeObserver(() => calcGrid());
371
+ const calcGrid = () => {
372
+ setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
373
+ const next = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
374
+ if (next && next !== ancestor) ancestor && resizeObserver.unobserve(ancestor), ancestor = next, resizeObserver.observe(ancestor);
375
+ };
363
376
  setGrid(grid), calcGrid();
364
- const ancestor = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
365
- const resizeObserver = new ResizeObserver(calcGrid);
366
- if (ancestor) resizeObserver.observe(ancestor);
367
377
  const destroy = () => {
368
378
  for (const el of interactiveEls) el?.removeEventListener("keydown", simulateKey);
369
379
  container.removeEventListener("focusout", handleFocusOut);
370
380
  for (const el of items) el.removeEventListener("mouseenter", handleHover);
371
381
  mutationObserver.disconnect(), resizeObserver.disconnect();
372
382
  if (timeout) clearTimeout(timeout);
383
+ t007._arrownavs.delete(container);
373
384
  };
374
- const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
385
+ const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, sync, destroy };
375
386
  return t007._arrownavs.set(container, handle), handle;
376
387
  }
377
388
  var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
389
+ var syncArrowNavigation = (container) => t007._arrownavs?.get(container)?.sync();
378
390
 
379
391
  // 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) {
392
+ var import_sia_reactor7 = require("sia-reactor");
393
+ 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
394
  const el = target || e.currentTarget;
383
395
  if (!el || e.target !== e.currentTarget && isInteractive(e.target) || el.hasAttribute("disabled") || e.pointerType === "mouse" && e.button !== 0) return;
384
396
  e.stopPropagation?.();
@@ -386,10 +398,13 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
386
398
  let canRelease = false;
387
399
  ripple.addEventListener("animationend", () => canRelease = true, { once: true });
388
400
  el.append(wrapper.appendChild(ripple).parentElement);
389
- const release = () => {
390
- if (!canRelease) return ripple.addEventListener("animationend", release, { once: true });
401
+ let released = false;
402
+ const release = (force) => {
403
+ if (released) return;
404
+ if (!canRelease && force !== true) return ripple.addEventListener("animationend", release, { once: true }), void setTimeout(release, maxDuration, true);
405
+ released = true;
391
406
  ripple.classList.replace(holdClassName, fadeClassName);
392
- ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove()));
407
+ ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove())), setTimeout(() => wrapper.remove(), maxDuration);
393
408
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).removeEventListener(evt, release);
394
409
  };
395
410
  for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
@@ -405,5 +420,7 @@ function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t00
405
420
  removeFocusTrap,
406
421
  removeOutsideClick,
407
422
  removeScrollAssist,
408
- rippleHandler
423
+ rippleHandler,
424
+ syncArrowNavigation,
425
+ syncFocusTrap
409
426
  });
@@ -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 { b as FocusTrapConfig, F as FocusTrapHandle, a as ScrollAssistConfig, S as ScrollAssistHandle, c as ScrollDir, i as initFocusTrap, d as initScrollAssist, r as removeFocusTrap, e as removeScrollAssist, s as syncFocusTrap } from '../scrollAssist-BNvPnsJq.cjs';
2
+ export { O as OutsideClickConfig, R as RippleConfig, i as initOutsideClick, r as removeOutsideClick, a as rippleHandler } from '../ripple-D1kGErTn.cjs';
3
+ export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation, s as syncArrowNavigation } from '../arrowNavigation-zEJbo8Er.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. */
@@ -1,22 +1,22 @@
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-C5MfEErC.js';
3
- export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation } from '../arrowNavigation-VenvPI4H.js';
1
+ export { b as FocusTrapConfig, F as FocusTrapHandle, a as ScrollAssistConfig, S as ScrollAssistHandle, c as ScrollDir, i as initFocusTrap, d as initScrollAssist, r as removeFocusTrap, e as removeScrollAssist, s as syncFocusTrap } from '../scrollAssist-BNvPnsJq.js';
2
+ export { O as OutsideClickConfig, R as RippleConfig, i as initOutsideClick, r as removeOutsideClick, a as rippleHandler } from '../ripple-D1kGErTn.js';
3
+ export { A as ArrowNavigationHandle, i as initArrowNavigation, r as removeArrowNavigation, s as syncArrowNavigation } from '../arrowNavigation-B9bEdoqR.js';
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. */
@@ -9,13 +9,14 @@ import {
9
9
  initOutsideClick,
10
10
  removeFocusTrap,
11
11
  removeOutsideClick,
12
- rippleHandler
13
- } from "../chunk-QJ72EQCJ.js";
12
+ rippleHandler,
13
+ syncFocusTrap
14
+ } from "../chunk-NX2LWYD6.js";
14
15
  import {
15
16
  INTERACTIVE_SELECTOR,
16
17
  createEl,
17
18
  getActiveEl
18
- } from "../chunk-4O76EZJ4.js";
19
+ } from "../chunk-ZALPHCSJ.js";
19
20
 
20
21
  // src/ts/hooks/vanilla/scrollAssist.ts
21
22
  import { NIL } from "sia-reactor";
@@ -113,7 +114,7 @@ function initArrowNavigation(container, config = {}) {
113
114
  if (!config.enabled || existing) return existing ? existing : void 0;
114
115
  const { enabled: isEnabled, selector, focusOnHover, loop, virtual, typeahead, resetMs, activeClass, inputSelector, defaultTabbableIndex, baseTabIndex, grid, rtl: isRtl, focusOptions, scrollIntoView, onSelect, onFocusOut, rovingTab } = { ...DEFAULT_CONFIG, ...config };
115
116
  let gridX = grid.x || 1, gridY = grid.y || 1, vGridY = grid.vY || 1, activeIndex = -1, buffer = "", timeout = null, items = [];
116
- const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.hasAttribute("disabled") || el.hasAttribute("aria-disabled"), getItems = () => items = Array.from(container.querySelectorAll(selector));
117
+ const enabled = isEnabled ?? virtual, roving = rovingTab ?? !virtual, rtl = isRtl ?? ("undefined" === typeof document ? false : getComputedStyle(container).direction === "rtl"), shouldSnub = () => !enabled || !container, isItemDisabled = (el) => !el || el.matches('[disabled],[aria-disabled="true"],[inert],:disabled'), getItems = () => items = Array.from(container.querySelectorAll(selector));
117
118
  const getAbleIndex = (targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
118
119
  if (shouldSnub() || !items.length) return null;
119
120
  let index = targetIndex;
@@ -188,37 +189,45 @@ function initArrowNavigation(container, config = {}) {
188
189
  if (idx !== -1) goToIndex(idx);
189
190
  };
190
191
  for (const el of items) el.addEventListener("mouseenter", handleHover);
191
- const mutationObserver = new MutationObserver(() => {
192
+ const sync = () => {
192
193
  const oldEl = items[activeIndex];
194
+ for (const el of items) el.removeEventListener("mouseenter", handleHover);
193
195
  getItems();
196
+ for (const el of items) el.addEventListener("mouseenter", handleHover);
197
+ calcGrid();
194
198
  const newEl = items[activeIndex];
195
199
  updateDOM();
196
200
  if (oldEl && newEl && oldEl === newEl) return;
197
- resetActiveIndex();
198
- updateDOM();
199
- });
201
+ resetActiveIndex(), updateDOM();
202
+ };
203
+ const mutationObserver = new MutationObserver(sync);
200
204
  mutationObserver.observe(container, { childList: true, subtree: true });
201
205
  const setGrid = (g) => {
202
206
  if (g.x !== void 0) gridX = g.x;
203
207
  if (g.y !== void 0) gridY = g.y;
204
208
  if (g.vY !== void 0) vGridY = g.vY;
205
209
  };
206
- const calcGrid = () => setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
210
+ let ancestor = null;
211
+ const resizeObserver = new ResizeObserver(() => calcGrid());
212
+ const calcGrid = () => {
213
+ setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
214
+ const next = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
215
+ if (next && next !== ancestor) ancestor && resizeObserver.unobserve(ancestor), ancestor = next, resizeObserver.observe(ancestor);
216
+ };
207
217
  setGrid(grid), calcGrid();
208
- const ancestor = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
209
- const resizeObserver = new ResizeObserver(calcGrid);
210
- if (ancestor) resizeObserver.observe(ancestor);
211
218
  const destroy = () => {
212
219
  for (const el of interactiveEls) el?.removeEventListener("keydown", simulateKey);
213
220
  container.removeEventListener("focusout", handleFocusOut);
214
221
  for (const el of items) el.removeEventListener("mouseenter", handleHover);
215
222
  mutationObserver.disconnect(), resizeObserver.disconnect();
216
223
  if (timeout) clearTimeout(timeout);
224
+ t007._arrownavs.delete(container);
217
225
  };
218
- const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
226
+ const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, sync, destroy };
219
227
  return t007._arrownavs.set(container, handle), handle;
220
228
  }
221
229
  var removeArrowNavigation = (container) => t007._arrownavs?.get(container)?.destroy();
230
+ var syncArrowNavigation = (container) => t007._arrownavs?.get(container)?.sync();
222
231
  export {
223
232
  initArrowNavigation,
224
233
  initFocusTrap,
@@ -229,5 +238,7 @@ export {
229
238
  removeFocusTrap,
230
239
  removeOutsideClick,
231
240
  removeScrollAssist,
232
- rippleHandler
241
+ rippleHandler,
242
+ syncArrowNavigation,
243
+ syncFocusTrap
233
244
  };