@monolith-forensics/monolith-ui 1.0.10 → 1.1.0

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