@monolith-forensics/monolith-ui 1.1.0 → 1.1.2
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/package.json +4 -1
- package/Button/Button.tsx +0 -382
- package/Button/index.ts +0 -2
- package/Calendar/Calendar.tsx +0 -376
- package/Calendar/CalendarStyles.tsx +0 -180
- package/Calendar/calendarHelpers.ts +0 -196
- package/Calendar/index.ts +0 -1
- package/CheckBox/CheckBox.tsx +0 -61
- package/CheckBox/index.ts +0 -1
- package/DateInput/DateInput.tsx +0 -717
- package/DateInput/index.ts +0 -1
- package/DropDownMenu/DropDownMenu.tsx +0 -402
- package/DropDownMenu/index.ts +0 -1
- package/Error/Error.tsx +0 -51
- package/Error/index.ts +0 -1
- package/FieldLabel/FieldLabel.tsx +0 -155
- package/FieldLabel/index.ts +0 -1
- package/FileInputField/FileInputField.tsx +0 -179
- package/FileInputField/index.ts +0 -1
- package/Flyout/Flyout.tsx +0 -172
- package/Flyout/FlyoutHeader.tsx +0 -14
- package/Flyout/FlyoutTitle.tsx +0 -10
- package/Flyout/index.ts +0 -3
- package/FormSection/FormSection.tsx +0 -71
- package/FormSection/index.ts +0 -1
- package/Grid/Grid.tsx +0 -18
- package/Grid/index.ts +0 -1
- package/IconButton/IconButton.tsx +0 -27
- package/IconButton/index.ts +0 -1
- package/Input/Input.tsx +0 -164
- package/Input/index.ts +0 -1
- package/Modal/Modal.tsx +0 -172
- package/Modal/index.ts +0 -1
- package/Pill/Pill.tsx +0 -174
- package/Pill/index.ts +0 -1
- package/SelectBox/SelectBox.tsx +0 -745
- package/SelectBox/index.ts +0 -1
- package/Switch/Switch.tsx +0 -204
- package/Switch/index.ts +0 -1
- package/TagBox/TagBox.tsx +0 -694
- package/TagBox/TagBoxStyles.tsx +0 -116
- package/TagBox/index.ts +0 -1
- package/TextArea/TextArea.tsx +0 -92
- package/TextArea/index.ts +0 -1
- package/TextAreaInput/TextAreaInput.tsx +0 -46
- package/TextAreaInput/index.ts +0 -1
- package/TextInput/TextInput.tsx +0 -48
- package/TextInput/index.ts +0 -1
- package/Tooltip/Tooltip.tsx +0 -68
- package/Tooltip/index.ts +0 -1
- package/core/ArrowButton.tsx +0 -51
- package/core/ClearButton.tsx +0 -51
- package/core/MonolithThemeProvider.js +0 -16
- package/core/StyledContent.tsx +0 -50
- package/core/StyledFloatContainer.tsx +0 -7
- package/core/Types/Size.ts +0 -3
- package/core/Types/Variant.ts +0 -10
- package/core/index.ts +0 -6
- package/index.ts +0 -22
- package/theme/breakpoints.js +0 -11
- package/theme/components.js +0 -138
- package/theme/index.js +0 -76
- package/theme/shadows.js +0 -33
- package/theme/typography.js +0 -58
- package/theme/variants.js +0 -234
- package/tsconfig.json +0 -109
package/SelectBox/SelectBox.tsx
DELETED
|
@@ -1,745 +0,0 @@
|
|
|
1
|
-
import styled from "styled-components";
|
|
2
|
-
import {
|
|
3
|
-
useFloating,
|
|
4
|
-
flip,
|
|
5
|
-
offset,
|
|
6
|
-
FloatingPortal,
|
|
7
|
-
Placement,
|
|
8
|
-
} from "@floating-ui/react";
|
|
9
|
-
import {
|
|
10
|
-
MouseEvent,
|
|
11
|
-
MutableRefObject,
|
|
12
|
-
ReactNode,
|
|
13
|
-
useCallback,
|
|
14
|
-
useEffect,
|
|
15
|
-
useRef,
|
|
16
|
-
useState,
|
|
17
|
-
} from "react";
|
|
18
|
-
import { Input, FieldLabel, Tooltip } from "..";
|
|
19
|
-
import { useDebouncedCallback } from "use-debounce";
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
StyledContent,
|
|
23
|
-
StyledFloatContainer,
|
|
24
|
-
ArrowButton,
|
|
25
|
-
ClearButton,
|
|
26
|
-
Size,
|
|
27
|
-
Variant,
|
|
28
|
-
} from "../core";
|
|
29
|
-
|
|
30
|
-
export const StyledInputContainer = styled.div<{
|
|
31
|
-
width?: string | number | null;
|
|
32
|
-
}>`
|
|
33
|
-
font-family: ${({ theme }) => theme.typography.fontFamily};
|
|
34
|
-
|
|
35
|
-
position: relative;
|
|
36
|
-
display: flex;
|
|
37
|
-
flex-direction: row;
|
|
38
|
-
border: none;
|
|
39
|
-
outline: none;
|
|
40
|
-
height: fit-content;
|
|
41
|
-
|
|
42
|
-
width: ${({ width }) => {
|
|
43
|
-
if (typeof width === "undefined") return "100%";
|
|
44
|
-
if (width === null) return "100%";
|
|
45
|
-
if (typeof width === "string") return width;
|
|
46
|
-
if (typeof width === "number") return `${width}px`;
|
|
47
|
-
}};
|
|
48
|
-
`;
|
|
49
|
-
|
|
50
|
-
const StyledInnerItemContainer = styled.div`
|
|
51
|
-
overflow-y: auto;
|
|
52
|
-
|
|
53
|
-
&[data-scroll-active="true"] {
|
|
54
|
-
padding-right: 5px;
|
|
55
|
-
}
|
|
56
|
-
`;
|
|
57
|
-
|
|
58
|
-
interface GroupTitleProps {
|
|
59
|
-
className?: string;
|
|
60
|
-
children?: ReactNode;
|
|
61
|
-
size?: Size;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const GroupTitle = styled(
|
|
65
|
-
({ className, children, ...props }: GroupTitleProps) => {
|
|
66
|
-
return (
|
|
67
|
-
<div className={className} {...props}>
|
|
68
|
-
<div className="group-line"></div>
|
|
69
|
-
<div className="group-label">{children}</div>
|
|
70
|
-
<div className="group-line"></div>
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
)`
|
|
75
|
-
display: flex;
|
|
76
|
-
flex-direction: row;
|
|
77
|
-
align-items: center;
|
|
78
|
-
justify-content: space-between;
|
|
79
|
-
gap: 10px;
|
|
80
|
-
|
|
81
|
-
color: ${(props) => props.theme.palette.text.secondary};
|
|
82
|
-
|
|
83
|
-
padding: ${({ size }) =>
|
|
84
|
-
size === "xs"
|
|
85
|
-
? "2px 8px"
|
|
86
|
-
: size === "sm"
|
|
87
|
-
? "4px 10px"
|
|
88
|
-
: size === "md"
|
|
89
|
-
? "4px 12px"
|
|
90
|
-
: size === "lg"
|
|
91
|
-
? "5px 14px"
|
|
92
|
-
: size === "xl"
|
|
93
|
-
? "6px 16px"
|
|
94
|
-
: "2px 8px"};
|
|
95
|
-
|
|
96
|
-
.group-label {
|
|
97
|
-
white-space: nowrap;
|
|
98
|
-
overflow: hidden;
|
|
99
|
-
text-overflow: ellipsis;
|
|
100
|
-
width: fit-content;
|
|
101
|
-
min-width: fit-content;
|
|
102
|
-
|
|
103
|
-
font-weight: 500;
|
|
104
|
-
|
|
105
|
-
font-size: ${({ size }) =>
|
|
106
|
-
size === "xs"
|
|
107
|
-
? "11px"
|
|
108
|
-
: size === "sm"
|
|
109
|
-
? "13px"
|
|
110
|
-
: size === "md"
|
|
111
|
-
? "15px"
|
|
112
|
-
: size === "lg"
|
|
113
|
-
? "17px"
|
|
114
|
-
: size === "xl"
|
|
115
|
-
? "19px"
|
|
116
|
-
: "11px"};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.group-line {
|
|
120
|
-
border-top: 1px solid ${(props) => props.theme.palette.divider};
|
|
121
|
-
width: 100%;
|
|
122
|
-
}
|
|
123
|
-
`;
|
|
124
|
-
|
|
125
|
-
const ActionMenu = styled.div``;
|
|
126
|
-
|
|
127
|
-
const StyledItem = styled.div<{
|
|
128
|
-
size: Size;
|
|
129
|
-
}>`
|
|
130
|
-
color: ${(props) => props.theme.palette.text.primary};
|
|
131
|
-
border-radius: 3px;
|
|
132
|
-
display: flex;
|
|
133
|
-
align-items: center;
|
|
134
|
-
min-height: 25px;
|
|
135
|
-
padding: 7px 10px;
|
|
136
|
-
position: relative;
|
|
137
|
-
user-select: none;
|
|
138
|
-
outline: none;
|
|
139
|
-
|
|
140
|
-
cursor: pointer;
|
|
141
|
-
|
|
142
|
-
font-family: ${({ theme }) => theme.typography.fontFamily};
|
|
143
|
-
|
|
144
|
-
font-size: ${({ size }) =>
|
|
145
|
-
size === "xs"
|
|
146
|
-
? "11px"
|
|
147
|
-
: size === "sm"
|
|
148
|
-
? "13px"
|
|
149
|
-
: size === "md"
|
|
150
|
-
? "15px"
|
|
151
|
-
: size === "lg"
|
|
152
|
-
? "17px"
|
|
153
|
-
: size === "xl"
|
|
154
|
-
? "19px"
|
|
155
|
-
: "11px"};
|
|
156
|
-
|
|
157
|
-
padding: ${({ size }) =>
|
|
158
|
-
size === "xs"
|
|
159
|
-
? "2px 8px"
|
|
160
|
-
: size === "sm"
|
|
161
|
-
? "4px 10px"
|
|
162
|
-
: size === "md"
|
|
163
|
-
? "4px 12px"
|
|
164
|
-
: size === "lg"
|
|
165
|
-
? "5px 14px"
|
|
166
|
-
: size === "xl"
|
|
167
|
-
? "6px 16px"
|
|
168
|
-
: "2px 8px"};
|
|
169
|
-
|
|
170
|
-
&[data-disabled] {
|
|
171
|
-
color: ${(props) => props.theme.palette.text.secondary};
|
|
172
|
-
pointer-events: "none";
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
&:hover {
|
|
176
|
-
background-color: ${(props) => props.theme.palette.action.hover};
|
|
177
|
-
color: ${(props) => props.theme.palette.text.primary};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
&[data-selected="true"] {
|
|
181
|
-
background-color: ${(props) => props.theme.palette.divider};
|
|
182
|
-
color: ${(props) => props.theme.palette.text.primary};
|
|
183
|
-
}
|
|
184
|
-
`;
|
|
185
|
-
|
|
186
|
-
type Option = {
|
|
187
|
-
label: string;
|
|
188
|
-
value: any;
|
|
189
|
-
group?: string;
|
|
190
|
-
data: any;
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
interface SelectBoxProps {
|
|
194
|
-
className?: string;
|
|
195
|
-
defaultValue?: Option | string;
|
|
196
|
-
data?: Option[] | string[];
|
|
197
|
-
placeholder?: string;
|
|
198
|
-
arrow?: boolean;
|
|
199
|
-
onChange?: (value: any, item: any) => void;
|
|
200
|
-
onSearch?: (value: string) => void;
|
|
201
|
-
searchFn?: (value: string) => void;
|
|
202
|
-
onScroll?: (e: any) => void;
|
|
203
|
-
loading?: boolean;
|
|
204
|
-
onItemAdded?: (item: any) => void;
|
|
205
|
-
size?: Size;
|
|
206
|
-
variant?: Variant;
|
|
207
|
-
width?: string | number | null;
|
|
208
|
-
allowCustomValue?: boolean;
|
|
209
|
-
searchable?: boolean;
|
|
210
|
-
clearable?: boolean;
|
|
211
|
-
label?: string;
|
|
212
|
-
description?: string;
|
|
213
|
-
required?: boolean;
|
|
214
|
-
error?: string;
|
|
215
|
-
openOnFocus?: boolean;
|
|
216
|
-
renderOption?: (item: Option | string) => ReactNode;
|
|
217
|
-
actionComponent?: JSX.Element;
|
|
218
|
-
focused?: boolean;
|
|
219
|
-
grouped?: boolean;
|
|
220
|
-
TooltipContent?: React.FC<{ data: any }>;
|
|
221
|
-
DropDownProps?: any;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const SelectBox = styled(
|
|
225
|
-
({
|
|
226
|
-
className,
|
|
227
|
-
data = [],
|
|
228
|
-
placeholder = "Select...",
|
|
229
|
-
arrow = true,
|
|
230
|
-
onChange,
|
|
231
|
-
onSearch,
|
|
232
|
-
searchFn,
|
|
233
|
-
onScroll,
|
|
234
|
-
loading,
|
|
235
|
-
defaultValue,
|
|
236
|
-
onItemAdded,
|
|
237
|
-
size = "sm",
|
|
238
|
-
variant = "outlined",
|
|
239
|
-
width = "100%",
|
|
240
|
-
allowCustomValue = false,
|
|
241
|
-
searchable = false,
|
|
242
|
-
clearable = false,
|
|
243
|
-
label,
|
|
244
|
-
description,
|
|
245
|
-
required = false,
|
|
246
|
-
error,
|
|
247
|
-
openOnFocus = true,
|
|
248
|
-
renderOption,
|
|
249
|
-
actionComponent,
|
|
250
|
-
focused,
|
|
251
|
-
grouped,
|
|
252
|
-
TooltipContent,
|
|
253
|
-
DropDownProps = {},
|
|
254
|
-
}: SelectBoxProps) => {
|
|
255
|
-
const isObjectArray = Object.keys(data?.[0] || {})?.includes("label");
|
|
256
|
-
|
|
257
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
258
|
-
const [selected, setSelected] = useState<Option | string | null>(null);
|
|
259
|
-
const [searchValue, setSearchValue] = useState("");
|
|
260
|
-
const [customItems, setCustomItems] = useState<(Option | string)[]>([]);
|
|
261
|
-
const [placement, setPlacement] = useState<Placement>("bottom-start");
|
|
262
|
-
const [dropDownHeight, setDropDownHeight] = useState<number | null>(null);
|
|
263
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
264
|
-
const containerRef: MutableRefObject<HTMLElement | null> =
|
|
265
|
-
useRef<HTMLElement>(null);
|
|
266
|
-
const scrollContainerRef = useRef(null);
|
|
267
|
-
|
|
268
|
-
const filteredItems = data
|
|
269
|
-
.concat(customItems) // Add custom items to the list
|
|
270
|
-
.filter((item: Option | string) => {
|
|
271
|
-
const itemValue = (
|
|
272
|
-
isObjectArray ? (item as Option).label : item
|
|
273
|
-
) as string;
|
|
274
|
-
|
|
275
|
-
return itemValue.toLowerCase().includes(searchValue.toLowerCase());
|
|
276
|
-
})
|
|
277
|
-
.sort((a, b) => {
|
|
278
|
-
if (grouped) {
|
|
279
|
-
const aValue = (a as Option).group as string;
|
|
280
|
-
const bValue = (b as Option).group as string;
|
|
281
|
-
return aValue.localeCompare(bValue);
|
|
282
|
-
}
|
|
283
|
-
// return current sort order
|
|
284
|
-
return 0;
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
const groupedItems = grouped
|
|
288
|
-
? filteredItems.reduce((acc: any, item: any) => {
|
|
289
|
-
if (!item.group) {
|
|
290
|
-
item.group = "Other";
|
|
291
|
-
}
|
|
292
|
-
if (acc[item.group]) {
|
|
293
|
-
acc[item.group].push(item);
|
|
294
|
-
} else {
|
|
295
|
-
acc[item.group] = [item];
|
|
296
|
-
}
|
|
297
|
-
return acc;
|
|
298
|
-
}, {})
|
|
299
|
-
: {};
|
|
300
|
-
|
|
301
|
-
const groups = grouped
|
|
302
|
-
? Object.keys(groupedItems)
|
|
303
|
-
.map((group) => ({
|
|
304
|
-
label: group,
|
|
305
|
-
items: groupedItems[group],
|
|
306
|
-
}))
|
|
307
|
-
.sort((a, b) => a.label.localeCompare(b.label))
|
|
308
|
-
: [];
|
|
309
|
-
|
|
310
|
-
const { refs, floatingStyles, update } = useFloating({
|
|
311
|
-
open: isOpen,
|
|
312
|
-
onOpenChange: setIsOpen,
|
|
313
|
-
placement,
|
|
314
|
-
strategy: "absolute",
|
|
315
|
-
// Handle collisions with the viewport
|
|
316
|
-
middleware: [flip(), offset(5)],
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
const toggleOpen = () => {
|
|
320
|
-
setIsOpen((prev) => {
|
|
321
|
-
if (!prev) {
|
|
322
|
-
if (inputRef.current) {
|
|
323
|
-
inputRef.current.focus();
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return !prev;
|
|
327
|
-
});
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const handleClear = (
|
|
331
|
-
e:
|
|
332
|
-
| MouseEvent<HTMLButtonElement>
|
|
333
|
-
| React.ChangeEvent<HTMLInputElement>
|
|
334
|
-
| React.ChangeEvent<HTMLTextAreaElement>
|
|
335
|
-
) => {
|
|
336
|
-
e.preventDefault();
|
|
337
|
-
e.stopPropagation();
|
|
338
|
-
|
|
339
|
-
if (inputRef.current) inputRef.current.value = "";
|
|
340
|
-
handleChangeSelection(null);
|
|
341
|
-
setSelected(null);
|
|
342
|
-
setSearchValue("");
|
|
343
|
-
searchFn?.("");
|
|
344
|
-
update();
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
const handleItemClick = (
|
|
348
|
-
event: MouseEvent<HTMLDivElement>,
|
|
349
|
-
item: string | Option
|
|
350
|
-
) => {
|
|
351
|
-
event.preventDefault();
|
|
352
|
-
event.stopPropagation();
|
|
353
|
-
handleChangeSelection(item);
|
|
354
|
-
setIsOpen(false);
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const handleChangeSelection = useCallback(
|
|
358
|
-
(option: Option | string | null) => {
|
|
359
|
-
setSelected(option);
|
|
360
|
-
onChange?.((option as Option)?.value || option, option);
|
|
361
|
-
},
|
|
362
|
-
[onChange]
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
const handleAddItem = useCallback(
|
|
366
|
-
(newItem: string) => {
|
|
367
|
-
const isNewItem = data.every(
|
|
368
|
-
(item) =>
|
|
369
|
-
(item as Option).label.toLowerCase() !==
|
|
370
|
-
(newItem as string).toLowerCase()
|
|
371
|
-
);
|
|
372
|
-
if (isNewItem) {
|
|
373
|
-
const newValue = isObjectArray
|
|
374
|
-
? ({ label: newItem, value: newItem } as Option)
|
|
375
|
-
: (newItem as string);
|
|
376
|
-
setCustomItems((prev) => {
|
|
377
|
-
prev.push(newValue);
|
|
378
|
-
return prev;
|
|
379
|
-
});
|
|
380
|
-
handleChangeSelection(newValue);
|
|
381
|
-
onItemAdded?.(newValue);
|
|
382
|
-
} else {
|
|
383
|
-
const item = data.find((item) => {
|
|
384
|
-
return isObjectArray
|
|
385
|
-
? (item as Option).label.toLowerCase() === newItem.toLowerCase()
|
|
386
|
-
: (item as string).toLowerCase() === newItem.toLowerCase();
|
|
387
|
-
}) as Option | string;
|
|
388
|
-
handleChangeSelection(item);
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
[onItemAdded, isObjectArray, data, handleChangeSelection]
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
395
|
-
const currentInputValue = inputRef?.current?.value;
|
|
396
|
-
|
|
397
|
-
// Escape key
|
|
398
|
-
if (e.key === "Escape") {
|
|
399
|
-
const reference = refs?.reference?.current as HTMLElement;
|
|
400
|
-
reference?.blur();
|
|
401
|
-
setSearchValue("");
|
|
402
|
-
update();
|
|
403
|
-
setIsOpen(false);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Enter key
|
|
407
|
-
if (e.key === "Enter") {
|
|
408
|
-
if (currentInputValue && currentInputValue !== "" && allowCustomValue) {
|
|
409
|
-
handleAddItem(currentInputValue);
|
|
410
|
-
setSearchValue("");
|
|
411
|
-
update();
|
|
412
|
-
} else {
|
|
413
|
-
// TODO: decide what to do when user presses enter after typing a search value
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
toggleOpen();
|
|
417
|
-
// setIsOpen(false);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Arrow down
|
|
421
|
-
if (e.key === "ArrowDown") {
|
|
422
|
-
e.preventDefault();
|
|
423
|
-
setSelected((prev) => {
|
|
424
|
-
let newItem = filteredItems[0]; // Loop back to the first item
|
|
425
|
-
const index = filteredItems.findIndex((item) =>
|
|
426
|
-
isObjectArray
|
|
427
|
-
? (item as Option).value === (prev as Option)?.value
|
|
428
|
-
: item === prev
|
|
429
|
-
);
|
|
430
|
-
if (index < filteredItems.length - 1) {
|
|
431
|
-
newItem = filteredItems[index + 1];
|
|
432
|
-
}
|
|
433
|
-
onChange?.((newItem as Option)?.value || newItem, newItem);
|
|
434
|
-
return newItem;
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Arrow up
|
|
439
|
-
if (e.key === "ArrowUp") {
|
|
440
|
-
e.preventDefault();
|
|
441
|
-
setSelected((prev) => {
|
|
442
|
-
let newItem = filteredItems[filteredItems.length - 1]; // Loop back to the last item
|
|
443
|
-
const index = filteredItems.findIndex((item) =>
|
|
444
|
-
isObjectArray
|
|
445
|
-
? (item as Option).value === (prev as Option)?.value
|
|
446
|
-
: item === prev
|
|
447
|
-
);
|
|
448
|
-
if (index > 0) {
|
|
449
|
-
newItem = filteredItems[index - 1];
|
|
450
|
-
}
|
|
451
|
-
onChange?.((newItem as Option)?.value || newItem, newItem);
|
|
452
|
-
return newItem;
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Tab key
|
|
457
|
-
if (e.key === "Tab") {
|
|
458
|
-
if (allowCustomValue && currentInputValue && currentInputValue !== "") {
|
|
459
|
-
handleAddItem(currentInputValue);
|
|
460
|
-
setSearchValue("");
|
|
461
|
-
} else if (!selected) {
|
|
462
|
-
// clear input
|
|
463
|
-
if (inputRef.current) {
|
|
464
|
-
inputRef.current.value = "";
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
setSearchValue("");
|
|
468
|
-
setIsOpen(false);
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
const handleOnChange = (
|
|
473
|
-
e:
|
|
474
|
-
| React.ChangeEvent<HTMLInputElement>
|
|
475
|
-
| React.ChangeEvent<HTMLTextAreaElement>
|
|
476
|
-
) => {
|
|
477
|
-
// Simulate clear event when the input is changed to an empty string
|
|
478
|
-
if (e.target.value === "") {
|
|
479
|
-
handleClear(e);
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (searchFn !== undefined) {
|
|
484
|
-
searchFn?.(e.target.value);
|
|
485
|
-
} else {
|
|
486
|
-
setSearchValue(e.target.value);
|
|
487
|
-
update();
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
const debouncedHandleOnChange = useDebouncedCallback(handleOnChange, 300);
|
|
492
|
-
|
|
493
|
-
const handleFocus = () => {
|
|
494
|
-
if (openOnFocus) {
|
|
495
|
-
setIsOpen(true);
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
// Close on outside click
|
|
500
|
-
useEffect(() => {
|
|
501
|
-
const close = (e: any) => {
|
|
502
|
-
const target = e.target as Node;
|
|
503
|
-
const referenceElement = refs?.reference?.current as Node;
|
|
504
|
-
const floatingElement = refs?.floating?.current as Node;
|
|
505
|
-
|
|
506
|
-
if (
|
|
507
|
-
floatingElement && // Check if the floating element exists
|
|
508
|
-
target !== referenceElement && // Check if the target is not the reference (input)
|
|
509
|
-
!referenceElement.contains(target) && // Check if the target is not inside the reference (input)
|
|
510
|
-
!floatingElement.contains(target) // Check if the target is not inside the floating element (content)
|
|
511
|
-
) {
|
|
512
|
-
setIsOpen(false);
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
document.addEventListener("click", close);
|
|
516
|
-
return () => document.removeEventListener("click", close);
|
|
517
|
-
}, [refs.floating, refs.reference]);
|
|
518
|
-
|
|
519
|
-
// Handle default value
|
|
520
|
-
useEffect(() => {
|
|
521
|
-
if (defaultValue) {
|
|
522
|
-
setSelected(
|
|
523
|
-
(data.find((item) =>
|
|
524
|
-
isObjectArray
|
|
525
|
-
? (item as Option).value === defaultValue
|
|
526
|
-
: (item as string) === defaultValue
|
|
527
|
-
) as Option | string) || null
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
}, [data, defaultValue, isObjectArray]);
|
|
531
|
-
|
|
532
|
-
// handle input value change
|
|
533
|
-
useEffect(() => {
|
|
534
|
-
if (inputRef.current) {
|
|
535
|
-
if (!(selected as Option)?.label) {
|
|
536
|
-
inputRef.current.value = "";
|
|
537
|
-
} else {
|
|
538
|
-
inputRef.current.value = isObjectArray
|
|
539
|
-
? ((selected as Option).label as string)
|
|
540
|
-
: (selected as string);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}, [selected]);
|
|
544
|
-
|
|
545
|
-
// handle scroll item into view
|
|
546
|
-
useEffect(() => {
|
|
547
|
-
const item = containerRef?.current?.querySelector?.(
|
|
548
|
-
".mfFloatingItem[data-selected=true]"
|
|
549
|
-
);
|
|
550
|
-
if (item) {
|
|
551
|
-
item.scrollIntoView({ block: "nearest" });
|
|
552
|
-
}
|
|
553
|
-
}, [selected]);
|
|
554
|
-
|
|
555
|
-
// make calls to onSearch callback
|
|
556
|
-
useEffect(() => {
|
|
557
|
-
if (searchable) {
|
|
558
|
-
update();
|
|
559
|
-
onSearch?.(searchValue);
|
|
560
|
-
}
|
|
561
|
-
}, [searchValue]);
|
|
562
|
-
|
|
563
|
-
const referenceEl = refs?.reference?.current as HTMLElement;
|
|
564
|
-
|
|
565
|
-
const contentWidth = referenceEl?.getClientRects?.()?.[0]?.width || "100%";
|
|
566
|
-
|
|
567
|
-
const scrollEl = scrollContainerRef?.current as HTMLElement | null;
|
|
568
|
-
|
|
569
|
-
const scrollActive = scrollEl
|
|
570
|
-
? scrollEl?.scrollHeight > scrollEl?.clientHeight
|
|
571
|
-
: false;
|
|
572
|
-
|
|
573
|
-
// get height between bottom of the floating container and the bottom of the viewport
|
|
574
|
-
const bottomHeight =
|
|
575
|
-
window.innerHeight - referenceEl?.getBoundingClientRect()?.bottom - 10;
|
|
576
|
-
|
|
577
|
-
// get height between top of the floating container and the top of the viewport
|
|
578
|
-
const topHeight = (referenceEl?.getBoundingClientRect()?.top -
|
|
579
|
-
10) as number;
|
|
580
|
-
|
|
581
|
-
useEffect(() => {
|
|
582
|
-
if (bottomHeight < 250) {
|
|
583
|
-
setPlacement("top-start");
|
|
584
|
-
setDropDownHeight(topHeight);
|
|
585
|
-
} else {
|
|
586
|
-
setPlacement("bottom-start");
|
|
587
|
-
setDropDownHeight(bottomHeight);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return () => {
|
|
591
|
-
setPlacement("bottom-start");
|
|
592
|
-
setDropDownHeight(bottomHeight);
|
|
593
|
-
};
|
|
594
|
-
}, [topHeight, bottomHeight, isOpen]);
|
|
595
|
-
|
|
596
|
-
return (
|
|
597
|
-
<div className={className}>
|
|
598
|
-
{label && (
|
|
599
|
-
<FieldLabel
|
|
600
|
-
error={error}
|
|
601
|
-
asterisk={required}
|
|
602
|
-
size={size}
|
|
603
|
-
description={description}
|
|
604
|
-
>
|
|
605
|
-
{label}
|
|
606
|
-
</FieldLabel>
|
|
607
|
-
)}
|
|
608
|
-
<StyledInputContainer
|
|
609
|
-
ref={refs.setReference}
|
|
610
|
-
onClick={() => setIsOpen(true)}
|
|
611
|
-
width={width}
|
|
612
|
-
onKeyDown={handleKeyDown}
|
|
613
|
-
>
|
|
614
|
-
<Input
|
|
615
|
-
ref={inputRef}
|
|
616
|
-
onChange={debouncedHandleOnChange}
|
|
617
|
-
onFocus={handleFocus}
|
|
618
|
-
autoFocus={focused}
|
|
619
|
-
placeholder={placeholder}
|
|
620
|
-
size={size}
|
|
621
|
-
readOnly={!searchable}
|
|
622
|
-
data-button-right={arrow || clearable}
|
|
623
|
-
/>
|
|
624
|
-
{clearable && (selected || !!inputRef?.current?.value) ? (
|
|
625
|
-
<ClearButton onClick={handleClear} />
|
|
626
|
-
) : arrow ? (
|
|
627
|
-
<ArrowButton
|
|
628
|
-
onClick={(e: MouseEvent<HTMLButtonElement>) => {
|
|
629
|
-
e.preventDefault();
|
|
630
|
-
e.stopPropagation();
|
|
631
|
-
}}
|
|
632
|
-
onMouseDown={(e: MouseEvent<HTMLButtonElement>) => {
|
|
633
|
-
e.preventDefault();
|
|
634
|
-
e.stopPropagation();
|
|
635
|
-
toggleOpen();
|
|
636
|
-
}}
|
|
637
|
-
/>
|
|
638
|
-
) : null}
|
|
639
|
-
</StyledInputContainer>
|
|
640
|
-
{isOpen && (
|
|
641
|
-
<FloatingPortal preserveTabOrder>
|
|
642
|
-
<StyledFloatContainer
|
|
643
|
-
ref={(ref) => {
|
|
644
|
-
containerRef.current = ref;
|
|
645
|
-
refs.setFloating(ref);
|
|
646
|
-
}}
|
|
647
|
-
style={floatingStyles}
|
|
648
|
-
className="mfFloating"
|
|
649
|
-
>
|
|
650
|
-
<StyledContent
|
|
651
|
-
className="mfFloatingContent"
|
|
652
|
-
style={{
|
|
653
|
-
width: contentWidth,
|
|
654
|
-
maxWidth: contentWidth,
|
|
655
|
-
maxHeight: DropDownProps.autoHeight
|
|
656
|
-
? (dropDownHeight || 250) - 10
|
|
657
|
-
: "",
|
|
658
|
-
}}
|
|
659
|
-
variant={variant}
|
|
660
|
-
data-empty={filteredItems.length === 0}
|
|
661
|
-
{...DropDownProps}
|
|
662
|
-
>
|
|
663
|
-
{actionComponent && <ActionMenu>{actionComponent}</ActionMenu>}
|
|
664
|
-
<StyledInnerItemContainer
|
|
665
|
-
ref={scrollContainerRef}
|
|
666
|
-
data-scroll-active={scrollActive}
|
|
667
|
-
onScroll={onScroll}
|
|
668
|
-
>
|
|
669
|
-
{loading && <div>Loading...</div>}
|
|
670
|
-
{!loading && grouped
|
|
671
|
-
? groups.map((group, index) => (
|
|
672
|
-
<div key={group.label}>
|
|
673
|
-
<GroupTitle size={size}>{group.label}</GroupTitle>
|
|
674
|
-
{group.items.map((item: Option, index: number) => {
|
|
675
|
-
return (
|
|
676
|
-
<Tooltip
|
|
677
|
-
content={
|
|
678
|
-
TooltipContent ? (
|
|
679
|
-
<TooltipContent data={item.data} />
|
|
680
|
-
) : null
|
|
681
|
-
}
|
|
682
|
-
side="left"
|
|
683
|
-
>
|
|
684
|
-
<StyledItem
|
|
685
|
-
key={index}
|
|
686
|
-
className="mfFloatingItem"
|
|
687
|
-
onClick={(e) => handleItemClick(e, item)}
|
|
688
|
-
data-selected={
|
|
689
|
-
(selected as Option)?.value ===
|
|
690
|
-
(item?.value || item)
|
|
691
|
-
}
|
|
692
|
-
size={size}
|
|
693
|
-
>
|
|
694
|
-
{renderOption?.(item) || (
|
|
695
|
-
<>{item?.label || item}</>
|
|
696
|
-
)}
|
|
697
|
-
</StyledItem>
|
|
698
|
-
</Tooltip>
|
|
699
|
-
);
|
|
700
|
-
})}
|
|
701
|
-
</div>
|
|
702
|
-
))
|
|
703
|
-
: filteredItems.map((item, index) => {
|
|
704
|
-
return (
|
|
705
|
-
<Tooltip
|
|
706
|
-
key={(item as Option).value || item}
|
|
707
|
-
content={
|
|
708
|
-
TooltipContent ? (
|
|
709
|
-
<TooltipContent data={(item as Option).data} />
|
|
710
|
-
) : null
|
|
711
|
-
}
|
|
712
|
-
side="left"
|
|
713
|
-
>
|
|
714
|
-
<StyledItem
|
|
715
|
-
key={index}
|
|
716
|
-
className="mfFloatingItem"
|
|
717
|
-
onClick={(e) => handleItemClick(e, item)}
|
|
718
|
-
data-selected={
|
|
719
|
-
(selected as Option)?.value ===
|
|
720
|
-
((item as Option)?.value || item)
|
|
721
|
-
}
|
|
722
|
-
size={size}
|
|
723
|
-
>
|
|
724
|
-
{renderOption?.(item) || (
|
|
725
|
-
<>{(item as Option)?.label || item}</>
|
|
726
|
-
)}
|
|
727
|
-
</StyledItem>
|
|
728
|
-
</Tooltip>
|
|
729
|
-
);
|
|
730
|
-
})}
|
|
731
|
-
</StyledInnerItemContainer>
|
|
732
|
-
</StyledContent>
|
|
733
|
-
</StyledFloatContainer>
|
|
734
|
-
</FloatingPortal>
|
|
735
|
-
)}
|
|
736
|
-
</div>
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
)`
|
|
740
|
-
position: relative;
|
|
741
|
-
cursor: pointer;
|
|
742
|
-
width: 100%;
|
|
743
|
-
`;
|
|
744
|
-
|
|
745
|
-
export default SelectBox;
|