@t007/utils 0.0.25 → 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/README.md +91 -4
- package/dist/arrowNavigation-DK8mqVOk.d.cts +31 -0
- package/dist/arrowNavigation-VenvPI4H.d.ts +31 -0
- package/dist/chunk-AI5O3OGE.js +165 -0
- package/dist/chunk-OGFBI6PS.js +16 -0
- package/dist/chunk-XVFFZZJA.js +185 -0
- package/dist/components/react.cjs +51 -0
- package/dist/components/react.d.cts +17 -0
- package/dist/components/react.d.ts +17 -0
- package/dist/components/react.js +15 -0
- package/dist/hooks/react.cjs +466 -0
- package/dist/hooks/react.d.cts +46 -0
- package/dist/hooks/react.d.ts +46 -0
- package/dist/hooks/react.js +276 -0
- package/dist/hooks/vanilla.cjs +407 -0
- package/dist/hooks/vanilla.d.cts +35 -0
- package/dist/hooks/vanilla.d.ts +35 -0
- package/dist/hooks/vanilla.js +232 -0
- package/dist/index.cjs +29 -96
- package/dist/index.d.cts +25 -65
- package/dist/index.d.ts +25 -65
- package/dist/index.js +55 -215
- package/dist/ripple-DcMWw_AP.d.cts +71 -0
- package/dist/ripple-DcMWw_AP.d.ts +71 -0
- package/dist/scrollAssist-y9wFmYgt.d.cts +72 -0
- package/dist/scrollAssist-y9wFmYgt.d.ts +72 -0
- package/dist/styles/ripple.css +55 -0
- package/dist/styles/scroll-assist.css +44 -0
- package/dist/useHighlight-DMpDCILK.d.cts +19 -0
- package/dist/useHighlight-DMpDCILK.d.ts +19 -0
- package/package.json +42 -4
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CONFIG,
|
|
3
|
+
H_NAV_KEYS,
|
|
4
|
+
NAV_KEYS,
|
|
5
|
+
getCommonAncestor,
|
|
6
|
+
getGrid,
|
|
7
|
+
getTargetIndex,
|
|
8
|
+
initFocusTrap,
|
|
9
|
+
initOutsideClick,
|
|
10
|
+
removeFocusTrap,
|
|
11
|
+
removeOutsideClick,
|
|
12
|
+
rippleHandler
|
|
13
|
+
} from "../chunk-AI5O3OGE.js";
|
|
14
|
+
import {
|
|
15
|
+
INTERACTIVE_SELECTOR,
|
|
16
|
+
createEl,
|
|
17
|
+
getActiveElement
|
|
18
|
+
} from "../chunk-XVFFZZJA.js";
|
|
19
|
+
|
|
20
|
+
// src/hooks/vanilla/scrollAssist.ts
|
|
21
|
+
import { NIL } from "sia-reactor";
|
|
22
|
+
function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = NIL) {
|
|
23
|
+
const parent = el?.parentElement, existing = (t007._scrollers ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
24
|
+
if (!parent || existing) return existing ? existing : void 0;
|
|
25
|
+
t007._scroller_r_observer ??= new ResizeObserver((entries) => {
|
|
26
|
+
for (const { target } of entries) t007._scrollers.get(target)?.update();
|
|
27
|
+
});
|
|
28
|
+
t007._scroller_m_observer ??= new MutationObserver((entries) => {
|
|
29
|
+
const els = /* @__PURE__ */ new Set();
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
let node = entry.target instanceof Element ? entry.target : null;
|
|
32
|
+
while (node && !t007._scrollers.has(node)) node = node.parentElement;
|
|
33
|
+
if (node) els.add(node);
|
|
34
|
+
}
|
|
35
|
+
for (const el2 of els) t007._scrollers.get(el2)?.update();
|
|
36
|
+
});
|
|
37
|
+
const assist = {};
|
|
38
|
+
let scrollId = null, last = performance.now(), assistWidth = 20, assistHeight = 20;
|
|
39
|
+
const update = () => {
|
|
40
|
+
const hasInteractive = !!parent.querySelector(INTERACTIVE_SELECTOR);
|
|
41
|
+
if (horizontal) {
|
|
42
|
+
const w = assist.left?.offsetWidth || assistWidth, check = hasInteractive ? el.clientWidth < w * 2 : false;
|
|
43
|
+
assist.left.style.display = check ? "none" : el.scrollLeft > 0 ? "block" : "none";
|
|
44
|
+
assist.right.style.display = check ? "none" : el.scrollLeft + el.clientWidth < el.scrollWidth - 1 ? "block" : "none";
|
|
45
|
+
assistWidth = w;
|
|
46
|
+
}
|
|
47
|
+
if (vertical) {
|
|
48
|
+
const h = assist.up?.offsetHeight || assistHeight, check = hasInteractive ? el.clientHeight < h * 2 : false;
|
|
49
|
+
assist.up.style.display = check ? "none" : el.scrollTop > 0 ? "block" : "none";
|
|
50
|
+
assist.down.style.display = check ? "none" : el.scrollTop + el.clientHeight < el.scrollHeight - 1 ? "block" : "none";
|
|
51
|
+
assistHeight = h;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const scroll = (dir) => {
|
|
55
|
+
const frame = () => {
|
|
56
|
+
const now = performance.now(), dt = now - last;
|
|
57
|
+
last = now;
|
|
58
|
+
const d = pxPerSecond * dt / 1e3;
|
|
59
|
+
if (dir === "left") el.scrollLeft = Math.max(0, el.scrollLeft - d);
|
|
60
|
+
if (dir === "right") el.scrollLeft = Math.min(el.scrollWidth - el.clientWidth, el.scrollLeft + d);
|
|
61
|
+
if (dir === "up") el.scrollTop = Math.max(0, el.scrollTop - d);
|
|
62
|
+
if (dir === "down") el.scrollTop = Math.min(el.scrollHeight - el.clientHeight, el.scrollTop + d);
|
|
63
|
+
scrollId = requestAnimationFrame(frame);
|
|
64
|
+
};
|
|
65
|
+
last = performance.now();
|
|
66
|
+
frame();
|
|
67
|
+
};
|
|
68
|
+
const stop = () => (cancelAnimationFrame(scrollId ?? 0), scrollId = null);
|
|
69
|
+
const addAssist = (dir) => {
|
|
70
|
+
const div = createEl("div", { className: assistClassName }, { scrollDirection: dir }, { display: "none" });
|
|
71
|
+
if (!div) return;
|
|
72
|
+
for (const evt of ["pointerenter", "dragenter"]) div.addEventListener(evt, () => scroll(dir));
|
|
73
|
+
for (const evt of ["pointerleave", "pointerup", "pointercancel", "dragleave", "dragend"]) div.addEventListener(evt, stop);
|
|
74
|
+
dir === "left" || dir === "up" ? parent.insertBefore(div, el) : parent.append(div);
|
|
75
|
+
assist[dir] = div;
|
|
76
|
+
};
|
|
77
|
+
if (horizontal) for (const dir of ["left", "right"]) addAssist(dir);
|
|
78
|
+
if (vertical) for (const dir of ["up", "down"]) addAssist(dir);
|
|
79
|
+
const handle = {
|
|
80
|
+
update,
|
|
81
|
+
destroy() {
|
|
82
|
+
stop(), el.removeEventListener("scroll", update);
|
|
83
|
+
t007._scroller_r_observer.unobserve(el), t007._scrollers.delete(el);
|
|
84
|
+
for (const a of Object.values(assist)) a.remove();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
update(), el.addEventListener("scroll", update);
|
|
88
|
+
t007._scroller_r_observer.observe(el), t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
|
|
89
|
+
return t007._scrollers.set(el, handle), handle;
|
|
90
|
+
}
|
|
91
|
+
var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
|
|
92
|
+
|
|
93
|
+
// src/hooks/vanilla/scrollerator.ts
|
|
94
|
+
import { NIL as NIL2 } from "sia-reactor";
|
|
95
|
+
function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = NIL2) {
|
|
96
|
+
let linesPerSec = baseSpeed, accelId = null, lastTime = null;
|
|
97
|
+
const drive = (clientY, brake = false, offsetY = 0) => {
|
|
98
|
+
if (car !== window) clientY -= offsetY;
|
|
99
|
+
const now = performance.now(), speed = linesPerSec * lineHeight * ((lastTime ? now - lastTime : baseRate) / 1e3);
|
|
100
|
+
if (!brake && (clientY < margin || clientY > (car.innerHeight ?? car.offsetHeight) - margin)) {
|
|
101
|
+
accelId === null ? accelId = setTimeout(() => linesPerSec += 1, stepDelay) : linesPerSec > baseSpeed && (linesPerSec = Math.min(linesPerSec + 1, maxSpeed));
|
|
102
|
+
car.scrollBy?.(0, clientY < margin ? -speed : speed);
|
|
103
|
+
} else reset();
|
|
104
|
+
return lastTime = !brake ? now : null, speed;
|
|
105
|
+
};
|
|
106
|
+
const reset = () => (accelId && clearTimeout(accelId), accelId = null, linesPerSec = baseSpeed, lastTime = null);
|
|
107
|
+
return { drive, reset };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/hooks/vanilla/arrowNavigation.ts
|
|
111
|
+
function initArrowNavigation(container, config = {}) {
|
|
112
|
+
const existing = (t007._ashooters ??= /* @__PURE__ */ new WeakMap()).get(container);
|
|
113
|
+
if (!config.enabled || existing) return existing ? existing : void 0;
|
|
114
|
+
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
|
+
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 ?? 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 getAbleIndex = (targetIndex, e = { key: "ArrowRight", ctrlKey: false }) => {
|
|
118
|
+
if (shouldSnub() || !items.length) return null;
|
|
119
|
+
let index = targetIndex;
|
|
120
|
+
let attempts = 0;
|
|
121
|
+
while (attempts < items.length) {
|
|
122
|
+
if (!isItemDisabled(items[index])) return index;
|
|
123
|
+
index = getTargetIndex({ key: e.key, currIndex: index, gridX, gridY, vGridY, length: items.length, loop, ctrlKey: e.ctrlKey, rtl });
|
|
124
|
+
attempts++;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
};
|
|
128
|
+
const goToIndex = (targetIndex, e = { key: "ArrowRight" }) => {
|
|
129
|
+
if (shouldSnub()) return;
|
|
130
|
+
const idx = getAbleIndex(targetIndex, e);
|
|
131
|
+
if (idx === null) return;
|
|
132
|
+
resetActiveIndex(idx);
|
|
133
|
+
onSelect?.(items[idx], e), updateDOM();
|
|
134
|
+
if (virtual) {
|
|
135
|
+
items[idx]?.scrollIntoView(scrollIntoView);
|
|
136
|
+
container.setAttribute("aria-activedescendant", items[idx].id || "");
|
|
137
|
+
} else items[idx]?.focus(focusOptions);
|
|
138
|
+
};
|
|
139
|
+
const updateDOM = () => {
|
|
140
|
+
if (shouldSnub() || !virtual && !roving || !items.length) return;
|
|
141
|
+
const hasDefaultTabbable = defaultTabbableIndex !== null && defaultTabbableIndex !== void 0, tabbableIndex = hasDefaultTabbable && !isItemDisabled(items[defaultTabbableIndex]) ? defaultTabbableIndex : getAbleIndex(0);
|
|
142
|
+
for (let i = 0, len = items.length; i < len; i++) {
|
|
143
|
+
const el = items[i], isActive = i === activeIndex;
|
|
144
|
+
if (roving) el.setAttribute("tabindex", i === activeIndex || activeIndex === -1 && i === tabbableIndex ? baseTabIndex : "-1");
|
|
145
|
+
else if (virtual && activeIndex > 0) el.setAttribute("tabindex", i > activeIndex ? baseTabIndex : "-1");
|
|
146
|
+
else el.setAttribute("tabindex", baseTabIndex);
|
|
147
|
+
if (virtual) el.setAttribute("aria-selected", String(isActive)), el.classList.toggle(activeClass, isActive);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const resetActiveIndex = (index = -1) => (activeIndex = index, updateDOM());
|
|
151
|
+
const typeAhead = (key) => {
|
|
152
|
+
if (shouldSnub() || !typeahead) return;
|
|
153
|
+
buffer += key.toLowerCase();
|
|
154
|
+
if (timeout) clearTimeout(timeout);
|
|
155
|
+
timeout = setTimeout(() => buffer = "", resetMs);
|
|
156
|
+
const start = activeIndex >= 0 ? activeIndex + 1 : 0;
|
|
157
|
+
for (let i = 0; i < items.length; i++) {
|
|
158
|
+
const idx = (start + i) % items.length, label = (items[idx].getAttribute("data-label") || items[idx].innerText || "").trim().toLowerCase();
|
|
159
|
+
if (label.startsWith(buffer)) return goToIndex(idx);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const simulateKey = (e) => {
|
|
163
|
+
if (shouldSnub() || getActiveElement()?.matches("option")) return;
|
|
164
|
+
const { key } = e;
|
|
165
|
+
if (!items.length) return;
|
|
166
|
+
if (virtual && (key === " " || key === "Enter")) return items[activeIndex]?.click();
|
|
167
|
+
if (e.target?.matches(DEFAULT_CONFIG.inputSelector) && !virtual) return;
|
|
168
|
+
if (typeahead && key.length === 1 && /^[a-z0-9]$/i.test(key)) return typeAhead(key);
|
|
169
|
+
if (!NAV_KEYS.includes(key)) return;
|
|
170
|
+
if (!(e.currentTarget?.matches(DEFAULT_CONFIG.inputSelector) && gridX <= 1 && H_NAV_KEYS.includes(key))) e.preventDefault?.(), e.stopPropagation?.();
|
|
171
|
+
const currIndex = virtual ? activeIndex : items.indexOf(getActiveElement()), targetIndex = getTargetIndex({ currIndex, gridX, gridY, vGridY, length: items.length, loop, rtl, key, ctrlKey: e.ctrlKey });
|
|
172
|
+
goToIndex(targetIndex, e);
|
|
173
|
+
};
|
|
174
|
+
getItems(), updateDOM();
|
|
175
|
+
const interactiveEls = !virtual ? [container] : [container.querySelector(inputSelector)];
|
|
176
|
+
for (const el of interactiveEls) el?.addEventListener("keydown", simulateKey);
|
|
177
|
+
const handleFocusOut = (e) => {
|
|
178
|
+
if (!container.contains(e.relatedTarget)) return resetActiveIndex(), updateDOM(), onFocusOut?.(e);
|
|
179
|
+
const among = items.includes(e.relatedTarget);
|
|
180
|
+
if (!among && (defaultTabbableIndex ?? -1) >= 0) return resetActiveIndex(), updateDOM();
|
|
181
|
+
if (virtual) resetActiveIndex(), updateDOM();
|
|
182
|
+
};
|
|
183
|
+
container.addEventListener("focusout", handleFocusOut);
|
|
184
|
+
const handleHover = (e) => {
|
|
185
|
+
if (!enabled || !focusOnHover) return;
|
|
186
|
+
const el = e.currentTarget, idx = items.indexOf(el);
|
|
187
|
+
if (idx !== -1) goToIndex(idx);
|
|
188
|
+
};
|
|
189
|
+
for (const el of items) el.addEventListener("mouseenter", handleHover);
|
|
190
|
+
const mutationObserver = new MutationObserver(() => {
|
|
191
|
+
const oldEl = items[activeIndex];
|
|
192
|
+
getItems();
|
|
193
|
+
const newEl = items[activeIndex];
|
|
194
|
+
updateDOM();
|
|
195
|
+
if (oldEl && newEl && oldEl === newEl) return;
|
|
196
|
+
resetActiveIndex();
|
|
197
|
+
updateDOM();
|
|
198
|
+
});
|
|
199
|
+
mutationObserver.observe(container, { childList: true, subtree: true });
|
|
200
|
+
const setGrid = (g) => {
|
|
201
|
+
if (g.x !== void 0) gridX = g.x;
|
|
202
|
+
if (g.y !== void 0) gridY = g.y;
|
|
203
|
+
if (g.vY !== void 0) vGridY = g.vY;
|
|
204
|
+
};
|
|
205
|
+
const calcGrid = () => setGrid(getGrid(items, !grid.x, !grid.y, !grid.vY));
|
|
206
|
+
setGrid(grid), calcGrid();
|
|
207
|
+
const ancestor = items.length > 1 ? getCommonAncestor(items[0], items[1]) : container;
|
|
208
|
+
const resizeObserver = new ResizeObserver(calcGrid);
|
|
209
|
+
if (ancestor) resizeObserver.observe(ancestor);
|
|
210
|
+
const destroy = () => {
|
|
211
|
+
for (const el of interactiveEls) el?.removeEventListener("keydown", simulateKey);
|
|
212
|
+
container.removeEventListener("focusout", handleFocusOut);
|
|
213
|
+
for (const el of items) el.removeEventListener("mouseenter", handleHover);
|
|
214
|
+
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
215
|
+
if (timeout) clearTimeout(timeout);
|
|
216
|
+
};
|
|
217
|
+
const handle = { gridX: () => gridX, gridY: () => gridY, vGridY: () => vGridY, items: () => items, activeIndex: () => activeIndex, activeItem: () => items[activeIndex] ?? null, getAbleIndex, typeAhead, goToIndex, simulateKey, destroy };
|
|
218
|
+
return t007._ashooters.set(container, handle), handle;
|
|
219
|
+
}
|
|
220
|
+
var removeArrowNavigation = (container) => t007._ashooters?.get(container)?.destroy();
|
|
221
|
+
export {
|
|
222
|
+
initArrowNavigation,
|
|
223
|
+
initFocusTrap,
|
|
224
|
+
initOutsideClick,
|
|
225
|
+
initScrollAssist,
|
|
226
|
+
initVScrollerator,
|
|
227
|
+
removeArrowNavigation,
|
|
228
|
+
removeFocusTrap,
|
|
229
|
+
removeOutsideClick,
|
|
230
|
+
removeScrollAssist,
|
|
231
|
+
rippleHandler
|
|
232
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
INTERACTIVE_SELECTOR: () => INTERACTIVE_SELECTOR,
|
|
24
|
+
NIL: () => import_sia_reactor.NIL,
|
|
25
|
+
NOOP: () => import_sia_reactor.NOOP,
|
|
23
26
|
VIRTUAL_RESOURCE: () => VIRTUAL_RESOURCE,
|
|
24
27
|
assignEl: () => import_utils.assignEl,
|
|
25
28
|
bindAllMethods: () => import_utils6.bindAllMethods,
|
|
@@ -31,16 +34,17 @@ __export(index_exports, {
|
|
|
31
34
|
deepBreath: () => deepBreath,
|
|
32
35
|
formatKeyForDisplay: () => import_utils5.formatKeyForDisplay,
|
|
33
36
|
formatKeyShortcutsForDisplay: () => import_utils5.formatKeyShortcutsForDisplay,
|
|
37
|
+
formatSize: () => formatSize,
|
|
38
|
+
getActiveElement: () => getActiveElement,
|
|
34
39
|
getTermsForKey: () => import_utils5.getTermsForKey,
|
|
35
40
|
guardAllMethods: () => import_utils6.guardAllMethods,
|
|
36
41
|
guardMethod: () => import_utils6.guardMethod,
|
|
37
42
|
inBoolArrOpt: () => inBoolArrOpt,
|
|
38
|
-
initScrollAssist: () => initScrollAssist,
|
|
39
|
-
initVScrollerator: () => initVScrollerator,
|
|
40
43
|
isArr: () => isArr,
|
|
41
44
|
isBool: () => isBool,
|
|
42
45
|
isDef: () => isDef,
|
|
43
46
|
isFunc: () => isFunc,
|
|
47
|
+
isInteractive: () => isInteractive,
|
|
44
48
|
isIter: () => isIter,
|
|
45
49
|
isNum: () => isNum,
|
|
46
50
|
isObj: () => import_utils2.isObj,
|
|
@@ -56,7 +60,6 @@ __export(index_exports, {
|
|
|
56
60
|
onAllMethods: () => import_utils6.onAllMethods,
|
|
57
61
|
parseForARIAKS: () => import_utils5.parseForARIAKS,
|
|
58
62
|
parseKeyCombo: () => import_utils5.parseKeyCombo,
|
|
59
|
-
removeScrollAssist: () => removeScrollAssist,
|
|
60
63
|
requestAnimationFrame: () => import_utils4.requestAnimationFrame,
|
|
61
64
|
setInterval: () => import_utils4.setInterval,
|
|
62
65
|
setTimeout: () => import_utils4.setTimeout,
|
|
@@ -67,6 +70,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
67
70
|
|
|
68
71
|
// src/core/dom.ts
|
|
69
72
|
var import_utils = require("sia-reactor/utils");
|
|
73
|
+
var INTERACTIVE_SELECTOR = 'button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex="-1"])';
|
|
74
|
+
var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
|
|
70
75
|
var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
|
|
71
76
|
function loadResource(req, type = "style", { module: module2, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
|
|
72
77
|
w.t007 ??= {}, w.t007._resourceCache ??= {};
|
|
@@ -95,6 +100,13 @@ function loadResource(req, type = "style", { module: module2, media, crossOrigin
|
|
|
95
100
|
});
|
|
96
101
|
return w.t007._resourceCache[src];
|
|
97
102
|
}
|
|
103
|
+
function getActiveElement(root = document) {
|
|
104
|
+
const activeEl = root.activeElement;
|
|
105
|
+
return !activeEl ? null : activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/index.ts
|
|
109
|
+
var import_sia_reactor = require("sia-reactor");
|
|
98
110
|
|
|
99
111
|
// src/core/obj.ts
|
|
100
112
|
var import_utils2 = require("sia-reactor/utils");
|
|
@@ -173,101 +185,19 @@ function bindCleanupToSignal(cleanup, signal) {
|
|
|
173
185
|
// src/core/keys.ts
|
|
174
186
|
var import_utils5 = require("sia-reactor/utils");
|
|
175
187
|
|
|
188
|
+
// src/core/file.ts
|
|
189
|
+
function formatSize(bytes, decimals = 3, base = 1e3) {
|
|
190
|
+
if (bytes < base) return `${bytes} byte${bytes == 1 ? "" : "s"}`;
|
|
191
|
+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(base)), units.length - 1);
|
|
192
|
+
return `${(bytes / Math.pow(base, exponent)).toFixed(decimals).replace(/\.0+$/, "")} ${units[exponent]}`;
|
|
193
|
+
}
|
|
194
|
+
|
|
176
195
|
// src/mixins/methd.ts
|
|
177
196
|
var import_utils6 = require("sia-reactor/utils");
|
|
178
197
|
|
|
179
|
-
// src/quirks/scroll.ts
|
|
180
|
-
function initVScrollerator({ baseSpeed = 3, maxSpeed = 10, stepDelay = 2e3, baseRate = 16, lineHeight = 80, margin = 80, car = window } = {}) {
|
|
181
|
-
let linesPerSec = baseSpeed, accelId = null, lastTime = null;
|
|
182
|
-
const drive = (clientY, brake = false, offsetY = 0) => {
|
|
183
|
-
if (car !== window) clientY -= offsetY;
|
|
184
|
-
const now = performance.now(), speed = linesPerSec * lineHeight * ((lastTime ? now - lastTime : baseRate) / 1e3);
|
|
185
|
-
if (!brake && (clientY < margin || clientY > (car.innerHeight ?? car.offsetHeight) - margin)) {
|
|
186
|
-
accelId === null ? accelId = setTimeout(() => linesPerSec += 1, stepDelay) : linesPerSec > baseSpeed && (linesPerSec = Math.min(linesPerSec + 1, maxSpeed));
|
|
187
|
-
car.scrollBy?.(0, clientY < margin ? -speed : speed);
|
|
188
|
-
} else reset();
|
|
189
|
-
return lastTime = !brake ? now : null, speed;
|
|
190
|
-
};
|
|
191
|
-
const reset = () => (accelId && clearTimeout(accelId), accelId = null, linesPerSec = baseSpeed, lastTime = null);
|
|
192
|
-
return { drive, reset };
|
|
193
|
-
}
|
|
194
|
-
function initScrollAssist(el, { pxPerSecond = 80, assistClassName = "t007-scroll-assist", vertical = true, horizontal = true } = {}) {
|
|
195
|
-
t007._scrollers ??= /* @__PURE__ */ new WeakMap();
|
|
196
|
-
t007._scroller_r_observer ??= new ResizeObserver((entries) => entries.forEach(({ target }) => t007._scrollers.get(target)?.update()));
|
|
197
|
-
t007._scroller_m_observer ??= new MutationObserver((entries) => {
|
|
198
|
-
const els = /* @__PURE__ */ new Set();
|
|
199
|
-
for (const entry of entries) {
|
|
200
|
-
let node = entry.target instanceof Element ? entry.target : null;
|
|
201
|
-
while (node && !t007._scrollers.has(node)) node = node.parentElement;
|
|
202
|
-
if (node) els.add(node);
|
|
203
|
-
}
|
|
204
|
-
for (const el2 of els) t007._scrollers.get(el2)?.update();
|
|
205
|
-
});
|
|
206
|
-
const parent = el?.parentElement;
|
|
207
|
-
if (!parent || t007._scrollers.has(el)) return;
|
|
208
|
-
const assist = {};
|
|
209
|
-
let scrollId = null, last = performance.now(), assistWidth = 20, assistHeight = 20;
|
|
210
|
-
const update = () => {
|
|
211
|
-
const hasInteractive = !!parent.querySelector('button, a[href], input, select, textarea, [contenteditable="true"], [tabindex]:not([tabindex="-1"])');
|
|
212
|
-
if (horizontal) {
|
|
213
|
-
const w = assist.left?.offsetWidth || assistWidth, check = hasInteractive ? el.clientWidth < w * 2 : false;
|
|
214
|
-
assist.left.style.display = check ? "none" : el.scrollLeft > 0 ? "block" : "none";
|
|
215
|
-
assist.right.style.display = check ? "none" : el.scrollLeft + el.clientWidth < el.scrollWidth - 1 ? "block" : "none";
|
|
216
|
-
assistWidth = w;
|
|
217
|
-
}
|
|
218
|
-
if (vertical) {
|
|
219
|
-
const h = assist.up?.offsetHeight || assistHeight, check = hasInteractive ? el.clientHeight < h * 2 : false;
|
|
220
|
-
assist.up.style.display = check ? "none" : el.scrollTop > 0 ? "block" : "none";
|
|
221
|
-
assist.down.style.display = check ? "none" : el.scrollTop + el.clientHeight < el.scrollHeight - 1 ? "block" : "none";
|
|
222
|
-
assistHeight = h;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
const scroll = (dir) => {
|
|
226
|
-
const frame = () => {
|
|
227
|
-
const now = performance.now(), dt = now - last;
|
|
228
|
-
last = now;
|
|
229
|
-
const d = pxPerSecond * dt / 1e3;
|
|
230
|
-
if (dir === "left") el.scrollLeft = Math.max(0, el.scrollLeft - d);
|
|
231
|
-
if (dir === "right") el.scrollLeft = Math.min(el.scrollWidth - el.clientWidth, el.scrollLeft + d);
|
|
232
|
-
if (dir === "up") el.scrollTop = Math.max(0, el.scrollTop - d);
|
|
233
|
-
if (dir === "down") el.scrollTop = Math.min(el.scrollHeight - el.clientHeight, el.scrollTop + d);
|
|
234
|
-
scrollId = requestAnimationFrame(frame);
|
|
235
|
-
};
|
|
236
|
-
last = performance.now();
|
|
237
|
-
frame();
|
|
238
|
-
};
|
|
239
|
-
const stop = () => (cancelAnimationFrame(scrollId ?? 0), scrollId = null);
|
|
240
|
-
const addAssist = (dir) => {
|
|
241
|
-
const div = (0, import_utils.createEl)("div", { className: assistClassName }, { scrollDirection: dir }, { display: "none" });
|
|
242
|
-
if (!div) return;
|
|
243
|
-
["pointerenter", "dragenter"].forEach((evt) => div.addEventListener(evt, () => scroll(dir)));
|
|
244
|
-
["pointerleave", "pointerup", "pointercancel", "dragleave", "dragend"].forEach((evt) => div.addEventListener(evt, stop));
|
|
245
|
-
dir === "left" || dir === "up" ? parent.insertBefore(div, el) : parent.append(div);
|
|
246
|
-
assist[dir] = div;
|
|
247
|
-
};
|
|
248
|
-
if (horizontal) ["left", "right"].forEach(addAssist);
|
|
249
|
-
if (vertical) ["up", "down"].forEach(addAssist);
|
|
250
|
-
el.addEventListener("scroll", update);
|
|
251
|
-
t007._scroller_r_observer.observe(el);
|
|
252
|
-
t007._scroller_m_observer.observe(el, { childList: true, subtree: true, characterData: true });
|
|
253
|
-
t007._scrollers.set(el, {
|
|
254
|
-
update,
|
|
255
|
-
destroy() {
|
|
256
|
-
stop();
|
|
257
|
-
el.removeEventListener("scroll", update);
|
|
258
|
-
t007._scroller_r_observer.unobserve(el);
|
|
259
|
-
t007._scrollers.delete(el);
|
|
260
|
-
Object.values(assist).forEach((a) => a.remove());
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
return update(), t007._scrollers.get(el);
|
|
264
|
-
}
|
|
265
|
-
var removeScrollAssist = (el) => t007._scrollers.get(el)?.destroy();
|
|
266
|
-
|
|
267
198
|
// src/index.ts
|
|
268
199
|
if ("undefined" !== typeof window) {
|
|
269
|
-
window.t007 ??= {};
|
|
270
|
-
t007.VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
|
|
200
|
+
(window.t007 ??= {}).VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
|
|
271
201
|
window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
|
|
272
202
|
window.T007_INPUT_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest`;
|
|
273
203
|
window.T007_DIALOG_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest`;
|
|
@@ -277,6 +207,9 @@ if ("undefined" !== typeof window) {
|
|
|
277
207
|
}
|
|
278
208
|
// Annotate the CommonJS export names for ESM import in node:
|
|
279
209
|
0 && (module.exports = {
|
|
210
|
+
INTERACTIVE_SELECTOR,
|
|
211
|
+
NIL,
|
|
212
|
+
NOOP,
|
|
280
213
|
VIRTUAL_RESOURCE,
|
|
281
214
|
assignEl,
|
|
282
215
|
bindAllMethods,
|
|
@@ -288,16 +221,17 @@ if ("undefined" !== typeof window) {
|
|
|
288
221
|
deepBreath,
|
|
289
222
|
formatKeyForDisplay,
|
|
290
223
|
formatKeyShortcutsForDisplay,
|
|
224
|
+
formatSize,
|
|
225
|
+
getActiveElement,
|
|
291
226
|
getTermsForKey,
|
|
292
227
|
guardAllMethods,
|
|
293
228
|
guardMethod,
|
|
294
229
|
inBoolArrOpt,
|
|
295
|
-
initScrollAssist,
|
|
296
|
-
initVScrollerator,
|
|
297
230
|
isArr,
|
|
298
231
|
isBool,
|
|
299
232
|
isDef,
|
|
300
233
|
isFunc,
|
|
234
|
+
isInteractive,
|
|
301
235
|
isIter,
|
|
302
236
|
isNum,
|
|
303
237
|
isObj,
|
|
@@ -313,7 +247,6 @@ if ("undefined" !== typeof window) {
|
|
|
313
247
|
onAllMethods,
|
|
314
248
|
parseForARIAKS,
|
|
315
249
|
parseKeyCombo,
|
|
316
|
-
removeScrollAssist,
|
|
317
250
|
requestAnimationFrame,
|
|
318
251
|
setInterval,
|
|
319
252
|
setTimeout,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,74 +1,18 @@
|
|
|
1
|
+
import { A as ArrowNavigationHandle } from './arrowNavigation-DK8mqVOk.cjs';
|
|
2
|
+
import { S as ScrollAssistHandle } from './scrollAssist-y9wFmYgt.cjs';
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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,
|
|
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 };
|