entangle-ui 0.8.2 → 0.9.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 (203) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/dist/esm/assets/src/components/controls/Combobox/Combobox.css.ts.vanilla-B7B5ttkq.css +210 -0
  3. package/dist/esm/assets/src/components/controls/FileUploader/FileUploader.css.ts.vanilla-T4nRiI7s.css +194 -0
  4. package/dist/esm/assets/src/components/controls/MultiSelect/MultiSelect.css.ts.vanilla-CdYayqaF.css +311 -0
  5. package/dist/esm/assets/src/components/controls/TagInput/TagInput.css.ts.vanilla-hnkMOPp1.css +141 -0
  6. package/dist/esm/assets/src/components/data/DataTable/DataTable.css.ts.vanilla-CmRgtjIW.css +231 -0
  7. package/dist/esm/assets/src/components/feedback/Alert/{Alert.css.ts.vanilla-CRAI-xHx.css → Alert.css.ts.vanilla-CfCDsIEg.css} +2 -0
  8. package/dist/esm/assets/src/components/feedback/CommandPalette/CommandPalette.css.ts.vanilla-DGdrLKYZ.css +160 -0
  9. package/dist/esm/assets/src/components/feedback/Drawer/Drawer.css.ts.vanilla-CLPTOUrA.css +247 -0
  10. package/dist/esm/assets/src/components/feedback/Skeleton/SkeletonLayout.css.ts.vanilla-Db7bpqiI.css +75 -0
  11. package/dist/esm/assets/src/components/feedback/Stat/Stat.css.ts.vanilla-GBk3JAMB.css +69 -0
  12. package/dist/esm/assets/src/components/layout/Card/Card.css.ts.vanilla-Ducn1gUX.css +124 -0
  13. package/dist/esm/assets/src/components/navigation/Pagination/Pagination.css.ts.vanilla-CmlFyyjh.css +103 -0
  14. package/dist/esm/assets/src/components/primitives/HoverCard/HoverCard.css.ts.vanilla-BYT0qbLp.css +41 -0
  15. package/dist/esm/components/Icons/CloudUploadIcon.js +24 -0
  16. package/dist/esm/components/Icons/CloudUploadIcon.js.map +1 -0
  17. package/dist/esm/components/Icons/ExternalLinkIcon.js +26 -0
  18. package/dist/esm/components/Icons/ExternalLinkIcon.js.map +1 -0
  19. package/dist/esm/components/Icons/FirstIcon.js +23 -0
  20. package/dist/esm/components/Icons/FirstIcon.js.map +1 -0
  21. package/dist/esm/components/Icons/LastIcon.js +23 -0
  22. package/dist/esm/components/Icons/LastIcon.js.map +1 -0
  23. package/dist/esm/components/Icons/UnlinkIcon.js +26 -0
  24. package/dist/esm/components/Icons/UnlinkIcon.js.map +1 -0
  25. package/dist/esm/components/controls/Combobox/Combobox.css.js +20 -0
  26. package/dist/esm/components/controls/Combobox/Combobox.css.js.map +1 -0
  27. package/dist/esm/components/controls/Combobox/Combobox.js +354 -0
  28. package/dist/esm/components/controls/Combobox/Combobox.js.map +1 -0
  29. package/dist/esm/components/controls/FileUploader/FileUploader.css.js +20 -0
  30. package/dist/esm/components/controls/FileUploader/FileUploader.css.js.map +1 -0
  31. package/dist/esm/components/controls/FileUploader/FileUploader.js +264 -0
  32. package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -0
  33. package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js +23 -0
  34. package/dist/esm/components/controls/MultiSelect/MultiSelect.css.js.map +1 -0
  35. package/dist/esm/components/controls/MultiSelect/MultiSelect.js +269 -0
  36. package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -0
  37. package/dist/esm/components/controls/Select/Select.js +5 -4
  38. package/dist/esm/components/controls/Select/Select.js.map +1 -1
  39. package/dist/esm/components/controls/TagInput/TagInput.css.js +12 -0
  40. package/dist/esm/components/controls/TagInput/TagInput.css.js.map +1 -0
  41. package/dist/esm/components/controls/TagInput/TagInput.js +189 -0
  42. package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -0
  43. package/dist/esm/components/controls/TreeView/TreeNode.js +87 -1
  44. package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
  45. package/dist/esm/components/controls/VectorInput/VectorInput.js +87 -4
  46. package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
  47. package/dist/esm/components/data/DataTable/DataTable.css.js +25 -0
  48. package/dist/esm/components/data/DataTable/DataTable.css.js.map +1 -0
  49. package/dist/esm/components/data/DataTable/DataTable.js +502 -0
  50. package/dist/esm/components/data/DataTable/DataTable.js.map +1 -0
  51. package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js +87 -5
  52. package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
  53. package/dist/esm/components/editor/ChatPanel/ChatInput.js +87 -5
  54. package/dist/esm/components/editor/ChatPanel/ChatInput.js.map +1 -1
  55. package/dist/esm/components/editor/ChatPanel/ChatMessage.js +87 -2
  56. package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
  57. package/dist/esm/components/editor/PropertyInspector/PropertyRow.js +87 -3
  58. package/dist/esm/components/editor/PropertyInspector/PropertyRow.js.map +1 -1
  59. package/dist/esm/components/editor/PropertyInspector/PropertySection.js +87 -3
  60. package/dist/esm/components/editor/PropertyInspector/PropertySection.js.map +1 -1
  61. package/dist/esm/components/feedback/Alert/Alert.css.js +1 -1
  62. package/dist/esm/components/feedback/Alert/Alert.js +3 -2
  63. package/dist/esm/components/feedback/Alert/Alert.js.map +1 -1
  64. package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js +20 -0
  65. package/dist/esm/components/feedback/CommandPalette/CommandPalette.css.js.map +1 -0
  66. package/dist/esm/components/feedback/CommandPalette/CommandPalette.js +261 -0
  67. package/dist/esm/components/feedback/CommandPalette/CommandPalette.js.map +1 -0
  68. package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js +86 -0
  69. package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -0
  70. package/dist/esm/components/feedback/CommandPalette/useRecentItems.js +63 -0
  71. package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -0
  72. package/dist/esm/components/feedback/Dialog/DialogHeader.js +2 -1
  73. package/dist/esm/components/feedback/Dialog/DialogHeader.js.map +1 -1
  74. package/dist/esm/components/feedback/Drawer/Drawer.css.js +17 -0
  75. package/dist/esm/components/feedback/Drawer/Drawer.css.js.map +1 -0
  76. package/dist/esm/components/feedback/Drawer/Drawer.js +120 -0
  77. package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -0
  78. package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js +74 -0
  79. package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -0
  80. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js +18 -0
  81. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.css.js.map +1 -0
  82. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js +95 -0
  83. package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -0
  84. package/dist/esm/components/feedback/Stat/Stat.css.js +15 -0
  85. package/dist/esm/components/feedback/Stat/Stat.css.js.map +1 -0
  86. package/dist/esm/components/feedback/Stat/Stat.js +55 -0
  87. package/dist/esm/components/feedback/Stat/Stat.js.map +1 -0
  88. package/dist/esm/components/feedback/Toast/ToastItem.js +12 -15
  89. package/dist/esm/components/feedback/Toast/ToastItem.js.map +1 -1
  90. package/dist/esm/components/layout/Accordion/Accordion.js +2 -1
  91. package/dist/esm/components/layout/Accordion/Accordion.js.map +1 -1
  92. package/dist/esm/components/layout/Accordion/AccordionTrigger.js +2 -3
  93. package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
  94. package/dist/esm/components/layout/Card/Card.css.js +18 -0
  95. package/dist/esm/components/layout/Card/Card.css.js.map +1 -0
  96. package/dist/esm/components/layout/Card/Card.js +66 -0
  97. package/dist/esm/components/layout/Card/Card.js.map +1 -0
  98. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js +1 -0
  99. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
  100. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js +1 -0
  101. package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js.map +1 -1
  102. package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js +5 -0
  103. package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
  104. package/dist/esm/components/navigation/Pagination/Pagination.css.js +12 -0
  105. package/dist/esm/components/navigation/Pagination/Pagination.css.js.map +1 -0
  106. package/dist/esm/components/navigation/Pagination/Pagination.js +107 -0
  107. package/dist/esm/components/navigation/Pagination/Pagination.js.map +1 -0
  108. package/dist/esm/components/navigation/Pagination/usePagination.js +143 -0
  109. package/dist/esm/components/navigation/Pagination/usePagination.js.map +1 -0
  110. package/dist/esm/components/primitives/Avatar/Avatar.js +87 -1
  111. package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
  112. package/dist/esm/components/primitives/Badge/Badge.js +87 -1
  113. package/dist/esm/components/primitives/Badge/Badge.js.map +1 -1
  114. package/dist/esm/components/primitives/Checkbox/Checkbox.js +5 -2
  115. package/dist/esm/components/primitives/Checkbox/Checkbox.js.map +1 -1
  116. package/dist/esm/components/primitives/Collapsible/Collapsible.js +2 -3
  117. package/dist/esm/components/primitives/Collapsible/Collapsible.js.map +1 -1
  118. package/dist/esm/components/primitives/HoverCard/HoverCard.css.js +7 -0
  119. package/dist/esm/components/primitives/HoverCard/HoverCard.css.js.map +1 -0
  120. package/dist/esm/components/primitives/HoverCard/HoverCard.js +169 -0
  121. package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -0
  122. package/dist/esm/components/primitives/Icon/Icon.js +16 -2
  123. package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
  124. package/dist/esm/components/primitives/Link/Link.js +3 -3
  125. package/dist/esm/components/primitives/Link/Link.js.map +1 -1
  126. package/dist/esm/components/primitives/Popover/PopoverClose.js +2 -3
  127. package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
  128. package/dist/esm/components/primitives/Radio/Radio.js +1 -1
  129. package/dist/esm/hooks/useBreakpoint/useBreakpoint.js +44 -0
  130. package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -0
  131. package/dist/esm/hooks/useDebounced/useDebouncedCallback.js +97 -0
  132. package/dist/esm/hooks/useDebounced/useDebouncedCallback.js.map +1 -0
  133. package/dist/esm/hooks/useDebounced/useDebouncedValue.js +35 -0
  134. package/dist/esm/hooks/useDebounced/useDebouncedValue.js.map +1 -0
  135. package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js +73 -0
  136. package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -0
  137. package/dist/esm/hooks/useListboxNav/useListboxNav.js +181 -0
  138. package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -0
  139. package/dist/esm/hooks/useMediaQuery/useMediaQuery.js +54 -0
  140. package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -0
  141. package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js +78 -0
  142. package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -0
  143. package/dist/esm/index.js +25 -0
  144. package/dist/esm/index.js.map +1 -1
  145. package/dist/esm/theme/breakpoints.js +27 -0
  146. package/dist/esm/theme/breakpoints.js.map +1 -0
  147. package/dist/esm/theme/index.js +1 -0
  148. package/dist/esm/theme/index.js.map +1 -1
  149. package/dist/tokens/tokens.dark.css +1 -1
  150. package/dist/tokens/tokens.json +1 -1
  151. package/dist/tokens/tokens.light.css +1 -1
  152. package/dist/types/components/Icons/CloudUploadIcon.d.ts +27 -0
  153. package/dist/types/components/Icons/ExternalLinkIcon.d.ts +29 -0
  154. package/dist/types/components/Icons/FirstIcon.d.ts +26 -0
  155. package/dist/types/components/Icons/LastIcon.d.ts +26 -0
  156. package/dist/types/components/Icons/UnlinkIcon.d.ts +29 -0
  157. package/dist/types/components/controls/Combobox/Combobox.d.ts +29 -0
  158. package/dist/types/components/controls/Combobox/Combobox.types.d.ts +109 -0
  159. package/dist/types/components/controls/FileUploader/FileUploader.d.ts +34 -0
  160. package/dist/types/components/controls/FileUploader/FileUploader.types.d.ts +94 -0
  161. package/dist/types/components/controls/MultiSelect/MultiSelect.d.ts +31 -0
  162. package/dist/types/components/controls/MultiSelect/MultiSelect.types.d.ts +85 -0
  163. package/dist/types/components/controls/TagInput/TagInput.d.ts +24 -0
  164. package/dist/types/components/controls/TagInput/TagInput.types.d.ts +100 -0
  165. package/dist/types/components/data/DataTable/DataTable.d.ts +8 -0
  166. package/dist/types/components/data/DataTable/DataTable.types.d.ts +159 -0
  167. package/dist/types/components/feedback/Alert/Alert.d.ts +1 -0
  168. package/dist/types/components/feedback/Alert/Alert.types.d.ts +7 -0
  169. package/dist/types/components/feedback/CommandPalette/CommandPalette.d.ts +29 -0
  170. package/dist/types/components/feedback/CommandPalette/CommandPalette.types.d.ts +61 -0
  171. package/dist/types/components/feedback/CommandPalette/fuzzySearch.d.ts +6 -0
  172. package/dist/types/components/feedback/Drawer/Drawer.d.ts +12 -0
  173. package/dist/types/components/feedback/Drawer/Drawer.types.d.ts +70 -0
  174. package/dist/types/components/feedback/Skeleton/Skeleton.types.d.ts +44 -1
  175. package/dist/types/components/feedback/Skeleton/SkeletonLayout.d.ts +314 -0
  176. package/dist/types/components/feedback/Stat/Stat.d.ts +23 -0
  177. package/dist/types/components/feedback/Stat/Stat.types.d.ts +38 -0
  178. package/dist/types/components/layout/Accordion/Accordion.types.d.ts +7 -0
  179. package/dist/types/components/layout/Card/Card.d.ts +12 -0
  180. package/dist/types/components/layout/Card/Card.types.d.ts +54 -0
  181. package/dist/types/components/navigation/Pagination/Pagination.d.ts +22 -0
  182. package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +49 -0
  183. package/dist/types/components/primitives/Button/Button.d.ts +1 -1
  184. package/dist/types/components/primitives/HoverCard/HoverCard.d.ts +10 -0
  185. package/dist/types/components/primitives/HoverCard/HoverCard.types.d.ts +64 -0
  186. package/dist/types/components/primitives/Icon/Icon.d.ts +14 -1
  187. package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
  188. package/dist/types/hooks/useBreakpoint/useBreakpoint.d.ts +19 -0
  189. package/dist/types/hooks/useBreakpoint/useBreakpoint.types.d.ts +20 -0
  190. package/dist/types/hooks/useDebounced/useDebounced.types.d.ts +15 -0
  191. package/dist/types/hooks/useDebounced/useDebouncedCallback.d.ts +22 -0
  192. package/dist/types/hooks/useDebounced/useDebouncedValue.d.ts +16 -0
  193. package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.d.ts +22 -0
  194. package/dist/types/hooks/useIntersectionObserver/useIntersectionObserver.types.d.ts +22 -0
  195. package/dist/types/hooks/useListboxNav/useListboxNav.d.ts +75 -0
  196. package/dist/types/hooks/useMediaQuery/useMediaQuery.d.ts +19 -0
  197. package/dist/types/hooks/useMediaQuery/useMediaQuery.types.d.ts +6 -0
  198. package/dist/types/hooks/useThrottledCallback/useThrottledCallback.d.ts +23 -0
  199. package/dist/types/hooks/useThrottledCallback/useThrottledCallback.types.d.ts +13 -0
  200. package/dist/types/index.d.ts +43 -1
  201. package/dist/types/theme/breakpoints.d.ts +22 -0
  202. package/dist/types/theme/index.d.ts +1 -0
  203. package/package.json +3 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Combobox.js","sources":["../../../../../../src/components/controls/Combobox/Combobox.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA6CA;AACE;AACA;AACA;;AAGF;AAOA;AAIE;AACA;AACF;AAEA;AAKE;AAAY;;AAGZ;AACE;;;AAGE;;;;;;;;AAMA;;;;;AAIJ;AACA;AACF;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;AACG;AAqCJ;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAIA;;;;AAIE;AACD;AAED;;AAEsB;AAClB;;AAEF;AAIF;;;;AAIA;;;;AAKE;AACE;;AAEJ;;AAGA;;;;;AAcA;AACE;AAAY;AACZ;;AAEF;AAEA;;;AAKA;AACE;;;AAGI;AACA;AACD;;AAEH;;AAGF;;;AAEE;;;AAEA;;AAGF;AACE;;;AAEA;AACF;AAEA;;;AAGI;;AAEE;;;;;;AAGA;;AAEF;AACA;AAEI;AACA;AAEJ;;;AAMF;;AAEA;AACA;AACD;AAED;AACE;AACA;;AACA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;;AAGE;;AACA;AACF;;;AAIE;;AACA;AACE;;;AAKE;;AAEJ;AACA;AACA;AACE;AACF;AACF;AAEA;AAEI;AACA;;AAEA;AACA;AAAa;;;;;AAKf;AAIF;;;AAII;;;AAGI;;;;AAKJ;;AAEE;;AAEE;AACE;;;;;;;;AAKF;AACA;;;;;;AAOF;;;;AAGE;;;;AAKJ;AACF;;;;;;;;;;;;;AAcC;AAGH;;AAEE;AAAiB;AACnB;AAEA;;AAEE;;;AAGE;;;AAIJ;;;;;AAMI;AACA;AACF;AAIF;;;AAEE;AAAY;;AACP;AACL;AACF;AAEA;AACA;;;AAII;;AAMF;AACE;;;AAGA;;AAEA;AACA;AACA;AASQ;AACA;AACA;;;AAMJ;AAEE;AAAiB;AACnB;;AAMI;AAYV;AACF;AAEA;AACA;;;;;;;AAuBO;;AAaK;;;AAsDN;AA8BR;AAEA;;"}
