@seekho/ui 0.1.9 → 0.1.12

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.mjs CHANGED
@@ -1,8 +1,385 @@
1
- import { useId, useEffect, useState } from 'react';
1
+ import React, { useId, useRef, useState, useEffect } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
3
  import { useHotkeys } from 'react-hotkeys-hook';
3
- import { jsx, jsxs } from 'react/jsx-runtime';
4
4
 
5
- // src/components/Modal/Modal.tsx
5
+ // src/components/AutoComplete/AutoComplete.tsx
6
+ function AutoComplete({
7
+ label,
8
+ value,
9
+ options,
10
+ disabled = false,
11
+ error = false,
12
+ className,
13
+ getOptionLabel,
14
+ isOptionEqualToValue,
15
+ isLoading = false,
16
+ renderOption,
17
+ onChange,
18
+ onInputChange
19
+ }) {
20
+ const inputId = useId();
21
+ const containerRef = useRef(null);
22
+ const listRef = useRef(null);
23
+ const [open, setOpen] = useState(false);
24
+ const [inputValue, setInputValue] = useState(
25
+ value ? getOptionLabel(value) : ""
26
+ );
27
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
28
+ const filteredOptions = options.filter(
29
+ (option) => getOptionLabel(option).toLowerCase().includes(inputValue.toLowerCase())
30
+ );
31
+ const handleInputChange = (e) => {
32
+ const val = e.target.value;
33
+ setInputValue(val);
34
+ setOpen(true);
35
+ setHighlightedIndex(-1);
36
+ onInputChange == null ? void 0 : onInputChange(val);
37
+ };
38
+ const handleSelect = ({ option }) => {
39
+ onChange(option);
40
+ setInputValue(getOptionLabel(option));
41
+ setOpen(false);
42
+ setHighlightedIndex(-1);
43
+ };
44
+ const handleKeyDown = (e) => {
45
+ if (!open && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
46
+ setOpen(true);
47
+ return;
48
+ }
49
+ if (e.key === "ArrowDown") {
50
+ setHighlightedIndex(
51
+ (prev) => Math.min(prev + 1, filteredOptions.length - 1)
52
+ );
53
+ } else if (e.key === "ArrowUp") {
54
+ setHighlightedIndex((prev) => Math.max(prev - 1, 0));
55
+ } else if (e.key === "Enter" && highlightedIndex >= 0) {
56
+ handleSelect({ option: filteredOptions[highlightedIndex] });
57
+ } else if (e.key === "Escape") {
58
+ setOpen(false);
59
+ setInputValue(value ? getOptionLabel(value) : "");
60
+ }
61
+ };
62
+ const isSelected = ({ option }) => {
63
+ if (!value) return false;
64
+ return isOptionEqualToValue ? isOptionEqualToValue(option, value) : getOptionLabel(option) === getOptionLabel(value);
65
+ };
66
+ useEffect(() => {
67
+ setInputValue(value ? getOptionLabel(value) : "");
68
+ }, [value]);
69
+ useEffect(() => {
70
+ const handleClickOutside = (e) => {
71
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
72
+ setOpen(false);
73
+ setInputValue(value ? getOptionLabel(value) : "");
74
+ }
75
+ };
76
+ document.addEventListener("mousedown", handleClickOutside);
77
+ return () => document.removeEventListener("mousedown", handleClickOutside);
78
+ }, [value]);
79
+ const isFloated = !!(inputValue || open);
80
+ return /* @__PURE__ */ jsxs(
81
+ "div",
82
+ {
83
+ ref: containerRef,
84
+ className: ["relative w-full", className || ""].join(" "),
85
+ children: [
86
+ /* @__PURE__ */ jsxs(
87
+ "div",
88
+ {
89
+ className: [
90
+ "relative flex items-center rounded-md bg-white px-3 py-2 gap-2 transition-colors",
91
+ disabled ? "bg-gray-50 opacity-60 cursor-not-allowed" : ""
92
+ ].join(" "),
93
+ children: [
94
+ /* @__PURE__ */ jsx(
95
+ "fieldset",
96
+ {
97
+ className: [
98
+ "absolute inset-0 rounded-md border pointer-events-none transition-colors",
99
+ error ? "border-red-400" : open ? "border-basePurple border-2" : "border-gray-300"
100
+ ].join(" "),
101
+ style: { paddingLeft: "8px" },
102
+ children: label && /* @__PURE__ */ jsx(
103
+ "legend",
104
+ {
105
+ className: "text-[11px] transition-all duration-150 h-0 overflow-visible",
106
+ style: {
107
+ maxWidth: isFloated ? "100%" : "0.01px",
108
+ visibility: "hidden",
109
+ whiteSpace: "nowrap"
110
+ },
111
+ children: /* @__PURE__ */ jsx("span", { className: "px-1 text-[11px]", children: label })
112
+ }
113
+ )
114
+ }
115
+ ),
116
+ label && /* @__PURE__ */ jsx(
117
+ "label",
118
+ {
119
+ htmlFor: inputId,
120
+ className: [
121
+ "absolute left-3 transition-all duration-150 pointer-events-none",
122
+ isFloated ? "-top-[9px] text-[11px] bg-white px-1" : "top-1/2 -translate-y-1/2 text-[13px]",
123
+ error ? "text-red-500" : isFloated || open ? "text-basePurple" : "text-gray-500"
124
+ ].join(" "),
125
+ children: label
126
+ }
127
+ ),
128
+ /* @__PURE__ */ jsx("div", { className: "relative flex-1", children: /* @__PURE__ */ jsx(
129
+ "input",
130
+ {
131
+ id: inputId,
132
+ value: inputValue,
133
+ disabled,
134
+ autoComplete: "off",
135
+ className: "w-full text-[13px] focus:outline-none bg-transparent text-gray-800 disabled:cursor-not-allowed",
136
+ onChange: handleInputChange,
137
+ onFocus: () => setOpen(true),
138
+ onKeyDown: handleKeyDown
139
+ }
140
+ ) }),
141
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
142
+ isLoading && /* @__PURE__ */ jsxs(
143
+ "svg",
144
+ {
145
+ className: "animate-spin w-4 h-4 text-gray-400",
146
+ xmlns: "http://www.w3.org/2000/svg",
147
+ fill: "none",
148
+ viewBox: "0 0 24 24",
149
+ children: [
150
+ /* @__PURE__ */ jsx(
151
+ "circle",
152
+ {
153
+ className: "opacity-25",
154
+ cx: "12",
155
+ cy: "12",
156
+ r: "10",
157
+ stroke: "currentColor",
158
+ strokeWidth: "4"
159
+ }
160
+ ),
161
+ /* @__PURE__ */ jsx(
162
+ "path",
163
+ {
164
+ className: "opacity-75",
165
+ fill: "currentColor",
166
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
167
+ }
168
+ )
169
+ ]
170
+ }
171
+ ),
172
+ value && !disabled && /* @__PURE__ */ jsx(
173
+ "button",
174
+ {
175
+ type: "button",
176
+ onClick: () => {
177
+ onChange(null);
178
+ setInputValue("");
179
+ onInputChange == null ? void 0 : onInputChange("");
180
+ },
181
+ className: "text-gray-400 hover:text-gray-600 transition-colors",
182
+ children: /* @__PURE__ */ jsxs(
183
+ "svg",
184
+ {
185
+ xmlns: "http://www.w3.org/2000/svg",
186
+ className: "w-3.5 h-3.5",
187
+ viewBox: "0 0 24 24",
188
+ fill: "none",
189
+ stroke: "currentColor",
190
+ strokeWidth: "2.5",
191
+ strokeLinecap: "round",
192
+ strokeLinejoin: "round",
193
+ children: [
194
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
195
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
196
+ ]
197
+ }
198
+ )
199
+ }
200
+ ),
201
+ /* @__PURE__ */ jsx(
202
+ "button",
203
+ {
204
+ type: "button",
205
+ disabled,
206
+ onClick: () => !disabled && setOpen((prev) => !prev),
207
+ className: "text-gray-400 hover:text-gray-600 transition-colors disabled:cursor-not-allowed",
208
+ children: /* @__PURE__ */ jsx(
209
+ "svg",
210
+ {
211
+ xmlns: "http://www.w3.org/2000/svg",
212
+ className: [
213
+ "w-4 h-4 transition-transform",
214
+ open ? "rotate-180" : ""
215
+ ].join(" "),
216
+ viewBox: "0 0 24 24",
217
+ fill: "none",
218
+ stroke: "currentColor",
219
+ strokeWidth: "2",
220
+ strokeLinecap: "round",
221
+ strokeLinejoin: "round",
222
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
223
+ }
224
+ )
225
+ }
226
+ )
227
+ ] })
228
+ ]
229
+ }
230
+ ),
231
+ open && !disabled && /* @__PURE__ */ jsx(
232
+ "ul",
233
+ {
234
+ ref: listRef,
235
+ className: "absolute z-50 mt-1 w-full bg-white border border-gray-200 rounded-md shadow-lg max-h-56 overflow-y-auto",
236
+ children: isLoading ? /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-[13px] text-gray-400", children: "Loading..." }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-[13px] text-gray-400", children: "No options" }) : filteredOptions.map((option, idx) => {
237
+ const optionProps = {
238
+ onClick: () => handleSelect({ option }),
239
+ onMouseEnter: () => setHighlightedIndex(idx),
240
+ className: [
241
+ "px-3 py-2 text-[13px] cursor-pointer select-none",
242
+ highlightedIndex === idx ? "bg-basePurple/10 text-basePurple" : "text-gray-700",
243
+ isSelected({ option }) ? "font-semibold" : ""
244
+ ].join(" ")
245
+ };
246
+ return renderOption ? /* @__PURE__ */ jsx(React.Fragment, { children: renderOption(optionProps, option) }, idx) : /* @__PURE__ */ jsx(
247
+ "li",
248
+ {
249
+ onMouseDown: () => handleSelect({ option }),
250
+ onMouseEnter: () => setHighlightedIndex(idx),
251
+ className: optionProps.className,
252
+ children: getOptionLabel(option)
253
+ },
254
+ idx
255
+ );
256
+ })
257
+ }
258
+ )
259
+ ]
260
+ }
261
+ );
262
+ }
263
+ var ButtonLoader = ({ className }) => {
264
+ return /* @__PURE__ */ jsxs(
265
+ "svg",
266
+ {
267
+ xmlns: "http://www.w3.org/2000/svg",
268
+ fill: "none",
269
+ viewBox: "0 0 24 24",
270
+ className: `animate-spin h-5 w-5 text-white ${className || ""}`,
271
+ children: [
272
+ /* @__PURE__ */ jsx(
273
+ "circle",
274
+ {
275
+ className: "opacity-25",
276
+ cx: "12",
277
+ cy: "12",
278
+ r: "10",
279
+ stroke: "currentColor",
280
+ strokeWidth: "4"
281
+ }
282
+ ),
283
+ /* @__PURE__ */ jsx(
284
+ "path",
285
+ {
286
+ className: "opacity-75",
287
+ fill: "currentColor",
288
+ d: "M4 12a8 8 0 018-8v4l3-3-3-3v4a8 8 0 018 8h-4l3 3 3-3h-4a8 8 0 01-8 8v-4l-3 3 3 3v-4a8 8 0 01-8-8z"
289
+ }
290
+ )
291
+ ]
292
+ }
293
+ );
294
+ };
295
+
296
+ // src/utils/helperFunctions.ts
297
+ var cn = (...classes) => classes.filter(Boolean).join(" ");
298
+ var handleCopyTextToClipboard = async ({
299
+ clipboardText
300
+ }) => {
301
+ if (clipboardText === void 0 || clipboardText === null) return false;
302
+ const text = String(clipboardText);
303
+ if (navigator.clipboard && window.isSecureContext) {
304
+ try {
305
+ await navigator.clipboard.writeText(text);
306
+ return true;
307
+ } catch (err) {
308
+ console.warn(
309
+ "navigator.clipboard.writeText failed, falling back to execCommand",
310
+ err
311
+ );
312
+ }
313
+ }
314
+ try {
315
+ const textArea = document.createElement("textarea");
316
+ textArea.value = text;
317
+ textArea.style.position = "fixed";
318
+ textArea.style.top = "0";
319
+ textArea.style.left = "0";
320
+ textArea.style.opacity = "0";
321
+ textArea.setAttribute("readonly", "");
322
+ document.body.appendChild(textArea);
323
+ textArea.focus();
324
+ textArea.select();
325
+ textArea.setSelectionRange(0, text.length);
326
+ const success = document.execCommand("copy");
327
+ document.body.removeChild(textArea);
328
+ if (success) return true;
329
+ } catch (err) {
330
+ console.warn("execCommand copy failed", err);
331
+ }
332
+ try {
333
+ window.prompt("Copy to clipboard: Cmd+C, Enter", text);
334
+ return false;
335
+ } catch (e) {
336
+ return false;
337
+ }
338
+ };
339
+ var CopyId = ({
340
+ id,
341
+ className,
342
+ displayHash = true,
343
+ onCopy
344
+ }) => {
345
+ const handleClick = async (e) => {
346
+ e.stopPropagation();
347
+ const success = await handleCopyTextToClipboard({ clipboardText: id });
348
+ onCopy == null ? void 0 : onCopy(success);
349
+ };
350
+ return /* @__PURE__ */ jsxs(
351
+ "button",
352
+ {
353
+ onClick: handleClick,
354
+ className: [
355
+ "border border-basePurple px-3 py-[1px] text-basePurple",
356
+ "rounded-[24px] text-[11px] mt-1 hover:bg-basePurple/10 transition-colors",
357
+ className || ""
358
+ ].join(" "),
359
+ children: [
360
+ displayHash && "#",
361
+ id
362
+ ]
363
+ }
364
+ );
365
+ };
366
+ var CustomAnchor = ({
367
+ displayText,
368
+ href,
369
+ className,
370
+ openInNewTab = false
371
+ }) => {
372
+ return /* @__PURE__ */ jsx(
373
+ "a",
374
+ {
375
+ href,
376
+ className: `hover:text-black hover:no-underline ${className || ""}`,
377
+ target: openInNewTab ? "_blank" : "",
378
+ rel: openInNewTab ? "noopener noreferrer" : void 0,
379
+ children: displayText
380
+ }
381
+ );
382
+ };
6
383
 
