next-helios-fe 1.9.18 → 1.10.1
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/components/form/index.d.ts +2 -2
- package/dist/components/form/other/modalSelect.d.ts +44 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/form/index.tsx +3 -3
- package/src/components/form/other/modalSelect.tsx +395 -0
- package/src/components/form/other/multipleSelect.tsx +5 -5
- package/src/components/form/other/select.tsx +5 -5
- package/src/components/form/other/autocomplete.tsx +0 -217
package/package.json
CHANGED
@@ -21,7 +21,7 @@ import {
|
|
21
21
|
MultipleSelect,
|
22
22
|
type MultipleSelectProps,
|
23
23
|
} from "./other/multipleSelect";
|
24
|
-
import {
|
24
|
+
import { ModalSelect, type ModalSelectProps } from "./other/modalSelect";
|
25
25
|
import { Rating, type RatingProps } from "./other/rating";
|
26
26
|
import { Emoji, type EmojiProps } from "./other/emoji";
|
27
27
|
|
@@ -48,7 +48,7 @@ interface FormComponent extends React.FC<FormProps> {
|
|
48
48
|
Textarea: React.FC<TextareaProps>;
|
49
49
|
Select: React.FC<SelectProps>;
|
50
50
|
MultipleSelect: React.FC<MultipleSelectProps>;
|
51
|
-
|
51
|
+
ModalSelect: React.FC<ModalSelectProps>;
|
52
52
|
Rating: React.FC<RatingProps>;
|
53
53
|
Emoji: React.FC<EmojiProps>;
|
54
54
|
}
|
@@ -85,6 +85,6 @@ Form.Pin = Pin;
|
|
85
85
|
Form.Textarea = Textarea;
|
86
86
|
Form.Select = Select;
|
87
87
|
Form.MultipleSelect = MultipleSelect;
|
88
|
-
Form.
|
88
|
+
Form.ModalSelect = ModalSelect;
|
89
89
|
Form.Rating = Rating;
|
90
90
|
Form.Emoji = Emoji;
|
@@ -0,0 +1,395 @@
|
|
1
|
+
"use client";
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
3
|
+
import { Modal, Form, Button } from "../../../components";
|
4
|
+
import { Icon } from "@iconify/react";
|
5
|
+
import { useDebouncedCallback } from "use-debounce";
|
6
|
+
|
7
|
+
export interface ModalSelectProps {
|
8
|
+
type: "select" | "multipleSelect";
|
9
|
+
data: {
|
10
|
+
searchableColumns?: { label: string; value: string }[];
|
11
|
+
menus: {
|
12
|
+
label: string;
|
13
|
+
subLabel?: string;
|
14
|
+
value: string;
|
15
|
+
disabled?: boolean;
|
16
|
+
[key: string]: any;
|
17
|
+
}[];
|
18
|
+
};
|
19
|
+
label?: string;
|
20
|
+
placeholder?: string;
|
21
|
+
description?: string;
|
22
|
+
max?: number;
|
23
|
+
// labelComponent?: (e?: any) => React.ReactNode;
|
24
|
+
options?: {
|
25
|
+
width?: "full" | "fit";
|
26
|
+
height?: "short" | "medium" | "high";
|
27
|
+
};
|
28
|
+
disabled?: boolean;
|
29
|
+
required?: boolean;
|
30
|
+
loading?: boolean;
|
31
|
+
value?: string | string[];
|
32
|
+
onChange?: (e: {
|
33
|
+
target: {
|
34
|
+
value: any;
|
35
|
+
};
|
36
|
+
}) => void;
|
37
|
+
dynamicSelect?: {
|
38
|
+
getValue?: (value: { filter: any[]; maxRow: number }) => void;
|
39
|
+
setValue?: {
|
40
|
+
totalData?: number;
|
41
|
+
};
|
42
|
+
};
|
43
|
+
}
|
44
|
+
|
45
|
+
export const ModalSelect: React.FC<ModalSelectProps> = ({
|
46
|
+
type,
|
47
|
+
data,
|
48
|
+
label,
|
49
|
+
placeholder,
|
50
|
+
description,
|
51
|
+
max,
|
52
|
+
// labelComponent,
|
53
|
+
options,
|
54
|
+
disabled,
|
55
|
+
required,
|
56
|
+
loading,
|
57
|
+
value,
|
58
|
+
onChange,
|
59
|
+
dynamicSelect,
|
60
|
+
}) => {
|
61
|
+
const optionContainerRef = useRef<HTMLDivElement>(null);
|
62
|
+
const [selectedMenuHistory, setSelectedMenuHistory] = useState<any>([]);
|
63
|
+
const [tempValue, setTempValue] = useState<any>();
|
64
|
+
const [openModal, setOpenModal] = useState<boolean>(false);
|
65
|
+
const [searchBy, setSearchBy] = useState<string>(
|
66
|
+
data.searchableColumns ? data.searchableColumns[0].value : ""
|
67
|
+
);
|
68
|
+
const [maxData, setMaxData] = useState<number>(20);
|
69
|
+
const [search, setSearch] = useState<string>("");
|
70
|
+
const [delayedSearch, setDelayedSearch] = useState<string>("");
|
71
|
+
|
72
|
+
useEffect(() => {
|
73
|
+
if (value) {
|
74
|
+
setTempValue(value);
|
75
|
+
}
|
76
|
+
}, [value]);
|
77
|
+
|
78
|
+
useEffect(() => {
|
79
|
+
if (!loading) {
|
80
|
+
if (openModal) {
|
81
|
+
const container = optionContainerRef.current;
|
82
|
+
|
83
|
+
if (container) {
|
84
|
+
const handleScroll = () => {
|
85
|
+
if (
|
86
|
+
container.scrollTop + container.offsetHeight - 3 >=
|
87
|
+
container.scrollHeight
|
88
|
+
) {
|
89
|
+
if (
|
90
|
+
dynamicSelect?.setValue?.totalData &&
|
91
|
+
data.menus.length !== dynamicSelect?.setValue?.totalData
|
92
|
+
) {
|
93
|
+
setMaxData((prev: any) => prev + 20);
|
94
|
+
if (dynamicSelect?.getValue) {
|
95
|
+
dynamicSelect?.getValue({
|
96
|
+
filter: [{ key: searchBy, value: delayedSearch }],
|
97
|
+
maxRow: maxData + 20,
|
98
|
+
});
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
};
|
103
|
+
|
104
|
+
container.addEventListener("scroll", handleScroll);
|
105
|
+
|
106
|
+
return () => {
|
107
|
+
container.removeEventListener("scroll", handleScroll);
|
108
|
+
};
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}, [
|
113
|
+
optionContainerRef,
|
114
|
+
loading,
|
115
|
+
data.menus,
|
116
|
+
openModal,
|
117
|
+
searchBy,
|
118
|
+
delayedSearch,
|
119
|
+
maxData,
|
120
|
+
dynamicSelect?.setValue?.totalData,
|
121
|
+
]);
|
122
|
+
|
123
|
+
useEffect(() => {
|
124
|
+
if (dynamicSelect?.getValue) {
|
125
|
+
dynamicSelect.getValue({
|
126
|
+
filter: [{ key: searchBy, value: delayedSearch }],
|
127
|
+
maxRow: maxData,
|
128
|
+
});
|
129
|
+
}
|
130
|
+
}, [delayedSearch]);
|
131
|
+
|
132
|
+
const debouncedUpdate = useDebouncedCallback((value: string) => {
|
133
|
+
setDelayedSearch(value);
|
134
|
+
}, 1250);
|
135
|
+
|
136
|
+
return (
|
137
|
+
<>
|
138
|
+
<div
|
139
|
+
className={`${options?.width === "fit" ? "w-fit" : "w-full"}`}
|
140
|
+
onClick={() => {
|
141
|
+
if (!disabled) {
|
142
|
+
setOpenModal(true);
|
143
|
+
}
|
144
|
+
}}
|
145
|
+
>
|
146
|
+
{type === "select" ? (
|
147
|
+
<Form.Select
|
148
|
+
menus={
|
149
|
+
dynamicSelect?.getValue
|
150
|
+
? [
|
151
|
+
...selectedMenuHistory,
|
152
|
+
...data.menus.filter(
|
153
|
+
(prev: any) =>
|
154
|
+
!selectedMenuHistory
|
155
|
+
.map((historyPrev: any) => historyPrev.value)
|
156
|
+
.includes(prev.value)
|
157
|
+
),
|
158
|
+
]
|
159
|
+
: data.menus
|
160
|
+
}
|
161
|
+
label={label}
|
162
|
+
placeholder={placeholder}
|
163
|
+
description={description}
|
164
|
+
options={options}
|
165
|
+
disabled={disabled}
|
166
|
+
required={required}
|
167
|
+
value={tempValue || ""}
|
168
|
+
readOnly
|
169
|
+
/>
|
170
|
+
) : (
|
171
|
+
<Form.MultipleSelect
|
172
|
+
menus={
|
173
|
+
dynamicSelect?.getValue
|
174
|
+
? [
|
175
|
+
...selectedMenuHistory,
|
176
|
+
...data.menus.filter(
|
177
|
+
(prev: any) =>
|
178
|
+
!selectedMenuHistory
|
179
|
+
.map((historyPrev: any) => historyPrev.value)
|
180
|
+
.includes(prev.value)
|
181
|
+
),
|
182
|
+
]
|
183
|
+
: data.menus
|
184
|
+
}
|
185
|
+
label={label}
|
186
|
+
placeholder={placeholder}
|
187
|
+
description={description}
|
188
|
+
options={options}
|
189
|
+
disabled={disabled}
|
190
|
+
required={required}
|
191
|
+
value={tempValue || []}
|
192
|
+
readOnly
|
193
|
+
/>
|
194
|
+
)}
|
195
|
+
</div>
|
196
|
+
<Modal
|
197
|
+
title={label || ""}
|
198
|
+
open={openModal}
|
199
|
+
onClose={() => {
|
200
|
+
setSearch("");
|
201
|
+
setOpenModal(false);
|
202
|
+
if (dynamicSelect?.getValue) {
|
203
|
+
setDelayedSearch("");
|
204
|
+
}
|
205
|
+
}}
|
206
|
+
>
|
207
|
+
<div className="flex flex-col gap-4 w-full h-full overflow-hidden">
|
208
|
+
<div className="flex items-end gap-4">
|
209
|
+
{data.searchableColumns && (
|
210
|
+
<div className="w-36">
|
211
|
+
<Form.Select
|
212
|
+
menus={data.searchableColumns || []}
|
213
|
+
placeholder="Search By"
|
214
|
+
value={searchBy}
|
215
|
+
onChange={(e) => {
|
216
|
+
setSearchBy(e.target.value);
|
217
|
+
setSearch("");
|
218
|
+
setDelayedSearch("");
|
219
|
+
if (dynamicSelect?.getValue) {
|
220
|
+
dynamicSelect.getValue({
|
221
|
+
filter: [{ key: e.target.value, value: "" }],
|
222
|
+
maxRow: maxData,
|
223
|
+
});
|
224
|
+
}
|
225
|
+
}}
|
226
|
+
/>
|
227
|
+
</div>
|
228
|
+
)}
|
229
|
+
<div className="flex-1">
|
230
|
+
<Form.Search
|
231
|
+
placeholder="Search..."
|
232
|
+
disabled={data.searchableColumns && !searchBy}
|
233
|
+
value={search || ""}
|
234
|
+
onChange={(e) => {
|
235
|
+
setSearch(e.target.value);
|
236
|
+
debouncedUpdate(e.target.value);
|
237
|
+
}}
|
238
|
+
/>
|
239
|
+
</div>
|
240
|
+
</div>
|
241
|
+
<div
|
242
|
+
ref={optionContainerRef}
|
243
|
+
className="relative flex-1 flex flex-col border rounded-md overflow-auto"
|
244
|
+
>
|
245
|
+
{(dynamicSelect?.getValue
|
246
|
+
? delayedSearch
|
247
|
+
? data.menus
|
248
|
+
: [
|
249
|
+
...selectedMenuHistory,
|
250
|
+
...data.menus.filter(
|
251
|
+
(prev: any) =>
|
252
|
+
!selectedMenuHistory
|
253
|
+
.map((historyPrev: any) => historyPrev.value)
|
254
|
+
.includes(prev.value)
|
255
|
+
),
|
256
|
+
]
|
257
|
+
: data.menus.filter((optionItem: any) =>
|
258
|
+
optionItem.label
|
259
|
+
.toLowerCase()
|
260
|
+
.includes(search.toLocaleLowerCase())
|
261
|
+
)
|
262
|
+
)?.length === 0 ? (
|
263
|
+
<div className="flex-1 flex justify-center items-center">
|
264
|
+
<span>No data found</span>
|
265
|
+
</div>
|
266
|
+
) : (
|
267
|
+
(dynamicSelect?.getValue
|
268
|
+
? delayedSearch
|
269
|
+
? data.menus
|
270
|
+
: [
|
271
|
+
...selectedMenuHistory,
|
272
|
+
...data.menus.filter(
|
273
|
+
(prev: any) =>
|
274
|
+
!selectedMenuHistory
|
275
|
+
.map((historyPrev: any) => historyPrev.value)
|
276
|
+
.includes(prev.value)
|
277
|
+
),
|
278
|
+
]
|
279
|
+
: data.menus.filter((optionItem: any) =>
|
280
|
+
optionItem.label
|
281
|
+
.toLowerCase()
|
282
|
+
.includes(search.toLocaleLowerCase())
|
283
|
+
)
|
284
|
+
).map((optionItem: any, index: number) => {
|
285
|
+
return (
|
286
|
+
<Button
|
287
|
+
key={index}
|
288
|
+
type="button"
|
289
|
+
className="disabled:bg-primary-transparent"
|
290
|
+
disabled={
|
291
|
+
type === "select" && tempValue === optionItem.value
|
292
|
+
}
|
293
|
+
onClick={() => {
|
294
|
+
if (type === "select") {
|
295
|
+
setTempValue(optionItem.value);
|
296
|
+
setOpenModal(false);
|
297
|
+
setSearch("");
|
298
|
+
if (dynamicSelect?.getValue) {
|
299
|
+
setSelectedMenuHistory([optionItem]);
|
300
|
+
setDelayedSearch("");
|
301
|
+
}
|
302
|
+
if (onChange) {
|
303
|
+
onChange({
|
304
|
+
target: {
|
305
|
+
value: optionItem.value,
|
306
|
+
} as any,
|
307
|
+
} as any);
|
308
|
+
}
|
309
|
+
} else {
|
310
|
+
if (tempValue?.includes(optionItem.value)) {
|
311
|
+
setTempValue((prev: any) =>
|
312
|
+
prev.filter(
|
313
|
+
(prevItem: any) => prevItem !== optionItem.value
|
314
|
+
)
|
315
|
+
);
|
316
|
+
if (dynamicSelect?.getValue) {
|
317
|
+
setSelectedMenuHistory((prev: any) =>
|
318
|
+
prev.filter(
|
319
|
+
(prevItem: any) =>
|
320
|
+
prevItem.value !== optionItem.value
|
321
|
+
)
|
322
|
+
);
|
323
|
+
}
|
324
|
+
if (onChange) {
|
325
|
+
onChange({
|
326
|
+
target: {
|
327
|
+
value: tempValue.filter(
|
328
|
+
(prevItem: any) =>
|
329
|
+
prevItem !== optionItem.value
|
330
|
+
),
|
331
|
+
} as any,
|
332
|
+
} as any);
|
333
|
+
}
|
334
|
+
} else {
|
335
|
+
if (!max || tempValue.length < max) {
|
336
|
+
setTempValue((prev: any) => [
|
337
|
+
...prev,
|
338
|
+
optionItem.value,
|
339
|
+
]);
|
340
|
+
if (dynamicSelect?.getValue) {
|
341
|
+
setSelectedMenuHistory((prev: any) => [
|
342
|
+
...prev,
|
343
|
+
optionItem,
|
344
|
+
]);
|
345
|
+
}
|
346
|
+
if (onChange) {
|
347
|
+
onChange({
|
348
|
+
target: {
|
349
|
+
value: [...tempValue, optionItem.value],
|
350
|
+
} as any,
|
351
|
+
} as any);
|
352
|
+
}
|
353
|
+
}
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}}
|
357
|
+
>
|
358
|
+
{type === "select" ? (
|
359
|
+
<div className="flex flex-col">
|
360
|
+
<span className="text-sm">{optionItem.label}</span>
|
361
|
+
<span className="text-xs">{optionItem.subLabel}</span>
|
362
|
+
</div>
|
363
|
+
) : (
|
364
|
+
<div className="flex gap-4 pointer-events-none">
|
365
|
+
<Form.Checkbox
|
366
|
+
options={{ disableHover: true }}
|
367
|
+
checked={
|
368
|
+
tempValue?.includes(optionItem.value) ?? false
|
369
|
+
}
|
370
|
+
readOnly
|
371
|
+
/>
|
372
|
+
<div className="flex flex-col">
|
373
|
+
<span className="text-sm">{optionItem.label}</span>
|
374
|
+
<span className="text-xs">{optionItem.subLabel}</span>
|
375
|
+
</div>
|
376
|
+
</div>
|
377
|
+
)}
|
378
|
+
</Button>
|
379
|
+
);
|
380
|
+
})
|
381
|
+
)}
|
382
|
+
{loading && (
|
383
|
+
<div className="absolute left-0 top-0 flex justify-center items-center w-full h-full backdrop-blur-sm">
|
384
|
+
<Icon
|
385
|
+
icon="mingcute:loading-fill"
|
386
|
+
className="text-xl text-primary animate-spin"
|
387
|
+
/>
|
388
|
+
</div>
|
389
|
+
)}
|
390
|
+
</div>
|
391
|
+
</div>
|
392
|
+
</Modal>
|
393
|
+
</>
|
394
|
+
);
|
395
|
+
};
|
@@ -179,12 +179,12 @@ export const MultipleSelect: React.FC<MultipleSelectProps> = ({
|
|
179
179
|
value={tempValue.join(", ")}
|
180
180
|
onChange={(e) => {}}
|
181
181
|
onClick={(e) => {
|
182
|
-
e.preventDefault();
|
183
|
-
dropdownTriggerRef.current?.click();
|
184
|
-
setDropdownWidth(
|
185
|
-
inputRef?.current?.getBoundingClientRect()?.width || 0
|
186
|
-
);
|
187
182
|
if (!readOnly) {
|
183
|
+
e.preventDefault();
|
184
|
+
dropdownTriggerRef.current?.click();
|
185
|
+
setDropdownWidth(
|
186
|
+
inputRef?.current?.getBoundingClientRect()?.width || 0
|
187
|
+
);
|
188
188
|
setOpenDropdown(true);
|
189
189
|
}
|
190
190
|
}}
|
@@ -116,12 +116,12 @@ export const Select: React.FC<SelectProps> = ({
|
|
116
116
|
}
|
117
117
|
onChange={(e) => {}}
|
118
118
|
onClick={(e) => {
|
119
|
-
e.preventDefault();
|
120
|
-
dropdownTriggerRef.current?.click();
|
121
|
-
setDropdownWidth(
|
122
|
-
inputRef?.current?.getBoundingClientRect()?.width || 0
|
123
|
-
);
|
124
119
|
if (!readOnly) {
|
120
|
+
e.preventDefault();
|
121
|
+
dropdownTriggerRef.current?.click();
|
122
|
+
setDropdownWidth(
|
123
|
+
inputRef?.current?.getBoundingClientRect()?.width || 0
|
124
|
+
);
|
125
125
|
setOpenDropdown(true);
|
126
126
|
}
|
127
127
|
}}
|
@@ -1,217 +0,0 @@
|
|
1
|
-
"use client";
|
2
|
-
import React, { useState, useEffect, useRef } from "react";
|
3
|
-
import { Tooltip } from "../../../components";
|
4
|
-
import { createPortal } from "react-dom";
|
5
|
-
import { Icon } from "@iconify/react";
|
6
|
-
|
7
|
-
export interface AutocompleteProps
|
8
|
-
extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
9
|
-
menus: {
|
10
|
-
label: string;
|
11
|
-
value: string;
|
12
|
-
[key: string]: any;
|
13
|
-
}[];
|
14
|
-
label?: string;
|
15
|
-
placeholder?: string;
|
16
|
-
description?: string;
|
17
|
-
options?: {
|
18
|
-
width?: "full" | "fit";
|
19
|
-
height?: "short" | "medium" | "high";
|
20
|
-
};
|
21
|
-
}
|
22
|
-
|
23
|
-
export const Autocomplete: React.FC<AutocompleteProps> = ({
|
24
|
-
menus,
|
25
|
-
label,
|
26
|
-
placeholder,
|
27
|
-
description,
|
28
|
-
options,
|
29
|
-
...rest
|
30
|
-
}) => {
|
31
|
-
const [tempValue, setTempValue] = useState("");
|
32
|
-
const [tempFilter, setTempFilter] = useState("");
|
33
|
-
const [openDropdown, setOpenDropdown] = useState(false);
|
34
|
-
const [position, setPosition] = useState<{
|
35
|
-
top: number;
|
36
|
-
left: number;
|
37
|
-
} | null>(null);
|
38
|
-
const [dropdownWidth, setDropdownWidth] = useState<number>(0);
|
39
|
-
const triggerRef = useRef<HTMLDivElement>(null);
|
40
|
-
const dropdownRef = useRef<HTMLDivElement>(null);
|
41
|
-
const width = options?.width === "fit" ? "w-fit" : "w-full";
|
42
|
-
const height =
|
43
|
-
options?.height === "short"
|
44
|
-
? "py-1"
|
45
|
-
: options?.height === "high"
|
46
|
-
? "py-2"
|
47
|
-
: "py-1.5";
|
48
|
-
|
49
|
-
useEffect(() => {
|
50
|
-
const handleClickOutside = (e: MouseEvent) => {
|
51
|
-
if (
|
52
|
-
dropdownRef.current &&
|
53
|
-
!dropdownRef.current.contains(e.target as Node) &&
|
54
|
-
!triggerRef.current?.contains(e.target as Node)
|
55
|
-
) {
|
56
|
-
setOpenDropdown(false);
|
57
|
-
}
|
58
|
-
};
|
59
|
-
|
60
|
-
document.addEventListener("mousedown", handleClickOutside);
|
61
|
-
return () => {
|
62
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
63
|
-
};
|
64
|
-
}, []);
|
65
|
-
|
66
|
-
useEffect(() => {
|
67
|
-
if (triggerRef.current) {
|
68
|
-
const rect = triggerRef.current.getBoundingClientRect();
|
69
|
-
const dropdownHeight = dropdownRef.current?.offsetHeight || 0;
|
70
|
-
const windowHeight = window.innerHeight;
|
71
|
-
|
72
|
-
setPosition({
|
73
|
-
top: rect.bottom + window.scrollY + 10,
|
74
|
-
left: rect.left + window.scrollX,
|
75
|
-
});
|
76
|
-
|
77
|
-
setDropdownWidth(rect.width);
|
78
|
-
|
79
|
-
if (rect.bottom + dropdownHeight > windowHeight) {
|
80
|
-
setPosition((prev) =>
|
81
|
-
prev
|
82
|
-
? { ...prev, top: rect.top + window.scrollY - dropdownHeight - 10 }
|
83
|
-
: null
|
84
|
-
);
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
if (openDropdown) {
|
89
|
-
document.getElementById("body")!.style.overflow = "hidden";
|
90
|
-
} else {
|
91
|
-
document.getElementById("body")!.style.overflow = "auto";
|
92
|
-
}
|
93
|
-
}, [openDropdown, tempValue]);
|
94
|
-
|
95
|
-
useEffect(() => {
|
96
|
-
if (rest.value || rest.value === "") {
|
97
|
-
setTempValue(rest.value as string);
|
98
|
-
setTempFilter(
|
99
|
-
menus.find((item) => item.value === rest.value)?.label as string
|
100
|
-
);
|
101
|
-
return;
|
102
|
-
} else if (rest.defaultValue || rest.defaultValue === "") {
|
103
|
-
setTempValue(rest.defaultValue as string);
|
104
|
-
setTempFilter(
|
105
|
-
menus.find((item) => item.value === rest.defaultValue)?.label as string
|
106
|
-
);
|
107
|
-
return;
|
108
|
-
}
|
109
|
-
}, [rest.value, rest.defaultValue]);
|
110
|
-
|
111
|
-
return (
|
112
|
-
<label className={`flex flex-col gap-2 ${width}`}>
|
113
|
-
{(label || description) && (
|
114
|
-
<div className="flex justify-between items-center gap-2">
|
115
|
-
{label && (
|
116
|
-
<span
|
117
|
-
className={`text-sm select-none ${
|
118
|
-
rest.required &&
|
119
|
-
"after:content-['*'] after:ml-1 after:text-danger"
|
120
|
-
}`}
|
121
|
-
>
|
122
|
-
{label}
|
123
|
-
</span>
|
124
|
-
)}
|
125
|
-
{description && (
|
126
|
-
<Tooltip content={description}>
|
127
|
-
<Icon
|
128
|
-
icon="octicon:info-16"
|
129
|
-
className="text-sm text-primary-dark"
|
130
|
-
/>
|
131
|
-
</Tooltip>
|
132
|
-
)}
|
133
|
-
</div>
|
134
|
-
)}
|
135
|
-
<div className="relative" ref={triggerRef}>
|
136
|
-
<div
|
137
|
-
className="relative flex items-center cursor-pointer"
|
138
|
-
onClick={() => setOpenDropdown(!openDropdown)}
|
139
|
-
>
|
140
|
-
<input
|
141
|
-
type="text"
|
142
|
-
className={`w-full ps-4 pe-14 border-default border rounded-md bg-secondary-bg placeholder:duration-300 placeholder:translate-x-0 focus:placeholder:translate-x-1 placeholder:text-silent focus:outline-none focus:ring-1 focus:ring-primary focus:shadow focus:shadow-primary focus:border-primary-dark disabled:bg-secondary-light disabled:text-disabled ${height}`}
|
143
|
-
placeholder={placeholder}
|
144
|
-
required={rest.required}
|
145
|
-
disabled={rest.disabled}
|
146
|
-
value={tempFilter || ""}
|
147
|
-
onChange={(e) => {
|
148
|
-
setTempFilter(e.target.value);
|
149
|
-
}}
|
150
|
-
onClick={() => {
|
151
|
-
setOpenDropdown(true);
|
152
|
-
setTempValue("");
|
153
|
-
setTempFilter("");
|
154
|
-
}}
|
155
|
-
/>
|
156
|
-
<div className="absolute right-4 text-xl text-disabled pointer-events-none">
|
157
|
-
<Icon icon={`gravity-ui:chevron-${openDropdown ? "up" : "down"}`} />
|
158
|
-
</div>
|
159
|
-
</div>
|
160
|
-
{openDropdown &&
|
161
|
-
position &&
|
162
|
-
createPortal(
|
163
|
-
<div
|
164
|
-
ref={dropdownRef}
|
165
|
-
className="absolute max-h-40 p-1 z-50 pointer-events-auto bg-secondary-bg shadow border rounded-md overflow-auto"
|
166
|
-
style={{
|
167
|
-
top: position.top,
|
168
|
-
left: position.left,
|
169
|
-
width: dropdownWidth,
|
170
|
-
}}
|
171
|
-
>
|
172
|
-
{menus.filter((item) =>
|
173
|
-
item.label.toLowerCase().includes(tempFilter.toLowerCase())
|
174
|
-
).length === 0 ? (
|
175
|
-
<div className="flex justify-center">
|
176
|
-
<span className="px-4 py-1">No data found</span>
|
177
|
-
</div>
|
178
|
-
) : (
|
179
|
-
menus
|
180
|
-
.filter((item) =>
|
181
|
-
item.label.toLowerCase().includes(tempFilter.toLowerCase())
|
182
|
-
)
|
183
|
-
.map((item, index) => (
|
184
|
-
<button
|
185
|
-
key={index}
|
186
|
-
type="button"
|
187
|
-
className="min-w-40 w-full my-0.5 px-4 py-2 rounded-md text-sm text-left text-default hover:bg-secondary-light disabled:bg-primary-transparent"
|
188
|
-
disabled={tempValue === item.value}
|
189
|
-
onMouseDown={() => {
|
190
|
-
setTempValue(item.value);
|
191
|
-
if (rest.onChange) {
|
192
|
-
rest.onChange({
|
193
|
-
target: { value: item.value } as HTMLSelectElement,
|
194
|
-
} as any);
|
195
|
-
}
|
196
|
-
setTempFilter(item.label);
|
197
|
-
setOpenDropdown(false);
|
198
|
-
}}
|
199
|
-
>
|
200
|
-
{item.label}
|
201
|
-
</button>
|
202
|
-
))
|
203
|
-
)}
|
204
|
-
</div>,
|
205
|
-
document.body
|
206
|
-
)}
|
207
|
-
</div>
|
208
|
-
<select className="hidden" {...rest}>
|
209
|
-
{menus.map((item, index) => (
|
210
|
-
<option key={index} value={item.value}>
|
211
|
-
{item.label}
|
212
|
-
</option>
|
213
|
-
))}
|
214
|
-
</select>
|
215
|
-
</label>
|
216
|
-
);
|
217
|
-
};
|