@@ -0,0 +1,20 @@
1
+ import './../../../assets/src/components/controls/FileUploader/FileUploader.css.ts.vanilla-T4nRiI7s.css';
2
+ import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
3
+
4
+ var containerStyle = 'FileUploader_containerStyle__yf371x0';
5
+ var dropZoneHintStyle = 'FileUploader_dropZoneHintStyle__yf371xc';
6
+ var dropZonePrimaryStyle = 'FileUploader_dropZonePrimaryStyle__yf371xb';
7
+ var dropZoneRecipe = createRuntimeFn({defaultClassName:'FileUploader_dropZoneRecipe__yf371x1',variantClassNames:{size:{sm:'FileUploader_dropZoneRecipe_size_sm__yf371x2',md:'FileUploader_dropZoneRecipe_size_md__yf371x3',lg:'FileUploader_dropZoneRecipe_size_lg__yf371x4'},dragging:{true:'FileUploader_dropZoneRecipe_dragging_true__yf371x5',false:'FileUploader_dropZoneRecipe_dragging_false__yf371x6'},disabled:{true:'FileUploader_dropZoneRecipe_disabled_true__yf371x7',false:'FileUploader_dropZoneRecipe_disabled_false__yf371x8'},error:{true:'FileUploader_dropZoneRecipe_error_true__yf371x9',false:'FileUploader_dropZoneRecipe_error_false__yf371xa'}},defaultVariants:{size:'md',dragging:false,disabled:false,error:false},compoundVariants:[]});
8
+ var fileListStyle = 'FileUploader_fileListStyle__yf371xe';
9
+ var fileMetaStyle = 'FileUploader_fileMetaStyle__yf371xk';
10
+ var fileNameStyle = 'FileUploader_fileNameStyle__yf371xl';
11
+ var fileRowRecipe = createRuntimeFn({defaultClassName:'FileUploader_fileRowRecipe__yf371xf',variantClassNames:{status:{pending:'FileUploader_fileRowRecipe_status_pending__yf371xg',uploading:'FileUploader_fileRowRecipe_status_uploading__yf371xh',done:'FileUploader_fileRowRecipe_status_done__yf371xi',error:'FileUploader_fileRowRecipe_status_error__yf371xj'}},defaultVariants:{status:'pending'},compoundVariants:[]});
12
+ var fileSubStyle = 'FileUploader_fileSubStyle__yf371xm';
13
+ var hiddenInputStyle = 'FileUploader_hiddenInputStyle__yf371xd';
14
+ var progressBarStyle = 'FileUploader_progressBarStyle__yf371xn';
15
+ var progressFillRecipe = createRuntimeFn({defaultClassName:'FileUploader_progressFillRecipe__yf371xo',variantClassNames:{status:{pending:'FileUploader_progressFillRecipe_status_pending__yf371xp',uploading:'FileUploader_progressFillRecipe_status_uploading__yf371xq',done:'FileUploader_progressFillRecipe_status_done__yf371xr',error:'FileUploader_progressFillRecipe_status_error__yf371xs'}},defaultVariants:{status:'uploading'},compoundVariants:[]});
16
+ var removeButtonStyle = 'FileUploader_removeButtonStyle__yf371xt';
17
+ var statusBadgeRecipe = createRuntimeFn({defaultClassName:'FileUploader_statusBadgeRecipe__yf371xu',variantClassNames:{status:{pending:'FileUploader_statusBadgeRecipe_status_pending__yf371xv',uploading:'FileUploader_statusBadgeRecipe_status_uploading__yf371xw',done:'FileUploader_statusBadgeRecipe_status_done__yf371xx',error:'FileUploader_statusBadgeRecipe_status_error__yf371xy'}},defaultVariants:{status:'pending'},compoundVariants:[]});
18
+
19
+ export { containerStyle, dropZoneHintStyle, dropZonePrimaryStyle, dropZoneRecipe, fileListStyle, fileMetaStyle, fileNameStyle, fileRowRecipe, fileSubStyle, hiddenInputStyle, progressBarStyle, progressFillRecipe, removeButtonStyle, statusBadgeRecipe };
20
+ //# sourceMappingURL=FileUploader.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileUploader.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,264 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import React, { useId, useRef, useState, useCallback, useMemo } from 'react';
4
+ import { CloudUploadIcon } from '../../Icons/CloudUploadIcon.js';
5
+ import { TrashIcon } from '../../Icons/TrashIcon.js';
6
+ import { FormHelperText } from '../../form/FormHelperText.js';
7
+ import { FormLabel } from '../../form/FormLabel.js';
8
+ import { useControlledState } from '../../../hooks/useControlledState/useControlledState.js';
9
+ import { useMergedRef } from '../../../hooks/useMergedRef/useMergedRef.js';
10
+ import { cx } from '../../../utils/cx.js';
11
+ import { dropZonePrimaryStyle, dropZoneHintStyle, hiddenInputStyle, dropZoneRecipe, fileListStyle, containerStyle, fileMetaStyle, fileNameStyle, fileSubStyle, statusBadgeRecipe, progressBarStyle, progressFillRecipe, removeButtonStyle, fileRowRecipe } from './FileUploader.css.js';
12
+
13
+ let __fileUploaderIdCounter = 0;
14
+ function nextFileId() {
15
+ __fileUploaderIdCounter += 1;
16
+ return `eui-file-${String(Date.now())}-${String(__fileUploaderIdCounter)}`;
17
+ }
18
+ const SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
19
+ function formatBytes(bytes) {
20
+ if (!Number.isFinite(bytes) || bytes < 0)
21
+ return '';
22
+ if (bytes === 0)
23
+ return '0 B';
24
+ const exponent = Math.min(SIZE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
25
+ const value = bytes / Math.pow(1024, exponent);
26
+ const formatted = value >= 100 || exponent === 0 ? value.toFixed(0) : value.toFixed(1);
27
+ return `${formatted} ${SIZE_UNITS[exponent] ?? ''}`;
28
+ }
29
+ function fileMatchesAccept(file, accept) {
30
+ const tokens = accept
31
+ .split(',')
32
+ .map(t => t.trim().toLowerCase())
33
+ .filter(Boolean);
34
+ if (tokens.length === 0)
35
+ return true;
36
+ const name = file.name.toLowerCase();
37
+ const type = file.type.toLowerCase();
38
+ for (const token of tokens) {
39
+ if (token.startsWith('.')) {
40
+ if (name.endsWith(token))
41
+ return true;
42
+ }
43
+ else if (token.endsWith('/*')) {
44
+ const prefix = token.slice(0, -2);
45
+ if (type.startsWith(`${prefix}/`))
46
+ return true;
47
+ }
48
+ else if (token === type) {
49
+ return true;
50
+ }
51
+ }
52
+ return false;
53
+ }
54
+ /**
55
+ * Drag-and-drop file uploader with click-to-browse fallback. Renders a list
56
+ * of uploaded items with per-file status (`pending` / `uploading` / `done` /
57
+ * `error`) and an animated progress bar.
58
+ *
59
+ * The component itself is purely presentational around the file list — it
60
+ * does not perform the actual upload. The consumer is expected to:
61
+ * 1. Listen to `onFilesAdd` (or `onChange`) to know which files were
62
+ * accepted by the uploader.
63
+ * 2. Drive the upload (fetch / XHR / SDK) externally.
64
+ * 3. Reflect progress back into the component by passing a controlled
65
+ * `value` and updating the `status` / `progress` of each item.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * const [items, setItems] = useState<FileUploaderItem[]>([]);
70
+ * <FileUploader
71
+ * value={items}
72
+ * onChange={setItems}
73
+ * accept="image/*,.pdf"
74
+ * maxSize={5 * 1024 * 1024}
75
+ * onFilesAdd={files => uploadAll(files, setItems)}
76
+ * />
77
+ * ```
78
+ */
79
+ function FileUploader({ value, defaultValue, onChange, onFilesAdd, onFileRemove, onReject, accept, multiple = true, maxFiles, maxSize, minSize, validate, renderItem, size = 'md', label, helperText, error = false, errorMessage, disabled = false, required = false, dropZoneText = 'Drag files here or click to browse', dropZoneHint, name, className, style, testId, id: idProp, ref, ...rest }) {
80
+ const autoId = useId();
81
+ const fieldId = idProp ?? autoId;
82
+ const labelId = `${fieldId}-label`;
83
+ const helperId = `${fieldId}-helper`;
84
+ const inputRef = useRef(null);
85
+ const dropZoneRef = useRef(null);
86
+ const containerRef = useRef(null);
87
+ const setContainerRef = useMergedRef(containerRef, ref);
88
+ const [items, setItems] = useControlledState({
89
+ value,
90
+ defaultValue,
91
+ onChange,
92
+ fallback: [],
93
+ });
94
+ const [dragging, setDragging] = useState(false);
95
+ const dragCounterRef = useRef(0);
96
+ const validateFile = useCallback((file, currentCount) => {
97
+ if (typeof maxFiles === 'number' && currentCount >= maxFiles) {
98
+ return 'max-files';
99
+ }
100
+ if (accept && !fileMatchesAccept(file, accept)) {
101
+ return 'wrong-type';
102
+ }
103
+ if (typeof maxSize === 'number' && file.size > maxSize) {
104
+ return 'too-large';
105
+ }
106
+ if (typeof minSize === 'number' && file.size < minSize) {
107
+ return 'too-small';
108
+ }
109
+ if (validate) {
110
+ const result = validate(file);
111
+ if (result === false)
112
+ return 'invalid';
113
+ if (typeof result === 'string')
114
+ return result;
115
+ }
116
+ return null;
117
+ }, [accept, maxFiles, maxSize, minSize, validate]);
118
+ const acceptFiles = useCallback((incoming) => {
119
+ if (incoming.length === 0)
120
+ return;
121
+ const accepted = [];
122
+ const newItems = [];
123
+ let count = items.length;
124
+ const list = multiple ? incoming : incoming.slice(0, 1);
125
+ for (const file of list) {
126
+ const rejection = validateFile(file, count);
127
+ if (rejection) {
128
+ onReject?.(file, rejection);
129
+ continue;
130
+ }
131
+ accepted.push(file);
132
+ newItems.push({
133
+ id: nextFileId(),
134
+ file,
135
+ status: 'pending',
136
+ });
137
+ count += 1;
138
+ }
139
+ if (newItems.length === 0)
140
+ return;
141
+ // In single mode, replace any existing item.
142
+ const next = multiple ? [...items, ...newItems] : newItems;
143
+ setItems(next);
144
+ onFilesAdd?.(accepted);
145
+ }, [items, multiple, onFilesAdd, onReject, setItems, validateFile]);
146
+ const removeAt = useCallback((index) => {
147
+ if (index < 0 || index >= items.length)
148
+ return;
149
+ const removed = items[index];
150
+ const next = items.slice(0, index).concat(items.slice(index + 1));
151
+ setItems(next);
152
+ if (removed)
153
+ onFileRemove?.(removed);
154
+ }, [items, onFileRemove, setItems]);
155
+ const handleInputChange = useCallback((event) => {
156
+ const list = event.target.files;
157
+ if (!list)
158
+ return;
159
+ acceptFiles(Array.from(list));
160
+ // Reset so selecting the same file again still fires onChange.
161
+ event.target.value = '';
162
+ }, [acceptFiles]);
163
+ const openPicker = useCallback(() => {
164
+ if (disabled)
165
+ return;
166
+ inputRef.current?.click();
167
+ }, [disabled]);
168
+ const handleZoneKeyDown = useCallback((event) => {
169
+ if (disabled)
170
+ return;
171
+ if (event.key === 'Enter' || event.key === ' ') {
172
+ event.preventDefault();
173
+ openPicker();
174
+ }
175
+ }, [disabled, openPicker]);
176
+ const handleDragEnter = useCallback((event) => {
177
+ if (disabled)
178
+ return;
179
+ event.preventDefault();
180
+ dragCounterRef.current += 1;
181
+ if (dragCounterRef.current === 1)
182
+ setDragging(true);
183
+ }, [disabled]);
184
+ const handleDragOver = useCallback((event) => {
185
+ if (disabled)
186
+ return;
187
+ event.preventDefault();
188
+ if (event.dataTransfer)
189
+ event.dataTransfer.dropEffect = 'copy';
190
+ }, [disabled]);
191
+ const handleDragLeave = useCallback((event) => {
192
+ if (disabled)
193
+ return;
194
+ event.preventDefault();
195
+ dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
196
+ if (dragCounterRef.current === 0)
197
+ setDragging(false);
198
+ }, [disabled]);
199
+ const handleDrop = useCallback((event) => {
200
+ if (disabled)
201
+ return;
202
+ event.preventDefault();
203
+ dragCounterRef.current = 0;
204
+ setDragging(false);
205
+ const dt = event.dataTransfer;
206
+ if (!dt)
207
+ return;
208
+ const dropped = [];
209
+ if (dt.files.length > 0) {
210
+ dropped.push(...Array.from(dt.files));
211
+ }
212
+ else if (dt.items.length > 0) {
213
+ for (const item of Array.from(dt.items)) {
214
+ if (item.kind === 'file') {
215
+ const f = item.getAsFile();
216
+ if (f)
217
+ dropped.push(f);
218
+ }
219
+ }
220
+ }
221
+ acceptFiles(dropped);
222
+ }, [acceptFiles, disabled]);
223
+ const showHelperText = error && errorMessage ? errorMessage : helperText;
224
+ const renderRow = (item, index) => {
225
+ if (renderItem) {
226
+ return (jsx(React.Fragment, { children: renderItem({
227
+ item,
228
+ index,
229
+ disabled,
230
+ remove: () => {
231
+ removeAt(index);
232
+ },
233
+ }) }, item.id));
234
+ }
235
+ const progress = Math.max(0, Math.min(100, item.progress ?? 0));
236
+ const sub = item.errorMessage ?? formatBytes(item.file.size);
237
+ return (jsxs("div", { className: fileRowRecipe({ status: item.status }), "data-testid": testId ? `${testId}-item-${item.id}` : undefined, children: [jsxs("div", { className: fileMetaStyle, children: [jsx("span", { className: fileNameStyle, title: item.file.name, children: item.file.name }), jsxs("span", { className: fileSubStyle, children: [jsx("span", { className: statusBadgeRecipe({ status: item.status }), children: item.status }), jsx("span", { children: sub })] }), (item.status === 'uploading' ||
238
+ (item.status === 'pending' && progress > 0)) && (jsx("div", { className: progressBarStyle, "aria-hidden": "true", children: jsx("div", { className: progressFillRecipe({ status: item.status }), style: { width: `${String(progress)}%` } }) }))] }), jsx("button", { type: "button", className: removeButtonStyle, "aria-label": `Remove ${item.file.name}`, disabled: disabled, onClick: () => {
239
+ removeAt(index);
240
+ }, children: jsx(TrashIcon, { size: "sm", decorative: true }) })] }, item.id));
241
+ };
242
+ const acceptHint = useMemo(() => {
243
+ if (dropZoneHint !== undefined)
244
+ return dropZoneHint;
245
+ const parts = [];
246
+ if (accept)
247
+ parts.push(accept);
248
+ if (typeof maxSize === 'number')
249
+ parts.push(`up to ${formatBytes(maxSize)}`);
250
+ if (typeof maxFiles === 'number')
251
+ parts.push(`max ${String(maxFiles)} files`);
252
+ return parts.length > 0 ? parts.join(' • ') : null;
253
+ }, [accept, dropZoneHint, maxFiles, maxSize]);
254
+ return (jsxs("div", { ref: setContainerRef, className: cx(containerStyle, className), style: style, "data-testid": testId, ...rest, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: fieldId, required: required, disabled: disabled, children: label })), jsxs("div", { ref: dropZoneRef, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, "aria-labelledby": label ? labelId : undefined, "aria-describedby": showHelperText ? helperId : undefined, className: dropZoneRecipe({
255
+ size,
256
+ dragging,
257
+ disabled,
258
+ error,
259
+ }), onClick: openPicker, onKeyDown: handleZoneKeyDown, onDragEnter: handleDragEnter, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, "data-testid": testId ? `${testId}-dropzone` : undefined, children: [jsx(CloudUploadIcon, { size: size === 'lg' ? 32 : size === 'sm' ? 18 : 24, decorative: true }), jsx("span", { className: dropZonePrimaryStyle, children: dropZoneText }), acceptHint && jsx("span", { className: dropZoneHintStyle, children: acceptHint }), jsx("input", { ref: inputRef, id: fieldId, type: "file", name: name, accept: accept, multiple: multiple, required: required, disabled: disabled, onChange: handleInputChange, className: hiddenInputStyle, "aria-hidden": "true", tabIndex: -1, "data-testid": testId ? `${testId}-input` : undefined })] }), items.length > 0 && (jsx("div", { className: fileListStyle, children: items.map((item, idx) => renderRow(item, idx)) })), showHelperText && (jsx(FormHelperText, { id: helperId, error: error, children: showHelperText }))] }));
260
+ }
261
+ FileUploader.displayName = 'FileUploader';
262
+
263
+ export { FileUploader };
264
+ //# sourceMappingURL=FileUploader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileUploader.js","sources":["../../../../../../src/components/controls/FileUploader/FileUploader.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;AAgCA;AACA;;AAEE;AACF;AAEA;AAEA;;AAC4C;;AACzB;AACjB;AAIA;;;AAIF;AAEA;;;AAGK;;AAEH;AAAyB;;;AAKzB;AACE;AACE;AAA0B;;AACrB;;AAEL;AAAmC;;AAC9B;AACL;;;AAGJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;;AAgCD;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;;;;AAIE;AACD;;AAGD;;;AAKM;;;AAGA;;;AAGA;;;AAGA;;;AAGA;;AACsB;;AACU;;AAElC;AACF;AAIF;AAEI;;;;AAGA;AAEA;AAEA;;;AAGI;;;AAGF;;;;AAIE;AACD;;;AAIH;;;AAGA;;AAEA;AACF;AAIF;;;AAGI;;;AAGA;AAAa;;AAKjB;AAEI;AACA;;;;AAGA;AACF;AAIF;AACE;;AACA;AACF;AAEA;AAEI;;AACA;;AAEE;;AAEJ;AAIF;AAEI;;;AAEA;AACA;;AACF;AAIF;AAEI;;;;AAEwB;AAC1B;AAIF;AAEI;;;AAEA;AACA;;AACF;AAIF;AAEI;;;AAEA;;AAEA;AACA;;;;AAGE;;;AAEA;AACE;AACE;AACA;AAAO;;;;;AAKf;AAIF;AAEA;;AAEI;;;;;;;AASK;;;AAMP;AAEA;AAiBQ;;AAgBF;AAMR;AAEA;;AACkC;;AAEhC;AAAY;;;;;AAKZ;;;;;;;;AAgFJ;AAEA;;"}
@@ -0,0 +1,23 @@
1
+ import './../../../assets/src/components/controls/MultiSelect/MultiSelect.css.ts.vanilla-CdYayqaF.css';
2
+ import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
3
+
4
+ var checkboxRecipe = createRuntimeFn({defaultClassName:'MultiSelect_checkboxRecipe__1a6uxqy1e',variantClassNames:{selected:{true:'MultiSelect_checkboxRecipe_selected_true__1a6uxqy1f',false:'MultiSelect_checkboxRecipe_selected_false__1a6uxqy1g'}},defaultVariants:{selected:false},compoundVariants:[]});
5
+ var chevronRecipe = createRuntimeFn({defaultClassName:'MultiSelect_chevronRecipe__1a6uxqyy',variantClassNames:{open:{true:'MultiSelect_chevronRecipe_open_true__1a6uxqyz',false:'MultiSelect_chevronRecipe_open_false__1a6uxqy10'}},defaultVariants:{open:false},compoundVariants:[]});
6
+ var chipLabelStyle = 'MultiSelect_chipLabelStyle__1a6uxqyt';
7
+ var chipRecipe = createRuntimeFn({defaultClassName:'MultiSelect_chipRecipe__1a6uxqyp',variantClassNames:{size:{sm:'MultiSelect_chipRecipe_size_sm__1a6uxqyq',md:'MultiSelect_chipRecipe_size_md__1a6uxqyr',lg:'MultiSelect_chipRecipe_size_lg__1a6uxqys'}},defaultVariants:{size:'md'},compoundVariants:[]});
8
+ var chipRemoveStyle = 'MultiSelect_chipRemoveStyle__1a6uxqyu';
9
+ var clearButtonStyle = 'MultiSelect_clearButtonStyle__1a6uxqyx';
10
+ var containerStyle = 'MultiSelect_containerStyle__1a6uxqy0';
11
+ var dropdownStyle = 'MultiSelect_dropdownStyle__1a6uxqy12';
12
+ var emptyMessageStyle = 'MultiSelect_emptyMessageStyle__1a6uxqy1d';
13
+ var groupLabelStyle = 'MultiSelect_groupLabelStyle__1a6uxqy1c';
14
+ var moreBadgeStyle = 'MultiSelect_moreBadgeStyle__1a6uxqyv';
15
+ var optionItemRecipe = createRuntimeFn({defaultClassName:'MultiSelect_optionItemRecipe__1a6uxqy15',variantClassNames:{active:{true:'MultiSelect_optionItemRecipe_active_true__1a6uxqy16',false:'MultiSelect_optionItemRecipe_active_false__1a6uxqy17'},selected:{true:'MultiSelect_optionItemRecipe_selected_true__1a6uxqy18',false:'MultiSelect_optionItemRecipe_selected_false__1a6uxqy19'},disabled:{true:'MultiSelect_optionItemRecipe_disabled_true__1a6uxqy1a',false:'MultiSelect_optionItemRecipe_disabled_false__1a6uxqy1b'}},defaultVariants:{active:false,selected:false,disabled:false},compoundVariants:[]});
16
+ var optionsListStyle = 'MultiSelect_optionsListStyle__1a6uxqy14';
17
+ var placeholderStyle = 'MultiSelect_placeholderStyle__1a6uxqyw';
18
+ var searchInputStyle = 'MultiSelect_searchInputStyle__1a6uxqy13';
19
+ var triggerContentStyle = 'MultiSelect_triggerContentStyle__1a6uxqyo';
20
+ var triggerRecipe = createRuntimeFn({defaultClassName:'MultiSelect_triggerRecipe__1a6uxqy1',variantClassNames:{size:{sm:'MultiSelect_triggerRecipe_size_sm__1a6uxqy2',md:'MultiSelect_triggerRecipe_size_md__1a6uxqy3',lg:'MultiSelect_triggerRecipe_size_lg__1a6uxqy4'},variant:{'default':'MultiSelect_triggerRecipe_variant_default__1a6uxqy5',ghost:'MultiSelect_triggerRecipe_variant_ghost__1a6uxqy6',filled:'MultiSelect_triggerRecipe_variant_filled__1a6uxqy7'},open:{true:'MultiSelect_triggerRecipe_open_true__1a6uxqy8',false:'MultiSelect_triggerRecipe_open_false__1a6uxqy9'},error:{true:'MultiSelect_triggerRecipe_error_true__1a6uxqya',false:'MultiSelect_triggerRecipe_error_false__1a6uxqyb'},disabled:{true:'MultiSelect_triggerRecipe_disabled_true__1a6uxqyc',false:'MultiSelect_triggerRecipe_disabled_false__1a6uxqyd'},empty:{true:'MultiSelect_triggerRecipe_empty_true__1a6uxqye',false:'MultiSelect_triggerRecipe_empty_false__1a6uxqyf'}},defaultVariants:{size:'md',variant:'default',open:false,disabled:false,error:false,empty:true},compoundVariants:[[{open:true},'MultiSelect_triggerRecipe_compound_0__1a6uxqyg'],[{error:true,variant:'default'},'MultiSelect_triggerRecipe_compound_1__1a6uxqyh'],[{error:true,variant:'filled'},'MultiSelect_triggerRecipe_compound_2__1a6uxqyi'],[{open:true,variant:'default'},'MultiSelect_triggerRecipe_compound_3__1a6uxqyj'],[{open:true,variant:'filled'},'MultiSelect_triggerRecipe_compound_4__1a6uxqyk'],[{empty:true,variant:'default'},'MultiSelect_triggerRecipe_compound_5__1a6uxqyl'],[{empty:true,variant:'ghost'},'MultiSelect_triggerRecipe_compound_6__1a6uxqym'],[{empty:true,variant:'filled'},'MultiSelect_triggerRecipe_compound_7__1a6uxqyn']]});
21
+
22
+ export { checkboxRecipe, chevronRecipe, chipLabelStyle, chipRecipe, chipRemoveStyle, clearButtonStyle, containerStyle, dropdownStyle, emptyMessageStyle, groupLabelStyle, moreBadgeStyle, optionItemRecipe, optionsListStyle, placeholderStyle, searchInputStyle, triggerContentStyle, triggerRecipe };
23
+ //# sourceMappingURL=MultiSelect.css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiSelect.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,269 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { CheckIcon } from '../../Icons/CheckIcon.js';
6
+ import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
7
+ import { CloseIcon } from '../../Icons/CloseIcon.js';
8
+ import { FormHelperText } from '../../form/FormHelperText.js';
9
+ import { FormLabel } from '../../form/FormLabel.js';
10
+ import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
11
+ import { useControlledState } from '../../../hooks/useControlledState/useControlledState.js';
12
+ import { useListboxNav } from '../../../hooks/useListboxNav/useListboxNav.js';
13
+ import { useMergedRef } from '../../../hooks/useMergedRef/useMergedRef.js';
14
+ import { cx } from '../../../utils/cx.js';
15
+ import { triggerContentStyle, placeholderStyle, chipLabelStyle, chipRemoveStyle, chipRecipe, moreBadgeStyle, clearButtonStyle, chevronRecipe, triggerRecipe, dropdownStyle, searchInputStyle, optionsListStyle, containerStyle, emptyMessageStyle, groupLabelStyle, checkboxRecipe, optionItemRecipe } from './MultiSelect.css.js';
16
+
17
+ function isOptionGroup(item) {
18
+ return 'options' in item && Array.isArray(item.options);
19
+ }
20
+ function flattenOptions(items) {
21
+ const result = [];
22
+ for (const item of items) {
23
+ if (isOptionGroup(item)) {
24
+ result.push(...item.options);
25
+ }
26
+ else {
27
+ result.push(item);
28
+ }
29
+ }
30
+ return result;
31
+ }
32
+ function defaultFilter(option, query) {
33
+ const label = (option.label ?? option.value).toLowerCase();
34
+ return label.includes(query.toLowerCase());
35
+ }
36
+ const CHEVRON_SIZES = {
37
+ sm: 10,
38
+ md: 12,
39
+ lg: 14,
40
+ };
41
+ /**
42
+ * Multi-value select that lets the user pick several options from a dropdown.
43
+ * Selected values are rendered as inline chips inside the trigger; the rest
44
+ * collapse into a "+N more" badge.
45
+ *
46
+ * Supports searchable mode, grouped options, controlled and uncontrolled
47
+ * modes, keyboard navigation, an optional `max` cap, and a clear-all button.
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * <MultiSelect
52
+ * label="Tags"
53
+ * options={[
54
+ * { value: 'react', label: 'React' },
55
+ * { value: 'vue', label: 'Vue' },
56
+ * { value: 'svelte', label: 'Svelte' },
57
+ * ]}
58
+ * value={tags}
59
+ * onChange={setTags}
60
+ * />
61
+ * ```
62
+ */
63
+ function MultiSelect({ value, defaultValue, options, placeholder = 'Select...', searchable = false, searchPlaceholder = 'Search...', filterFn, emptyMessage = 'No results found', max, maxInlineChips = 3, size = 'md', variant = 'default', label, helperText, error = false, errorMessage, disabled = false, required = false, clearable = false, maxDropdownHeight = 240, minDropdownWidth, closeOnSelect = false, name, onChange, onOpenChange, className, style, testId, ref, id: idProp, ...rest }) {
64
+ const autoId = useId();
65
+ const fieldId = idProp ?? autoId;
66
+ const labelId = `${fieldId}-label`;
67
+ const helperId = `${fieldId}-helper`;
68
+ const listboxId = `${fieldId}-listbox`;
69
+ const triggerRef = useRef(null);
70
+ const dropdownRef = useRef(null);
71
+ const searchRef = useRef(null);
72
+ const setTriggerRef = useMergedRef(triggerRef, ref);
73
+ const [selected, setSelected] = useControlledState({
74
+ value,
75
+ defaultValue,
76
+ onChange,
77
+ fallback: [],
78
+ });
79
+ const [isOpen, setIsOpen] = useState(false);
80
+ const [searchQuery, setSearchQuery] = useState('');
81
+ const [dropdownPos, setDropdownPos] = useState({});
82
+ const allOptions = useMemo(() => flattenOptions(options), [options]);
83
+ const filter = filterFn ?? defaultFilter;
84
+ const filteredOptions = useMemo(() => {
85
+ if (!searchable || !searchQuery)
86
+ return allOptions;
87
+ return allOptions.filter(opt => filter(opt, searchQuery));
88
+ }, [allOptions, searchable, searchQuery, filter]);
89
+ const selectedSet = useMemo(() => new Set(selected), [selected]);
90
+ const toggleValue = useCallback((val, isDisabled) => {
91
+ if (isDisabled)
92
+ return;
93
+ const isSelected = selectedSet.has(val);
94
+ if (isSelected) {
95
+ setSelected(selected.filter(v => v !== val));
96
+ }
97
+ else {
98
+ if (typeof max === 'number' && selected.length >= max)
99
+ return;
100
+ setSelected([...selected, val]);
101
+ }
102
+ }, [max, selected, selectedSet, setSelected]);
103
+ const open = useCallback(() => {
104
+ if (disabled)
105
+ return;
106
+ setIsOpen(true);
107
+ setSearchQuery('');
108
+ onOpenChange?.(true);
109
+ }, [disabled, onOpenChange]);
110
+ const close = useCallback(() => {
111
+ setIsOpen(false);
112
+ setSearchQuery('');
113
+ onOpenChange?.(false);
114
+ triggerRef.current?.focus();
115
+ }, [onOpenChange]);
116
+ const listbox = useListboxNav({
117
+ items: filteredOptions,
118
+ isItemDisabled: opt => Boolean(opt.disabled),
119
+ onSelect: opt => {
120
+ toggleValue(opt.value, Boolean(opt.disabled));
121
+ if (closeOnSelect)
122
+ close();
123
+ },
124
+ onEscape: close,
125
+ });
126
+ const updateDropdownPosition = useCallback(() => {
127
+ const trigger = triggerRef.current;
128
+ if (!trigger)
129
+ return;
130
+ const rect = trigger.getBoundingClientRect();
131
+ const spaceBelow = window.innerHeight - rect.bottom;
132
+ const openAbove = spaceBelow < maxDropdownHeight + 8 && rect.top > spaceBelow;
133
+ const dropdownW = minDropdownWidth !== undefined
134
+ ? Math.max(rect.width, minDropdownWidth)
135
+ : rect.width;
136
+ setDropdownPos({
137
+ left: rect.left,
138
+ width: dropdownW,
139
+ ...(openAbove
140
+ ? {
141
+ bottom: window.innerHeight - rect.top + 4,
142
+ transformOrigin: 'bottom',
143
+ }
144
+ : { top: rect.bottom + 4, transformOrigin: 'top' }),
145
+ });
146
+ }, [maxDropdownHeight, minDropdownWidth]);
147
+ useEffect(() => {
148
+ if (!isOpen)
149
+ return;
150
+ updateDropdownPosition();
151
+ if (searchable) {
152
+ requestAnimationFrame(() => {
153
+ searchRef.current?.focus();
154
+ });
155
+ }
156
+ }, [isOpen, updateDropdownPosition, searchable]);
157
+ // Outside click handling
158
+ useEffect(() => {
159
+ if (!isOpen)
160
+ return;
161
+ const handleClick = (e) => {
162
+ const target = e.target;
163
+ if (!triggerRef.current?.contains(target) &&
164
+ !dropdownRef.current?.contains(target)) {
165
+ close();
166
+ }
167
+ };
168
+ document.addEventListener('mousedown', handleClick);
169
+ return () => {
170
+ document.removeEventListener('mousedown', handleClick);
171
+ };
172
+ }, [isOpen, close]);
173
+ const handleTriggerKeyDown = useCallback((e) => {
174
+ if (disabled)
175
+ return;
176
+ switch (e.key) {
177
+ case 'Enter':
178
+ case ' ':
179
+ case 'ArrowDown':
180
+ case 'ArrowUp':
181
+ e.preventDefault();
182
+ if (!isOpen)
183
+ open();
184
+ break;
185
+ }
186
+ }, [disabled, isOpen, open]);
187
+ const handleDropdownKeyDown = useCallback((e) => {
188
+ listbox.handleKeyDown(e);
189
+ }, [listbox]);
190
+ const removeValue = useCallback((val) => {
191
+ setSelected(selected.filter(v => v !== val));
192
+ }, [selected, setSelected]);
193
+ const handleClearAll = useCallback((e) => {
194
+ e.stopPropagation();
195
+ setSelected([]);
196
+ }, [setSelected]);
197
+ const showHelperText = error && errorMessage ? errorMessage : helperText;
198
+ const chevronSize = CHEVRON_SIZES[size];
199
+ const selectedOptions = useMemo(() => selected
200
+ .map(v => allOptions.find(o => o.value === v))
201
+ .filter((o) => Boolean(o)), [allOptions, selected]);
202
+ const isEmpty = selected.length === 0;
203
+ const visibleChips = maxInlineChips === Infinity
204
+ ? selectedOptions
205
+ : selectedOptions.slice(0, maxInlineChips);
206
+ const overflowCount = selectedOptions.length - visibleChips.length;
207
+ // Render options
208
+ const renderOptionsList = () => {
209
+ if (filteredOptions.length === 0) {
210
+ return jsx("div", { className: emptyMessageStyle, children: emptyMessage });
211
+ }
212
+ const hasGroups = options.some(isOptionGroup);
213
+ if (hasGroups && !searchQuery) {
214
+ let flatIndex = 0;
215
+ return options.map((item, groupIdx) => {
216
+ if (isOptionGroup(item)) {
217
+ const groupOptions = item.options;
218
+ const startIdx = flatIndex;
219
+ flatIndex += groupOptions.length;
220
+ return (jsxs("div", { role: "group", "aria-label": item.label, children: [jsx("div", { className: groupLabelStyle, children: item.label }), groupOptions.map((opt, optIdx) => renderOptionItem(opt, startIdx + optIdx))] }, `group-${String(groupIdx)}`));
221
+ }
222
+ const idx = flatIndex;
223
+ flatIndex += 1;
224
+ return renderOptionItem(item, idx);
225
+ });
226
+ }
227
+ return filteredOptions.map((opt, idx) => renderOptionItem(opt, idx));
228
+ };
229
+ const renderOptionItem = (opt, index) => {
230
+ const isSelected = selectedSet.has(opt.value);
231
+ const isActive = index === listbox.activeIndex;
232
+ const isDisabled = opt.disabled ?? false;
233
+ const reachedMax = typeof max === 'number' && !isSelected && selected.length >= max;
234
+ const interactiveDisabled = isDisabled || reachedMax;
235
+ return (jsxs("div", { role: "option", "aria-selected": isSelected, "aria-disabled": interactiveDisabled || undefined, id: `${listboxId}-${opt.value}`, className: optionItemRecipe({
236
+ active: isActive,
237
+ selected: isSelected,
238
+ disabled: interactiveDisabled,
239
+ }), onClick: () => {
240
+ if (!interactiveDisabled) {
241
+ toggleValue(opt.value, false);
242
+ if (closeOnSelect)
243
+ close();
244
+ }
245
+ }, onMouseEnter: () => {
246
+ if (!interactiveDisabled)
247
+ listbox.setActiveIndex(index);
248
+ }, children: [jsx("span", { className: checkboxRecipe({ selected: isSelected }), "aria-hidden": "true", children: isSelected && jsx(CheckIcon, { size: 10, decorative: true }) }), opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value })] }, opt.value));
249
+ };
250
+ const formValue = selected.join(',');
251
+ return (jsxs("div", { className: cx(containerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: fieldId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, id: fieldId, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () => (isOpen ? close() : open()), onKeyDown: handleTriggerKeyDown, className: triggerRecipe({
252
+ size,
253
+ variant,
254
+ open: isOpen,
255
+ disabled,
256
+ error,
257
+ empty: isEmpty,
258
+ }), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: isEmpty ? (jsx("span", { className: placeholderStyle, children: placeholder })) : (jsxs(Fragment, { children: [visibleChips.map(opt => (jsxs("span", { className: chipRecipe({ size }), "data-testid": testId ? `${testId}-chip-${opt.value}` : undefined, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { className: chipLabelStyle, children: opt.label ?? opt.value }), !disabled && (jsx("span", { role: "button", tabIndex: -1, "aria-label": `Remove ${opt.label ?? opt.value}`, className: chipRemoveStyle, onClick: e => {
259
+ e.stopPropagation();
260
+ removeValue(opt.value);
261
+ }, children: jsx(CloseIcon, { size: "sm", decorative: true }) }))] }, opt.value))), overflowCount > 0 && (jsxs("span", { className: moreBadgeStyle, children: ["+", overflowCount, " more"] }))] })) }), clearable && !isEmpty && !disabled && (jsx("span", { role: "button", tabIndex: -1, "aria-label": "Clear selection", className: clearButtonStyle, onClick: handleClearAll, children: jsx(CloseIcon, { size: "sm", decorative: true }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize, decorative: true }) })] }), name && jsx("input", { type: "hidden", name: name, value: formValue }), isOpen &&
262
+ createPortal(jsxs("div", { ref: dropdownRef, role: "listbox", id: listboxId, "aria-multiselectable": "true", "aria-labelledby": label ? labelId : undefined, className: dropdownStyle, style: dropdownPos, onKeyDown: handleDropdownKeyDown, tabIndex: -1, children: [searchable && (jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: e => {
263
+ setSearchQuery(e.target.value);
264
+ }, onKeyDown: handleDropdownKeyDown, className: searchInputStyle, "aria-label": "Search options" })), jsx(ScrollArea, { className: optionsListStyle, maxHeight: maxDropdownHeight - (searchable ? 32 : 0), scrollbarWidth: 4, scrollbarVisibility: "auto", hideDelay: 600, children: renderOptionsList() })] }), document.body), showHelperText && (jsx(FormHelperText, { id: helperId, error: error, children: showHelperText }))] }));
265
+ }
266
+ MultiSelect.displayName = 'MultiSelect';
267
+
268
+ export { MultiSelect };
269
+ //# sourceMappingURL=MultiSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiSelect.js","sources":["../../../../../../src/components/controls/MultiSelect/MultiSelect.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;AA+CA;AAGE;AACF;AAEA;;AAIE;AACE;;;;AAGE;;;AAGJ;AACF;AAEA;AAIE;;AAEF;AAEA;AACE;AACA;AACA;;AAGF;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG;AAiCJ;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAIA;;;;AAIE;AACD;;;;AAMD;AAEA;AACA;AACE;AAAiC;AACjC;;AAGF;;AAII;;;;AAGE;;;;;;;;AASN;AACE;;;;AAGA;AACF;AAEA;;;AAGE;AACA;AACF;;AAGE;;;AAGE;AACA;AAAmB;;AAErB;AACD;AAED;AACE;AACA;;AACA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;;AAGE;;AACA;;;AAGI;AACF;;;;;AAMF;;AACA;AACE;;;AAKE;;AAEJ;AACA;AACA;AACE;AACF;AACF;AAEA;AAEI;;AACA;AACE;AACA;AACA;AACA;;AAEE;AAAa;;;;AAOrB;AAEI;AACF;AAIF;AAEI;AACF;AAIF;;;AAIE;AAIF;AACA;AAEA;AAGO;AACA;AAIP;AACA;AAEI;;;;;AAMF;AACE;;;AAIF;;;AAGI;AACE;;AAEA;;;;;AAgBF;AACF;;AAGF;AACF;AAEA;;AAEE;AACA;AACA;AAEA;;AAUM;AACA;AACA;AACD;;AAGG;AACA;AAAmB;;AAEvB;AAEE;AAA0B;;AAalC;;AAIA;;;AA+BQ;;;AAGA;;;AA8Bc;;AAmChB;AAmBU;AACF;AA0BhB;AAEA;;"}
@@ -2,6 +2,9 @@
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
+ import { CheckIcon } from '../../Icons/CheckIcon.js';
6
+ import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
7
+ import { CloseIcon } from '../../Icons/CloseIcon.js';
5
8
  import { FormLabel } from '../../form/FormLabel.js';