7
384
  // src/utils/modalStack.ts
8
385
  var stack = [];
@@ -158,11 +535,17 @@ function ReadMore({
158
535
  displayText,
159
536
  className,
160
537
  numberOfCharactersToShow = 300,
161
- handleCopyDisplayText,
538
+ showCopy = false,
539
+ onCopy,
162
540
  handleToggleReadMore
163
541
  }) {
164
542
  const isExpanded = readMore == null ? void 0 : readMore[index];
165
543
  const isLong = (displayText == null ? void 0 : displayText.length) > numberOfCharactersToShow;
544
+ const handleCopy = async (e) => {
545
+ e.stopPropagation();
546
+ const success = await handleCopyTextToClipboard({ clipboardText: displayText });
547
+ onCopy == null ? void 0 : onCopy(success);
548
+ };
166
549
  return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
167
550
  "p",
168
551
  {
@@ -179,32 +562,23 @@ function ReadMore({
179
562
  onClick: handleToggleReadMore,
180
563
  className: "text-basePurple cursor-pointer font-semibold text-[11px]",
181
564
  children: isExpanded ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
182
- handleCopyDisplayText && /* @__PURE__ */ jsx(
183
- "span",
565
+ showCopy && /* @__PURE__ */ jsx("span", { onClick: handleCopy, children: /* @__PURE__ */ jsxs(
566
+ "svg",
184
567
  {
185
- onClick: (e) => {
186
- e.stopPropagation();
187
- handleCopyDisplayText({ displayText });
188
- },
189
- children: /* @__PURE__ */ jsxs(
190
- "svg",
191
- {
192
- xmlns: "http://www.w3.org/2000/svg",
193
- className: "text-gray-500 w-[14px] h-[14px]",
194
- viewBox: "0 0 24 24",
195
- fill: "none",
196
- stroke: "currentColor",
197
- strokeWidth: "2",
198
- strokeLinecap: "round",
199
- strokeLinejoin: "round",
200
- children: [
201
- /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
202
- /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
203
- ]
204
- }
205
- )
568
+ xmlns: "http://www.w3.org/2000/svg",
569
+ className: "text-gray-500 w-[14px] h-[14px]",
570
+ viewBox: "0 0 24 24",
571
+ fill: "none",
572
+ stroke: "currentColor",
573
+ strokeWidth: "2",
574
+ strokeLinecap: "round",
575
+ strokeLinejoin: "round",
576
+ children: [
577
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
578
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
579
+ ]
206
580
  }
207
- ),
581
+ ) }),
208
582
  " ",
209
583
  "...Read Less"
210
584
  ] }) : /* @__PURE__ */ jsx("span", { children: "...Read More" })
@@ -214,6 +588,77 @@ function ReadMore({
214
588
  }
215
589
  ) });
