@mdigital_ui/ui 0.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 (205) hide show
  1. package/README.md +296 -0
  2. package/dist/accordion/index.js +5 -0
  3. package/dist/accordion/index.js.map +1 -0
  4. package/dist/badge/index.js +5 -0
  5. package/dist/badge/index.js.map +1 -0
  6. package/dist/button/index.js +6 -0
  7. package/dist/button/index.js.map +1 -0
  8. package/dist/card/index.js +4 -0
  9. package/dist/card/index.js.map +1 -0
  10. package/dist/carousel/index.js +3 -0
  11. package/dist/carousel/index.js.map +1 -0
  12. package/dist/cascader/index.js +4 -0
  13. package/dist/cascader/index.js.map +1 -0
  14. package/dist/chart/index.js +4 -0
  15. package/dist/chart/index.js.map +1 -0
  16. package/dist/checkbox/index.js +5 -0
  17. package/dist/checkbox/index.js.map +1 -0
  18. package/dist/checkbox-group/index.js +4 -0
  19. package/dist/checkbox-group/index.js.map +1 -0
  20. package/dist/chunk-2JGAYDZR.js +181 -0
  21. package/dist/chunk-2JGAYDZR.js.map +1 -0
  22. package/dist/chunk-3PFA3YG6.js +228 -0
  23. package/dist/chunk-3PFA3YG6.js.map +1 -0
  24. package/dist/chunk-4OMLQCUV.js +96 -0
  25. package/dist/chunk-4OMLQCUV.js.map +1 -0
  26. package/dist/chunk-4P5EMRFI.js +298 -0
  27. package/dist/chunk-4P5EMRFI.js.map +1 -0
  28. package/dist/chunk-5UEWVFF6.js +212 -0
  29. package/dist/chunk-5UEWVFF6.js.map +1 -0
  30. package/dist/chunk-5VCGW53O.js +332 -0
  31. package/dist/chunk-5VCGW53O.js.map +1 -0
  32. package/dist/chunk-75XESYGN.js +49 -0
  33. package/dist/chunk-75XESYGN.js.map +1 -0
  34. package/dist/chunk-7AEGBABZ.js +1102 -0
  35. package/dist/chunk-7AEGBABZ.js.map +1 -0
  36. package/dist/chunk-AOITJRSV.js +134 -0
  37. package/dist/chunk-AOITJRSV.js.map +1 -0
  38. package/dist/chunk-AWPKZYHT.js +152 -0
  39. package/dist/chunk-AWPKZYHT.js.map +1 -0
  40. package/dist/chunk-BNILRB4T.js +37 -0
  41. package/dist/chunk-BNILRB4T.js.map +1 -0
  42. package/dist/chunk-BP434VYV.js +448 -0
  43. package/dist/chunk-BP434VYV.js.map +1 -0
  44. package/dist/chunk-C7SXY3ZV.js +65 -0
  45. package/dist/chunk-C7SXY3ZV.js.map +1 -0
  46. package/dist/chunk-CLLQDCDR.js +560 -0
  47. package/dist/chunk-CLLQDCDR.js.map +1 -0
  48. package/dist/chunk-CWHFK7ZC.js +128 -0
  49. package/dist/chunk-CWHFK7ZC.js.map +1 -0
  50. package/dist/chunk-D3JWPGCA.js +123 -0
  51. package/dist/chunk-D3JWPGCA.js.map +1 -0
  52. package/dist/chunk-DOKTHDG3.js +55 -0
  53. package/dist/chunk-DOKTHDG3.js.map +1 -0
  54. package/dist/chunk-DPOSWW22.js +126 -0
  55. package/dist/chunk-DPOSWW22.js.map +1 -0
  56. package/dist/chunk-E2CYDDYC.js +39 -0
  57. package/dist/chunk-E2CYDDYC.js.map +1 -0
  58. package/dist/chunk-EYTOKUBM.js +401 -0
  59. package/dist/chunk-EYTOKUBM.js.map +1 -0
  60. package/dist/chunk-FGWSUPVW.js +356 -0
  61. package/dist/chunk-FGWSUPVW.js.map +1 -0
  62. package/dist/chunk-FPOXTCYV.js +166 -0
  63. package/dist/chunk-FPOXTCYV.js.map +1 -0
  64. package/dist/chunk-FTJOSVTY.js +104 -0
  65. package/dist/chunk-FTJOSVTY.js.map +1 -0
  66. package/dist/chunk-FYHQDFKE.js +164 -0
  67. package/dist/chunk-FYHQDFKE.js.map +1 -0
  68. package/dist/chunk-H2HIBD5Y.js +158 -0
  69. package/dist/chunk-H2HIBD5Y.js.map +1 -0
  70. package/dist/chunk-J3G5WWGR.js +53 -0
  71. package/dist/chunk-J3G5WWGR.js.map +1 -0
  72. package/dist/chunk-JZCHZ4B3.js +487 -0
  73. package/dist/chunk-JZCHZ4B3.js.map +1 -0
  74. package/dist/chunk-KBCBVH7B.js +51 -0
  75. package/dist/chunk-KBCBVH7B.js.map +1 -0
  76. package/dist/chunk-KNQ7UQ2W.js +143 -0
  77. package/dist/chunk-KNQ7UQ2W.js.map +1 -0
  78. package/dist/chunk-KTBPIEP2.js +102 -0
  79. package/dist/chunk-KTBPIEP2.js.map +1 -0
  80. package/dist/chunk-L3SP7GHC.js +1023 -0
  81. package/dist/chunk-L3SP7GHC.js.map +1 -0
  82. package/dist/chunk-LBJG2UWT.js +100 -0
  83. package/dist/chunk-LBJG2UWT.js.map +1 -0
  84. package/dist/chunk-MLDX3Z67.js +470 -0
  85. package/dist/chunk-MLDX3Z67.js.map +1 -0
  86. package/dist/chunk-NNSS366W.js +331 -0
  87. package/dist/chunk-NNSS366W.js.map +1 -0
  88. package/dist/chunk-OQANRZPV.js +197 -0
  89. package/dist/chunk-OQANRZPV.js.map +1 -0
  90. package/dist/chunk-OW5A5IIF.js +175 -0
  91. package/dist/chunk-OW5A5IIF.js.map +1 -0
  92. package/dist/chunk-R225A5II.js +187 -0
  93. package/dist/chunk-R225A5II.js.map +1 -0
  94. package/dist/chunk-ROR4E6IE.js +119 -0
  95. package/dist/chunk-ROR4E6IE.js.map +1 -0
  96. package/dist/chunk-RPAQAZTI.js +54 -0
  97. package/dist/chunk-RPAQAZTI.js.map +1 -0
  98. package/dist/chunk-RQBXZKTH.js +452 -0
  99. package/dist/chunk-RQBXZKTH.js.map +1 -0
  100. package/dist/chunk-S5XJXU52.js +178 -0
  101. package/dist/chunk-S5XJXU52.js.map +1 -0
  102. package/dist/chunk-SAVE5ACL.js +324 -0
  103. package/dist/chunk-SAVE5ACL.js.map +1 -0
  104. package/dist/chunk-SERJ3TZE.js +640 -0
  105. package/dist/chunk-SERJ3TZE.js.map +1 -0
  106. package/dist/chunk-SK5ECBBK.js +175 -0
  107. package/dist/chunk-SK5ECBBK.js.map +1 -0
  108. package/dist/chunk-SOV4PE3P.js +218 -0
  109. package/dist/chunk-SOV4PE3P.js.map +1 -0
  110. package/dist/chunk-W7BQYIXF.js +687 -0
  111. package/dist/chunk-W7BQYIXF.js.map +1 -0
  112. package/dist/chunk-XMAH5PDW.js +59 -0
  113. package/dist/chunk-XMAH5PDW.js.map +1 -0
  114. package/dist/chunk-XOBGEMQY.js +94 -0
  115. package/dist/chunk-XOBGEMQY.js.map +1 -0
  116. package/dist/chunk-YNNAOXU5.js +57 -0
  117. package/dist/chunk-YNNAOXU5.js.map +1 -0
  118. package/dist/chunk-YZVSDRJD.js +253 -0
  119. package/dist/chunk-YZVSDRJD.js.map +1 -0
  120. package/dist/collapse/index.js +4 -0
  121. package/dist/collapse/index.js.map +1 -0
  122. package/dist/command/index.js +5 -0
  123. package/dist/command/index.js.map +1 -0
  124. package/dist/date-picker/index.js +5 -0
  125. package/dist/date-picker/index.js.map +1 -0
  126. package/dist/descriptions/index.js +4 -0
  127. package/dist/descriptions/index.js.map +1 -0
  128. package/dist/drawer/index.js +4 -0
  129. package/dist/drawer/index.js.map +1 -0
  130. package/dist/dropdown/index.js +5 -0
  131. package/dist/dropdown/index.js.map +1 -0
  132. package/dist/empty/index.js +4 -0
  133. package/dist/empty/index.js.map +1 -0
  134. package/dist/fetching-overlay/index.js +5 -0
  135. package/dist/fetching-overlay/index.js.map +1 -0
  136. package/dist/image/index.js +4 -0
  137. package/dist/image/index.js.map +1 -0
  138. package/dist/index.d.ts +2672 -0
  139. package/dist/index.js +976 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/input/index.js +5 -0
  142. package/dist/input/index.js.map +1 -0
  143. package/dist/input-group/index.js +4 -0
  144. package/dist/input-group/index.js.map +1 -0
  145. package/dist/input-otp/index.js +4 -0
  146. package/dist/input-otp/index.js.map +1 -0
  147. package/dist/input-password/index.js +6 -0
  148. package/dist/input-password/index.js.map +1 -0
  149. package/dist/kbd/index.js +4 -0
  150. package/dist/kbd/index.js.map +1 -0
  151. package/dist/modal/index.js +4 -0
  152. package/dist/modal/index.js.map +1 -0
  153. package/dist/multi-select/index.js +5 -0
  154. package/dist/multi-select/index.js.map +1 -0
  155. package/dist/notification/index.js +4 -0
  156. package/dist/notification/index.js.map +1 -0
  157. package/dist/pagination/index.js +4 -0
  158. package/dist/pagination/index.js.map +1 -0
  159. package/dist/popover/index.js +4 -0
  160. package/dist/popover/index.js.map +1 -0
  161. package/dist/progress/index.js +4 -0
  162. package/dist/progress/index.js.map +1 -0
  163. package/dist/radio/index.js +4 -0
  164. package/dist/radio/index.js.map +1 -0
  165. package/dist/radio-group/index.js +4 -0
  166. package/dist/radio-group/index.js.map +1 -0
  167. package/dist/rating/index.js +4 -0
  168. package/dist/rating/index.js.map +1 -0
  169. package/dist/ribbon/index.js +4 -0
  170. package/dist/ribbon/index.js.map +1 -0
  171. package/dist/select/index.js +6 -0
  172. package/dist/select/index.js.map +1 -0
  173. package/dist/skeleton/index.js +4 -0
  174. package/dist/skeleton/index.js.map +1 -0
  175. package/dist/slider/index.js +4 -0
  176. package/dist/slider/index.js.map +1 -0
  177. package/dist/spinner/index.js +4 -0
  178. package/dist/spinner/index.js.map +1 -0
  179. package/dist/stepper/index.js +4 -0
  180. package/dist/stepper/index.js.map +1 -0
  181. package/dist/styles/base.css +161 -0
  182. package/dist/styles/global.css +633 -0
  183. package/dist/styles/themes/dark.css +84 -0
  184. package/dist/styles/themes/light.css +84 -0
  185. package/dist/switch/index.js +4 -0
  186. package/dist/switch/index.js.map +1 -0
  187. package/dist/table/index.js +12 -0
  188. package/dist/table/index.js.map +1 -0
  189. package/dist/tabs/index.js +5 -0
  190. package/dist/tabs/index.js.map +1 -0
  191. package/dist/textarea/index.js +4 -0
  192. package/dist/textarea/index.js.map +1 -0
  193. package/dist/toggle/index.js +4 -0
  194. package/dist/toggle/index.js.map +1 -0
  195. package/dist/toggle-group/index.js +4 -0
  196. package/dist/toggle-group/index.js.map +1 -0
  197. package/dist/tooltip/index.js +4 -0
  198. package/dist/tooltip/index.js.map +1 -0
  199. package/dist/transfer/index.js +6 -0
  200. package/dist/transfer/index.js.map +1 -0
  201. package/dist/tree/index.js +4 -0
  202. package/dist/tree/index.js.map +1 -0
  203. package/dist/tree-select/index.js +6 -0
  204. package/dist/tree-select/index.js.map +1 -0
  205. package/package.json +107 -0
