@litelens/design-system 1.0.3

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.
@@ -0,0 +1,148 @@
1
+ import { SearchIcon, Input } from './chunk-7YPL4ZPM.js';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useState, useRef } from 'react';
4
+
5
+ var FullTextSearchInput = ({
6
+ searchTerm,
7
+ matchCount,
8
+ currentMatchIdx,
9
+ onSearch,
10
+ onSearchNext,
11
+ ariaLabel = "Search"
12
+ }) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
13
+ /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground absolute left-1.5 top-1/2 size-3 -translate-y-1/2" }),
14
+ /* @__PURE__ */ jsx(
15
+ Input,
16
+ {
17
+ className: "h-6 w-44 pl-6 pr-9 text-xs",
18
+ placeholder: "Search\u2026",
19
+ value: searchTerm,
20
+ onChange: (e) => onSearch(e.target.value),
21
+ onKeyDown: (e) => {
22
+ if (e.key === "Enter") {
23
+ e.preventDefault();
24
+ onSearchNext();
25
+ }
26
+ },
27
+ "aria-label": ariaLabel
28
+ }
29
+ ),
30
+ /* @__PURE__ */ jsx(
31
+ "output",
32
+ {
33
+ "aria-live": "polite",
34
+ className: "text-muted-foreground absolute right-1.5 top-1/2 -translate-y-1/2 text-[10px] tabular-nums",
35
+ children: searchTerm ? matchCount === 0 ? "0" : `${currentMatchIdx + 1}/${matchCount}` : ""
36
+ }
37
+ )
38
+ ] });
39
+ function useFullTextSearch(options) {
40
+ const [searchTerm, setSearchTerm] = useState("");
41
+ const [matchCount, setMatchCount] = useState(0);
42
+ const [currentMatchIdx, setCurrentMatchIdx] = useState(0);
43
+ const [matches, setMatches] = useState([]);
44
+ const contentRef = useRef(null);
45
+ const resetMatches = () => {
46
+ setMatchCount(0);
47
+ setCurrentMatchIdx(0);
48
+ };
49
+ const base = {
50
+ searchTerm,
51
+ matchCount,
52
+ currentMatchIdx,
53
+ setSearchTerm,
54
+ setMatchCount,
55
+ setCurrentMatchIdx,
56
+ resetMatches
57
+ };
58
+ if (!options) return base;
59
+ const isCustomBackend = "onSearch" in options;
60
+ if (isCustomBackend) {
61
+ const { onSearch, onSearchNext } = options;
62
+ const handleSearch2 = (term) => {
63
+ setSearchTerm(term);
64
+ if (!term) {
65
+ setMatches([]);
66
+ resetMatches();
67
+ return;
68
+ }
69
+ onSearch(term);
70
+ setCurrentMatchIdx(0);
71
+ };
72
+ const handleSearchNext2 = () => {
73
+ if (matchCount === 0) return;
74
+ const nextIdx = (currentMatchIdx + 1) % matchCount;
75
+ setCurrentMatchIdx(nextIdx);
76
+ onSearchNext();
77
+ };
78
+ return {
79
+ ...base,
80
+ matches: [],
81
+ activeMatchCharIdx: -1,
82
+ contentRef,
83
+ handleSearch: handleSearch2,
84
+ handleSearchNext: handleSearchNext2
85
+ };
86
+ }
87
+ const { text } = options;
88
+ const scrollToLine = (charIdx) => {
89
+ if (!text) return;
90
+ const lineNum = text.slice(0, charIdx).split("\n").length - 1;
91
+ const parseLineHeight = (el) => {
92
+ const raw = globalThis.getComputedStyle(el).lineHeight;
93
+ return raw === "normal" ? 20 : Number.parseFloat(raw) || 20;
94
+ };
95
+ const textarea = contentRef.current?.querySelector("textarea[data-yaml-editor]");
96
+ if (textarea) {
97
+ textarea.scrollTop = Math.max(
98
+ 0,
99
+ lineNum * parseLineHeight(textarea) - textarea.clientHeight / 2
100
+ );
101
+ return;
102
+ }
103
+ const scrollableDiv = contentRef.current?.querySelector("[data-yaml-scroll]");
104
+ if (scrollableDiv) {
105
+ scrollableDiv.scrollTop = Math.max(
106
+ 0,
107
+ lineNum * parseLineHeight(scrollableDiv) - scrollableDiv.clientHeight / 2
108
+ );
109
+ }
110
+ };
111
+ const handleSearch = (term) => {
112
+ setSearchTerm(term);
113
+ if (!term || !text) {
114
+ setMatches([]);
115
+ resetMatches();
116
+ return;
117
+ }
118
+ const lowerText = text.toLowerCase();
119
+ const lowerTerm = term.toLowerCase();
120
+ const found = [];
121
+ let idx = 0;
122
+ let pos;
123
+ while ((pos = lowerText.indexOf(lowerTerm, idx)) !== -1) {
124
+ found.push(pos);
125
+ idx = pos + lowerTerm.length;
126
+ }
127
+ setMatches(found);
128
+ setMatchCount(found.length);
129
+ setCurrentMatchIdx(0);
130
+ if (found.length > 0) scrollToLine(found[0]);
131
+ };
132
+ const handleSearchNext = () => {
133
+ if (!matches.length) return;
134
+ const nextIdx = (currentMatchIdx + 1) % matches.length;
135
+ setCurrentMatchIdx(nextIdx);
136
+ scrollToLine(matches[nextIdx]);
137
+ };
138
+ return {
139
+ ...base,
140
+ matches,
141
+ activeMatchCharIdx: matches.length > 0 ? matches[currentMatchIdx] : -1,
142
+ contentRef,
143
+ handleSearch,
144
+ handleSearchNext
145
+ };
146
+ }
147
+
148
+ export { FullTextSearchInput, useFullTextSearch };
@@ -0,0 +1,41 @@
1
+ import { cn } from './chunk-QUNCRUSO.js';
2
+ import { jsx, Fragment } from 'react/jsx-runtime';
3
+
4
+ var SplitAndHighlightText = ({
5
+ text,
6
+ term,
7
+ absoluteStart,
8
+ activeMatchCharIdx
9
+ }) => {
10
+ if (!term) return /* @__PURE__ */ jsx(Fragment, { children: text });
11
+ const lower = text.toLowerCase();
12
+ const lowerTerm = term.toLowerCase();
13
+ const segments = [];
14
+ let lastIdx = 0;
15
+ let idx = lower.indexOf(lowerTerm, 0);
16
+ while (idx !== -1) {
17
+ if (idx > lastIdx)
18
+ segments.push(/* @__PURE__ */ jsx("span", { children: text.slice(lastIdx, idx) }, `text-${lastIdx}`));
19
+ const isActive = absoluteStart !== void 0 && activeMatchCharIdx !== void 0 && activeMatchCharIdx >= 0 && absoluteStart + idx === activeMatchCharIdx;
20
+ segments.push(
21
+ /* @__PURE__ */ jsx(
22
+ "mark",
23
+ {
24
+ className: cn(
25
+ "inline rounded-none bg-transparent px-0.5 py-0 text-inherit",
26
+ isActive ? "border border-orange-400" : "border border-yellow-300"
27
+ ),
28
+ children: text.slice(idx, idx + term.length)
29
+ },
30
+ `match-${idx}-${text.slice(idx, idx + term.length)}`
31
+ )
32
+ );
33
+ lastIdx = idx + term.length;
34
+ idx = lower.indexOf(lowerTerm, lastIdx);
35
+ }
36
+ if (lastIdx < text.length)
37
+ segments.push(/* @__PURE__ */ jsx("span", { children: text.slice(lastIdx) }, `text-${lastIdx}`));
38
+ return /* @__PURE__ */ jsx(Fragment, { children: segments.length > 0 ? segments : text });
39
+ };
40
+
41
+ export { SplitAndHighlightText };