@skalfa/skalfa-component 1.0.6 → 1.0.8

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.
Files changed (59) hide show
  1. package/dist/index.js +10 -10
  2. package/package.json +2 -2
  3. package/src/accordion/Accordion.component.tsx +87 -0
  4. package/src/breadcrumb/Breadcrumb.component.tsx +79 -0
  5. package/src/button/Button.component.tsx +89 -0
  6. package/src/card/AlertCard.component.tsx +69 -0
  7. package/src/card/Card.component.tsx +25 -0
  8. package/src/card/DashboardCard.component.tsx +44 -0
  9. package/src/card/GalleryCard.component.tsx +50 -0
  10. package/src/card/ProductCard.component.tsx +65 -0
  11. package/src/card/ProfileCard.component.tsx +71 -0
  12. package/src/carousel/Carousel.component.tsx +111 -0
  13. package/src/chip/Chip.component.tsx +39 -0
  14. package/src/index.ts +70 -0
  15. package/src/input/Checkbox.component.tsx +102 -0
  16. package/src/input/Input.component.tsx +334 -0
  17. package/src/input/InputCheckbox.component.tsx +174 -0
  18. package/src/input/InputCurrency.component.tsx +165 -0
  19. package/src/input/InputDate.component.tsx +356 -0
  20. package/src/input/InputDatetime.component.tsx +267 -0
  21. package/src/input/InputDocument.component.tsx +360 -0
  22. package/src/input/InputImage.component.tsx +535 -0
  23. package/src/input/InputNumber.component.tsx +194 -0
  24. package/src/input/InputOtp.component.tsx +169 -0
  25. package/src/input/InputPassword.component.tsx +245 -0
  26. package/src/input/InputRadio.component.tsx +174 -0
  27. package/src/input/InputTime.component.tsx +280 -0
  28. package/src/input/InputValues.component.tsx +71 -0
  29. package/src/input/Radio.component.tsx +98 -0
  30. package/src/input/Select.component.tsx +557 -0
  31. package/src/modal/BottomSheet.component.tsx +246 -0
  32. package/src/modal/FloatingPage.component.tsx +103 -0
  33. package/src/modal/Modal.component.tsx +95 -0
  34. package/src/modal/ModalConfirm.component.tsx +219 -0
  35. package/src/modal/Toast.component.tsx +125 -0
  36. package/src/nav/Bottombar.component.tsx +72 -0
  37. package/src/nav/Footer.component.tsx +177 -0
  38. package/src/nav/Headbar.component.tsx +33 -0
  39. package/src/nav/Navbar.component.tsx +138 -0
  40. package/src/nav/Sidebar.component.tsx +298 -0
  41. package/src/nav/Tabbar.component.tsx +61 -0
  42. package/src/nav/Wizard.component.tsx +80 -0
  43. package/src/supervision/FormSupervision.component.tsx +425 -0
  44. package/src/supervision/TableSupervision.component.tsx +688 -0
  45. package/src/table/ControlBar.component.tsx +501 -0
  46. package/src/table/FilterComponent.tsx +519 -0
  47. package/src/table/Pagination.component.tsx +152 -0
  48. package/src/table/Table.component.tsx +436 -0
  49. package/src/types.d.ts +7 -0
  50. package/src/typography/TypographyArticle.component.tsx +26 -0
  51. package/src/typography/TypographyColumn.component.tsx +20 -0
  52. package/src/typography/TypographyContent.component.tsx +20 -0
  53. package/src/typography/TypographyTips.component.tsx +20 -0
  54. package/src/wrap/Draggable.component.tsx +303 -0
  55. package/src/wrap/Image.component.tsx +10 -0
  56. package/src/wrap/OutsideClick.component.tsx +48 -0
  57. package/src/wrap/ScrollContainer.component.tsx +107 -0
  58. package/src/wrap/ShortcutProvider.tsx +57 -0
  59. package/src/wrap/Swipe.component.tsx +121 -0