@@ -0,0 +1,687 @@
1
+ import { Popover, PopoverTrigger, PopoverContent } from './chunk-3PFA3YG6.js';
2
+ import { cn, iconSizes } from './chunk-YNNAOXU5.js';
3
+ import { useVirtualizer } from '@tanstack/react-virtual';
4
+ import { X, Loader2, Search, ChevronDown, ChevronUp, Check } from 'lucide-react';
5
+ import React, { useCallback } from 'react';
6
+ import { cva } from 'class-variance-authority';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
+
9
+ var multiSelectTriggerVariants = cva(
10
+ "w-full flex items-center justify-between rounded-md bg-background text-text-primary border focus:border-primary outline-none disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer gap-1",
11
+ {
12
+ variants: {
13
+ status: {
14
+ default: "border-border",
15
+ error: "border-error",
16
+ warning: "border-warning",
17
+ info: "border-info",
18
+ success: "border-success"
19
+ },
20
+ size: {
21
+ xs: "h-8 pl-1 pr-2 text-xs",
22
+ sm: "h-[var(--input-height-sm)] px-1.5 text-sm",
23
+ md: "h-[var(--input-height-md)] px-2 text-base",
24
+ lg: "h-[var(--input-height-lg)] px-2 text-lg"
25
+ },
26
+ fullWidth: {
27
+ true: "w-full",
28
+ false: "max-w-full"
29
+ }
30
+ },
31
+ defaultVariants: {
32
+ status: "default",
33
+ size: "md",
34
+ fullWidth: true
35
+ }
36
+ }
37
+ );
38
+ cva(
39
+ "absolute z-50 mt-1 overflow-auto rounded-md border border-border bg-background"
40
+ );
41
+ var multiSelectOptionVariants = cva(
42
+ "relative cursor-pointer select-none px-3 py-2 text-text-primary",
43
+ {
44
+ variants: {
45
+ selected: {
46
+ true: "bg-primary/10 text-primary font-medium",
47
+ false: "hover:bg-surface"
48
+ },
49
+ disabled: {
50
+ true: "opacity-50 cursor-not-allowed",
51
+ false: ""
52
+ }
53
+ },
54
+ defaultVariants: {
55
+ selected: false,
56
+ disabled: false
57
+ }
58
+ }
59
+ );
60
+ var multiSelectGroupVariants = cva(
61
+ "px-3 py-2 text-xs font-semibold text-text-secondary uppercase tracking-wider bg-surface"
62
+ );
63
+ var MultiSelect = React.memo(
64
+ ({
65
+ size = "md",
66
+ label,
67
+ helperText,
68
+ error,
69
+ warning,
70
+ info,
71
+ success,
72
+ options = [],
73
+ placeholder = "Select options",
74
+ loading = false,
75
+ fullWidth = true,
76
+ maxChipsVisible = 3,
77
+ value,
78
+ defaultValue,
79
+ onChange,
80
+ disabled,
81
+ required,
82
+ clearable = false,
83
+ virtualizeThreshold = 50,
84
+ maxDropdownHeight = 300,
85
+ className,
86
+ ref,
87
+ ...props
88
+ }) => {
89
+ const [isOpen, setIsOpen] = React.useState(false);
90
+ const [internalValue, setInternalValue] = React.useState(
91
+ defaultValue || []
92
+ );
93
+ const [parentNode, setParentNode] = React.useState(
94
+ null
95
+ );
96
+ const [searchQuery, setSearchQuery] = React.useState("");
97
+ const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
98
+ const [visibleChipsCount, setVisibleChipsCount] = React.useState(maxChipsVisible);
99
+ const [showTopArrow, setShowTopArrow] = React.useState(false);
100
+ const [showBottomArrow, setShowBottomArrow] = React.useState(false);
101
+ const triggerRef = React.useRef(null);
102
+ const inputRef = React.useRef(null);
103
+ const scrollContainerRef = React.useRef(null);
104
+ const currentValue = value !== void 0 ? value : internalValue;
105
+ let status = "default";
106
+ const helperMessage = error || warning || info || success || helperText;
107
+ if (error) {
108
+ status = "error";
109
+ } else if (warning) {
110
+ status = "warning";
111
+ } else if (success) {
112
+ status = "success";
113
+ } else if (info) {
114
+ status = "info";
115
+ }
116
+ const groupedOptions = React.useMemo(() => {
117
+ const groups = {};
118
+ const ungrouped = [];
119
+ options.forEach((option) => {
120
+ if (option.group) {
121
+ if (!groups[option.group]) {
122
+ groups[option.group] = [];
123
+ }
124
+ groups[option.group].push(option);
125
+ } else {
126
+ ungrouped.push(option);
127
+ }
128
+ });
129
+ return { groups, ungrouped };
130
+ }, [options]);
131
+ const flattenedOptions = React.useMemo(() => {
132
+ const items = [];
133
+ groupedOptions.ungrouped.forEach((option) => {
134
+ items.push({ type: "option", data: option });
135
+ });
136
+ Object.entries(groupedOptions.groups).forEach(
137
+ ([groupName, groupOptions]) => {
138
+ items.push({ type: "group", data: groupName });
139
+ groupOptions.forEach((option) => {
140
+ items.push({ type: "option", data: option });
141
+ });
142
+ }
143
+ );
144
+ return items;
145
+ }, [groupedOptions]);
146
+ const filteredOptions = React.useMemo(() => {
147
+ if (!searchQuery) return flattenedOptions;
148
+ return flattenedOptions.filter((item) => {
149
+ if (item.type === "group") return true;
150
+ const option = item.data;
151
+ return option.label.toLowerCase().includes(searchQuery.toLowerCase());
152
+ });
153
+ }, [flattenedOptions, searchQuery]);
154
+ const shouldVirtualize = filteredOptions.length > virtualizeThreshold;
155
+ const virtualizer = useVirtualizer({
156
+ count: filteredOptions.length,
157
+ getScrollElement: () => parentNode,
158
+ estimateSize: () => 40,
159
+ enabled: shouldVirtualize
160
+ });
161
+ const virtualizerRef = React.useRef(virtualizer);
162
+ React.useEffect(() => {
163
+ virtualizerRef.current = virtualizer;
164
+ }, [virtualizer]);
165
+ const refCallback = useCallback((node) => {
166
+ if (node) {
167
+ setParentNode(node);
168
+ scrollContainerRef.current = node;
169
+ }
170
+ }, []);
171
+ const selectedOptions = options.filter(
172
+ (opt) => currentValue.includes(opt.value)
173
+ );
174
+ const handleSelect = React.useCallback(
175
+ (optionValue) => {
176
+ const newValue = currentValue.includes(optionValue) ? currentValue.filter((v) => v !== optionValue) : [...currentValue, optionValue];
177
+ if (value === void 0) {
178
+ setInternalValue(newValue);
179
+ }
180
+ onChange?.(newValue);
181
+ setHighlightedIndex(-1);
182
+ },
183
+ [value, onChange, currentValue]
184
+ );
185
+ const handleRemoveChip = React.useCallback(
186
+ (optionValue, e) => {
187
+ e.stopPropagation();
188
+ const newValue = currentValue.filter((v) => v !== optionValue);
189
+ if (value === void 0) {
190
+ setInternalValue(newValue);
191
+ }
192
+ onChange?.(newValue);
193
+ },
194
+ [value, onChange, currentValue]
195
+ );
196
+ const handleClear = React.useCallback(
197
+ (e) => {
198
+ e.stopPropagation();
199
+ if (value === void 0) {
200
+ setInternalValue([]);
201
+ }
202
+ onChange?.([]);
203
+ },
204
+ [value, onChange]
205
+ );
206
+ const handleInputChange = React.useCallback(
207
+ (e) => {
208
+ setSearchQuery(e.target.value);
209
+ if (!isOpen) setIsOpen(true);
210
+ },
211
+ [isOpen]
212
+ );
213
+ const handleInputFocus = React.useCallback(() => {
214
+ setIsOpen(true);
215
+ }, []);
216
+ const handleKeyDown = React.useCallback(
217
+ (e) => {
218
+ if (!isOpen && e.key !== "Tab") {
219
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
220
+ e.preventDefault();
221
+ setIsOpen(true);
222
+ }
223
+ return;
224
+ }
225
+ const selectableOptions = filteredOptions.filter(
226
+ (item) => item.type === "option" && !item.data.disabled
227
+ );
228
+ switch (e.key) {
229
+ case "Escape":
230
+ e.preventDefault();
231
+ setIsOpen(false);
232
+ setSearchQuery("");
233
+ setHighlightedIndex(-1);
234
+ inputRef.current?.blur();
235
+ break;
236
+ case "ArrowDown":
237
+ e.preventDefault();
238
+ setHighlightedIndex((prev) => {
239
+ const nextIndex = Math.min(prev + 1, selectableOptions.length - 1);
240
+ const optionIndex = filteredOptions.indexOf(
241
+ selectableOptions[nextIndex]
242
+ );
243
+ if (shouldVirtualize && virtualizerRef.current) {
244
+ virtualizerRef.current.scrollToIndex(optionIndex, {
245
+ align: "auto"
246
+ });
247
+ }
248
+ return nextIndex;
249
+ });
250
+ break;
251
+ case "ArrowUp":
252
+ e.preventDefault();
253
+ setHighlightedIndex((prev) => {
254
+ const nextIndex = Math.max(prev - 1, 0);
255
+ const optionIndex = filteredOptions.indexOf(
256
+ selectableOptions[nextIndex]
257
+ );
258
+ if (shouldVirtualize && virtualizerRef.current) {
259
+ virtualizerRef.current.scrollToIndex(optionIndex, {
260
+ align: "auto"
261
+ });
262
+ }
263
+ return nextIndex;
264
+ });
265
+ break;
266
+ case "Enter":
267
+ e.preventDefault();
268
+ if (highlightedIndex >= 0 && highlightedIndex < selectableOptions.length) {
269
+ const option = selectableOptions[highlightedIndex].data;
270
+ handleSelect(option.value);
271
+ }
272
+ break;
273
+ case "Home":
274
+ e.preventDefault();
275
+ setHighlightedIndex(0);
276
+ if (shouldVirtualize && virtualizerRef.current) {
277
+ virtualizerRef.current.scrollToIndex(0);
278
+ }
279
+ break;
280
+ case "End":
281
+ e.preventDefault();
282
+ setHighlightedIndex(selectableOptions.length - 1);
283
+ if (shouldVirtualize && virtualizerRef.current) {
284
+ virtualizerRef.current.scrollToIndex(filteredOptions.length - 1);
285
+ }
286
+ break;
287
+ }
288
+ },
289
+ [
290
+ isOpen,
291
+ filteredOptions,
292
+ highlightedIndex,
293
+ handleSelect,
294
+ shouldVirtualize
295
+ ]
296
+ );
297
+ const handleInputKeyDown = React.useCallback(
298
+ (e) => {
299
+ if (e.key === "Backspace" && !searchQuery && currentValue.length > 0) {
300
+ e.preventDefault();
301
+ const lastValue = currentValue[currentValue.length - 1];
302
+ handleRemoveChip(lastValue, e);
303
+ } else {
304
+ handleKeyDown(e);
305
+ }
306
+ },
307
+ [searchQuery, currentValue, handleRemoveChip, handleKeyDown]
308
+ );
309
+ React.useEffect(() => {
310
+ if (selectedOptions.length === 0 || !triggerRef.current) {
311
+ setVisibleChipsCount(maxChipsVisible);
312
+ return;
313
+ }
314
+ const calculateVisibleChips = () => {
315
+ const container = triggerRef.current;
316
+ if (!container) return;
317
+ const hiddenChips = container.querySelectorAll("[data-hidden-chip]");
318
+ if (hiddenChips.length === 0) return;
319
+ const containerWidth = container.offsetWidth;
320
+ const padding = size === "sm" ? 12 : size === "md" ? 16 : size === "lg" ? 20 : 24;
321
+ const chevronWidth = 32;
322
+ const gap = 4;
323
+ const plusIndicatorWidth = 50;
324
+ const availableWidth = containerWidth - padding * 2 - chevronWidth;
325
+ let totalWidth = 0;
326
+ let visibleCount = 0;
327
+ for (let i = 0; i < hiddenChips.length; i++) {
328
+ const chipWidth = hiddenChips[i].getBoundingClientRect().width;
329
+ const requiredSpace = totalWidth + chipWidth + (visibleCount > 0 ? gap : 0);
330
+ const needsPlusIndicator = i < hiddenChips.length - 1;
331
+ const spaceWithIndicator = requiredSpace + (needsPlusIndicator ? gap + plusIndicatorWidth : 0);
332
+ if (spaceWithIndicator <= availableWidth) {
333
+ totalWidth = requiredSpace;
334
+ visibleCount++;
335
+ } else {
336
+ break;
337
+ }
338
+ }
339
+ setVisibleChipsCount(Math.max(1, visibleCount));
340
+ };
341
+ const timeoutId = setTimeout(calculateVisibleChips, 0);
342
+ const resizeObserver = new ResizeObserver(() => {
343
+ calculateVisibleChips();
344
+ });
345
+ if (triggerRef.current) {
346
+ resizeObserver.observe(triggerRef.current);
347
+ }
348
+ return () => {
349
+ clearTimeout(timeoutId);
350
+ resizeObserver.disconnect();
351
+ };
352
+ }, [selectedOptions, size, maxChipsVisible]);
353
+ const handleScroll = React.useCallback(
354
+ (e) => {
355
+ const element = e.currentTarget;
356
+ const { scrollTop, scrollHeight, clientHeight } = element;
357
+ setShowTopArrow(scrollTop > 0);
358
+ setShowBottomArrow(scrollTop + clientHeight < scrollHeight - 1);
359
+ },
360
+ []
361
+ );
362
+ React.useEffect(() => {
363
+ if (isOpen) {
364
+ const timeoutId = setTimeout(() => {
365
+ const element = scrollContainerRef.current;
366
+ if (element) {
367
+ const { scrollTop, scrollHeight, clientHeight } = element;
368
+ setShowTopArrow(scrollTop > 0);
369
+ setShowBottomArrow(scrollTop + clientHeight < scrollHeight - 1);
370
+ }
371
+ }, 50);
372
+ return () => clearTimeout(timeoutId);
373
+ } else {
374
+ setShowTopArrow(false);
375
+ setShowBottomArrow(false);
376
+ return;
377
+ }
378
+ }, [isOpen, filteredOptions, shouldVirtualize]);
379
+ const renderOptionsList = () => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
380
+ showTopArrow && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-text-primary" }) }),
381
+ /* @__PURE__ */ jsx(
382
+ "div",
383
+ {
384
+ ref: scrollContainerRef,
385
+ className: "overflow-auto relative",
386
+ style: { maxHeight: maxDropdownHeight },
387
+ role: "listbox",
388
+ "aria-labelledby": label ? "multiselect-label" : void 0,
389
+ onScroll: handleScroll,
390
+ children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-4 text-center text-sm text-text-muted", children: "No options found" }) : shouldVirtualize ? /* @__PURE__ */ jsx(
391
+ "div",
392
+ {
393
+ ref: refCallback,
394
+ style: {
395
+ height: `${Math.min(virtualizer.getTotalSize(), maxDropdownHeight)}px`,
396
+ overflow: "auto"
397
+ },
398
+ onScroll: handleScroll,
399
+ children: /* @__PURE__ */ jsx(
400
+ "div",
401
+ {
402
+ style: {
403
+ height: `${virtualizer.getTotalSize()}px`,
404
+ width: "100%",
405
+ position: "relative"
406
+ },
407
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
408
+ const item = filteredOptions[virtualItem.index];
409
+ if (item?.type === "group") {
410
+ return /* @__PURE__ */ jsx(
411
+ "div",
412
+ {
413
+ style: {
414
+ position: "absolute",
415
+ top: 0,
416
+ left: 0,
417
+ width: "100%",
418
+ height: `${virtualItem.size}px`,
419
+ transform: `translateY(${virtualItem.start}px)`
420
+ },
421
+ className: multiSelectGroupVariants(),
422
+ children: item?.data
423
+ },
424
+ `group-${virtualItem.index}`
425
+ );
426
+ }
427
+ const option = item?.data;
428
+ const isSelected = currentValue.includes(option.value);
429
+ const selectableOptions = filteredOptions.filter(
430
+ (i) => i.type === "option" && !i.data.disabled
431
+ );
432
+ const selectableIndex = selectableOptions.findIndex(
433
+ (i) => i === item
434
+ );
435
+ const isHighlighted = selectableIndex === highlightedIndex;
436
+ return /* @__PURE__ */ jsxs(
437
+ "div",
438
+ {
439
+ style: {
440
+ position: "absolute",
441
+ top: 0,
442
+ left: 0,
443
+ width: "100%",
444
+ height: `${virtualItem.size}px`,
445
+ transform: `translateY(${virtualItem.start}px)`
446
+ },
447
+ className: cn(
448
+ multiSelectOptionVariants({
449
+ selected: isSelected,
450
+ disabled: !!option.disabled
451
+ }),
452
+ isHighlighted && "bg-surface"
453
+ ),
454
+ onClick: () => !option.disabled && handleSelect(option.value),
455
+ role: "option",
456
+ "aria-selected": isSelected,
457
+ "aria-disabled": option.disabled,
458
+ children: [
459
+ option.label,
460
+ isSelected && /* @__PURE__ */ jsx("span", { className: "absolute right-3 top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx(
461
+ Check,
462
+ {
463
+ className: cn("text-primary", iconSizes[size])
464
+ }
465
+ ) })
466
+ ]
467
+ },
468
+ option.value
469
+ );
470
+ })
471
+ }
472
+ )
473
+ }
474
+ ) : /* @__PURE__ */ jsx("div", { className: "py-1", children: filteredOptions.map((item, index) => {
475
+ if (item.type === "group") {
476
+ return /* @__PURE__ */ jsx(
477
+ "div",
478
+ {
479
+ className: multiSelectGroupVariants(),
480
+ children: item.data
481
+ },
482
+ `group-${index}`
483
+ );
484
+ }
485
+ const option = item.data;
486
+ const isSelected = currentValue.includes(option.value);
487
+ const selectableOptions = filteredOptions.filter(
488
+ (i) => i.type === "option" && !i.data.disabled
489
+ );
490
+ const selectableIndex = selectableOptions.findIndex(
491
+ (i) => i === item
492
+ );
493
+ const isHighlighted = selectableIndex === highlightedIndex;
494
+ return /* @__PURE__ */ jsxs(
495
+ "div",
496
+ {
497
+ className: cn(
498
+ multiSelectOptionVariants({
499
+ selected: isSelected,
500
+ disabled: !!option.disabled
501
+ }),
502
+ isHighlighted && "bg-surface"
503
+ ),
504
+ onClick: () => !option.disabled && handleSelect(option.value),
505
+ role: "option",
506
+ "aria-selected": isSelected,
507
+ "aria-disabled": option.disabled,
508
+ children: [
509
+ option.label,
510
+ isSelected && /* @__PURE__ */ jsx("span", { className: "absolute right-3 top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx(
511
+ Check,
512
+ {
513
+ className: cn("text-primary", iconSizes[size])
514
+ }
515
+ ) })
516
+ ]
517
+ },
518
+ option.value
519
+ );
520
+ }) })
521
+ }
522
+ ),
523
+ showBottomArrow && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-text-primary" }) })
524
+ ] });
525
+ const triggerButton = /* @__PURE__ */ jsxs(
526
+ "button",
527
+ {
528
+ ref: triggerRef,
529
+ type: "button",
530
+ className: cn(
531
+ multiSelectTriggerVariants({ status, size, fullWidth }),
532
+ loading && "opacity-50",
533
+ className
534
+ ),
535
+ disabled: disabled || loading,
536
+ "aria-haspopup": "listbox",
537
+ "aria-expanded": isOpen,
538
+ "aria-labelledby": label ? "multiselect-label" : void 0,
539
+ onClick: () => setIsOpen(!isOpen),
540
+ children: [
541
+ selectedOptions.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex gap-1 flex-shrink-0", children: [
542
+ selectedOptions.slice(0, visibleChipsCount).map((option) => /* @__PURE__ */ jsxs(
543
+ "span",
544
+ {
545
+ className: "inline-flex items-center gap-1 px-2 py-0.5 bg-primary/10 text-primary rounded text-sm whitespace-nowrap",
546
+ children: [
547
+ option.label,
548
+ /* @__PURE__ */ jsx(
549
+ "button",
550
+ {
551
+ type: "button",
552
+ onClick: (e) => handleRemoveChip(option.value, e),
553
+ className: "hover:text-error",
554
+ disabled,
555
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
556
+ }
557
+ )
558
+ ]
559
+ },
560
+ option.value
561
+ )),
562
+ selectedOptions.length > visibleChipsCount && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-2 py-0.5 bg-surface text-text-muted rounded text-sm whitespace-nowrap", children: [
563
+ "+",
564
+ selectedOptions.length - visibleChipsCount
565
+ ] })
566
+ ] }),
567
+ /* @__PURE__ */ jsx(
568
+ "div",
569
+ {
570
+ className: "absolute opacity-0 pointer-events-none whitespace-nowrap",
571
+ "aria-hidden": "true",
572
+ children: selectedOptions.map((option) => /* @__PURE__ */ jsxs(
573
+ "span",
574
+ {
575
+ "data-hidden-chip": true,
576
+ className: "inline-flex items-center gap-1 px-2 py-0.5 bg-primary/10 text-primary rounded text-sm whitespace-nowrap",
577
+ children: [
578
+ option.label,
579
+ /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
580
+ ]
581
+ },
582
+ option.value
583
+ ))
584
+ }
585
+ ),
586
+ /* @__PURE__ */ jsx(
587
+ "input",
588
+ {
589
+ ref: inputRef,
590
+ type: "text",
591
+ className: "bg-transparent outline-none cursor-pointer placeholder:text-text-muted flex-1 min-w-0 pointer-events-none",
592
+ placeholder: selectedOptions.length > 0 ? "" : placeholder,
593
+ value: searchQuery,
594
+ onChange: handleInputChange,
595
+ onFocus: handleInputFocus,
596
+ onKeyDown: handleInputKeyDown,
597
+ disabled: disabled || loading,
598
+ readOnly: !isOpen,
599
+ tabIndex: -1
600
+ }
601
+ ),
602
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 text-text-muted", children: loading ? /* @__PURE__ */ jsx(Loader2, { className: cn("animate-spin", iconSizes[size]) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
603
+ clearable && selectedOptions.length > 0 && !disabled && /* @__PURE__ */ jsx(
604
+ "button",
605
+ {
606
+ type: "button",
607
+ onClick: handleClear,
608
+ className: "hover:text-text-primary transition-colors",
609
+ "aria-label": "Clear selection",
610
+ children: /* @__PURE__ */ jsx(X, { className: iconSizes[size] })
611
+ }
612
+ ),
613
+ isOpen ? /* @__PURE__ */ jsx(Search, { className: iconSizes[size] }) : /* @__PURE__ */ jsx(ChevronDown, { className: iconSizes[size] })
614
+ ] }) })
615
+ ]
616
+ }
617
+ );
618
+ const selectElement = /* @__PURE__ */ jsx(
619
+ "div",
620
+ {
621
+ className: cn("relative group", fullWidth ? "w-full" : "inline-block"),
622
+ ref,
623
+ ...props,
624
+ children: /* @__PURE__ */ jsxs(
625
+ Popover,
626
+ {
627
+ open: isOpen,
628
+ onOpenChange: setIsOpen,
629
+ children: [
630
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: triggerButton }),
631
+ /* @__PURE__ */ jsx(
632
+ PopoverContent,
633
+ {
634
+ className: "p-0 w-[var(--radix-popover-trigger-width)]",
635
+ align: "start",
636
+ sideOffset: 4,
637
+ onOpenAutoFocus: (e) => {
638
+ e.preventDefault();
639
+ inputRef.current?.focus();
640
+ },
641
+ children: renderOptionsList()
642
+ }
643
+ )
644
+ ]
645
+ }
646
+ )
647
+ }
648
+ );
649
+ if (!label && !helperMessage) return selectElement;
650
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full flex flex-col", !fullWidth && "inline-block"), children: [
651
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
652
+ label && /* @__PURE__ */ jsx(
653
+ "label",
654
+ {
655
+ id: "multiselect-label",
656
+ className: "block mb-0.5",
657
+ children: /* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-text-muted", children: [
658
+ label,
659
+ required && /* @__PURE__ */ jsx("span", { className: "text-error ml-1", children: "*" })
660
+ ] })
661
+ }
662
+ ),
663
+ helperMessage && /* @__PURE__ */ jsx(
664
+ "p",
665
+ {
666
+ className: cn(
667
+ "text-xs",
668
+ status === "error" && "text-error",
669
+ status === "warning" && "text-warning",
670
+ status === "info" && "text-info",
671
+ status === "success" && "text-success",
672
+ status === "default" && "text-text-muted"
673
+ ),
674
+ children: helperMessage
675
+ }
676
+ )
677
+ ] }),
678
+ selectElement
679
+ ] });
680
+ }
681
+ );
682
+ MultiSelect.displayName = "MultiSelect";
683
+ var multi_select_default = MultiSelect;
684
+
685
+ export { multi_select_default };
686
+ //# sourceMappingURL=chunk-W7BQYIXF.js.map
687
+ //# sourceMappingURL=chunk-W7BQYIXF.js.map