@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
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @t007/utils
|
|
2
2
|
|
|
3
|
-
> The foundational utility belt and central nervous system for the `@t007` UI ecosystem. A collection of highly optimized,
|
|
3
|
+
> The foundational utility belt and central nervous system for the `@t007` UI ecosystem. A collection of highly optimized, JavaScript helpers for DOM manipulation, async resource loading, and math operations.
|
|
4
4
|
|
|
5
5
|
[](https://github.com/Tobi007-del/t007-tools/blob/main/LICENSE)
|
|
6
6
|
[](https://www.npmjs.com/package/@t007/utils)
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
|
|
33
33
|
- ✅ **Tree-Shakeable:** Every utility is exported individually. Modern bundlers will only compile the exact code you import, resulting in zero bloat.
|
|
34
34
|
- ✅ **Shared Memory:** By acting as a peer dependency for the other `@t007` packages, it ensures that your application doesn't download duplicate helper functions.
|
|
35
|
-
- ✅ **Zero Frameworks:** 100% pure vanilla JavaScript.
|
|
36
35
|
|
|
37
36
|
---
|
|
38
37
|
|
|
@@ -50,7 +49,7 @@
|
|
|
50
49
|
|
|
51
50
|
### Built with
|
|
52
51
|
|
|
53
|
-
-
|
|
52
|
+
- JavaScript (ES6+)
|
|
54
53
|
- Bundled via `tsup` (ESM, CJS, IIFE outputs)
|
|
55
54
|
- Built for extreme execution speed and minimal byte size.
|
|
56
55
|
|
|
@@ -86,7 +85,95 @@ import { createEl, loadResource, uid } from '@t007/utils';
|
|
|
86
85
|
const myBtn = createEl('button', { className: 'my-custom-btn', textContent: 'Click Me' });
|
|
87
86
|
|
|
88
87
|
// Inject a stylesheet dynamically
|
|
89
|
-
await loadResource('https://cdn.example.com/styles.css, 'link');
|
|
88
|
+
await loadResource('https://cdn.example.com/styles.css', 'link');
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Built-in Hooks
|
|
92
|
+
|
|
93
|
+
| React Hook | Vanilla Counterpart |
|
|
94
|
+
| --------------- | ------------------- |
|
|
95
|
+
| useRipple | rippleHandler |
|
|
96
|
+
| useScrollAssist | initScrollAssist |
|
|
97
|
+
| useFocusTrap | initFocusTrap |
|
|
98
|
+
| useOutsideClick | initOutsideClick |
|
|
99
|
+
| useHighlight | N/A |
|
|
100
|
+
| N/A | initVScrollerator |
|
|
101
|
+
|
|
102
|
+
### Built-in Components
|
|
103
|
+
| React Component | Vanilla Counterpart |
|
|
104
|
+
| --------------- | ------------------- |
|
|
105
|
+
| HighlightText | N/A |
|
|
106
|
+
|
|
107
|
+
### Code Sample
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { useRef } from "react";
|
|
111
|
+
import { useScrollAssist, useRipple } from "@t007/utils/hooks/react";
|
|
112
|
+
import { HighlightText } from "@t007/utils/components/react";
|
|
113
|
+
import '@t007/utils/styles/ripple.css';
|
|
114
|
+
import "@t007/utils/styles/scroll-assist.css";
|
|
115
|
+
|
|
116
|
+
export function HelperTextScroller() {
|
|
117
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
118
|
+
useScrollAssist(ref, { vertical: false, assistClassName: "scroll-assist" });
|
|
119
|
+
const rippleHandler = useRipple();
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div className="text-wrapper">
|
|
123
|
+
<p ref={ref} className="text">
|
|
124
|
+
<HighlightText query="helper" className="highlight">
|
|
125
|
+
Long helper text to demonstrate highlighting and scroll assistance.
|
|
126
|
+
</HighlightText>
|
|
127
|
+
</p>
|
|
128
|
+
<button onPointerDown={rippleHandler}>Click Me</button>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Styles
|
|
135
|
+
|
|
136
|
+
It includes:
|
|
137
|
+
- `.t007-ripple-wrapper`, `.t007-ripple`, `.t007-ripple-hold`, `.t007-ripple-fade` (default ripple classes)
|
|
138
|
+
- `.t007-scroll-assist` (default assist class)
|
|
139
|
+
|
|
140
|
+
All defaults are exposed as root-level, `t007`-prefixed variables:
|
|
141
|
+
|
|
142
|
+
```css
|
|
143
|
+
:root {
|
|
144
|
+
/** default ripple values */
|
|
145
|
+
--t007-ripple-initial-opacity: 0.4;
|
|
146
|
+
--t007-ripple-initial-scale: 0.5;
|
|
147
|
+
--t007-ripple-expand-scale: 2.05;
|
|
148
|
+
--t007-ripple-color: rgb(0 0 0 / 0.24);
|
|
149
|
+
--t007-ripple-expand-duration: 350ms;
|
|
150
|
+
--t007-ripple-fade-duration: 350ms;
|
|
151
|
+
/** default scroll assist values */
|
|
152
|
+
--t007-scroll-assist-color: rgb(0 0 0 / 1);
|
|
153
|
+
--t007-scroll-assist-opacity: 0.07;
|
|
154
|
+
--t007-scroll-assist-min-width: 2rem;
|
|
155
|
+
--t007-scroll-assist-height: 2rem;
|
|
156
|
+
--t007-scroll-assist-inline-offset: -0.35rem;
|
|
157
|
+
--t007-scroll-assist-block-offset: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** custom pre-requisites */
|
|
161
|
+
.highlight {
|
|
162
|
+
background-color: yellow;
|
|
163
|
+
}
|
|
164
|
+
.text-wrapper {
|
|
165
|
+
position: relative;
|
|
166
|
+
flex: 1;
|
|
167
|
+
min-width: 2rem;
|
|
168
|
+
}
|
|
169
|
+
.text {
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
overflow: auto hidden;
|
|
172
|
+
scrollbar-width: none;
|
|
173
|
+
}
|
|
174
|
+
.text::-webkit-scrollbar {
|
|
175
|
+
display: none;
|
|
176
|
+
}
|
|
90
177
|
```
|
|
91
178
|
|
|
92
179
|
-----
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { K as KeyEvent, C as Config } from './scrollAssist-y9wFmYgt.cjs';
|
|
2
|
+
|
|
3
|
+
type ArrowNavigationHandle = {
|
|
4
|
+
/** Current computed horizontal grid size. */
|
|
5
|
+
gridX: () => number;
|
|
6
|
+
/** Current computed vertical grid size. */
|
|
7
|
+
gridY: () => number;
|
|
8
|
+
/** Current computed virtual vertical grid size. */
|
|
9
|
+
vGridY: () => number;
|
|
10
|
+
/** Current live list of navigable items. */
|
|
11
|
+
items: () => HTMLElement[];
|
|
12
|
+
/** Current active index. */
|
|
13
|
+
activeIndex: () => number;
|
|
14
|
+
/** Current active element or null when none is active. */
|
|
15
|
+
activeItem: () => HTMLElement | null;
|
|
16
|
+
/** Resolve the next enabled index from a directional move. */
|
|
17
|
+
getAbleIndex: (targetIndex: number, e?: KeyEvent) => number | null;
|
|
18
|
+
/** Run type-ahead selection logic. */
|
|
19
|
+
typeAhead: (key: string) => void;
|
|
20
|
+
/** Move active selection/focus to a target index. */
|
|
21
|
+
goToIndex: (index: number, e?: KeyEvent) => void;
|
|
22
|
+
/** Simulate directional key navigation with a keyboard-like event. */
|
|
23
|
+
simulateKey: (e: KeyEvent) => void;
|
|
24
|
+
/** Remove listeners/observers and release resources. */
|
|
25
|
+
destroy: () => void;
|
|
26
|
+
};
|
|
27
|
+
/** A vanilla JavaScript utility for managing robust arrow-key roving focus navigation. */
|
|
28
|
+
declare function initArrowNavigation(container: HTMLElement, config?: Config): ArrowNavigationHandle | void;
|
|
29
|
+
declare const removeArrowNavigation: (container: HTMLElement) => void | undefined;
|
|
30
|
+
|
|
31
|
+
export { type ArrowNavigationHandle as A, initArrowNavigation as i, removeArrowNavigation as r };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { K as KeyEvent, C as Config } from './scrollAssist-y9wFmYgt.js';
|
|
2
|
+
|
|
3
|
+
type ArrowNavigationHandle = {
|
|
4
|
+
/** Current computed horizontal grid size. */
|
|
5
|
+
gridX: () => number;
|
|
6
|
+
/** Current computed vertical grid size. */
|
|
7
|
+
gridY: () => number;
|
|
8
|
+
/** Current computed virtual vertical grid size. */
|
|
9
|
+
vGridY: () => number;
|
|
10
|
+
/** Current live list of navigable items. */
|
|
11
|
+
items: () => HTMLElement[];
|
|
12
|
+
/** Current active index. */
|
|
13
|
+
activeIndex: () => number;
|
|
14
|
+
/** Current active element or null when none is active. */
|
|
15
|
+
activeItem: () => HTMLElement | null;
|
|
16
|
+
/** Resolve the next enabled index from a directional move. */
|
|
17
|
+
getAbleIndex: (targetIndex: number, e?: KeyEvent) => number | null;
|
|
18
|
+
/** Run type-ahead selection logic. */
|
|
19
|
+
typeAhead: (key: string) => void;
|
|
20
|
+
/** Move active selection/focus to a target index. */
|
|
21
|
+
goToIndex: (index: number, e?: KeyEvent) => void;
|
|
22
|
+
/** Simulate directional key navigation with a keyboard-like event. */
|
|
23
|
+
simulateKey: (e: KeyEvent) => void;
|
|
24
|
+
/** Remove listeners/observers and release resources. */
|
|
25
|
+
destroy: () => void;
|
|
26
|
+
};
|
|
27
|
+
/** A vanilla JavaScript utility for managing robust arrow-key roving focus navigation. */
|
|
28
|
+
declare function initArrowNavigation(container: HTMLElement, config?: Config): ArrowNavigationHandle | void;
|
|
29
|
+
declare const removeArrowNavigation: (container: HTMLElement) => void | undefined;
|
|
30
|
+
|
|
31
|
+
export { type ArrowNavigationHandle as A, initArrowNavigation as i, removeArrowNavigation as r };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INTERACTIVE_SELECTOR,
|
|
3
|
+
clamp,
|
|
4
|
+
createEl,
|
|
5
|
+
getActiveElement,
|
|
6
|
+
isInteractive
|
|
7
|
+
} from "./chunk-XVFFZZJA.js";
|
|
8
|
+
|
|
9
|
+
// src/hooks/vanilla/outsideClick.ts
|
|
10
|
+
import { NIL, NOOP } from "sia-reactor";
|
|
11
|
+
var stacks = /* @__PURE__ */ new WeakMap();
|
|
12
|
+
function initOutsideClick(el, { enabled = false, onOutsideClick = NOOP, clickOnClick = true, clickOnEscape = true, clickOnFocusOut = false, allowInputs = true, root = window, scoped = true, capture = true } = NIL) {
|
|
13
|
+
const existing = (t007._outsiders ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
14
|
+
if (!enabled || existing) return existing ? existing : void 0;
|
|
15
|
+
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
16
|
+
const stack = stacks.get(root) ?? [], onScopedOut = (e, t, p = e.touches?.[0] || e, rect = el.getBoundingClientRect()) => {
|
|
17
|
+
if (stack.at(-1) !== el || p.clientX >= rect.left && p.clientX <= rect.right && p.clientY >= rect.top && p.clientY <= rect.bottom) return false;
|
|
18
|
+
return (!scoped ? true : root.contains(t)) && onOutsideClick(e);
|
|
19
|
+
}, handleClick = ((e) => clickOnClick && !(allowInputs && isInteractive(e.target)) && onScopedOut(e, e.target)), handleEscape = ((e) => clickOnEscape && e.key === "Escape" && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey && stack.at(-1) === el && onOutsideClick(e)), handleFocusOut = (e) => clickOnFocusOut && onScopedOut(e, e.relatedTarget);
|
|
20
|
+
root.addEventListener("mousedown", handleClick, capture), root.addEventListener("touchstart", handleClick, { passive: true, capture });
|
|
21
|
+
root.addEventListener("keydown", handleEscape, capture), el.addEventListener("focusout", handleFocusOut, capture);
|
|
22
|
+
if (!stack.includes(el)) stack.push(el), stacks.set(root, stack);
|
|
23
|
+
const destroy = () => {
|
|
24
|
+
root.removeEventListener("mousedown", handleClick, capture), root.removeEventListener("touchstart", handleClick, capture);
|
|
25
|
+
root.removeEventListener("keydown", handleEscape, capture);
|
|
26
|
+
el.removeEventListener("focusout", handleFocusOut, capture);
|
|
27
|
+
t007._outsiders.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
28
|
+
};
|
|
29
|
+
return t007._outsiders.set(el, destroy), destroy;
|
|
30
|
+
}
|
|
31
|
+
var removeOutsideClick = (el) => t007._outsiders?.get(el)?.();
|
|
32
|
+
|
|
33
|
+
// src/hooks/vanilla/focusTrap.ts
|
|
34
|
+
import { NIL as NIL2 } from "sia-reactor";
|
|
35
|
+
var stacks2 = /* @__PURE__ */ new WeakMap();
|
|
36
|
+
function initFocusTrap(el, { enabled = false, initialSelector = "[data-autofocus]", ringClassName = "focus-outline", root = window, scoped = true, capture = true } = NIL2) {
|
|
37
|
+
const existing = (t007._ftrappers ??= /* @__PURE__ */ new WeakMap()).get(el);
|
|
38
|
+
if (!enabled || existing) return existing ? existing : void 0;
|
|
39
|
+
scoped = scoped && root instanceof HTMLElement, root = scoped ? root : root === document ? document : window;
|
|
40
|
+
const stack = stacks2.get(root) ?? [], focused = document.querySelector(":focus"), initial = el.querySelector(initialSelector), first = createEl("span", { tabIndex: 0 }, { focusGuard: "start" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), last = createEl("span", { tabIndex: 0 }, { focusGuard: "end" }, { position: "absolute", width: "0", height: "0", pointerEvents: "none" }), getFocusable = (c = el) => Array.prototype.filter.call(c.querySelectorAll(INTERACTIVE_SELECTOR), (el2) => !el2.hasAttribute("disabled") && !el2.hasAttribute("aria-hidden") && !el2.hasAttribute("data-focus-guard")), resetFocus = (i = 0, els = getFocusable()) => els?.length ? els.at(i).focus() : (!el.hasAttribute("tabindex") && (el.tabIndex = -1), el.focus()), edgeFocus = (pre = false) => {
|
|
41
|
+
if (!scoped) return resetFocus(pre ? -1 : 0);
|
|
42
|
+
else if (root.hasAttribute("tabindex")) return root.focus();
|
|
43
|
+
const items = getFocusable();
|
|
44
|
+
if (!items.length) return resetFocus(0, null);
|
|
45
|
+
const all = getFocusable(root.parentElement?.closest(`:has(${INTERACTIVE_SELECTOR})`) || document.body);
|
|
46
|
+
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 (!root.contains(target = all[i])) return target.focus();
|
|
47
|
+
(pre ? first : last).blur();
|
|
48
|
+
}, handleFocusIn = () => stack.at(-1) === el && getActiveElement() !== root && !el.contains(getActiveElement()) && setTimeout(resetFocus, 0, 0), handleInitialBlur = () => initial.classList.remove(ringClassName);
|
|
49
|
+
first.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus(true) : resetFocus(), capture), el.prepend(first);
|
|
50
|
+
last.addEventListener("focus", (e) => el.contains(e.relatedTarget) ? edgeFocus() : resetFocus(-1), capture), el.append(last);
|
|
51
|
+
root.addEventListener("focusin", handleFocusIn, capture);
|
|
52
|
+
if (!el.querySelector(":focus")) !initial ? resetFocus() : setTimeout(() => (initial.classList.add(ringClassName), initial.focus(), initial.addEventListener("blur", handleInitialBlur, capture)));
|
|
53
|
+
if (!stack.includes(el)) stack.push(el), stacks2.set(root, stack);
|
|
54
|
+
const destroy = () => {
|
|
55
|
+
focused?.isConnected && focused.focus(), first.remove(), last.remove();
|
|
56
|
+
root.removeEventListener("focusin", handleFocusIn, capture);
|
|
57
|
+
initial?.removeEventListener("blur", handleInitialBlur, capture);
|
|
58
|
+
t007._ftrappers.delete(el), stack.splice(stack.indexOf(el), 1);
|
|
59
|
+
};
|
|
60
|
+
return t007._ftrappers.set(el, destroy), destroy;
|
|
61
|
+
}
|
|
62
|
+
var removeFocusTrap = (el) => t007._ftrappers?.get(el)?.();
|
|
63
|
+
|
|
64
|
+
// src/hooks/vanilla/ripple.ts
|
|
65
|
+
import { NIL as NIL3 } from "sia-reactor";
|
|
66
|
+
function rippleHandler(e, { target, forceCenter = false, wrapperClassName = "t007-ripple-wrapper", className = "t007-ripple", holdClassName = "t007-ripple-hold", fadeClassName = "t007-ripple-fade" } = NIL3) {
|
|
67
|
+
const el = target || e.currentTarget;
|
|
68
|
+
if (!el || e.target !== e.currentTarget && isInteractive(e.target) || el.hasAttribute("disabled") || e.pointerType === "mouse" && e.button !== 0) return;
|
|
69
|
+
e.stopPropagation?.();
|
|
70
|
+
const { offsetWidth: rW, offsetHeight: rH } = el, { width: w, height: h, left: l, top: t } = el.getBoundingClientRect(), size = Math.max(rW, rH), x = forceCenter ? rW / 2 - size / 2 : (e.clientX - l) * rW / w - size / 2, y = forceCenter ? rH / 2 - size / 2 : (e.clientY - t) * rH / h - size / 2, wrapper = createEl("span", { className: wrapperClassName }), ripple = createEl("span", { className: className + " " + holdClassName }, {}, { cssText: `width:${size}px;height:${size}px;left:${x}px;top:${y}px;` });
|
|
71
|
+
let canRelease = false;
|
|
72
|
+
ripple.addEventListener("animationend", () => canRelease = true, { once: true });
|
|
73
|
+
el.append(wrapper.appendChild(ripple).parentElement);
|
|
74
|
+
const release = () => {
|
|
75
|
+
if (!canRelease) return ripple.addEventListener("animationend", release, { once: true });
|
|
76
|
+
ripple.classList.replace(holdClassName, fadeClassName);
|
|
77
|
+
ripple.addEventListener("animationend", () => setTimeout(() => wrapper.remove()));
|
|
78
|
+
for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).removeEventListener(evt, release);
|
|
79
|
+
};
|
|
80
|
+
for (const evt of ["pointerup", "pointercancel"]) (el.ownerDocument?.defaultView || window).addEventListener(evt, release);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/hooks/react/useArrowNavigation/utils.ts
|
|
84
|
+
var getTargetIndex = ({ key, currIndex, length, gridX, gridY, vGridY, loop, ctrlKey = false, rtl }) => {
|
|
85
|
+
const rowStart = currIndex - currIndex % gridX, rowEnd = Math.min(rowStart + gridX - 1, length - 1), colStart = currIndex % gridX, colEnd = Math.min(colStart + gridX * (gridY - 1), length - 1), canX = gridX > 1, canY = gridY > 1, horizontalMove = rtl ? { ArrowRight: canX ? -1 : 0, ArrowLeft: canX ? 1 : 0 } : { ArrowRight: canX ? 1 : 0, ArrowLeft: canX ? -1 : 0 }, move = { ...horizontalMove, ArrowDown: canY ? gridX : 0, ArrowUp: canY ? -gridX : 0, Home: ctrlKey ? 0 : rowStart, End: ctrlKey ? length - 1 : rowEnd, PageDown: (vGridY - 1) * gridX, PageUp: -(vGridY - 1) * gridX }[key] ?? 0;
|
|
86
|
+
let targetIndex = key === "Home" || key === "End" ? move : currIndex + move;
|
|
87
|
+
if (key === "ArrowDown") {
|
|
88
|
+
if (!loop && targetIndex >= length) targetIndex -= move;
|
|
89
|
+
} else if (key === "ArrowUp") {
|
|
90
|
+
if (!loop && targetIndex < 0) targetIndex += Math.abs(move);
|
|
91
|
+
} else if (key === "PageDown") {
|
|
92
|
+
if (!loop && targetIndex >= length) targetIndex = colEnd;
|
|
93
|
+
} else if (key === "PageUp") {
|
|
94
|
+
if (!loop && targetIndex < 0) targetIndex = colStart;
|
|
95
|
+
}
|
|
96
|
+
return loop ? (targetIndex + length) % length : clamp(0, targetIndex, length - 1);
|
|
97
|
+
};
|
|
98
|
+
var getCommonAncestor = (first, second) => {
|
|
99
|
+
if (!first) return null;
|
|
100
|
+
if (!second) return first.parentElement;
|
|
101
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
102
|
+
let current = first;
|
|
103
|
+
while (current) ancestors.add(current), current = current.parentElement;
|
|
104
|
+
current = second;
|
|
105
|
+
while (current) {
|
|
106
|
+
if (ancestors.has(current)) return current;
|
|
107
|
+
current = current.parentElement;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
var getGrid = (all, x = true, y = true, vY = true) => {
|
|
112
|
+
const len = all.length, grid = {};
|
|
113
|
+
if (!len) return grid;
|
|
114
|
+
let cols = all.findIndex((el) => el.offsetTop !== all[0].offsetTop);
|
|
115
|
+
cols = cols > 0 ? cols : len;
|
|
116
|
+
if (x) grid.x = cols;
|
|
117
|
+
let rows = Math.ceil(len / cols);
|
|
118
|
+
if (y) grid.y = rows;
|
|
119
|
+
if (vY) {
|
|
120
|
+
const itemHeight = all[0].offsetHeight ?? 0, containerHeight = getCommonAncestor(all[0], all[1])?.clientHeight ?? 0;
|
|
121
|
+
rows = clamp(1, Math.floor(containerHeight / itemHeight), rows) || rows;
|
|
122
|
+
grid.vY = rows;
|
|
123
|
+
}
|
|
124
|
+
return grid;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/hooks/react/useArrowNavigation/consts.ts
|
|
128
|
+
import { NOOP as NOOP2 } from "sia-reactor";
|
|
129
|
+
var H_NAV_KEYS = ["ArrowRight", "ArrowLeft", "Home", "End"];
|
|
130
|
+
var V_NAV_KEYS = ["ArrowUp", "ArrowDown", "PageDown", "PageUp"];
|
|
131
|
+
var NAV_KEYS = [...H_NAV_KEYS, ...V_NAV_KEYS];
|
|
132
|
+
var DEFAULT_CONFIG = {
|
|
133
|
+
enabled: null,
|
|
134
|
+
selector: "[data-arrow-item]",
|
|
135
|
+
focusOnHover: true,
|
|
136
|
+
loop: true,
|
|
137
|
+
virtual: false,
|
|
138
|
+
typeahead: false,
|
|
139
|
+
rovingTab: null,
|
|
140
|
+
defaultTabbableIndex: null,
|
|
141
|
+
baseTabIndex: "0",
|
|
142
|
+
resetMs: 500,
|
|
143
|
+
rtl: null,
|
|
144
|
+
grid: {},
|
|
145
|
+
activeClass: "focus-outlined",
|
|
146
|
+
inputSelector: "input,textarea,[contenteditable='true']",
|
|
147
|
+
focusOptions: { preventScroll: false },
|
|
148
|
+
scrollIntoView: { block: "nearest", inline: "nearest" },
|
|
149
|
+
onSelect: NOOP2,
|
|
150
|
+
onFocusOut: NOOP2
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
initOutsideClick,
|
|
155
|
+
removeOutsideClick,
|
|
156
|
+
getTargetIndex,
|
|
157
|
+
getCommonAncestor,
|
|
158
|
+
getGrid,
|
|
159
|
+
H_NAV_KEYS,
|
|
160
|
+
NAV_KEYS,
|
|
161
|
+
DEFAULT_CONFIG,
|
|
162
|
+
initFocusTrap,
|
|
163
|
+
removeFocusTrap,
|
|
164
|
+
rippleHandler
|
|
165
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/hooks/react/useHighlight.ts
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
function useHighlight(text, query, ignoreCase = true, options) {
|
|
4
|
+
const { trimQuery = true, wholeWord = false } = options ?? {};
|
|
5
|
+
return useMemo(() => {
|
|
6
|
+
if (!query) return [{ text, match: false }];
|
|
7
|
+
const queries = (Array.isArray(query) ? query : [query]).sort((a, b) => b.length - a.length).map((q) => (trimQuery ? q.trim() : q)?.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).filter(Boolean);
|
|
8
|
+
if (!queries.length) return [{ text, match: false }];
|
|
9
|
+
const boundary = wholeWord ? "\\b" : "", regex = new RegExp(`(${queries.map((q) => boundary + q + boundary).join("|")})`, ignoreCase ? "gi" : "g"), parts = text.split(regex);
|
|
10
|
+
return parts.map((part) => ({ text: part, match: !!part.match(regex) }));
|
|
11
|
+
}, [text, query, ignoreCase, trimQuery, wholeWord]);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
useHighlight
|
|
16
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// src/core/dom.ts
|
|
2
|
+
import { createEl, assignEl } from "sia-reactor/utils";
|
|
3
|
+
var INTERACTIVE_SELECTOR = 'button,[href],input,label,select,textarea,details>summary,[contenteditable],iframe,audio[controls],video[controls],[tabindex]:not([tabindex="-1"])';
|
|
4
|
+
var isInteractive = (target) => target instanceof HTMLElement && target.matches(INTERACTIVE_SELECTOR);
|
|
5
|
+
var VIRTUAL_RESOURCE = /* @__PURE__ */ Symbol.for("T007_VIRTUAL_RESOURCE");
|
|
6
|
+
function loadResource(req, type = "style", { module, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, attempts = 3, retryKey = false } = {}, w = window) {
|
|
7
|
+
w.t007 ??= {}, w.t007._resourceCache ??= {};
|
|
8
|
+
if (req === VIRTUAL_RESOURCE || isSym(req)) return Promise.resolve();
|
|
9
|
+
const src = req;
|
|
10
|
+
if (w.t007._resourceCache[src]) return w.t007._resourceCache[src];
|
|
11
|
+
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;
|
|
12
|
+
if (existing) return w.t007._resourceCache[src] = Promise.resolve(existing);
|
|
13
|
+
w.t007._resourceCache[src] = new Promise((resolve, reject) => {
|
|
14
|
+
(function tryLoad(remaining, el) {
|
|
15
|
+
const onerror = () => {
|
|
16
|
+
el?.remove?.();
|
|
17
|
+
if (remaining > 1) {
|
|
18
|
+
setTimeout(tryLoad, 1e3, remaining - 1);
|
|
19
|
+
console.warn(`Retrying ${type} load (${attempts - remaining + 1}): ${src}...`);
|
|
20
|
+
} else {
|
|
21
|
+
delete w.t007._resourceCache[src];
|
|
22
|
+
reject(new Error(`${type} load failed after ${attempts} attempts: ${src}`));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const url = retryKey && remaining < attempts ? `${src}${src.includes("?") ? "&" : "?"}_${retryKey}=${Date.now()}` : src;
|
|
26
|
+
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 }) || "");
|
|
27
|
+
else if (type === "style") w.document.head.append(el = createEl("link", { rel: "stylesheet", href: url, media, crossOrigin, integrity, referrerPolicy, nonce, fetchPriority, onload: () => resolve(el), onerror }) || "");
|
|
28
|
+
else reject(new Error(`Unsupported resource type: ${type}`));
|
|
29
|
+
})(attempts);
|
|
30
|
+
});
|
|
31
|
+
return w.t007._resourceCache[src];
|
|
32
|
+
}
|
|
33
|
+
function getActiveElement(root = document) {
|
|
34
|
+
const activeEl = root.activeElement;
|
|
35
|
+
return !activeEl ? null : activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/index.ts
|
|
39
|
+
import { NIL, NOOP } from "sia-reactor";
|
|
40
|
+
|
|
41
|
+
// src/core/obj.ts
|
|
42
|
+
import { isObj } from "sia-reactor/utils";
|
|
43
|
+
function isDef(val) {
|
|
44
|
+
return "undefined" !== typeof val;
|
|
45
|
+
}
|
|
46
|
+
function isSym(val) {
|
|
47
|
+
return "symbol" === typeof val;
|
|
48
|
+
}
|
|
49
|
+
function isBool(val) {
|
|
50
|
+
return "boolean" === typeof val;
|
|
51
|
+
}
|
|
52
|
+
function isNum(val) {
|
|
53
|
+
return "number" === typeof val;
|
|
54
|
+
}
|
|
55
|
+
function isStr(val) {
|
|
56
|
+
return "string" === typeof val;
|
|
57
|
+
}
|
|
58
|
+
function isArr(obj) {
|
|
59
|
+
return Array.isArray(obj);
|
|
60
|
+
}
|
|
61
|
+
function isPOJO(obj, crossRealms = false, typecheck = true) {
|
|
62
|
+
return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
|
|
63
|
+
}
|
|
64
|
+
function isIter(obj) {
|
|
65
|
+
return obj != null && "function" === typeof obj[Symbol.iterator];
|
|
66
|
+
}
|
|
67
|
+
function isFunc(val) {
|
|
68
|
+
return "function" === typeof val;
|
|
69
|
+
}
|
|
70
|
+
function inBoolArrOpt(opt, str) {
|
|
71
|
+
return opt?.includes?.(str) ?? opt;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/core/num.ts
|
|
75
|
+
import { clamp } from "sia-reactor/utils";
|
|
76
|
+
|
|
77
|
+
// src/core/str.ts
|
|
78
|
+
function uid(prefix = "") {
|
|
79
|
+
return prefix + Date.now().toString(36) + "_" + performance.now().toString(36).replace(".", "") + "_" + Math.random().toString(36).slice(2);
|
|
80
|
+
}
|
|
81
|
+
function isSameURL(src1, src2) {
|
|
82
|
+
if (!isStr(src1) || !isStr(src2) || !src1 || !src2) return false;
|
|
83
|
+
try {
|
|
84
|
+
const u1 = new URL(src1, window.location.href), u2 = new URL(src2, window.location.href);
|
|
85
|
+
return decodeURIComponent(u1.origin + u1.pathname) === decodeURIComponent(u2.origin + u2.pathname);
|
|
86
|
+
} catch {
|
|
87
|
+
return src1.replace(/\\/g, "/").split("?")[0].trim() === src2.replace(/\\/g, "/").split("?")[0].trim();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/core/fn.ts
|
|
92
|
+
import { setTimeout as setTimeout2, setInterval, requestAnimationFrame } from "sia-reactor/utils";
|
|
93
|
+
function limited(FN_KEY, fn, opts = {}) {
|
|
94
|
+
let count = 0, { key, maxTimes: max = 1 } = isStr(opts) ? { key: opts } : opts;
|
|
95
|
+
const getReg = () => JSON.parse(localStorage.getItem(FN_KEY) || "{}"), setReg = (r) => localStorage.setItem(FN_KEY, JSON.stringify(r));
|
|
96
|
+
const handle = (...args) => {
|
|
97
|
+
if (!key) return count++ < max ? fn(...args) : void 0;
|
|
98
|
+
const r = getReg(), c = r[key] || 0;
|
|
99
|
+
return c < max ? (r[key] = c + 1, setReg(r), fn(...args)) : void 0;
|
|
100
|
+
};
|
|
101
|
+
handle.left = max - (handle.count = count);
|
|
102
|
+
handle.reset = () => (count = 0, key && ((r) => (delete r[key], setReg(r)))(getReg()));
|
|
103
|
+
handle.block = () => (count = max, key && ((r) => (r[key] = max, setReg(r)))(getReg()));
|
|
104
|
+
return handle;
|
|
105
|
+
}
|
|
106
|
+
var mockAsync = (timeout = 250) => new Promise((resolve) => setTimeout(resolve, timeout));
|
|
107
|
+
var breath = (w = window) => new Promise((res) => w.requestAnimationFrame(res));
|
|
108
|
+
var deepBreath = (w = window) => new Promise((res) => w.requestAnimationFrame(() => w.requestAnimationFrame(res)));
|
|
109
|
+
function bindCleanupToSignal(cleanup, signal) {
|
|
110
|
+
signal?.aborted ? cleanup() : signal?.addEventListener("abort", cleanup, { once: true });
|
|
111
|
+
if (signal && !signal.aborted) cleanup = (() => (signal.removeEventListener("abort", cleanup), cleanup()));
|
|
112
|
+
return cleanup;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/core/keys.ts
|
|
116
|
+
import { parseKeyCombo, stringifyKeyEvent, cleanKeyCombo, matchKeys, getTermsForKey, keyEventAllowed, formatKeyForDisplay, formatKeyShortcutsForDisplay, parseForARIAKS } from "sia-reactor/utils";
|
|
117
|
+
|
|
118
|
+
// src/core/file.ts
|
|
119
|
+
function formatSize(bytes, decimals = 3, base = 1e3) {
|
|
120
|
+
if (bytes < base) return `${bytes} byte${bytes == 1 ? "" : "s"}`;
|
|
121
|
+
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);
|
|
122
|
+
return `${(bytes / Math.pow(base, exponent)).toFixed(decimals).replace(/\.0+$/, "")} ${units[exponent]}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/mixins/methd.ts
|
|
126
|
+
import { onAllMethods, bindAllMethods, guardAllMethods, guardMethod } from "sia-reactor/utils";
|
|
127
|
+
|
|
128
|
+
// src/index.ts
|
|
129
|
+
if ("undefined" !== typeof window) {
|
|
130
|
+
(window.t007 ??= {}).VIRTUAL_RESOURCE = VIRTUAL_RESOURCE;
|
|
131
|
+
window.T007_TOAST_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest`;
|
|
132
|
+
window.T007_INPUT_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest`;
|
|
133
|
+
window.T007_DIALOG_JS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest`;
|
|
134
|
+
window.T007_TOAST_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/toast@latest/dist/index.min.css`;
|
|
135
|
+
window.T007_INPUT_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/input@latest/dist/index.min.css`;
|
|
136
|
+
window.T007_DIALOG_CSS_SRC ??= `https://cdn.jsdelivr.net/npm/@t007/dialog@latest/dist/index.min.css`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
createEl,
|
|
141
|
+
assignEl,
|
|
142
|
+
INTERACTIVE_SELECTOR,
|
|
143
|
+
isInteractive,
|
|
144
|
+
VIRTUAL_RESOURCE,
|
|
145
|
+
loadResource,
|
|
146
|
+
getActiveElement,
|
|
147
|
+
isObj,
|
|
148
|
+
isDef,
|
|
149
|
+
isSym,
|
|
150
|
+
isBool,
|
|
151
|
+
isNum,
|
|
152
|
+
isStr,
|
|
153
|
+
isArr,
|
|
154
|
+
isPOJO,
|
|
155
|
+
isIter,
|
|
156
|
+
isFunc,
|
|
157
|
+
inBoolArrOpt,
|
|
158
|
+
clamp,
|
|
159
|
+
uid,
|
|
160
|
+
isSameURL,
|
|
161
|
+
limited,
|
|
162
|
+
mockAsync,
|
|
163
|
+
breath,
|
|
164
|
+
deepBreath,
|
|
165
|
+
bindCleanupToSignal,
|
|
166
|
+
setTimeout2 as setTimeout,
|
|
167
|
+
setInterval,
|
|
168
|
+
requestAnimationFrame,
|
|
169
|
+
parseKeyCombo,
|
|
170
|
+
stringifyKeyEvent,
|
|
171
|
+
cleanKeyCombo,
|
|
172
|
+
matchKeys,
|
|
173
|
+
getTermsForKey,
|
|
174
|
+
keyEventAllowed,
|
|
175
|
+
formatKeyForDisplay,
|
|
176
|
+
formatKeyShortcutsForDisplay,
|
|
177
|
+
parseForARIAKS,
|
|
178
|
+
formatSize,
|
|
179
|
+
onAllMethods,
|
|
180
|
+
bindAllMethods,
|
|
181
|
+
guardAllMethods,
|
|
182
|
+
guardMethod,
|
|
183
|
+
NIL,
|
|
184
|
+
NOOP
|
|
185
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/components/react.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
HighlightText: () => HighlightText
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(react_exports);
|
|
26
|
+
|
|
27
|
+
// src/hooks/react/useHighlight.ts
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
function useHighlight(text, query, ignoreCase = true, options) {
|
|
30
|
+
const { trimQuery = true, wholeWord = false } = options ?? {};
|
|
31
|
+
return (0, import_react.useMemo)(() => {
|
|
32
|
+
if (!query) return [{ text, match: false }];
|
|
33
|
+
const queries = (Array.isArray(query) ? query : [query]).sort((a, b) => b.length - a.length).map((q) => (trimQuery ? q.trim() : q)?.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).filter(Boolean);
|
|
34
|
+
if (!queries.length) return [{ text, match: false }];
|
|
35
|
+
const boundary = wholeWord ? "\\b" : "", regex = new RegExp(`(${queries.map((q) => boundary + q + boundary).join("|")})`, ignoreCase ? "gi" : "g"), parts = text.split(regex);
|
|
36
|
+
return parts.map((part) => ({ text: part, match: !!part.match(regex) }));
|
|
37
|
+
}, [text, query, ignoreCase, trimQuery, wholeWord]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/components/react/HighlightText.tsx
|
|
41
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
42
|
+
var HighlightText = ({ children = "", query = "", className = "highlight", ignoreCase = true, trimQuery = true, wholeWord = false }) => {
|
|
43
|
+
const chunks = useHighlight(children, query, ignoreCase, { trimQuery, wholeWord });
|
|
44
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: chunks.map(
|
|
45
|
+
({ text, match }, i) => match ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className, children: text }, i) : text
|
|
46
|
+
) });
|
|
47
|
+
};
|
|
48
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
49
|
+
0 && (module.exports = {
|
|
50
|
+
HighlightText
|
|
51
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { H as HighlightOptions } from '../useHighlight-DMpDCILK.cjs';
|
|
3
|
+
|
|
4
|
+
interface HighlightTextProps extends HighlightOptions {
|
|
5
|
+
/** The text to be processed and displayed. */
|
|
6
|
+
children?: string;
|
|
7
|
+
/** The string or array of strings to match and highlight within the children text. */
|
|
8
|
+
query?: string | string[];
|
|
9
|
+
/** The CSS class to apply to the highlighted parts of the text. Defaults to `"highlight"`. */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Whether the matching should be case-insensitive. Defaults to `true`. */
|
|
12
|
+
ignoreCase?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Component to highlight parts of text that match a query. It splits the text into chunks based on the query and wraps matching parts in a span with the specified className. */
|
|
15
|
+
declare const HighlightText: React.FC<HighlightTextProps>;
|
|
16
|
+
|
|
17
|
+
export { HighlightText, type HighlightTextProps };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { H as HighlightOptions } from '../useHighlight-DMpDCILK.js';
|
|
3
|
+
|
|
4
|
+
interface HighlightTextProps extends HighlightOptions {
|
|
5
|
+
/** The text to be processed and displayed. */
|
|
6
|
+
children?: string;
|
|
7
|
+
/** The string or array of strings to match and highlight within the children text. */
|
|
8
|
+
query?: string | string[];
|
|
9
|
+
/** The CSS class to apply to the highlighted parts of the text. Defaults to `"highlight"`. */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Whether the matching should be case-insensitive. Defaults to `true`. */
|
|
12
|
+
ignoreCase?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Component to highlight parts of text that match a query. It splits the text into chunks based on the query and wraps matching parts in a span with the specified className. */
|
|
15
|
+
declare const HighlightText: React.FC<HighlightTextProps>;
|
|
16
|
+
|
|
17
|
+
export { HighlightText, type HighlightTextProps };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useHighlight
|
|
3
|
+
} from "../chunk-OGFBI6PS.js";
|
|
4
|
+
|
|
5
|
+
// src/components/react/HighlightText.tsx
|
|
6
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
7
|
+
var HighlightText = ({ children = "", query = "", className = "highlight", ignoreCase = true, trimQuery = true, wholeWord = false }) => {
|
|
8
|
+
const chunks = useHighlight(children, query, ignoreCase, { trimQuery, wholeWord });
|
|
9
|
+
return /* @__PURE__ */ jsx(Fragment, { children: chunks.map(
|
|
10
|
+
({ text, match }, i) => match ? /* @__PURE__ */ jsx("span", { className, children: text }, i) : text
|
|
11
|
+
) });
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
HighlightText
|
|
15
|
+
};
|