@@ -0,0 +1,111 @@
1
+ "use client"
2
+
3
+ import { useState, useRef, useEffect } from "react";
4
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
+ import { faChevronLeft, faChevronRight } from "@fortawesome/free-solid-svg-icons";
6
+ import { cn, pcn } from "@utils";
7
+
8
+
9
+
10
+ type CT = "item" | "prev-button" | "next-button" | "navigation" | "base";
11
+
12
+ interface CarouselItem {
13
+ background : string;
14
+ content ?: string;
15
+ }
16
+
17
+ interface CarouselProps {
18
+ items : CarouselItem[];
19
+ noButton ?: boolean;
20
+ noNavigation ?: boolean;
21
+
22
+ /** Use custom class with: "item::", "prev-button::", "next-button::", "navigation::". */
23
+ className ?: string;
24
+ }
25
+
26
+
27
+
28
+ export function CarouselComponent({
29
+ items,
30
+ className = "",
31
+ noButton,
32
+ noNavigation,
33
+ }: CarouselProps) {
34
+ const [currentIndex, setCurrentIndex] = useState<number>(0);
35
+ const touchStartX = useRef<number | null>(null);
36
+ const touchEndX = useRef<number | null>(null);
37
+ const intervalRef = useRef<NodeJS.Timeout | null>(null);
38
+
39
+ const handlePrev = (): void => setCurrentIndex((prevIndex) => (prevIndex - 1 + items.length) % items.length);
40
+
41
+ const handleNext = (): void => setCurrentIndex((prevIndex) => (prevIndex + 1) % items.length);
42
+
43
+ const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>): void => {
44
+ touchStartX.current = e.touches[0].clientX;
45
+ };
46
+
47
+ const handleTouchEnd = (e: React.TouchEvent<HTMLDivElement>): void => {
48
+ touchEndX.current = e.changedTouches[0].clientX;
49
+ if (touchStartX.current !== null && touchEndX.current !== null) {
50
+ if (touchStartX.current - touchEndX.current > 50) handleNext();
51
+ if (touchEndX.current - touchStartX.current > 50) handlePrev();
52
+ }
53
+ };
54
+
55
+ useEffect(() => {
56
+ intervalRef.current = setInterval(handleNext, 10000);
57
+ return () => {
58
+ if (intervalRef.current) clearInterval(intervalRef.current);
59
+ };
60
+ }, []);
61
+
62
+ return (
63
+ <div className={cn("carousel", pcn<CT>(className, "base"))}>
64
+ <div
65
+ className="carousel-inner"
66
+ style={{ transform: `translateX(-${currentIndex * 100}%)` }}
67
+ onTouchStart={handleTouchStart}
68
+ onTouchEnd={handleTouchEnd}
69
+ >
70
+ {items.map((item, index) => (
71
+ <div
72
+ key={index}
73
+ className={cn("carousel-item", pcn<CT>(className, "item"))}
74
+ style={{ backgroundImage: `url(${item.background})` }}
75
+ >
76
+ {item.content}
77
+ </div>
78
+ ))}
79
+ </div>
80
+
81
+ {!noNavigation && (
82
+ <div className={cn("carousel-navigation", pcn<CT>(className, "navigation"))}>
83
+ {items.map((_, index) => (
84
+ <button
85
+ key={index}
86
+ className={`carousel-indicator ${currentIndex === index ? "carousel-indicator-active" : ""}`}
87
+ onClick={() => setCurrentIndex(index)}
88
+ ></button>
89
+ ))}
90
+ </div>
91
+ )}
92
+
93
+ {!noButton && (
94
+ <>
95
+ <button
96
+ className={cn("carousel-btn carousel-prev-btn", pcn<CT>(className, "prev-button"))}
97
+ onClick={handlePrev}
98
+ >
99
+ <FontAwesomeIcon icon={faChevronLeft} />
100
+ </button>
101
+ <button
102
+ className={cn("carousel-btn carousel-next-btn", pcn<CT>(className, "next-button"))}
103
+ onClick={handleNext}
104
+ >
105
+ <FontAwesomeIcon icon={faChevronRight} />
106
+ </button>
107
+ </>
108
+ )}
109
+ </div>
110
+ );
111
+ }
@@ -0,0 +1,39 @@
1
+ import { faTimes } from "@fortawesome/free-solid-svg-icons";
2
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
+ import { cn } from "@utils";
4
+
5
+ export function ChipComponent({
6
+ items,
7
+ onClick,
8
+ onDelete,
9
+ className,
10
+ } : {
11
+ items : Record<string, any>,
12
+ onClick ?: (item: any, index: number) => void,
13
+ onDelete ?: (item: any, index: number) => void,
14
+ className ?: string,
15
+ }) {
16
+ return (
17
+ <div className={cn("chip-group", className)}>
18
+ {items?.map((item: any, key: number) => {
19
+ return (
20
+ <div
21
+ key={key}
22
+ className="chip"
23
+ onClick={() => onClick?.(item, key)}
24
+ >
25
+ <span>{item}</span>
26
+
27
+ {onDelete && (
28
+ <FontAwesomeIcon
29
+ icon={faTimes}
30
+ className="chip-delete"
31
+ onClick={() => onDelete?.(item, key)}
32
+ />
33
+ )}
34
+ </div>
35
+ );
36
+ })}
37
+ </div>
38
+ )
39
+ }
package/src/index.ts ADDED
@@ -0,0 +1,70 @@
1
+ export * from "./accordion/Accordion.component";
2
+ export * from "./breadcrumb/Breadcrumb.component";
3
+ export * from "./button/Button.component";
4
+ export * from "./card/AlertCard.component";
5
+ export * from "./card/Card.component";
6
+ export * from "./card/DashboardCard.component";
7
+ export * from "./card/GalleryCard.component";
8
+ export * from "./card/ProductCard.component";
9
+ export * from "./card/ProfileCard.component";
10
+ export * from "./carousel/Carousel.component";
11
+ export * from "./chip/Chip.component";
12
+ export * from "./input/Checkbox.component";
13
+ export * from "./input/Input.component";
14
+ export * from "./input/InputCheckbox.component";
15
+ export * from "./input/InputCurrency.component";
16
+ export * from "./input/InputDate.component";
17
+ export * from "./input/InputDatetime.component";
18
+ export * from "./input/InputDocument.component";
19
+ export * from "./input/InputImage.component";
20
+ export * from "./input/InputNumber.component";
21
+ export * from "./input/InputOtp.component";
22
+ export * from "./input/InputPassword.component";
23
+ export * from "./input/InputRadio.component";
24
+ export * from "./input/InputTime.component";
25
+ export * from "./input/InputValues.component";
26
+ export * from "./input/Radio.component";
27
+ export * from "./input/Select.component";
28
+ export * from "./modal/BottomSheet.component";
29
+ export * from "./modal/FloatingPage.component";
30
+ export * from "./modal/Modal.component";
31
+ export * from "./modal/ModalConfirm.component";
32
+ export * from "./modal/Toast.component";
33
+ export * from "./nav/Bottombar.component";
34
+ export * from "./nav/Footer.component";
35
+ export * from "./nav/Headbar.component";
36
+ export * from "./nav/Navbar.component";
37
+ export * from "./nav/Sidebar.component";
38
+ export * from "./nav/Tabbar.component";
39
+ export * from "./nav/Wizard.component";
40
+ export * from "./supervision/FormSupervision.component";
41
+ export * from "./supervision/TableSupervision.component";
42
+ export * from "./table/ControlBar.component";
43
+ export * from "./table/FilterComponent";
44
+ export * from "./table/Pagination.component";
45
+ export * from "./table/Table.component";
46
+ export * from "./typography/TypographyArticle.component";
47
+ export * from "./typography/TypographyColumn.component";
48
+ export * from "./typography/TypographyContent.component";
49
+ export * from "./typography/TypographyTips.component";
50
+ export * from "./wrap/Draggable.component";
51
+ export * from "./wrap/Image.component";
52
+ export * from "./wrap/OutsideClick.component";
53
+ export * from "./wrap/ScrollContainer.component";
54
+ export * from "./wrap/ShortcutProvider";
55
+ export * from "./wrap/Swipe.component";
56
+
57
+ import { registry } from "@utils";
58
+ import { TableComponent } from "./table/Table.component";
59
+ import { ButtonComponent } from "./button/Button.component";
60
+ import { SelectComponent } from "./input/Select.component";
61
+ import { ModalComponent } from "./modal/Modal.component";
62
+ import { FilterComponent } from "./table/FilterComponent";
63
+ import { useToggleContext } from "@contexts";
64
+
65
+ registry.register("TableComponent", TableComponent);
66
+ registry.register("ButtonComponent", ButtonComponent);
67
+ registry.register("SelectComponent", SelectComponent);
68
+ registry.register("ModalComponent", ModalComponent);
69
+ registry.register("FilterComponent", FilterComponent);
70
+ registry.register("useToggleContext", useToggleContext);
@@ -0,0 +1,102 @@
1
+ "use client"
2
+
3
+ import { ReactNode, useEffect, useState } from "react";
4
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
+ import { faCheck } from "@fortawesome/free-solid-svg-icons";
6
+ import { cn, pcn, useInputRandomId } from "@utils";
7
+
8
+ type CT = "label" | "checked" | "error" | "base";
9
+
10
+ export type CheckboxProps = {
11
+ name : string;
12
+ label ?: string | ReactNode;
13
+
14
+ value ?: string;
15
+ disabled ?: boolean;
16
+ checked ?: boolean;
17
+ invalid ?: string;
18
+
19
+ onChange ?: () => void;
20
+
21
+ /** Use custom class with: "label::", "checked::", "error::". */
22
+ className ?: string;
23
+ };
24
+
25
+ export function CheckboxComponent({
26
+ name,
27
+ label,
28
+
29
+ value,
30
+ disabled = false,
31
+ checked = false,
32
+ invalid,
33
+
34
+ onChange,
35
+
36
+ className = "",
37
+ }: CheckboxProps) {
38
+
39
+ // =========================>
40
+ // ## Initial
41
+ // =========================>
42
+ const randomId = useInputRandomId()
43
+ const [invalidMessage, setInvalidMessage] = useState("");
44
+
45
+ // =========================>
46
+ // ## Invalid handler
47
+ // =========================>
48
+ useEffect(() => {
49
+ setInvalidMessage(invalid || "");
50
+ }, [invalid]);
51
+
52
+ return (
53
+ <div className="checkbox-container">
54
+ <input
55
+ type="checkbox"
56
+ className="hidden"
57
+ id={randomId}
58
+ name={name}
59
+ onChange={onChange}
60
+ defaultChecked={checked}
61
+ value={value}
62
+ disabled={disabled}
63
+ />
64
+
65
+ <label
66
+ htmlFor={randomId}
67
+ className={cn(
68
+ "checkbox-wrapper",
69
+ disabled && "checkbox-wrapper-disabled"
70
+ )}
71
+ >
72
+ <div>
73
+ <div
74
+ className={cn(
75
+ "checkbox-input",
76
+ checked && "checkbox-input-checked",
77
+ checked && pcn<CT>(className, "checked"),
78
+ pcn<CT>(className, "base"),
79
+ )}
80
+ >
81
+ {checked && <FontAwesomeIcon icon={faCheck} className="text-sm" />}
82
+ </div>
83
+ </div>
84
+ <span
85
+ className={cn(
86
+ "checkbox-label",
87
+ checked && "checkbox-label-checked",
88
+ pcn<CT>(className, "label"),
89
+ checked && pcn<CT>(className, "label", "checked"),
90
+ disabled && pcn<CT>(className, "label", "disabled"),
91
+ )}
92
+ >
93
+ {label}
94
+ </span>
95
+ </label>
96
+
97
+ {invalidMessage && (
98
+ <small className={cn("input-error-message", pcn<CT>(className, "error"))}>{invalidMessage}</small>
99
+ )}
100
+ </div>
101
+ );
102
+ }
@@ -0,0 +1,334 @@
1
+ "use client"
2
+
3
+ import { InputHTMLAttributes, ReactNode, Ref, useEffect, useState } from "react";
4
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
+ import { cn, pcn, useInputHandler, useInputRandomId, useValidation, validation, ValidationRules } from "@utils";
6
+ import { InputValues } from "./InputValues.component";
7
+
8
+
9
+
10
+ type CT = "label" | "tip" | "error" | "base" | "icon" | "suggest" | "suggest-item";
11
+
12
+ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange"> {
13
+ label ?: string;
14
+ tip ?: string | ReactNode;
15
+ leftIcon ?: any;
16
+ rightIcon ?: any;
17
+
18
+ value ?: any;
19
+ invalid ?: string;
20
+ suggestions ?: string[];
21
+
22
+ validations ?: ValidationRules;
23
+ onlyAlphabet ?: boolean;
24
+ uppercase ?: boolean;
25
+ lowercase ?: boolean;
26
+ multiple ?: boolean;
27
+
28
+ onChange ?: (value: any) => any;
29
+ register ?: (name: string, validations?: ValidationRules) => void;
30
+ unregister ?: (name: string) => void;
31
+
32
+ ref ?: Ref<HTMLInputElement>,
33
+
34
+ /** Use custom class with: "label::", "tip::", "error::", "icon::", "suggest::", "suggest-item::". */
35
+ className ?: string;
36
+ }
37
+
38
+
39
+
40
+ export function InputComponent({
41
+ label,
42
+ tip,
43
+ leftIcon,
44
+ rightIcon,
45
+ className = "",
46
+
47
+ value,
48
+ invalid,
49
+ suggestions,
50
+
51
+ validations,
52
+ onlyAlphabet,
53
+ uppercase,
54
+ lowercase,
55
+ multiple,
56
+
57
+ register,
58
+ unregister,
59
+ onChange,
60
+
61
+ ref,
62
+ ...props
63
+ }: InputProps) {
64
+
65
+
66
+ const [activeSuggestion, setActiveSuggestion] = useState(0);
67
+ const [showSuggestions, setShowSuggestions] = useState(false);
68
+ const [dataSuggestions, setDataSuggestions] = useState<string[] | undefined>([]);
69
+ const [filteredSuggestions, setFilteredSuggestions] = useState<string[] | undefined>([]);
70
+
71
+
72
+ // =========================>
73
+ // ## Initial
74
+ // =========================>
75
+ const inputHandler = useInputHandler(props.name, value, validations, register, unregister, props.type == "file")
76
+ const randomId = useInputRandomId()
77
+
78
+
79
+ // =========================>
80
+ // ## Invalid handler
81
+ // =========================>
82
+ const [invalidMessage] = useValidation(inputHandler.value, validations, invalid, inputHandler.idle);
83
+
84
+
85
+ // =========================>
86
+ // ## Change value handler
87
+ // =========================>
88
+ useEffect(() => {
89
+ if (inputHandler.value && typeof inputHandler.value === "string") {
90
+ let newVal = onlyAlphabet ? inputHandler.value.replace(/[^A-Za-z ]+/g, "") : inputHandler.value;
91
+
92
+ if (uppercase) newVal = newVal.toUpperCase();
93
+ if (lowercase) newVal = newVal.toLowerCase();
94
+
95
+ if (validations && validation.hasRules(validations, "max")) newVal = newVal.slice(0, parseInt(validation.getRules(validations, "max") || "0"));
96
+
97
+ inputHandler.setValue(newVal);
98
+ }
99
+ }, [inputHandler.value, onlyAlphabet, uppercase, lowercase, validations]);
100
+
101
+
102
+ // =========================>
103
+ // ## suggestions handler
104
+ // =========================>
105
+ useEffect(() => {
106
+ setDataSuggestions(suggestions);
107
+ }, [suggestions]);
108
+
109
+ const filterSuggestion = (e: any) => {
110
+ if (dataSuggestions?.length) {
111
+ let filteredSuggestion = [];
112
+
113
+ if (e.target.value) {
114
+ filteredSuggestion = dataSuggestions
115
+ .filter((suggestion) => suggestion.toLowerCase().indexOf(e.target.value.toLowerCase()) > -1)
116
+ .slice(0, 10);
117
+ } else {
118
+ filteredSuggestion = dataSuggestions.slice(0, 10);
119
+ }
120
+
121
+ setActiveSuggestion(-1);
122
+ setFilteredSuggestions(filteredSuggestion);
123
+ setShowSuggestions(true);
124
+ }
125
+ };
126
+
127
+
128
+ const onKeyDownSuggestion = (e: any) => {
129
+ if (dataSuggestions?.length) {
130
+ if (e.keyCode === 13) {
131
+ const resultValue = filteredSuggestions?.at(activeSuggestion);
132
+ setActiveSuggestion(-1);
133
+ setFilteredSuggestions([]);
134
+ setShowSuggestions(false);
135
+ inputHandler.setValue(resultValue ? resultValue : inputHandler.value);
136
+ if (onChange) {
137
+ onChange(resultValue ? resultValue : inputHandler.value);
138
+ }
139
+ e.preventDefault();
140
+ } else if (e.keyCode === 38) {
141
+ if (activeSuggestion === 0) {
142
+ return;
143
+ }
144
+
145
+ setActiveSuggestion(activeSuggestion - 1);
146
+ } else if (e.keyCode === 40) {
147
+ if (activeSuggestion + 1 >= (filteredSuggestions?.length || 0)) {
148
+ return;
149
+ }
150
+
151
+ setActiveSuggestion(activeSuggestion + 1);
152
+ }
153
+ }
154
+ };
155
+
156
+ return (
157
+ <>
158
+ <div className="relative flex flex-col gap-y-0.5">
159
+ <label
160
+ htmlFor={randomId}
161
+ className={cn(
162
+ "input-label",
163
+ props.disabled && "input-label-disabled",
164
+ inputHandler.focus && "input-label-focus",
165
+ !!invalidMessage && "input-label-error",
166
+ pcn<CT>(className, "label"),
167
+ props.disabled && pcn<CT>(className, "label", "disabled"),
168
+ inputHandler.focus && pcn<CT>(className, "label", "focus"),
169
+ !!invalidMessage && pcn<CT>(className, "label", "error"),
170
+ )}
171
+ >
172
+ {label}
173
+ {validations && validation.hasRules(validations, "required") && <span className="text-danger ml-1">*</span>}
174
+ </label>
175
+
176
+ {tip && (
177
+ <small
178
+ className={cn(
179
+ "input-tip",
180
+ props.disabled && "input-tip-disabled",
181
+ pcn<CT>(className, "tip"),
182
+ props.disabled && pcn<CT>(className, "tip", "disabled"),
183
+ )}
184
+ >{tip}</small>
185
+ )}
186
+
187
+ <div className="relative">
188
+ <input
189
+ {...props}
190
+ ref={ref}
191
+ id={randomId}
192
+ placeholder={!multiple || (multiple && !inputHandler.value?.length) ? props.placeholder : ""}
193
+ className={cn(
194
+ "input",
195
+ props.type == "file" && "input-file",
196
+ leftIcon && "input-with-left-icon",
197
+ rightIcon && "input-with-right-icon",
198
+ !!invalidMessage && "input-error",
199
+ pcn<CT>(className, "base"),
200
+ !!invalidMessage && pcn<CT>(className, "base", "error"),
201
+ )}
202
+ value={!multiple ? inputHandler.value: undefined}
203
+ onChange={(e) => {
204
+ if(!multiple) {
205
+ inputHandler.setValue(e.target.value);
206
+ inputHandler.setIdle(false);
207
+ onChange?.(props.type == "file" ? e.target?.files && e.target?.files[0] : e.target.value);
208
+ dataSuggestions?.length && filterSuggestion(e);
209
+ }
210
+ }}
211
+ onFocus={(e) => {
212
+ props.onFocus?.(e);
213
+ inputHandler.setFocus(true);
214
+ dataSuggestions?.length && filterSuggestion(e);
215
+ }}
216
+ onBlur={(e) => {
217
+ props.onBlur?.(e);
218
+ setTimeout(() => inputHandler.setFocus(false), 100);
219
+ }}
220
+ onKeyDown={(e) => {
221
+ dataSuggestions?.length && onKeyDownSuggestion(e);
222
+
223
+ if (multiple && e.key === "Enter" || e.key === ",") {
224
+ e.preventDefault();
225
+ const currentValue = e.currentTarget.value.trim();
226
+ if (!currentValue) return;
227
+
228
+ const currentValues = Array.isArray(inputHandler.value) ? [...inputHandler.value] : [];
229
+ if (!currentValues.includes(currentValue)) {
230
+ const newValues = [...currentValues, currentValue];
231
+ onChange?.(newValues);
232
+ inputHandler.setValue(newValues);
233
+ e.currentTarget.value = "";
234
+ }
235
+ }
236
+ }}
237
+ autoComplete={props.autoComplete || dataSuggestions?.length ? "off" : ""}
238
+ />
239
+
240
+
241
+ {(multiple) && (
242
+ <InputValues
243
+ value={inputHandler.value || []}
244
+ isFocus={inputHandler.focus}
245
+ onFocus={() => setTimeout(() => inputHandler.setFocus(true), 110)}
246
+ onDelete={(_, index) => {
247
+ const values = Array().concat(inputHandler.value);
248
+ const newValues = values.filter((_, val) => val != index);
249
+
250
+ inputHandler.setValue(newValues);
251
+ onChange?.(newValues);
252
+ }}
253
+ className={`${!inputHandler.focus && (leftIcon ? "ml-[2.5rem]" : "ml-[1rem]")}`}
254
+ style={{ maxWidth: `calc(100% - ${leftIcon ? "5.2rem" : "2rem"})` }}
255
+ />
256
+ )}
257
+
258
+ {leftIcon && (
259
+ <FontAwesomeIcon
260
+ className={cn(
261
+ "input-icon",
262
+ "input-icon-left",
263
+ props.disabled && "input-icon-disabled",
264
+ inputHandler.focus && "input-icon-focus",
265
+ pcn<CT>(className, "icon"),
266
+ props.disabled && pcn<CT>(className, "icon", "disabled"),
267
+ inputHandler.focus && pcn<CT>(className, "icon", "focus"),
268
+ )}
269
+ icon={leftIcon}
270
+ />
271
+ )}
272
+
273
+ {rightIcon && (
274
+ <FontAwesomeIcon
275
+ className={cn(
276
+ "input-icon",
277
+ "input-icon-right",
278
+ props.disabled && "input-icon-disabled",
279
+ inputHandler.focus && "input-icon-focus",
280
+ pcn<CT>(className, "icon"),
281
+ props.disabled && pcn<CT>(className, "icon", "disabled"),
282
+ inputHandler.focus && pcn<CT>(className, "icon", "focus"),
283
+ )}
284
+ icon={rightIcon}
285
+ />
286
+ )}
287
+ </div>
288
+
289
+ {!!dataSuggestions?.length && showSuggestions && !!filteredSuggestions?.length && (
290
+ <div>
291
+ <ul
292
+ className={cn(
293
+ "input-suggest-container",
294
+ inputHandler.focus && "input-suggest-container-active",
295
+ pcn<CT>(className, "suggest"),
296
+ )}
297
+ >
298
+ {filteredSuggestions.map((suggestion, key) => {
299
+ return (
300
+ <li
301
+ className={cn(
302
+ "input-suggest",
303
+ inputHandler.value == suggestion && "input-suggest-active",
304
+ pcn<CT>(className, "suggest-item"),
305
+ inputHandler.value == suggestion && pcn<CT>(className, "suggest-item", "active"),
306
+ )}
307
+ key={suggestion}
308
+ onMouseDown={() => {
309
+ setTimeout(() => inputHandler.setFocus(true), 110);
310
+ }}
311
+ onMouseUp={() => {
312
+ setActiveSuggestion(key);
313
+ setFilteredSuggestions([]);
314
+ setShowSuggestions(false);
315
+ inputHandler.setValue(filteredSuggestions[key] || inputHandler.value);
316
+ onChange?.(filteredSuggestions[key] || inputHandler.value);
317
+ setTimeout(() => inputHandler.setFocus(false), 120);
318
+ }}
319
+ >
320
+ {suggestion}
321
+ </li>
322
+ );
323
+ })}
324
+ </ul>
325
+ </div>
326
+ )}
327
+
328
+ {invalidMessage && (
329
+ <small className={cn("input-error-message", pcn<CT>(className, "error"))}>{invalidMessage}</small>
330
+ )}
331
+ </div>
332
+ </>
333
+ );
334
+ }