216
590
  }
591
+ function SearchBar({
592
+ value,
593
+ placeHolder,
594
+ wrapperClass,
595
+ className,
596
+ searchParamQuery = "",
597
+ onChange,
598
+ handleInputKey,
599
+ handleClearInput
600
+ }) {
601
+ const showClear = searchParamQuery || value;
602
+ return /* @__PURE__ */ jsxs(
603
+ "div",
604
+ {
605
+ className: [
606
+ "flex items-center rounded-full justify-between border px-2 bg-white",
607
+ wrapperClass || ""
608
+ ].join(" "),
609
+ children: [
610
+ /* @__PURE__ */ jsxs(
611
+ "svg",
612
+ {
613
+ xmlns: "http://www.w3.org/2000/svg",
614
+ className: "text-gray-400 w-[14px] h-[14px] ml-2 shrink-0",
615
+ viewBox: "0 0 24 24",
616
+ fill: "none",
617
+ stroke: "currentColor",
618
+ strokeWidth: "2",
619
+ strokeLinecap: "round",
620
+ strokeLinejoin: "round",
621
+ children: [
622
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
623
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
624
+ ]
625
+ }
626
+ ),
627
+ /* @__PURE__ */ jsx(
628
+ "input",
629
+ {
630
+ value,
631
+ placeholder: placeHolder,
632
+ className: [
633
+ "w-[95%] focus:outline-none rounded-r-full",
634
+ "placeholder:text-[13px] text-[13px] py-[8px] px-2",
635
+ className || ""
636
+ ].join(" "),
637
+ onKeyPress: handleInputKey,
638
+ onChange
639
+ }
640
+ ),
641
+ /* @__PURE__ */ jsx("div", { className: "w-[20px] shrink-0", children: showClear && handleClearInput && /* @__PURE__ */ jsx("button", { onClick: handleClearInput, children: /* @__PURE__ */ jsxs(
642
+ "svg",
643
+ {
644
+ xmlns: "http://www.w3.org/2000/svg",
645
+ className: "w-[16px] h-[16px]",
646
+ viewBox: "0 0 24 24",
647
+ fill: "none",
648
+ stroke: "currentColor",
649
+ strokeWidth: "2.5",
650
+ strokeLinecap: "round",
651
+ strokeLinejoin: "round",
652
+ children: [
653
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
654
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
655
+ ]
656
+ }
657
+ ) }) })
658
+ ]
659
+ }
660
+ );
661
+ }
217
662
  function useLocalStorage(key, initialValue) {
218
663
  const [storedValue, setStoredValue] = useState(() => {
219
664
  if (typeof window === "undefined") return initialValue;
@@ -248,11 +693,6 @@ function useLocalStorage(key, initialValue) {
248
693
  return [storedValue, setValue, remove];
249
694
  }
250
695
 
251
- // src/utils/cn.ts
252
- function cn(...classes) {
253
- return classes.filter(Boolean).join(" ");
254
- }
255
-
256
- export { Modal, ReadMore, cn, useLocalStorage };
696
+ export { AutoComplete, ButtonLoader, CopyId, CustomAnchor, Modal, ReadMore, SearchBar, cn, handleCopyTextToClipboard, useLocalStorage };
257
697
  //# sourceMappingURL=index.mjs.map
258
698
  //# sourceMappingURL=index.mjs.map