6
9
  import { FormHelperText } from '../../form/FormHelperText.js';
7
10
  import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
@@ -34,8 +37,6 @@ const CHEVRON_SIZES = {
34
37
  md: 12,
35
38
  lg: 14,
36
39
  };
37
- // --- Chevron icon ---
38
- const ChevronDownIcon = ({ size }) => (jsx("svg", { width: size, height: size, viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: jsx("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
39
40
  // --- Component ---
40
41
  /**
41
42
  * Select component for choosing single values from a dropdown list.
@@ -305,7 +306,7 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
305
306
  if (!opt.disabled) {
306
307
  setHighlightedIndex(index);
307
308
  }
308
- }, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value }), isSelected && (jsx("span", { className: checkmarkStyle, children: jsx("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true", children: jsx("path", { d: "M2 6L5 9L10 3", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }, opt.value));
309
+ }, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value }), isSelected && (jsx("span", { className: checkmarkStyle, children: jsx(CheckIcon, { size: 10, decorative: true }) }))] }, opt.value));
309
310
  };
310
311
  return (jsxs("div", { className: cx(selectContainerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: selectId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () => (isOpen ? close() : open()), onKeyDown: handleTriggerKeyDown, className: triggerRecipe({
311
312
  size,
@@ -314,7 +315,7 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
314
315
  disabled,
315
316
  error,
316
317
  hasValue: currentValue !== null,
317
- }), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.icon && jsx("span", { children: selectedOption.icon }), jsx("span", { children: selectedOption.label ?? selectedOption.value })] })) : (jsx("span", { children: placeholder })) }), clearable && currentValue !== null && (jsx("span", { className: clearButtonStyle, role: "button", onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", "aria-hidden": "true", children: jsx("path", { d: "M1 1L7 7M7 1L1 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize }) })] }), name && jsx("input", { type: "hidden", name: name, value: currentValue ?? '' }), isOpen &&
318
+ }), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.icon && jsx("span", { children: selectedOption.icon }), jsx("span", { children: selectedOption.label ?? selectedOption.value })] })) : (jsx("span", { children: placeholder })) }), clearable && currentValue !== null && (jsx("span", { className: clearButtonStyle, role: "button", onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx(CloseIcon, { size: "sm", decorative: true }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize, decorative: true }) })] }), name && jsx("input", { type: "hidden", name: name, value: currentValue ?? '' }), isOpen &&
318
319
  createPortal(jsxs("div", { ref: dropdownRef, role: "listbox", id: listboxId, "aria-labelledby": label ? labelId : undefined, className: dropdownStyle, style: dropdownStyleState, onKeyDown: handleDropdownKeyDown, tabIndex: -1, children: [searchable && (jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: e => {
319
320
  setSearchQuery(e.target.value);
320
321
  setHighlightedIndex(-1);