@t007/utils 0.0.24 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,74 +1,18 @@
1
+ import { A as ArrowNavigationHandle } from './arrowNavigation-VenvPI4H.js';
2
+ import { S as ScrollAssistHandle } from './scrollAssist-y9wFmYgt.js';
3
+ export { NIL, NOOP } from 'sia-reactor';
1
4
  export { KeyStruct, assignEl, bindAllMethods, clamp, cleanKeyCombo, createEl, formatKeyForDisplay, formatKeyShortcutsForDisplay, getTermsForKey, guardAllMethods, guardMethod, isObj, keyEventAllowed, keysSettings, matchKeys, onAllMethods, parseForARIAKS, parseKeyCombo, requestAnimationFrame, setInterval, setTimeout, stringifyKeyEvent } from 'sia-reactor/utils';
2
5
 
3
- /** Configuration for the vertical edge-scrolling helper. */
4
- interface ScrolleratorOptions {
5
- /** Starting lines-per-second speed. */
6
- baseSpeed?: number;
7
- /** Maximum accelerated speed. */
8
- maxSpeed?: number;
9
- /** Delay before acceleration kicks in. */
10
- stepDelay?: number;
11
- /** Base frame rate used to estimate movement. */
12
- baseRate?: number;
13
- /** Approximate line height in pixels. */
14
- lineHeight?: number;
15
- /** Edge margin that triggers scrolling. */
16
- margin?: number;
17
- /** Scroll container or window target. */
18
- car?: Window | HTMLElement;
19
- }
20
- /** Scrolling controls returned by initVScrollerator. */
21
- interface Scrollerator {
22
- /** Trigger a scroll frame and return the computed distance. */
23
- drive: (clientY: number, brake?: boolean, offsetY?: number) => number;
24
- /** Reset speed and timers. */
25
- reset: () => void;
26
- }
27
- /** Create an edge-driven vertical scrolling controller.
28
- * @param options Scrollerator configuration.
29
- * @returns Drive and reset controls for the controller.
30
- */
31
- declare function initVScrollerator({ baseSpeed, maxSpeed, stepDelay, baseRate, lineHeight, margin, car }?: ScrolleratorOptions): Scrollerator;
32
- /** Scroll assist control object returned by initScrollAssist. */
33
- interface ScrollAssistControl {
34
- /** Recompute assist visibility. */
35
- update: () => void;
36
- /** Tear down observers and assist elements. */
37
- destroy: () => void;
38
- }
39
- /** Configuration for scroll assist overlays. */
40
- interface ScrollAssistOptions {
41
- /** Scroll speed in pixels per second. */
42
- pxPerSecond?: number;
43
- /** Class name applied to assist overlays. */
44
- assistClassName?: string;
45
- /** Enable vertical assist overlays. */
46
- vertical?: boolean;
47
- /** Enable horizontal assist overlays. */
48
- horizontal?: boolean;
49
- }
50
- /** Attach directional scroll assist overlays to an element.
51
- * @param el Scrollable element to enhance.
52
- * @param options Scroll assist configuration.
53
- * @returns Scroll assist controls or void when the element is already managed.
54
- */
55
- declare function initScrollAssist(el: HTMLElement, { pxPerSecond, assistClassName, vertical, horizontal }?: ScrollAssistOptions): ScrollAssistControl | void;
56
- /** Remove scroll assist from an element.
57
- * @param el Target element.
58
- */
59
- declare const removeScrollAssist: (el: HTMLElement) => void | undefined;
60
-
61
6
  declare global {
62
7
  interface T007Namespace {
63
8
  /** Symbol used to mark virtual resources that should not load a real asset. */
64
9
  VIRTUAL_RESOURCE: symbol;
65
- /** Cache used to deduplicate resource loading promises. */
66
10
  _resourceCache: Partial<Record<string, Promise<HTMLElement | void>>>;
67
- /** Active scroll assist controllers keyed by element. */
68
- _scrollers?: WeakMap<HTMLElement, ScrollAssistControl>;
69
- /** Resize observer used by scroll assist controllers. */
11
+ _ftrappers?: WeakMap<HTMLElement, () => void>;
12
+ _outsiders?: WeakMap<HTMLElement, () => void>;
13
+ _ashooters?: WeakMap<HTMLElement, ArrowNavigationHandle>;
14
+ _scrollers?: WeakMap<HTMLElement, ScrollAssistHandle>;
70
15
  _scroller_r_observer?: ResizeObserver;
71
- /** Mutation observer used by scroll assist controllers. */
72
16
  _scroller_m_observer?: MutationObserver;
73
17
  }
74
18
  interface Window {
@@ -162,6 +106,10 @@ declare const deepBreath: (w?: Window & typeof globalThis) => Promise<unknown>;
162
106
  */
163
107
  declare function bindCleanupToSignal<Cb extends () => any>(cleanup: Cb, signal?: AbortSignal): Cb;
164
108
 
109
+ /** Exhaustive Selector used for interactive, tabbable UI controls. */
110
+ declare const INTERACTIVE_SELECTOR = "button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex=\"-1\"])";
111
+ /** Check whether an event target points to an interactive element. */
112
+ declare const isInteractive: (target: EventTarget | null) => target is HTMLElement;
165
113
  /** Resource type accepted by loadResource. */
166
114
  type ResourceType = "style" | "script" | string;
167
115
  /** Options used when loading a script or stylesheet resource. */
@@ -185,7 +133,6 @@ type LoadResourceOptions = Partial<{
185
133
  /** Cache-busting retry token key. */
186
134
  retryKey: boolean | string;
187
135
  }>;
188
-
189
136
  /** Virtual resource marker used to skip real network loading. */
190
137
  declare const VIRTUAL_RESOURCE: symbol;
191
138
  /** Load a stylesheet or script into the current document with retry support.
@@ -196,5 +143,18 @@ declare const VIRTUAL_RESOURCE: symbol;
196
143
  * @returns Promise resolving to the created element or void.
197
144
  */
198
145
  declare function loadResource(req: string | symbol, type?: ResourceType, { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts, retryKey }?: LoadResourceOptions, w?: Window & typeof globalThis): Promise<HTMLElement | void>;
146
+ /** Get the currently active element, traversing into shadow roots if necessary.
147
+ * @param root Root node to start searching from, defaults to the main document.
148
+ * @returns The active element or null if none found.
149
+ */
150
+ declare function getActiveElement(root?: Document | ShadowRoot): Element | null;
151
+
152
+ /** Format a file size for display.
153
+ * @param size Size in bytes.
154
+ * @param decimals Decimal precision.
155
+ * @param base Size base, usually 1000 or 1024.
156
+ * @returns Human-readable size string, i.e., "1.234 KB".
157
+ */
158
+ declare function formatSize(bytes: number, decimals?: number, base?: number): string;
199
159
 
200
- export { type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, type ScrollAssistControl, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, inBoolArrOpt, initScrollAssist, initVScrollerator, isArr, isBool, isDef, isFunc, isIter, isNum, isPOJO, isSameURL, isStr, isSym, limited, loadResource, mockAsync, removeScrollAssist, uid };
160
+ export { INTERACTIVE_SELECTOR, type LimitedHandle, type LimitedOptions, type LoadResourceOptions, type ResourceType, VIRTUAL_RESOURCE, bindCleanupToSignal, breath, deepBreath, formatSize, getActiveElement, inBoolArrOpt, isArr, isBool, isDef, isFunc, isInteractive, isIter, isNum, isPOJO, isSameURL, isStr, isSym, limited, loadResource, mockAsync, uid };
package/dist/index.js CHANGED
@@ -1,214 +1,54 @@
1
- // src/core/dom.ts
2
- import { createEl, assignEl } from "sia-reactor/utils";
3
- var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
4
- function loadResource(req, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
5
- w.t007 ??= {}, w.t007._resourceCache ??= {};
6
- if (req === VIRTUAL_RESOURCE || isSym(req)) return Promise.resolve();
7
- const src = req;
8
- if (w.t007._resourceCache[src]) return w.t007._resourceCache[src];
9
- 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;
10
- if (existing) return w.t007._resourceCache[src] = Promise.resolve(existing);
11
- w.t007._resourceCache[src] = new Promise((resolve, reject) => {
12
- (function tryLoad(remaining, el) {
13
- const onerror = () => {
14
- el?.remove?.();
15
- if (remaining > 1) {
16
- setTimeout(tryLoad, 1e3, remaining - 1);
17
- console.warn(`Retrying ${type} load (${attempts - remaining + 1}): ${src}...`);
18
- } else {
19
- delete w.t007._resourceCache[src];
20
- reject(new Error(`${type} load failed after ${attempts} attempts: ${src}`));
21
- }
22
- };
23
- const url = retryKey && remaining < attempts ? `${src}${src.includes("?") ? "&" : "?"}_${retryKey}=${Date.now()}` : src;
24
- 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 }) || "");
25
- 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 }) || "");
26
- else reject(new Error(`Unsupported resource type: ${type}`));
27
- })(attempts);
28
- });
29
- return w.t007._resourceCache[src];
30
- }
31
-
32
- // src/core/obj.ts
33
- import { isObj } from "sia-reactor/utils";
34
- function isDef(val) {
35
- return "undefined" !== typeof val;
36
- }
37
- function isSym(val) {
38
- return "symbol" === typeof val;
39
- }
40
- function isBool(val) {
41
- return "boolean" === typeof val;
42
- }
43
- function isNum(val) {
44
- return "number" === typeof val;
45
- }
46
- function isStr(val) {
47
- return "string" === typeof val;
48
- }
49
- function isArr(obj) {
50
- return Array.isArray(obj);
51
- }
52
- function isPOJO(obj, crossRealms = false, typecheck = true) {
53
- return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
54
- }
55
- function isIter(obj) {
56
- return obj != null && "function" === typeof obj[Symbol.iterator];
57
- }
58
- function isFunc(val) {
59
- return "function" === typeof val;
60
- }
61
- function inBoolArrOpt(opt, str) {
62
- return opt?.includes?.(str) ?? opt;
63
- }
64
-
65
- // src/core/num.ts
66
- import { clamp } from "sia-reactor/utils";
67
-
68
- // src/core/str.ts
69
- function uid(prefix = "") {
70
- return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
71
- }
72
- function isSameURL(src1, src2) {
73
- if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
74
- try {
75
- const u1 = new URL(src1, window.location.href), u2 = new URL(src2, window.location.href);
76
- return decodeURIComponent(u1.origin + u1.pathname) === decodeURIComponent(u2.origin + u2.pathname);
77
- } catch {
78
- return src1.replace(/\\/g, "/").split("?")[0].trim() === src2.replace(/\\/g, "/").split("?")[0].trim();
79
- }
80
- }
81
-
82
- // src/core/fn.ts
83
- import { setTimeout as setTimeout2, setInterval, requestAnimationFrame as requestAnimationFrame2 } from "sia-reactor/utils";
84
- function limited(FN_KEY, fn, opts = {}) {
85
- let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
86
- const getReg = () => JSON.parse(localStorage.getItem(FN_KEY) || "{}"), setReg = (r) => localStorage.setItem(FN_KEY, JSON.stringify(r));
87
- const handle = (...args) => {
88
- if (!key) return count++ < max ? fn(...args) : void 0;
89
- const r = getReg(), c = r[key] || 0;
90
- return c < max ? (r[key] = c + 1, setReg(r), fn(...args)) : void 0;
91
- };
92
- handle.left = max - (handle.count = count);
93
- handle.reset = () => (count = 0, key && ((r) => (delete r[key], setReg(r)))(getReg()));
94
- handle.block = () => (count = max, key && ((r) => (r[key] = max, setReg(r)))(getReg()));
95
- return handle;
96
- }
97
- var mockAsync = (timeout = 250) => new Promise((resolve) => setTimeout(resolve, timeout));
98
- var breath = (w = window) => new Promise((res) => w.requestAnimationFrame(res));
99
- var deepBreath = (w = window) => new Promise((res) => w.requestAnimationFrame(() => w.requestAnimationFrame(res)));
100
- function bindCleanupToSignal(cleanup, signal) {
101
- signal?.aborted ? cleanup() : signal?.addEventListener("abort", cleanup, { once: true });
102
- if (signal && !signal.aborted) cleanup = (() => (signal.removeEventListener("abort", cleanup), cleanup()));
103
- return cleanup;
104
- }
105
-
106
- // src/core/keys.ts
107
- import { parseKeyCombo, stringifyKeyEvent, cleanKeyCombo, matchKeys, getTermsForKey, keyEventAllowed, formatKeyForDisplay, formatKeyShortcutsForDisplay, parseForARIAKS } from "sia-reactor/utils";
108
-
109
- // src/mixins/methd.ts
110
- import { onAllMethods, bindAllMethods, guardAllMethods, guardMethod } from "sia-reactor/utils";
111
-
112
- // src/quirks/scroll.ts
113
- function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = {}) {
114
- let linesPerSec = baseSpeed, accelId = null, lastTime = null;
115
- const drive = (clientY, brake = false, offsetY = 0) => {
116
- if (car !== window) clientY -= offsetY;
117
- const now = performance.now(), speed = linesPerSec * lineHeight * ((lastTime ? now - lastTime : baseRate) / 1e3);
118
- if (!brake && (clientY < margin || clientY > (car.innerHeight ?? car.offsetHeight) - margin)) {
119
- accelId === null ? accelId = setTimeout(() => linesPerSec += 1, stepDelay) : linesPerSec > baseSpeed && (linesPerSec = Math.min(linesPerSec + 1, maxSpeed));
120
- car.scrollBy?.(0, clientY < margin ? -speed : speed);
121
- } else reset();
122
- return lastTime = !brake ? now : null, speed;
123
- };
124
- const reset = () => (accelId && clearTimeout(accelId), accelId = null, linesPerSec = baseSpeed, lastTime = null);
125
- return { drive, reset };
126
- }
127
- function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "tmg-video-controls-scroll-assist", vertical = true, horizontal = true } = {}) {
128
- t007._scrollers ??= /* @__PURE__ */ new WeakMap();
129
- t007._scroller_r_observer ??= new ResizeObserver((entries) => entries.forEach(({ target }) => t007._scrollers.get(target)?.update()));
130
- t007._scroller_m_observer ??= new MutationObserver((entries) => {
131
- const els = /* @__PURE__ */ new Set();
132
- for (const entry of entries) {
133
- let node = entry.target instanceof Element ? entry.target : null;
134
- while (node && !t007._scrollers.has(node)) node = node.parentElement;
135
- if (node) els.add(node);
136
- }
137
- for (const el2 of els) t007._scrollers.get(el2)?.update();
138
- });
139
- const parent = el?.parentElement;
140
- if (!parent || t007._scrollers.has(el)) return;
141
- const assist = {};
142
- let scrollId = null, last = performance.now(), assistWidth = 20, assistHeight = 20;
143
- const update = () => {
144
- const hasInteractive = !!parent.querySelector('button, a[href], input, select, textarea, [contenteditable="true"], [tabindex]:not([tabindex="-1"])');
145
- if (horizontal) {
146
- const w = assist.left?.offsetWidth || assistWidth, check = hasInteractive ? el.clientWidth < w * 2 : false;
147
- assist.left.style.display = check ? "none" : el.scrollLeft > 0 ? "block" : "none";
148
- assist.right.style.display = check ? "none" : el.scrollLeft + el.clientWidth < el.scrollWidth - 1 ? "block" : "none";
149
- assistWidth = w;
150
- }
151
- if (vertical) {
152
- const h = assist.up?.offsetHeight || assistHeight, check = hasInteractive ? el.clientHeight < h * 2 : false;
153
- assist.up.style.display = check ? "none" : el.scrollTop > 0 ? "block" : "none";
154
- assist.down.style.display = check ? "none" : el.scrollTop + el.clientHeight < el.scrollHeight - 1 ? "block" : "none";
155
- assistHeight = h;
156
- }
157
- };
158
- const scroll = (dir) => {
159
- const frame = () => {
160
- const now = performance.now(), dt = now - last;
161
- last = now;
162
- const d = pxPerSecond * dt / 1e3;
163
- if (dir === "left") el.scrollLeft = Math.max(0, el.scrollLeft - d);
164
- if (dir === "right") el.scrollLeft = Math.min(el.scrollWidth - el.clientWidth, el.scrollLeft + d);
165
- if (dir === "up") el.scrollTop = Math.max(0, el.scrollTop - d);
166
- if (dir === "down") el.scrollTop = Math.min(el.scrollHeight - el.clientHeight, el.scrollTop + d);
167
- scrollId = requestAnimationFrame(frame);
168
- };
169
- last = performance.now();
170
- frame();
171
- };
172
- const stop = () => (cancelAnimationFrame(scrollId ?? 0), scrollId = null);
173
- const addAssist = (dir) => {
174
- const div = createEl("div", { className: assistClassName }, { scrollDirection: dir }, { display: "none" });
175
- if (!div) return;
176
- ["pointerenter", "dragenter"].forEach((evt) => div.addEventListener(evt, () => scroll(dir)));
177
- ["pointerleave", "pointerup", "pointercancel", "dragleave", "dragend"].forEach((evt) => div.addEventListener(evt, stop));
178
- dir === "left" || dir === "up" ? parent.insertBefore(div, el) : parent.append(div);
179
- assist[dir] = div;
180
- };
181
- if (horizontal) ["left", "right"].forEach(addAssist);
182
- if (vertical) ["up", "down"].forEach(addAssist);
183
- el.addEventListener("scroll", update);
184
- t007._scroller_r_observer.observe(el);
185
- t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
186
- t007._scrollers.set(el, {
187
- update,
188
- destroy() {
189
- stop();
190
- el.removeEventListener("scroll", update);
191
- t007._scroller_r_observer.unobserve(el);
192
- t007._scrollers.delete(el);
193
- Object.values(assist).forEach((a) => a.remove());
194
- }
195
- });
196
- return update(), t007._scrollers.get(el);
197
- }
198
- var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
199
-
200
- // src/index.ts
201
- if ("undefined" !== typeof window) {
202
- window.t007 ??= {};
203
- t007.VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
204
- window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
205
- window.T007_INPUT_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest`;
206
- window.T007_DIALOG_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest`;
207
- window.T007_TOAST_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest/dist/index.min.css`;
208
- window.T007_INPUT_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest/dist/index.min.css`;
209
- window.T007_DIALOG_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest/dist/index.min.css`;
210
- }
1
+ import {
2
+ INTERACTIVE_SELECTOR,
3
+ NIL,
4
+ NOOP,
5
+ VIRTUAL_RESOURCE,
6
+ assignEl,
7
+ bindAllMethods,
8
+ bindCleanupToSignal,
9
+ breath,
10
+ clamp,
11
+ cleanKeyCombo,
12
+ createEl,
13
+ deepBreath,
14
+ formatKeyForDisplay,
15
+ formatKeyShortcutsForDisplay,
16
+ formatSize,
17
+ getActiveElement,
18
+ getTermsForKey,
19
+ guardAllMethods,
20
+ guardMethod,
21
+ inBoolArrOpt,
22
+ isArr,
23
+ isBool,
24
+ isDef,
25
+ isFunc,
26
+ isInteractive,
27
+ isIter,
28
+ isNum,
29
+ isObj,
30
+ isPOJO,
31
+ isSameURL,
32
+ isStr,
33
+ isSym,
34
+ keyEventAllowed,
35
+ limited,
36
+ loadResource,
37
+ matchKeys,
38
+ mockAsync,
39
+ onAllMethods,
40
+ parseForARIAKS,
41
+ parseKeyCombo,
42
+ requestAnimationFrame,
43
+ setInterval,
44
+ setTimeout,
45
+ stringifyKeyEvent,
46
+ uid
47
+ } from "./chunk-XVFFZZJA.js";
211
48
  export {
49
+ INTERACTIVE_SELECTOR,
50
+ NIL,
51
+ NOOP,
212
52
  VIRTUAL_RESOURCE,
213
53
  assignEl,
214
54
  bindAllMethods,
@@ -220,16 +60,17 @@ export {
220
60
  deepBreath,
221
61
  formatKeyForDisplay,
222
62
  formatKeyShortcutsForDisplay,
63
+ formatSize,
64
+ getActiveElement,
223
65
  getTermsForKey,
224
66
  guardAllMethods,
225
67
  guardMethod,
226
68
  inBoolArrOpt,
227
- initScrollAssist,
228
- initVScrollerator,
229
69
  isArr,
230
70
  isBool,
231
71
  isDef,
232
72
  isFunc,
73
+ isInteractive,
233
74
  isIter,
234
75
  isNum,
235
76
  isObj,
@@ -245,10 +86,9 @@ export {
245
86
  onAllMethods,
246
87
  parseForARIAKS,
247
88
  parseKeyCombo,
248
- removeScrollAssist,
249
- requestAnimationFrame2 as requestAnimationFrame,
89
+ requestAnimationFrame,
250
90
  setInterval,
251
- setTimeout2 as setTimeout,
91
+ setTimeout,
252
92
  stringifyKeyEvent,
253
93
  uid
254
94
  };
@@ -0,0 +1,71 @@
1
+ interface OutsideClickConfig {
2
+ /** Enables or disables outside-click handling. Defaults to `false`. */
3
+ enabled?: boolean;
4
+ /** Callback invoked when an outside interaction is detected. Defaults to `()=>{}`. */
5
+ onOutsideClick?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
6
+ /** Whether pointer/touch outside interactions should trigger callback. Defaults to `true`. */
7
+ clickOnClick?: boolean;
8
+ /** Whether Escape key should trigger callback. Defaults to `true`. */
9
+ clickOnEscape?: boolean;
10
+ /** Whether focus leaving the container should trigger callback. Defaults to `false`. */
11
+ clickOnFocusOut?: boolean;
12
+ /** Allow interactive elements like outsiders to bypass click callback. Defaults to `true`. */
13
+ allowInputs?: boolean;
14
+ /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
15
+ root?: HTMLElement | Document | Window;
16
+ /** Whether the outside click handling is scoped to the root provided it is an HTMLElement. Defaults to `true`. */
17
+ scoped?: boolean;
18
+ /** Passed down to all event listeners used. Defaults to `true`. */
19
+ capture?: boolean;
20
+ }
21
+ /** Hook to attach outside-click, escape, and optional focus-out handling to an element. */
22
+ declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, clickOnClick, clickOnEscape, clickOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
23
+ /** Remove outside-click handling from an element. */
24
+ declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
25
+
26
+ interface FocusTrapConfig {
27
+ /** Enables or disables the focus trap. Defaults to `false`. */
28
+ enabled?: boolean;
29
+ /** The preferred initial focus target selector within the element. Defaults to `[data-autofocus]`. */
30
+ initialSelector?: string;
31
+ /** The class name for the initial focus ring since programmatic focus is not always visible. Defaults to `"focus-outline"`. */
32
+ ringClassName?: string;
33
+ /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
34
+ root?: HTMLElement | Document | Window;
35
+ /** Whether the focus trap is scoped to the root provided it is an HTMLElement. Defaults to `true`. */
36
+ scoped?: boolean;
37
+ /** Passed down to all event listeners used. Defaults to `true`. */
38
+ capture?: boolean;
39
+ }
40
+ /** Hook to keep focus trapped inside an element until disabled. */
41
+ declare function initFocusTrap(el: HTMLElement, { enabled, initialSelector, ringClassName, root, scoped, capture }?: FocusTrapConfig): (() => void) | void;
42
+ /** Remove the focus trap guard from an element. */
43
+ declare const removeFocusTrap: (el: HTMLElement) => void | undefined;
44
+
45
+ interface RippleConfig {
46
+ /** Optional explicit ripple host element. Defaults to event currentTarget. */
47
+ target?: HTMLElement;
48
+ /** Forces the ripple origin to the center of the host. By default, the ripple will originate from the pointer event coordinates. */
49
+ forceCenter?: boolean;
50
+ /** CSS class added to the ripple wrapper element. Defaults to `"t007-ripple-wrapper"`. */
51
+ wrapperClassName?: string;
52
+ /** CSS general class added to the ripple element. Defaults to `"t007-ripple"`. */
53
+ className?: string;
54
+ /** CSS class added to the ripple element while the pointer is held down. Should contain the initial expansion animation. Defaults to `"t007-ripple-hold"`. */
55
+ holdClassName?: string;
56
+ /** CSS class added to the ripple element when released. Should contain the fade-out animation. Defaults to `"t007-ripple-fade"`. */
57
+ fadeClassName?: string;
58
+ }
59
+ /** Render and control a material-style ripple animation on an element.
60
+ * @param e Pointer event used to place and gate the ripple.
61
+ * @param options Ripple configuration options.
62
+ * @details
63
+ * The ripple will be triggered on the event's currentTarget by default, but an explicit target can be provided.
64
+ * The ripple will originate from the pointer coordinates relative to the target, unless forceCenter is enabled.
65
+ * The ripple element will receive a hold class until the pointer is released, at which point it will switch to a fade class and be removed after the animation completes.
66
+ * Pointer events that are not left-clicks or that originate from interactive elements other than the currentTarget will be ignored to prevent interference with native behaviors.
67
+ */
68
+ declare function rippleHandler(e: RipplePointerLikeEvent, { target, forceCenter, wrapperClassName, className, holdClassName, fadeClassName }?: RippleConfig): void;
69
+ type RipplePointerLikeEvent = Pick<PointerEvent, "target" | "currentTarget" | "pointerType" | "button" | "clientX" | "clientY" | "stopPropagation">;
70
+
71
+ export { type FocusTrapConfig as F, type OutsideClickConfig as O, type RippleConfig as R, initOutsideClick as a, removeOutsideClick as b, rippleHandler as c, initFocusTrap as i, removeFocusTrap as r };
@@ -0,0 +1,71 @@
1
+ interface OutsideClickConfig {
2
+ /** Enables or disables outside-click handling. Defaults to `false`. */
3
+ enabled?: boolean;
4
+ /** Callback invoked when an outside interaction is detected. Defaults to `()=>{}`. */
5
+ onOutsideClick?: (e: MouseEvent | TouchEvent | KeyboardEvent | FocusEvent) => void;
6
+ /** Whether pointer/touch outside interactions should trigger callback. Defaults to `true`. */
7
+ clickOnClick?: boolean;
8
+ /** Whether Escape key should trigger callback. Defaults to `true`. */
9
+ clickOnEscape?: boolean;
10
+ /** Whether focus leaving the container should trigger callback. Defaults to `false`. */
11
+ clickOnFocusOut?: boolean;
12
+ /** Allow interactive elements like outsiders to bypass click callback. Defaults to `true`. */
13
+ allowInputs?: boolean;
14
+ /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
15
+ root?: HTMLElement | Document | Window;
16
+ /** Whether the outside click handling is scoped to the root provided it is an HTMLElement. Defaults to `true`. */
17
+ scoped?: boolean;
18
+ /** Passed down to all event listeners used. Defaults to `true`. */
19
+ capture?: boolean;
20
+ }
21
+ /** Hook to attach outside-click, escape, and optional focus-out handling to an element. */
22
+ declare function initOutsideClick(el: HTMLElement, { enabled, onOutsideClick, clickOnClick, clickOnEscape, clickOnFocusOut, allowInputs, root, scoped, capture }?: OutsideClickConfig): (() => void) | void;
23
+ /** Remove outside-click handling from an element. */
24
+ declare const removeOutsideClick: (el: HTMLElement) => void | undefined;
25
+
26
+ interface FocusTrapConfig {
27
+ /** Enables or disables the focus trap. Defaults to `false`. */
28
+ enabled?: boolean;
29
+ /** The preferred initial focus target selector within the element. Defaults to `[data-autofocus]`. */
30
+ initialSelector?: string;
31
+ /** The class name for the initial focus ring since programmatic focus is not always visible. Defaults to `"focus-outline"`. */
32
+ ringClassName?: string;
33
+ /** Optional root used to scope focus listeners to an element instead of the window. Defaults to `window`. */
34
+ root?: HTMLElement | Document | Window;
35
+ /** Whether the focus trap is scoped to the root provided it is an HTMLElement. Defaults to `true`. */
36
+ scoped?: boolean;
37
+ /** Passed down to all event listeners used. Defaults to `true`. */
38
+ capture?: boolean;
39
+ }
40
+ /** Hook to keep focus trapped inside an element until disabled. */
41
+ declare function initFocusTrap(el: HTMLElement, { enabled, initialSelector, ringClassName, root, scoped, capture }?: FocusTrapConfig): (() => void) | void;
42
+ /** Remove the focus trap guard from an element. */
43
+ declare const removeFocusTrap: (el: HTMLElement) => void | undefined;
44
+
45
+ interface RippleConfig {
46
+ /** Optional explicit ripple host element. Defaults to event currentTarget. */
47
+ target?: HTMLElement;
48
+ /** Forces the ripple origin to the center of the host. By default, the ripple will originate from the pointer event coordinates. */
49
+ forceCenter?: boolean;
50
+ /** CSS class added to the ripple wrapper element. Defaults to `"t007-ripple-wrapper"`. */
51
+ wrapperClassName?: string;
52
+ /** CSS general class added to the ripple element. Defaults to `"t007-ripple"`. */
53
+ className?: string;
54
+ /** CSS class added to the ripple element while the pointer is held down. Should contain the initial expansion animation. Defaults to `"t007-ripple-hold"`. */
55
+ holdClassName?: string;
56
+ /** CSS class added to the ripple element when released. Should contain the fade-out animation. Defaults to `"t007-ripple-fade"`. */
57
+ fadeClassName?: string;
58
+ }
59
+ /** Render and control a material-style ripple animation on an element.
60
+ * @param e Pointer event used to place and gate the ripple.
61
+ * @param options Ripple configuration options.
62
+ * @details
63
+ * The ripple will be triggered on the event's currentTarget by default, but an explicit target can be provided.
64
+ * The ripple will originate from the pointer coordinates relative to the target, unless forceCenter is enabled.
65
+ * The ripple element will receive a hold class until the pointer is released, at which point it will switch to a fade class and be removed after the animation completes.
66
+ * Pointer events that are not left-clicks or that originate from interactive elements other than the currentTarget will be ignored to prevent interference with native behaviors.
67
+ */
68
+ declare function rippleHandler(e: RipplePointerLikeEvent, { target, forceCenter, wrapperClassName, className, holdClassName, fadeClassName }?: RippleConfig): void;
69
+ type RipplePointerLikeEvent = Pick<PointerEvent, "target" | "currentTarget" | "pointerType" | "button" | "clientX" | "clientY" | "stopPropagation">;
70
+
71
+ export { type FocusTrapConfig as F, type OutsideClickConfig as O, type RippleConfig as R, initOutsideClick as a, removeOutsideClick as b, rippleHandler as c, initFocusTrap as i, removeFocusTrap as r };
@@ -0,0 +1,72 @@
1
+ type KeyEvent = Partial<KeyboardEvent> & Pick<KeyboardEvent, "key">;
2
+ type Config = {
3
+ /** Enables or disables navigation logic. Defaults to `null`. */
4
+ enabled?: boolean | null;
5
+ /** CSS selector used to collect focusable nav items. Defaults to `"[data-arrow-item]"` */
6
+ selector?: string;
7
+ /** Whether hover should also move active selection. Defaults to `true`. */
8
+ focusOnHover?: boolean;
9
+ /** Whether directional movement wraps around edges. Defaults to `true`. */
10
+ loop?: boolean;
11
+ /** Enables virtual focus (aria-activedescendant) mode. Defaults to `false`. */
12
+ virtual?: boolean;
13
+ /** Enables alphanumeric type-ahead matching. Defaults to `false`. */
14
+ typeahead?: boolean;
15
+ /** Idle timeout before clearing type-ahead buffer (ms). Defaults to `500`. */
16
+ resetMs?: number;
17
+ /** Explicit RTL override; null auto-detects from computed style. Defaults to `null`. */
18
+ rtl?: boolean | null;
19
+ /** Enables roving tabindex when not in virtual mode. Defaults to `null`. */
20
+ rovingTab?: boolean | null;
21
+ /** Default tabbable index when no active item is selected. Defaults to `null`. */
22
+ defaultTabbableIndex?: number | null;
23
+ /** Base tabindex for non-active items, use `"-1"` to kill virtual list. Defaults to `"0"`. */
24
+ baseTabIndex?: string;
25
+ /** Class applied to active item in virtual mode. Defaults to `"focus-outlined"`. */
26
+ activeClass?: string;
27
+ /** Selector used for keyboard event source in virtual mode. Defaults to `"input[value],textarea,[contenteditable='true']"`. */
28
+ inputSelector?: string;
29
+ /** Scroll behavior options used when moving active item. Defaults to `{ block: "nearest", inline: "nearest" }`. */
30
+ scrollIntoView?: ScrollIntoViewOptions;
31
+ /** Focus behavior options used in non-virtual mode. Defaults to `{ preventScroll: false }`. */
32
+ focusOptions?: FocusOptions;
33
+ /** Explicit or computed grid dimensions for navigation math. Defaults to `{}`. */
34
+ grid?: Partial<Record<"x" | "y" | "vY", number>>;
35
+ /** Callback fired when an item becomes active/selected. */
36
+ onSelect?: (el: HTMLElement, e: KeyEvent) => void;
37
+ /** Callback fired when focus leaves the navigation container. */
38
+ onFocusOut?: (e: FocusEvent) => void;
39
+ };
40
+
41
+ type ScrollDir = "left" | "right" | "up" | "down";
42
+ /** Scroll assist control object returned by initScrollAssist. */
43
+ interface ScrollAssistHandle {
44
+ /** Recompute assist visibility. */
45
+ update: () => void;
46
+ /** Tear down observers and assist elements. */
47
+ destroy: () => void;
48
+ }
49
+ /** Configuration for scroll assist overlays. */
50
+ interface ScrollAssistConfig {
51
+ /** Scroll speed in pixels per second. Defaults to `80`. */
52
+ pxPerSecond?: number;
53
+ /** Class name applied to assist overlays. Defaults to `"t007-scroll-assist"`. */
54
+ assistClassName?: string;
55
+ /** Enable vertical assist overlays. Defaults to `true`. */
56
+ vertical?: boolean;
57
+ /** Enable horizontal assist overlays. Defaults to `true`. */
58
+ horizontal?: boolean;
59
+ }
60
+ /** 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.
61
+ * The assist is automatically disabled when the element is too small or contains interactive elements near the edges to prevent interference.
62
+ * @param el Scrollable element to enhance.
63
+ * @param options Scroll assist configuration.
64
+ * @returns Scroll assist controls or void when the element is already managed.
65
+ */
66
+ declare function initScrollAssist(el: HTMLElement, { pxPerSecond, assistClassName, vertical, horizontal }?: ScrollAssistConfig): ScrollAssistHandle | void;
67
+ /** Remove scroll assist from an element.
68
+ * @param el Target element.
69
+ */
70
+ declare const removeScrollAssist: (el: HTMLElement) => void | undefined;
71
+
72
+ export { type Config as C, type KeyEvent as K, type ScrollAssistHandle as S, type ScrollAssistConfig as a, type ScrollDir as b, initScrollAssist as i, removeScrollAssist as r };