@transferwise/components 0.0.0-experimental-57e70f3 → 0.0.0-experimental-09066ec

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 (563) hide show
  1. package/build/dateInput/DateInput.js +12 -5
  2. package/build/dateInput/DateInput.js.map +1 -1
  3. package/build/dateInput/DateInput.mjs +11 -4
  4. package/build/dateInput/DateInput.mjs.map +1 -1
  5. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js +16 -8
  6. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js.map +1 -1
  7. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs +14 -6
  8. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs.map +1 -1
  9. package/build/index.js +12 -7
  10. package/build/index.js.map +1 -1
  11. package/build/index.mjs +9 -3
  12. package/build/index.mjs.map +1 -1
  13. package/build/inputs/{_BottomSheet.js → SelectInput/BottomSheet/SelectInputBottomSheet.js} +7 -7
  14. package/build/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.js.map +1 -0
  15. package/build/inputs/{_BottomSheet.mjs → SelectInput/BottomSheet/SelectInputBottomSheet.mjs} +7 -7
  16. package/build/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.mjs.map +1 -0
  17. package/build/inputs/{_ButtonInput.js → SelectInput/ButtonInput/SelectInputButtonInput.js} +5 -5
  18. package/build/inputs/SelectInput/ButtonInput/SelectInputButtonInput.js.map +1 -0
  19. package/build/inputs/{_ButtonInput.mjs → SelectInput/ButtonInput/SelectInputButtonInput.mjs} +5 -5
  20. package/build/inputs/SelectInput/ButtonInput/SelectInputButtonInput.mjs.map +1 -0
  21. package/build/inputs/SelectInput/ClearButton/SelectInputClearButton.js +26 -0
  22. package/build/inputs/SelectInput/ClearButton/SelectInputClearButton.js.map +1 -0
  23. package/build/inputs/SelectInput/ClearButton/SelectInputClearButton.mjs +24 -0
  24. package/build/inputs/SelectInput/ClearButton/SelectInputClearButton.mjs.map +1 -0
  25. package/build/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.js +59 -0
  26. package/build/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.js.map +1 -0
  27. package/build/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.mjs +56 -0
  28. package/build/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.mjs.map +1 -0
  29. package/build/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.js +50 -0
  30. package/build/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.js.map +1 -0
  31. package/build/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.mjs +48 -0
  32. package/build/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.mjs.map +1 -0
  33. package/build/inputs/SelectInput/ItemView/SelectInputItemView.js +47 -0
  34. package/build/inputs/SelectInput/ItemView/SelectInputItemView.js.map +1 -0
  35. package/build/inputs/SelectInput/ItemView/SelectInputItemView.mjs +45 -0
  36. package/build/inputs/SelectInput/ItemView/SelectInputItemView.mjs.map +1 -0
  37. package/build/inputs/SelectInput/Option/SelectInputOption.js +42 -0
  38. package/build/inputs/SelectInput/Option/SelectInputOption.js.map +1 -0
  39. package/build/inputs/SelectInput/Option/SelectInputOption.mjs +40 -0
  40. package/build/inputs/SelectInput/Option/SelectInputOption.mjs.map +1 -0
  41. package/build/inputs/SelectInput/OptionContent/SelectInputOptionContent.js +40 -0
  42. package/build/inputs/SelectInput/OptionContent/SelectInputOptionContent.js.map +1 -0
  43. package/build/inputs/SelectInput/OptionContent/SelectInputOptionContent.mjs +38 -0
  44. package/build/inputs/SelectInput/OptionContent/SelectInputOptionContent.mjs.map +1 -0
  45. package/build/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.js +48 -0
  46. package/build/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.js.map +1 -0
  47. package/build/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.mjs +46 -0
  48. package/build/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.mjs.map +1 -0
  49. package/build/inputs/SelectInput/Options/SelectInputOptions.js +300 -0
  50. package/build/inputs/SelectInput/Options/SelectInputOptions.js.map +1 -0
  51. package/build/inputs/SelectInput/Options/SelectInputOptions.mjs +298 -0
  52. package/build/inputs/SelectInput/Options/SelectInputOptions.mjs.map +1 -0
  53. package/build/inputs/{_Popover.js → SelectInput/Popover/SelectInputPopover.js} +7 -7
  54. package/build/inputs/SelectInput/Popover/SelectInputPopover.js.map +1 -0
  55. package/build/inputs/{_Popover.mjs → SelectInput/Popover/SelectInputPopover.mjs} +7 -7
  56. package/build/inputs/SelectInput/Popover/SelectInputPopover.mjs.map +1 -0
  57. package/build/inputs/SelectInput/SelectInput.contexts.js +29 -0
  58. package/build/inputs/SelectInput/SelectInput.contexts.js.map +1 -0
  59. package/build/inputs/SelectInput/SelectInput.contexts.mjs +24 -0
  60. package/build/inputs/SelectInput/SelectInput.contexts.mjs.map +1 -0
  61. package/build/inputs/SelectInput/SelectInput.js +222 -0
  62. package/build/inputs/SelectInput/SelectInput.js.map +1 -0
  63. package/build/inputs/SelectInput/SelectInput.messages.js.map +1 -0
  64. package/build/inputs/SelectInput/SelectInput.messages.mjs.map +1 -0
  65. package/build/inputs/SelectInput/SelectInput.mjs +216 -0
  66. package/build/inputs/SelectInput/SelectInput.mjs.map +1 -0
  67. package/build/inputs/SelectInput/SelectInput.utils.js +164 -0
  68. package/build/inputs/SelectInput/SelectInput.utils.js.map +1 -0
  69. package/build/inputs/SelectInput/SelectInput.utils.mjs +154 -0
  70. package/build/inputs/SelectInput/SelectInput.utils.mjs.map +1 -0
  71. package/build/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.js +42 -0
  72. package/build/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.js.map +1 -0
  73. package/build/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.mjs +36 -0
  74. package/build/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.mjs.map +1 -0
  75. package/build/main.css +97 -27094
  76. package/build/moneyInput/MoneyInput.js +9 -2
  77. package/build/moneyInput/MoneyInput.js.map +1 -1
  78. package/build/moneyInput/MoneyInput.mjs +8 -1
  79. package/build/moneyInput/MoneyInput.mjs.map +1 -1
  80. package/build/phoneNumberInput/PhoneNumberInput.js +10 -3
  81. package/build/phoneNumberInput/PhoneNumberInput.js.map +1 -1
  82. package/build/phoneNumberInput/PhoneNumberInput.mjs +9 -2
  83. package/build/phoneNumberInput/PhoneNumberInput.mjs.map +1 -1
  84. package/build/styles/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.css +96 -0
  85. package/build/styles/inputs/SelectInput/ButtonInput/SelectInputButtonInput.css +16 -0
  86. package/build/styles/inputs/SelectInput/ClearButton/SelectInputClearButton.css +46 -0
  87. package/build/styles/inputs/SelectInput/ItemView/SelectInputItemView.css +16 -0
  88. package/build/styles/inputs/SelectInput/Option/SelectInputOption.css +33 -0
  89. package/build/styles/inputs/SelectInput/OptionContent/SelectInputOptionContent.css +37 -0
  90. package/build/styles/inputs/SelectInput/Options/SelectInputOptions.css +81 -0
  91. package/build/styles/inputs/SelectInput/Popover/SelectInputPopover.css +46 -0
  92. package/build/styles/main.css +97 -27094
  93. package/build/types/index.d.ts +1 -1
  94. package/build/types/index.d.ts.map +1 -1
  95. package/build/types/inputs/{_BottomSheet.d.ts → SelectInput/BottomSheet/SelectInputBottomSheet.d.ts} +3 -3
  96. package/build/types/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.d.ts.map +1 -0
  97. package/build/types/inputs/SelectInput/BottomSheet/index.d.ts +3 -0
  98. package/build/types/inputs/SelectInput/BottomSheet/index.d.ts.map +1 -0
  99. package/build/types/inputs/SelectInput/ButtonInput/SelectInputButtonInput.d.ts +5 -0
  100. package/build/types/inputs/SelectInput/ButtonInput/SelectInputButtonInput.d.ts.map +1 -0
  101. package/build/types/inputs/SelectInput/ButtonInput/index.d.ts +3 -0
  102. package/build/types/inputs/SelectInput/ButtonInput/index.d.ts.map +1 -0
  103. package/build/types/inputs/SelectInput/ClearButton/SelectInputClearButton.d.ts +7 -0
  104. package/build/types/inputs/SelectInput/ClearButton/SelectInputClearButton.d.ts.map +1 -0
  105. package/build/types/inputs/SelectInput/ClearButton/index.d.ts +3 -0
  106. package/build/types/inputs/SelectInput/ClearButton/index.d.ts.map +1 -0
  107. package/build/types/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.d.ts +16 -0
  108. package/build/types/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.d.ts.map +1 -0
  109. package/build/types/inputs/SelectInput/DefaultRenderTrigger/index.d.ts +2 -0
  110. package/build/types/inputs/SelectInput/DefaultRenderTrigger/index.d.ts.map +1 -0
  111. package/build/types/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.d.ts +9 -0
  112. package/build/types/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.d.ts.map +1 -0
  113. package/build/types/inputs/SelectInput/ItemView/GroupItemView/index.d.ts +3 -0
  114. package/build/types/inputs/SelectInput/ItemView/GroupItemView/index.d.ts.map +1 -0
  115. package/build/types/inputs/SelectInput/ItemView/SelectInputItemView.d.ts +11 -0
  116. package/build/types/inputs/SelectInput/ItemView/SelectInputItemView.d.ts.map +1 -0
  117. package/build/types/inputs/SelectInput/ItemView/index.d.ts +4 -0
  118. package/build/types/inputs/SelectInput/ItemView/index.d.ts.map +1 -0
  119. package/build/types/inputs/SelectInput/Option/SelectInputOption.d.ts +11 -0
  120. package/build/types/inputs/SelectInput/Option/SelectInputOption.d.ts.map +1 -0
  121. package/build/types/inputs/SelectInput/Option/index.d.ts +3 -0
  122. package/build/types/inputs/SelectInput/Option/index.d.ts.map +1 -0
  123. package/build/types/inputs/SelectInput/OptionContent/SelectInputOptionContent.d.ts +13 -0
  124. package/build/types/inputs/SelectInput/OptionContent/SelectInputOptionContent.d.ts.map +1 -0
  125. package/build/types/inputs/SelectInput/OptionContent/index.d.ts +3 -0
  126. package/build/types/inputs/SelectInput/OptionContent/index.d.ts.map +1 -0
  127. package/build/types/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.d.ts +9 -0
  128. package/build/types/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.d.ts.map +1 -0
  129. package/build/types/inputs/SelectInput/Options/OptionsContainer/index.d.ts +3 -0
  130. package/build/types/inputs/SelectInput/Options/OptionsContainer/index.d.ts.map +1 -0
  131. package/build/types/inputs/SelectInput/Options/SelectInputOptions.d.ts +21 -0
  132. package/build/types/inputs/SelectInput/Options/SelectInputOptions.d.ts.map +1 -0
  133. package/build/types/inputs/SelectInput/Options/index.d.ts +4 -0
  134. package/build/types/inputs/SelectInput/Options/index.d.ts.map +1 -0
  135. package/build/types/inputs/{_Popover.d.ts → SelectInput/Popover/SelectInputPopover.d.ts} +3 -3
  136. package/build/types/inputs/SelectInput/Popover/SelectInputPopover.d.ts.map +1 -0
  137. package/build/types/inputs/SelectInput/Popover/index.d.ts +3 -0
  138. package/build/types/inputs/SelectInput/Popover/index.d.ts.map +1 -0
  139. package/build/types/inputs/SelectInput/SelectInput.contexts.d.ts +33 -0
  140. package/build/types/inputs/SelectInput/SelectInput.contexts.d.ts.map +1 -0
  141. package/build/types/inputs/SelectInput/SelectInput.d.ts +10 -0
  142. package/build/types/inputs/SelectInput/SelectInput.d.ts.map +1 -0
  143. package/build/types/inputs/SelectInput/SelectInput.messages.d.ts.map +1 -0
  144. package/build/types/inputs/{SelectInput.d.ts → SelectInput/SelectInput.types.d.ts} +12 -38
  145. package/build/types/inputs/SelectInput/SelectInput.types.d.ts.map +1 -0
  146. package/build/types/inputs/SelectInput/SelectInput.utils.d.ts +60 -0
  147. package/build/types/inputs/SelectInput/SelectInput.utils.d.ts.map +1 -0
  148. package/build/types/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts +12 -0
  149. package/build/types/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts.map +1 -0
  150. package/build/types/inputs/SelectInput/TriggerButton/index.d.ts +3 -0
  151. package/build/types/inputs/SelectInput/TriggerButton/index.d.ts.map +1 -0
  152. package/build/types/inputs/SelectInput/components.d.ts +10 -0
  153. package/build/types/inputs/SelectInput/components.d.ts.map +1 -0
  154. package/build/types/inputs/SelectInput/index.d.ts +10 -0
  155. package/build/types/inputs/SelectInput/index.d.ts.map +1 -0
  156. package/package.json +4 -8
  157. package/src/accordion/Accordion.css +74 -0
  158. package/src/accordion/Accordion.less +3 -3
  159. package/src/actionButton/ActionButton.css +64 -0
  160. package/src/actionButton/ActionButton.less +1 -1
  161. package/src/actionOption/ActionOption.css +4 -0
  162. package/src/alert/Alert.css +7 -0
  163. package/src/avatar/Avatar.css +250 -0
  164. package/src/avatar/Avatar.less +3 -3
  165. package/src/avatarLayout/AvatarLayout.css +42 -0
  166. package/src/avatarView/AvatarView.css +44 -0
  167. package/src/avatarView/Dot.css +26 -0
  168. package/src/badge/Badge.css +65 -0
  169. package/src/badge/Badge.less +1 -1
  170. package/src/body/Body.css +3 -0
  171. package/src/button/Button.css +246 -0
  172. package/src/button/Button.vars.css +46 -0
  173. package/src/button/LegacyButton.css +23 -0
  174. package/src/button/LegacyButton.less +1 -1
  175. package/src/card/Card.css +100 -0
  176. package/src/card/Card.less +3 -3
  177. package/src/carousel/Carousel.css +134 -0
  178. package/src/checkboxButton/CheckboxButton.css +43 -0
  179. package/src/checkboxButton/CheckboxButton.less +2 -2
  180. package/src/chips/Chip.css +190 -0
  181. package/src/chips/Chip.less +3 -3
  182. package/src/circularButton/CircularButton.css +34 -0
  183. package/src/common/Option/Option.css +122 -0
  184. package/src/common/Option/Option.less +2 -2
  185. package/src/common/RadioButton/RadioButton.css +4 -0
  186. package/src/common/baseCard/BaseCard.css +49 -0
  187. package/src/common/bottomSheet/BottomSheet.css +53 -0
  188. package/src/common/bottomSheet/BottomSheet.less +1 -1
  189. package/src/common/circle/Circle.css +43 -0
  190. package/src/common/closeButton/CloseButton.css +94 -0
  191. package/src/common/closeButton/CloseButton.less +1 -1
  192. package/src/common/panel/Panel.css +73 -0
  193. package/src/common/panel/Panel.less +1 -1
  194. package/src/criticalBanner/CriticalCommsBanner.css +63 -0
  195. package/src/dateInput/DateInput.css +16 -0
  196. package/src/dateLookup/DateLookup.css +93 -0
  197. package/src/dateLookup/DateLookup.less +1 -1
  198. package/src/dateLookup/dateTrigger/DateTrigger.css +29 -0
  199. package/src/dateLookup/dateTrigger/DateTrigger.less +2 -2
  200. package/src/decision/Decision.css +17 -0
  201. package/src/decision/Decision.less +1 -1
  202. package/src/definitionList/DefinitionList.css +53 -0
  203. package/src/definitionList/DefinitionList.less +1 -1
  204. package/src/dimmer/Dimmer.css +70 -0
  205. package/src/dimmer/Dimmer.less +2 -2
  206. package/src/divider/Divider.css +24 -0
  207. package/src/drawer/Drawer.css +56 -0
  208. package/src/dropFade/DropFade.css +19 -0
  209. package/src/emphasis/Emphasis.css +31 -0
  210. package/src/expressiveMoneyInput/ExpressiveMoneyInput.css +58 -0
  211. package/src/expressiveMoneyInput/amountInput/AmountInput.css +32 -0
  212. package/src/expressiveMoneyInput/chevron/Chevron.css +12 -0
  213. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.css +6 -0
  214. package/src/field/Field.css +13 -0
  215. package/src/flowNavigation/FlowNavigation.css +76 -0
  216. package/src/flowNavigation/FlowNavigation.less +1 -1
  217. package/src/flowNavigation/animatedLabel/AnimatedLabel.css +30 -0
  218. package/src/flowNavigation/animatedLabel/AnimatedLabel.less +2 -2
  219. package/src/header/Header.css +33 -0
  220. package/src/iconButton/IconButton.css +82 -0
  221. package/src/image/Image.css +13 -0
  222. package/src/index.ts +0 -1
  223. package/src/info/Info.css +9 -0
  224. package/src/info/Info.less +1 -1
  225. package/src/inputs/Input.css +158 -0
  226. package/src/inputs/InputGroup.css +63 -0
  227. package/src/inputs/SelectInput/BottomSheet/SelectInputBottomSheet.css +96 -0
  228. package/src/inputs/{_BottomSheet.tsx → SelectInput/BottomSheet/SelectInputBottomSheet.tsx} +7 -7
  229. package/src/inputs/SelectInput/BottomSheet/index.ts +2 -0
  230. package/src/inputs/SelectInput/ButtonInput/SelectInputButtonInput.css +16 -0
  231. package/src/inputs/{_ButtonInput.tsx → SelectInput/ButtonInput/SelectInputButtonInput.tsx} +5 -5
  232. package/src/inputs/SelectInput/ButtonInput/index.ts +2 -0
  233. package/src/inputs/SelectInput/ClearButton/SelectInputClearButton.css +46 -0
  234. package/src/inputs/SelectInput/ClearButton/SelectInputClearButton.less +39 -0
  235. package/src/inputs/SelectInput/ClearButton/SelectInputClearButton.tsx +27 -0
  236. package/src/inputs/SelectInput/ClearButton/index.ts +2 -0
  237. package/src/inputs/SelectInput/DefaultRenderTrigger/SelectInputDefaultRenderTrigger.tsx +74 -0
  238. package/src/inputs/SelectInput/DefaultRenderTrigger/index.ts +5 -0
  239. package/src/inputs/SelectInput/ItemView/GroupItemView/SelectInputGroupItemView.tsx +61 -0
  240. package/src/inputs/SelectInput/ItemView/GroupItemView/index.ts +2 -0
  241. package/src/inputs/SelectInput/ItemView/SelectInputItemView.css +16 -0
  242. package/src/inputs/SelectInput/ItemView/SelectInputItemView.less +17 -0
  243. package/src/inputs/SelectInput/ItemView/SelectInputItemView.tsx +48 -0
  244. package/src/inputs/SelectInput/ItemView/index.ts +3 -0
  245. package/src/inputs/SelectInput/Option/SelectInputOption.css +33 -0
  246. package/src/inputs/SelectInput/Option/SelectInputOption.less +32 -0
  247. package/src/inputs/SelectInput/Option/SelectInputOption.tsx +57 -0
  248. package/src/inputs/SelectInput/Option/index.ts +2 -0
  249. package/src/inputs/SelectInput/OptionContent/SelectInputOptionContent.css +37 -0
  250. package/src/inputs/SelectInput/OptionContent/SelectInputOptionContent.less +38 -0
  251. package/src/inputs/SelectInput/OptionContent/SelectInputOptionContent.tsx +72 -0
  252. package/src/inputs/SelectInput/OptionContent/index.ts +2 -0
  253. package/src/inputs/SelectInput/Options/OptionsContainer/SelectInputOptionsContainer.tsx +59 -0
  254. package/src/inputs/SelectInput/Options/OptionsContainer/index.ts +2 -0
  255. package/src/inputs/SelectInput/Options/SelectInputOptions.css +81 -0
  256. package/src/inputs/SelectInput/Options/SelectInputOptions.less +77 -0
  257. package/src/inputs/SelectInput/Options/SelectInputOptions.tsx +411 -0
  258. package/src/inputs/SelectInput/Options/index.ts +3 -0
  259. package/src/inputs/SelectInput/Popover/SelectInputPopover.css +46 -0
  260. package/src/inputs/{_Popover.tsx → SelectInput/Popover/SelectInputPopover.tsx} +7 -7
  261. package/src/inputs/SelectInput/Popover/index.ts +2 -0
  262. package/src/inputs/SelectInput/SelectInput.contexts.tsx +40 -0
  263. package/src/inputs/SelectInput/SelectInput.css +380 -0
  264. package/src/inputs/SelectInput/SelectInput.less +22 -0
  265. package/src/inputs/{SelectInput.test.tsx → SelectInput/SelectInput.test.tsx} +9 -11
  266. package/src/inputs/SelectInput/SelectInput.tsx +257 -0
  267. package/src/inputs/SelectInput/SelectInput.types.ts +113 -0
  268. package/src/inputs/SelectInput/SelectInput.utils.ts +205 -0
  269. package/src/inputs/SelectInput/TriggerButton/SelectInputTriggerButton.tsx +36 -0
  270. package/src/inputs/SelectInput/TriggerButton/index.ts +5 -0
  271. package/src/inputs/{SelectInput.docs.mdx → SelectInput/_stories/SelectInput.docs.mdx} +0 -1
  272. package/src/inputs/{SelectInput.story.tsx → SelectInput/_stories/SelectInput.story.tsx} +11 -8
  273. package/src/inputs/{SelectInput.test.story.tsx → SelectInput/_stories/SelectInput.test.story.tsx} +6 -10
  274. package/src/inputs/SelectInput/components.ts +10 -0
  275. package/src/inputs/SelectInput/index.ts +12 -0
  276. package/src/inputs/TextArea.css +161 -0
  277. package/src/inputs/_common.less +1 -1
  278. package/src/instructionsList/InstructionsList.css +26 -0
  279. package/src/legacylistItem/LegacyListItem.css +6 -0
  280. package/src/link/Link.css +24 -0
  281. package/src/link/Link.less +2 -2
  282. package/src/listItem/ListItem.css +659 -0
  283. package/src/listItem/ListItem.grid.css +384 -0
  284. package/src/listItem/ListItem.less +1 -1
  285. package/src/listItem/Prompt/ListItemPrompt.css +4 -0
  286. package/src/loader/Loader.css +227 -0
  287. package/src/logo/Logo.css +26 -0
  288. package/src/main.css +7529 -0
  289. package/src/main.less +1 -3
  290. package/src/modal/Modal.css +95 -0
  291. package/src/moneyInput/MoneyInput.css +74 -0
  292. package/src/moneyInput/MoneyInput.less +2 -2
  293. package/src/navigationOption/NavigationOption.css +69 -0
  294. package/src/navigationOption/NavigationOption.less +3 -3
  295. package/src/navigationOptionsList/NavigationOptionsList.css +13 -0
  296. package/src/navigationOptionsList/NavigationOptionsList.less +1 -1
  297. package/src/nudge/Nudge.css +206 -0
  298. package/src/overlayHeader/OverlayHeader.css +15 -0
  299. package/src/overlayHeader/OverlayHeader.less +1 -1
  300. package/src/phoneNumberInput/PhoneNumberInput.css +23 -0
  301. package/src/phoneNumberInput/PhoneNumberInput.less +1 -1
  302. package/src/popover/Popover.css +76 -0
  303. package/src/popover/Popover.less +1 -1
  304. package/src/progress/Progress.css +43 -0
  305. package/src/progressBar/ProgressBar.css +14 -0
  306. package/src/promoCard/PromoCard.css +186 -0
  307. package/src/prompt/ActionPrompt/ActionPrompt.css +25 -0
  308. package/src/prompt/InfoPrompt/InfoPrompt.css +33 -0
  309. package/src/prompt/InlinePrompt/InlinePrompt.css +57 -0
  310. package/src/prompt/PrimitivePrompt/PrimitivePrompt.css +51 -0
  311. package/src/radioGroup/RadioGroup.css +3 -0
  312. package/src/section/Section.css +10 -0
  313. package/src/segmentedControl/SegmentedControl.css +101 -0
  314. package/src/select/Select.css +368 -0
  315. package/src/select/Select.less +5 -5
  316. package/src/sentimentSurface/SentimentSurface.css +487 -0
  317. package/src/slidingPanel/SlidingPanel.css +105 -0
  318. package/src/slidingPanel/SlidingPanel.less +2 -2
  319. package/src/snackbar/Snackbar.css +93 -0
  320. package/src/snackbar/Snackbar.less +3 -3
  321. package/src/statusIcon/StatusIcon.css +6 -0
  322. package/src/stepper/Stepper.css +77 -0
  323. package/src/stepper/Stepper.less +3 -3
  324. package/src/summary/Summary.css +81 -0
  325. package/src/summary/Summary.less +1 -1
  326. package/src/switch/Switch.css +63 -0
  327. package/src/switch/Switch.less +4 -4
  328. package/src/switchOption/SwitchOption.css +4 -0
  329. package/src/table/Table.css +274 -0
  330. package/src/table/Table.less +1 -1
  331. package/src/tabs/Tabs.css +121 -0
  332. package/src/tabs/Tabs.less +4 -4
  333. package/src/tile/Tile.css +68 -0
  334. package/src/tile/Tile.less +1 -1
  335. package/src/tooltip/Tooltip.css +50 -0
  336. package/src/tooltip/Tooltip.less +1 -1
  337. package/src/typeahead/Typeahead.css +187 -0
  338. package/src/typeahead/Typeahead.less +2 -2
  339. package/src/typeahead/typeaheadOption/TypeaheadOption.css +9 -0
  340. package/src/typeahead/typeaheadOption/TypeaheadOption.less +1 -1
  341. package/src/upload/Upload.css +34 -0
  342. package/src/upload/Upload.less +1 -1
  343. package/src/uploadInput/UploadInput.css +18 -0
  344. package/src/uploadInput/UploadInput.less +2 -2
  345. package/src/uploadInput/uploadButton/UploadButton.css +90 -0
  346. package/src/uploadInput/uploadItem/UploadItem.css +166 -0
  347. package/src/uploadInput/uploadItem/UploadItem.less +1 -1
  348. package/build/inputs/SelectInput.js +0 -890
  349. package/build/inputs/SelectInput.js.map +0 -1
  350. package/build/inputs/SelectInput.messages.js.map +0 -1
  351. package/build/inputs/SelectInput.messages.mjs.map +0 -1
  352. package/build/inputs/SelectInput.mjs +0 -881
  353. package/build/inputs/SelectInput.mjs.map +0 -1
  354. package/build/inputs/_BottomSheet.js.map +0 -1
  355. package/build/inputs/_BottomSheet.mjs.map +0 -1
  356. package/build/inputs/_ButtonInput.js.map +0 -1
  357. package/build/inputs/_ButtonInput.mjs.map +0 -1
  358. package/build/inputs/_Popover.js.map +0 -1
  359. package/build/inputs/_Popover.mjs.map +0 -1
  360. package/build/neptune-css/fonts/TW-Averta-Bold.woff +0 -0
  361. package/build/neptune-css/fonts/TW-Averta-Bold.woff2 +0 -0
  362. package/build/neptune-css/fonts/TW-Averta-Regular.woff +0 -0
  363. package/build/neptune-css/fonts/TW-Averta-Regular.woff2 +0 -0
  364. package/build/neptune-css/fonts/TW-Averta-Semibold.woff +0 -0
  365. package/build/neptune-css/fonts/TW-Averta-Semibold.woff2 +0 -0
  366. package/build/neptune-css/fonts/TransferWise-IconFont.svg +0 -81
  367. package/build/neptune-css/fonts/TransferWise-IconFont.woff +0 -0
  368. package/build/neptune-css/fonts/WiseSans-Heavy.woff2 +0 -0
  369. package/build/neptune-css/fonts/inter-cyrillic-ext-variable-wghtOnly-normal.woff2 +0 -0
  370. package/build/neptune-css/fonts/inter-cyrillic-variable-wghtOnly-normal.woff2 +0 -0
  371. package/build/neptune-css/fonts/inter-greek-ext-variable-wghtOnly-normal.woff2 +0 -0
  372. package/build/neptune-css/fonts/inter-greek-variable-wghtOnly-normal.woff2 +0 -0
  373. package/build/neptune-css/fonts/inter-latin-ext-variable-wghtOnly-normal.woff2 +0 -0
  374. package/build/neptune-css/fonts/inter-latin-variable-wghtOnly-normal.woff2 +0 -0
  375. package/build/neptune-css/fonts/inter-vietnamese-variable-wghtOnly-normal.woff2 +0 -0
  376. package/build/neptune-css/img/bg-dark.svg +0 -31
  377. package/build/neptune-css/img/bg-light.svg +0 -26
  378. package/build/neptune-css/less/legacy-variables.less +0 -1299
  379. package/build/neptune-css/less/neptune-tokens.css +0 -0
  380. package/build/neptune-css/less/neptune-tokens.less +0 -275
  381. package/build/neptune-css/less/zindex.css +0 -0
  382. package/build/neptune-css/less/zindex.less +0 -17
  383. package/build/neptune-css/props/custom-media.css +0 -11
  384. package/build/neptune-css/props/legacy-custom-props.css +0 -69
  385. package/build/neptune-css/props/neptune-tokens.css +0 -152
  386. package/build/neptune.css +0 -25724
  387. package/build/styles/neptune-css/less/accordion.css +0 -255
  388. package/build/styles/neptune-css/less/alerts.css +0 -751
  389. package/build/styles/neptune-css/less/background.css +0 -20
  390. package/build/styles/neptune-css/less/badge.css +0 -90
  391. package/build/styles/neptune-css/less/border-radius.css +0 -3
  392. package/build/styles/neptune-css/less/breadcrumbs.css +0 -20
  393. package/build/styles/neptune-css/less/button-groups.css +0 -495
  394. package/build/styles/neptune-css/less/buttons.css +0 -1390
  395. package/build/styles/neptune-css/less/chevron.css +0 -35
  396. package/build/styles/neptune-css/less/circles.css +0 -248
  397. package/build/styles/neptune-css/less/close.css +0 -47
  398. package/build/styles/neptune-css/less/column-layout.css +0 -164
  399. package/build/styles/neptune-css/less/core/viewport-themes.css +0 -59
  400. package/build/styles/neptune-css/less/currency-flags.css +0 -46
  401. package/build/styles/neptune-css/less/decision.css +0 -136
  402. package/build/styles/neptune-css/less/dropdowns.css +0 -472
  403. package/build/styles/neptune-css/less/droppable.css +0 -221
  404. package/build/styles/neptune-css/less/flex.css +0 -598
  405. package/build/styles/neptune-css/less/footer.css +0 -129
  406. package/build/styles/neptune-css/less/forms/bootstrap-forms.css +0 -2277
  407. package/build/styles/neptune-css/less/forms/checkbox-radio.css +0 -367
  408. package/build/styles/neptune-css/less/grid.css +0 -2369
  409. package/build/styles/neptune-css/less/input-groups.css +0 -2740
  410. package/build/styles/neptune-css/less/link-callout.css +0 -20
  411. package/build/styles/neptune-css/less/list-group.css +0 -350
  412. package/build/styles/neptune-css/less/loaders.css +0 -76
  413. package/build/styles/neptune-css/less/media.css +0 -74
  414. package/build/styles/neptune-css/less/modals.css +0 -185
  415. package/build/styles/neptune-css/less/navbar.css +0 -2406
  416. package/build/styles/neptune-css/less/navs.css +0 -296
  417. package/build/styles/neptune-css/less/neptune-addons.css +0 -1857
  418. package/build/styles/neptune-css/less/neptune-core.css +0 -2049
  419. package/build/styles/neptune-css/less/neptune-social-media.css +0 -144
  420. package/build/styles/neptune-css/less/neptune.css +0 -25724
  421. package/build/styles/neptune-css/less/panels.css +0 -528
  422. package/build/styles/neptune-css/less/popovers.css +0 -661
  423. package/build/styles/neptune-css/less/process.css +0 -483
  424. package/build/styles/neptune-css/less/progress-bars.css +0 -64
  425. package/build/styles/neptune-css/less/ring.css +0 -55
  426. package/build/styles/neptune-css/less/select.css +0 -99
  427. package/build/styles/neptune-css/less/sequences.css +0 -855
  428. package/build/styles/neptune-css/less/table.css +0 -446
  429. package/build/styles/neptune-css/less/tick.css +0 -32
  430. package/build/styles/neptune-css/less/tooltip.css +0 -160
  431. package/build/styles/neptune-css/less/utilities.css +0 -145
  432. package/build/styles/neptune-css/less/wells.css +0 -74
  433. package/build/styles/neptune-css/props/custom-media.css +0 -11
  434. package/build/styles/neptune-css/props/legacy-custom-props.css +0 -69
  435. package/build/styles/neptune-css/props/neptune-tokens.css +0 -11
  436. package/build/styles/neptune-css/variables/legacy-variables.css +0 -0
  437. package/build/styles/neptune-css/variables/neptune-tokens.css +0 -0
  438. package/build/styles/neptune-css/variables/zindex.css +0 -0
  439. package/build/types/inputs/SelectInput.d.ts.map +0 -1
  440. package/build/types/inputs/SelectInput.messages.d.ts.map +0 -1
  441. package/build/types/inputs/_BottomSheet.d.ts.map +0 -1
  442. package/build/types/inputs/_ButtonInput.d.ts +0 -5
  443. package/build/types/inputs/_ButtonInput.d.ts.map +0 -1
  444. package/build/types/inputs/_Popover.d.ts.map +0 -1
  445. package/src/inputs/SelectInput.less +0 -219
  446. package/src/inputs/SelectInput.tsx +0 -1269
  447. package/src/neptune-css/fonts/TW-Averta-Bold.woff +0 -0
  448. package/src/neptune-css/fonts/TW-Averta-Bold.woff2 +0 -0
  449. package/src/neptune-css/fonts/TW-Averta-Regular.woff +0 -0
  450. package/src/neptune-css/fonts/TW-Averta-Regular.woff2 +0 -0
  451. package/src/neptune-css/fonts/TW-Averta-Semibold.woff +0 -0
  452. package/src/neptune-css/fonts/TW-Averta-Semibold.woff2 +0 -0
  453. package/src/neptune-css/fonts/TransferWise-IconFont.svg +0 -81
  454. package/src/neptune-css/fonts/TransferWise-IconFont.woff +0 -0
  455. package/src/neptune-css/fonts/WiseSans-Heavy.woff2 +0 -0
  456. package/src/neptune-css/fonts/inter-cyrillic-ext-variable-wghtOnly-normal.woff2 +0 -0
  457. package/src/neptune-css/fonts/inter-cyrillic-variable-wghtOnly-normal.woff2 +0 -0
  458. package/src/neptune-css/fonts/inter-greek-ext-variable-wghtOnly-normal.woff2 +0 -0
  459. package/src/neptune-css/fonts/inter-greek-variable-wghtOnly-normal.woff2 +0 -0
  460. package/src/neptune-css/fonts/inter-latin-ext-variable-wghtOnly-normal.woff2 +0 -0
  461. package/src/neptune-css/fonts/inter-latin-variable-wghtOnly-normal.woff2 +0 -0
  462. package/src/neptune-css/fonts/inter-vietnamese-variable-wghtOnly-normal.woff2 +0 -0
  463. package/src/neptune-css/img/bg-dark.svg +0 -31
  464. package/src/neptune-css/img/bg-light.svg +0 -26
  465. package/src/neptune-css/less/accordion.less +0 -15
  466. package/src/neptune-css/less/addons/_background-utilities.less +0 -82
  467. package/src/neptune-css/less/addons/_border.less +0 -3
  468. package/src/neptune-css/less/addons/_display-utilities.less +0 -159
  469. package/src/neptune-css/less/addons/_spacing-utilities.less +0 -73
  470. package/src/neptune-css/less/addons/_utilities.less +0 -147
  471. package/src/neptune-css/less/alerts.less +0 -252
  472. package/src/neptune-css/less/background.less +0 -24
  473. package/src/neptune-css/less/badge.less +0 -85
  474. package/src/neptune-css/less/border-radius.less +0 -3
  475. package/src/neptune-css/less/breadcrumbs.less +0 -27
  476. package/src/neptune-css/less/button-groups.less +0 -424
  477. package/src/neptune-css/less/buttons.less +0 -738
  478. package/src/neptune-css/less/chevron.less +0 -39
  479. package/src/neptune-css/less/circles.less +0 -233
  480. package/src/neptune-css/less/close.less +0 -48
  481. package/src/neptune-css/less/column-layout.less +0 -167
  482. package/src/neptune-css/less/components/_component-animations.less +0 -43
  483. package/src/neptune-css/less/core/_fonts.less +0 -97
  484. package/src/neptune-css/less/core/_scaffolding.less +0 -380
  485. package/src/neptune-css/less/core/_typography-utilities.less +0 -255
  486. package/src/neptune-css/less/core/_typography.less +0 -575
  487. package/src/neptune-css/less/core/viewport-themes.less +0 -65
  488. package/src/neptune-css/less/currency-flags.less +0 -42
  489. package/src/neptune-css/less/decision.less +0 -98
  490. package/src/neptune-css/less/dropdowns.less +0 -365
  491. package/src/neptune-css/less/droppable.less +0 -213
  492. package/src/neptune-css/less/flex.less +0 -113
  493. package/src/neptune-css/less/footer.less +0 -113
  494. package/src/neptune-css/less/forms/bootstrap-forms.less +0 -1218
  495. package/src/neptune-css/less/forms/checkbox-radio.less +0 -330
  496. package/src/neptune-css/less/grid.less +0 -187
  497. package/src/neptune-css/less/input-groups.less +0 -298
  498. package/src/neptune-css/less/link-callout.less +0 -17
  499. package/src/neptune-css/less/list-group.less +0 -260
  500. package/src/neptune-css/less/loaders.less +0 -91
  501. package/src/neptune-css/less/media.less +0 -68
  502. package/src/neptune-css/less/mixins/_alerts.less +0 -39
  503. package/src/neptune-css/less/mixins/_arrows.less +0 -52
  504. package/src/neptune-css/less/mixins/_border-radius.less +0 -11
  505. package/src/neptune-css/less/mixins/_buttons.less +0 -82
  506. package/src/neptune-css/less/mixins/_center-block.less +0 -7
  507. package/src/neptune-css/less/mixins/_clearfix.less +0 -23
  508. package/src/neptune-css/less/mixins/_flex.less +0 -105
  509. package/src/neptune-css/less/mixins/_forms.less +0 -130
  510. package/src/neptune-css/less/mixins/_grid-framework.less +0 -104
  511. package/src/neptune-css/less/mixins/_grid.less +0 -158
  512. package/src/neptune-css/less/mixins/_hide-text.less +0 -20
  513. package/src/neptune-css/less/mixins/_links.less +0 -7
  514. package/src/neptune-css/less/mixins/_list-group.less +0 -23
  515. package/src/neptune-css/less/mixins/_logical-properties-IE-friendly.less +0 -381
  516. package/src/neptune-css/less/mixins/_logical-properties-modern-browsers.less +0 -226
  517. package/src/neptune-css/less/mixins/_logical-properties.less +0 -5
  518. package/src/neptune-css/less/mixins/_nav-divider.less +0 -10
  519. package/src/neptune-css/less/mixins/_nav-vertical-align.less +0 -10
  520. package/src/neptune-css/less/mixins/_panels.less +0 -23
  521. package/src/neptune-css/less/mixins/_physical-properties.less +0 -110
  522. package/src/neptune-css/less/mixins/_reset-text.less +0 -22
  523. package/src/neptune-css/less/mixins/_sequence.less +0 -203
  524. package/src/neptune-css/less/mixins/_spacing.less +0 -122
  525. package/src/neptune-css/less/mixins/_tab-focus.less +0 -5
  526. package/src/neptune-css/less/mixins/_table-row.less +0 -47
  527. package/src/neptune-css/less/mixins/_text-emphasis.less +0 -39
  528. package/src/neptune-css/less/mixins/_text-overflow.less +0 -8
  529. package/src/neptune-css/less/mixins/_theming.less +0 -7
  530. package/src/neptune-css/less/modals.less +0 -239
  531. package/src/neptune-css/less/navbar.less +0 -1442
  532. package/src/neptune-css/less/navs.less +0 -339
  533. package/src/neptune-css/less/neptune-addons.less +0 -4
  534. package/src/neptune-css/less/neptune-core.less +0 -4
  535. package/src/neptune-css/less/neptune-social-media.less +0 -84
  536. package/src/neptune-css/less/neptune.bundle.less +0 -65
  537. package/src/neptune-css/less/panels.less +0 -410
  538. package/src/neptune-css/less/popovers.less +0 -379
  539. package/src/neptune-css/less/process.less +0 -426
  540. package/src/neptune-css/less/progress-bars.less +0 -55
  541. package/src/neptune-css/less/ring.less +0 -51
  542. package/src/neptune-css/less/select.less +0 -88
  543. package/src/neptune-css/less/sequences.less +0 -511
  544. package/src/neptune-css/less/table.less +0 -385
  545. package/src/neptune-css/less/tick.less +0 -37
  546. package/src/neptune-css/less/tooltip.less +0 -130
  547. package/src/neptune-css/less/utilities.less +0 -153
  548. package/src/neptune-css/less/wells.less +0 -37
  549. package/src/neptune-css/props/custom-media.css +0 -11
  550. package/src/neptune-css/props/legacy-custom-props.css +0 -69
  551. package/src/neptune-css/props/neptune-tokens.css +0 -11
  552. package/src/neptune-css/variables/legacy-variables.less +0 -1299
  553. package/src/neptune-css/variables/neptune-tokens.less +0 -12
  554. package/src/neptune-css/variables/zindex.less +0 -17
  555. package/build/inputs/{SelectInput.messages.js → SelectInput/SelectInput.messages.js} +0 -0
  556. package/build/inputs/{SelectInput.messages.mjs → SelectInput/SelectInput.messages.mjs} +0 -0
  557. package/build/styles/inputs/{SelectInput.css → SelectInput/SelectInput.css} +90 -90
  558. /package/build/types/inputs/{SelectInput.messages.d.ts → SelectInput/SelectInput.messages.d.ts} +0 -0
  559. /package/src/inputs/{_BottomSheet.less → SelectInput/BottomSheet/SelectInputBottomSheet.less} +0 -0
  560. /package/src/inputs/{_ButtonInput.less → SelectInput/ButtonInput/SelectInputButtonInput.less} +0 -0
  561. /package/src/inputs/{_Popover.less → SelectInput/Popover/SelectInputPopover.less} +0 -0
  562. /package/src/inputs/{SelectInput.messages.ts → SelectInput/SelectInput.messages.ts} +0 -0
  563. /package/{build/neptune-css/less/legacy-variables.css → src/listItem/ListItem.vars.css} +0 -0
@@ -1,1269 +0,0 @@
1
- import {
2
- Listbox as ListboxBase,
3
- ListboxButton,
4
- ListboxOption,
5
- ListboxOptions,
6
- } from '@headlessui/react';
7
- import { Check, ChevronDown, Cross, CrossCircle } from '@transferwise/icons';
8
- import { clsx } from 'clsx';
9
- import mergeProps from 'merge-props';
10
- import {
11
- createContext,
12
- forwardRef,
13
- ReactNode,
14
- useContext,
15
- useDeferredValue,
16
- useEffect,
17
- useId,
18
- useMemo,
19
- useRef,
20
- useState,
21
- } from 'react';
22
- import { useIntl } from 'react-intl';
23
- import { Virtualizer, type VirtualizerHandle } from 'virtua';
24
-
25
- import { useEffectEvent } from '../common/hooks/useEffectEvent';
26
- import { useScreenSize } from '../common/hooks/useScreenSize';
27
- import { PolymorphicWithOverrides } from '../common/polymorphicWithOverrides/PolymorphicWithOverrides';
28
- import { Breakpoint } from '../common/propsValues/breakpoint';
29
- import dateTriggerMessages from '../dateLookup/dateTrigger/DateTrigger.messages';
30
- import { Merge } from '../utils';
31
-
32
- import { BottomSheet } from './_BottomSheet';
33
- import { ButtonInput } from './_ButtonInput';
34
- import { Popover } from './_Popover';
35
- import { useInputAttributes, WithInputAttributesProps } from './contexts';
36
- import { InputGroup } from './InputGroup';
37
- import { SearchInput } from './SearchInput';
38
- import messages from './SelectInput.messages';
39
- import Header from '../header';
40
- import Section from '../section';
41
- import { ButtonProps } from '../button/Button.types';
42
-
43
- const MAX_ITEMS_WITHOUT_VIRTUALIZATION = 50;
44
-
45
- function searchableString(value: string) {
46
- return (
47
- value
48
- .trim()
49
- .replace(/\s+/gu, ' ')
50
- // NFD converts an Å to A + ̊ (and other special characters)
51
- .normalize('NFD')
52
- // and then this replaces the ̊ with nothing (and other special characters)
53
- .replace(/[\u0300-\u036f]/g, '')
54
- .toLowerCase()
55
- );
56
- }
57
-
58
- function inferSearchableStrings(value: unknown) {
59
- if (typeof value === 'string') {
60
- return [searchableString(value)];
61
- }
62
-
63
- if (typeof value === 'object' && value != null) {
64
- return Object.values(value)
65
- .filter((innerValue) => typeof innerValue === 'string')
66
- .map((innerValue) => searchableString(innerValue));
67
- }
68
-
69
- return [];
70
- }
71
-
72
- export interface SelectInputOptionItem<T = string> {
73
- type: 'option';
74
- value: T;
75
- filterMatchers?: readonly string[];
76
- disabled?: boolean;
77
- }
78
-
79
- export interface SelectInputGroupItem<T = string> {
80
- type: 'group';
81
- label: ReactNode;
82
- options: readonly SelectInputOptionItem<T>[];
83
- action?: {
84
- label: string;
85
- onClick: ButtonProps['onClick'];
86
- };
87
- }
88
-
89
- export interface SelectInputSeparatorItem {
90
- type: 'separator';
91
- }
92
-
93
- export type SelectInputItem<T = string> =
94
- | SelectInputOptionItem<T>
95
- | SelectInputGroupItem<T>
96
- | SelectInputSeparatorItem;
97
-
98
- function dedupeSelectInputOptionItem<T>(
99
- item: SelectInputOptionItem<T>,
100
- existingValues: Set<T>,
101
- compareValues?: (a: T, b: T) => boolean,
102
- ): SelectInputOptionItem<T | undefined> {
103
- const isDuplicate = compareValues
104
- ? Array.from(existingValues).some((existingValue) => compareValues(item.value, existingValue))
105
- : existingValues.has(item.value);
106
-
107
- if (!isDuplicate) {
108
- existingValues.add(item.value);
109
- return item;
110
- }
111
- return { ...item, value: undefined };
112
- }
113
-
114
- /**
115
- * Sets the `value` of duplicate option items to `undefined`, hiding them when
116
- * rendered. Indexes are kept intact within groups to preserve the active item
117
- * between filter changes when possible.
118
- */
119
- function dedupeSelectInputItems<T>(
120
- items: readonly SelectInputItem<T>[],
121
- compareValues?: (a: T, b: T) => boolean,
122
- ): SelectInputItem<T | undefined>[] {
123
- const existingValues = new Set<T>();
124
-
125
- return items.map((item) => {
126
- switch (item.type) {
127
- case 'option': {
128
- return dedupeSelectInputOptionItem(item, existingValues, compareValues);
129
- }
130
- case 'group': {
131
- return {
132
- ...item,
133
- options: item.options.map((option) =>
134
- dedupeSelectInputOptionItem(option, existingValues, compareValues),
135
- ),
136
- };
137
- }
138
- default:
139
- }
140
- return item;
141
- });
142
- }
143
-
144
- function selectInputOptionItemIncludesNeedle<T>(item: SelectInputOptionItem<T>, needle: string) {
145
- return inferSearchableStrings(item.filterMatchers ?? item.value).some((haystack) =>
146
- haystack.includes(needle),
147
- );
148
- }
149
-
150
- function filterSelectInputItems<T>(
151
- items: readonly SelectInputItem<T>[],
152
- predicate: (item: SelectInputOptionItem<T>) => boolean,
153
- ) {
154
- return items.filter((item) => {
155
- switch (item.type) {
156
- case 'option': {
157
- return predicate(item);
158
- }
159
- case 'group': {
160
- return item.options.some((option) => predicate(option));
161
- }
162
- default:
163
- }
164
- return false;
165
- });
166
- }
167
-
168
- /**
169
- * Flattens and sorts filtered options using the provided comparator.
170
- * Extracts all options from groups, filters out undefined values (deduplicated items),
171
- * sorts them, and returns as a flat list of option items.
172
- */
173
- function sortSelectInputItems<T>(
174
- items: readonly SelectInputItem<T | undefined>[],
175
- compareFn: (
176
- a: SelectInputOptionItem<NonNullable<T>>,
177
- b: SelectInputOptionItem<NonNullable<T>>,
178
- searchQuery: string,
179
- ) => number,
180
- searchQuery: string,
181
- ): SelectInputItem<NonNullable<T>>[] {
182
- const flattenedOption = items.flatMap((item) => {
183
- if (item.type === 'option') {
184
- return item.value !== undefined ? [item as SelectInputOptionItem<NonNullable<T>>] : [];
185
- }
186
-
187
- if (item.type === 'group') {
188
- return item.options.filter(
189
- (option): option is SelectInputOptionItem<NonNullable<T>> => option.value !== undefined,
190
- );
191
- }
192
-
193
- return [];
194
- });
195
-
196
- // eslint-disable-next-line functional/immutable-data
197
- return flattenedOption.sort((a, b) => compareFn(a, b, searchQuery));
198
- }
199
-
200
- /**
201
- * A prebuilt sort function for `sortFilteredOptions` that sorts options by relevance to the search query.
202
- * Prioritizes: exact matches > starts with > contains > alphabetical.
203
- *
204
- * @param getLabel - Function to extract the label string from the option value. Defaults to using `title` property.
205
- *
206
- * @example
207
- * ```tsx
208
- * <SelectInput
209
- * filterable
210
- * sortFilteredOptions={sortByRelevance((value) => value.name)}
211
- * // ...
212
- * />
213
- * ```
214
- */
215
- export function sortByRelevance<T>(
216
- getLabel: (value: T) => string = (value) => (value as { title: string }).title,
217
- ): (a: SelectInputOptionItem<T>, b: SelectInputOptionItem<T>, searchQuery: string) => number {
218
- return (a, b, searchQuery) => {
219
- const normalizedQuery = searchQuery.toLowerCase();
220
- const labelA = getLabel(a.value).toLowerCase();
221
- const labelB = getLabel(b.value).toLowerCase();
222
-
223
- // Prioritize exact matches
224
- const aExactMatch = labelA === normalizedQuery;
225
- const bExactMatch = labelB === normalizedQuery;
226
- if (aExactMatch && !bExactMatch) return -1;
227
- if (!aExactMatch && bExactMatch) return 1;
228
-
229
- // Then prioritize options where label starts with the search query
230
- const aStartsWith = labelA.startsWith(normalizedQuery);
231
- const bStartsWith = labelB.startsWith(normalizedQuery);
232
- if (aStartsWith && !bStartsWith) return -1;
233
- if (!aStartsWith && bStartsWith) return 1;
234
-
235
- // Then prioritize options where label contains the search query
236
- const aContains = labelA.includes(normalizedQuery);
237
- const bContains = labelB.includes(normalizedQuery);
238
- if (aContains && !bContains) return -1;
239
- if (!aContains && bContains) return 1;
240
-
241
- // Finally sort alphabetically
242
- return labelA.localeCompare(labelB);
243
- };
244
- }
245
-
246
- export interface SelectInputProps<T = string, M extends boolean = false> {
247
- id?: string;
248
- /**
249
- * Sets the `data-wds-parent` attribute on the listbox container, which is needed for complex components like DateInput to correctly manage event handling.
250
- * @internal
251
- */
252
- parentId?: string;
253
- name?: string;
254
- multiple?: M;
255
- placeholder?: string;
256
- items: readonly SelectInputItem<NonNullable<T>>[];
257
- /**
258
- * Enables browser autocomplete integration through the search input.
259
- * Accepts standard HTML autocomplete values (e.g., "country-name", "address-level1").
260
- *
261
- * Requires `filterable={true}` to enable the search input.
262
- *
263
- * @example
264
- * <SelectInput
265
- * name="country"
266
- * autocomplete="country-name"
267
- * filterable={true}
268
- * items={[{
269
- * type: 'option',
270
- * value: 'GB',
271
- * filterMatchers: ['United Kingdom', 'UK']
272
- * }]}
273
- * />
274
- */
275
- autocomplete?: string;
276
- defaultValue?: M extends true ? readonly T[] : T;
277
- value?: M extends true ? readonly T[] : T;
278
- compareValues?:
279
- | (keyof NonNullable<T> & string)
280
- | ((a: T | undefined, b: T | undefined) => boolean);
281
- renderValue?: (value: NonNullable<T>, withinTrigger: boolean) => React.ReactNode;
282
- renderFooter?: (args: {
283
- resultsEmpty: boolean;
284
- queryNormalized: string | null | undefined;
285
- }) => React.ReactNode;
286
- renderTrigger?: (args: {
287
- content: React.ReactNode;
288
- placeholderShown: boolean;
289
- clear: (() => void) | undefined;
290
- disabled: boolean;
291
- size: 'sm' | 'md' | 'lg';
292
- className: string | undefined;
293
- }) => React.ReactNode;
294
- filterable?: boolean;
295
- filterPlaceholder?: string;
296
- sortFilteredOptions?: (
297
- a: SelectInputOptionItem<NonNullable<T>>,
298
- b: SelectInputOptionItem<NonNullable<T>>,
299
- searchQuery: string,
300
- ) => number;
301
- disabled?: boolean;
302
- size?: 'sm' | 'md' | 'lg';
303
- className?: string;
304
- UNSAFE_triggerButtonProps?: WithInputAttributesProps['inputAttributes'] & {
305
- 'aria-label'?: string;
306
- };
307
- /** Ref to the select trigger button element. */
308
- triggerRef?: React.MutableRefObject<HTMLButtonElement | null>;
309
- onFilterChange?: (args: { query: string; queryNormalized: string | null }) => void;
310
- onChange?: (value: M extends true ? T[] : T) => void;
311
- onOpen?: () => void;
312
- onClose?: () => void;
313
- onClear?: () => void;
314
- }
315
-
316
- const defaultRenderTrigger = (({ content, placeholderShown, clear, disabled, size, className }) => (
317
- <InputGroup
318
- addonEnd={{
319
- content: (
320
- <span className={clsx('np-select-input-addon-container', disabled && 'disabled')}>
321
- {clear != null && !placeholderShown ? (
322
- <>
323
- <SelectInputClearButton
324
- onClick={(event) => {
325
- event.preventDefault();
326
- clear();
327
- }}
328
- />
329
- <span className="np-select-input-addon-separator" />
330
- </>
331
- ) : null}
332
-
333
- <span className="np-select-input-addon">
334
- <ChevronDown size={16} />
335
- </span>
336
- </span>
337
- ),
338
- initialContentWidth: 24 + 4,
339
- padding: 'sm',
340
- }}
341
- disabled={disabled}
342
- className={className}
343
- >
344
- <SelectInputTriggerButton as={ButtonInput} size={size}>
345
- <span
346
- className={clsx(
347
- 'np-select-input-content',
348
- placeholderShown && 'np-select-input-placeholder',
349
- )}
350
- >
351
- {content}
352
- </span>
353
- </SelectInputTriggerButton>
354
- </InputGroup>
355
- )) satisfies SelectInputProps['renderTrigger'];
356
-
357
- interface SelectInputClearButtonProps extends Pick<
358
- React.ComponentPropsWithoutRef<'button'>,
359
- 'className' | 'onClick'
360
- > {}
361
-
362
- function SelectInputClearButton({ className, onClick }: SelectInputClearButtonProps) {
363
- const intl = useIntl();
364
-
365
- return (
366
- <button
367
- type="button"
368
- aria-label={intl.formatMessage(dateTriggerMessages.ariaLabel)}
369
- className={clsx(className, 'np-select-input-addon np-select-input-addon--interactive')}
370
- onClick={onClick}
371
- >
372
- <Cross size={16} />
373
- </button>
374
- );
375
- }
376
-
377
- const noop = () => {};
378
-
379
- export function SelectInput<T = string, M extends boolean = false>({
380
- id: idProp,
381
- parentId,
382
- name,
383
- multiple,
384
- placeholder,
385
- autocomplete,
386
- items,
387
- defaultValue,
388
- value: controlledValue,
389
- compareValues,
390
- renderValue = String,
391
- renderFooter,
392
- renderTrigger = defaultRenderTrigger,
393
- filterable,
394
- filterPlaceholder,
395
- sortFilteredOptions,
396
- disabled,
397
- size = 'md',
398
- className,
399
- UNSAFE_triggerButtonProps,
400
- triggerRef: externalTriggerRef,
401
- onFilterChange = noop,
402
- onChange,
403
- onOpen,
404
- onClose,
405
- onClear,
406
- }: SelectInputProps<T, M>) {
407
- const inputAttributes = useInputAttributes({ nonLabelable: true });
408
- const id = idProp ?? inputAttributes.id;
409
-
410
- const [open, setOpen] = useState(false);
411
-
412
- const initialized = useRef(false);
413
- const handleClose = useEffectEvent(onClose ?? (() => {}));
414
- const handleOpen = useEffectEvent(onOpen ?? (() => {}));
415
- useEffect(() => {
416
- if (initialized.current) {
417
- if (open) {
418
- handleOpen?.();
419
- } else {
420
- handleClose?.();
421
- }
422
- } else {
423
- initialized.current = true;
424
- }
425
- }, [handleClose, handleOpen, open]);
426
-
427
- const [filterQuery, _setFilterQuery] = useState('');
428
- const deferredFilterQuery = useDeferredValue(filterQuery);
429
- const setFilterQuery = useEffectEvent((query: string) => {
430
- _setFilterQuery(query);
431
- if (query !== filterQuery) {
432
- onFilterChange({
433
- query,
434
- queryNormalized: query ? searchableString(query) : null,
435
- });
436
- }
437
- });
438
-
439
- const internalTriggerRef = useRef<HTMLButtonElement | null>(null);
440
-
441
- const screenSm = useScreenSize(Breakpoint.SMALL);
442
- const OptionsOverlay = screenSm ? Popover : BottomSheet;
443
-
444
- const searchInputRef = useRef<HTMLInputElement>(null);
445
- const listboxRef = useRef<HTMLDivElement>(null);
446
- const controllerRef = filterable ? searchInputRef : listboxRef;
447
-
448
- /**
449
- * Attempts to resolve the `listbox` label
450
- * @see https://storybook.wise.design/?path=/docs/forms-selectinput-accessibility--docs#labelling
451
- */
452
- const getListBoxLabelProps = (): {
453
- listBoxLabel?: string;
454
- listBoxLabelledBy?: string;
455
- } => {
456
- if (UNSAFE_triggerButtonProps?.['aria-label']) {
457
- return {
458
- listBoxLabel: UNSAFE_triggerButtonProps['aria-label'],
459
- };
460
- }
461
-
462
- if (UNSAFE_triggerButtonProps?.['aria-labelledby']) {
463
- return {
464
- listBoxLabelledBy: UNSAFE_triggerButtonProps['aria-labelledby'],
465
- };
466
- }
467
-
468
- if (inputAttributes['aria-labelledby']) {
469
- return {
470
- listBoxLabelledBy: inputAttributes['aria-labelledby'],
471
- };
472
- }
473
-
474
- return {};
475
- };
476
-
477
- return (
478
- <ListboxBase
479
- name={name}
480
- multiple={multiple}
481
- defaultValue={defaultValue as M extends true ? T[] : T}
482
- value={controlledValue as M extends true ? T[] : T}
483
- by={compareValues}
484
- disabled={disabled}
485
- onChange={
486
- ((value) => {
487
- if (!multiple) {
488
- setOpen(false);
489
- }
490
- onChange?.(value);
491
- }) satisfies SelectInputProps<T, M>['onChange']
492
- }
493
- >
494
- {({ disabled: uiDisabled, value }) => {
495
- const placeholderShown =
496
- multiple && Array.isArray(value) ? value.length === 0 : value == null;
497
- return (
498
- <OptionsOverlay
499
- placement="bottom-start"
500
- open={open}
501
- renderTrigger={({ ref, getInteractionProps }) => (
502
- <SelectInputTriggerButtonPropsContext.Provider
503
- // eslint-disable-next-line react/jsx-no-constructed-context-values
504
- value={{
505
- ref: (node) => {
506
- ref(node);
507
- if (externalTriggerRef) {
508
- // eslint-disable-next-line no-param-reassign
509
- externalTriggerRef.current = node;
510
- } else {
511
- internalTriggerRef.current = node;
512
- }
513
- },
514
- ...inputAttributes,
515
- ...UNSAFE_triggerButtonProps,
516
- id,
517
- ...mergeProps(
518
- {
519
- onClick: () => {
520
- setOpen((prev) => !prev);
521
- },
522
- onKeyDown: (event: React.KeyboardEvent) => {
523
- if (
524
- event.key === ' ' ||
525
- event.key === 'Enter' ||
526
- event.key === 'ArrowDown' ||
527
- event.key === 'ArrowUp'
528
- ) {
529
- setOpen((prev) => !prev);
530
- }
531
- },
532
- },
533
- getInteractionProps(),
534
- ),
535
- }}
536
- >
537
- {renderTrigger({
538
- content: !placeholderShown ? (
539
- <SelectInputOptionContentWithinTriggerContext.Provider value>
540
- {multiple && Array.isArray(value)
541
- ? (value as readonly NonNullable<T>[])
542
- .map((option) => renderValue(option, true))
543
- .filter((node) => node != null)
544
- .join(', ')
545
- : renderValue(value as NonNullable<T>, true)}
546
- </SelectInputOptionContentWithinTriggerContext.Provider>
547
- ) : (
548
- placeholder
549
- ),
550
- placeholderShown,
551
- clear:
552
- onClear != null
553
- ? () => {
554
- onClear();
555
- (externalTriggerRef?.current ?? internalTriggerRef.current)?.focus({
556
- preventScroll: true,
557
- });
558
- }
559
- : undefined,
560
- disabled: uiDisabled,
561
- size,
562
- className,
563
- })}
564
- </SelectInputTriggerButtonPropsContext.Provider>
565
- )}
566
- initialFocusRef={controllerRef}
567
- size={filterable ? 'lg' : 'md'}
568
- padding="none"
569
- onClose={() => {
570
- setOpen(false);
571
- }}
572
- onCloseEnd={() => {
573
- setFilterQuery('');
574
- }}
575
- >
576
- <SelectInputOptions
577
- id={id ? `${id}Search` : undefined}
578
- parentId={parentId}
579
- items={items}
580
- compareValues={compareValues}
581
- renderValue={renderValue}
582
- renderFooter={renderFooter}
583
- filterable={filterable}
584
- filterPlaceholder={filterPlaceholder}
585
- sortFilteredOptions={sortFilteredOptions}
586
- searchInputRef={searchInputRef}
587
- listboxRef={listboxRef}
588
- filterQuery={deferredFilterQuery}
589
- autocomplete={autocomplete}
590
- name={name}
591
- onFilterChange={setFilterQuery}
592
- onAutocompleteSelect={(matchedValue) => {
593
- onChange?.(matchedValue as M extends true ? T[] : T);
594
- if (!multiple) {
595
- setOpen(false);
596
- }
597
- }}
598
- {...getListBoxLabelProps()}
599
- />
600
- </OptionsOverlay>
601
- );
602
- }}
603
- </ListboxBase>
604
- );
605
- }
606
-
607
- SelectInput.sortByRelevance = sortByRelevance;
608
-
609
- const SelectInputTriggerButtonPropsContext = createContext<{
610
- ref?: React.ForwardedRef<HTMLButtonElement | null>;
611
- id?: string;
612
- onClick?: (event: React.MouseEvent) => void;
613
- onKeyDown?: (event: React.KeyboardEvent) => void;
614
- [key: string]: unknown;
615
- }>({});
616
-
617
- type SelectInputTriggerButtonElementType = 'button' | React.ComponentType;
618
-
619
- export type SelectInputTriggerButtonProps<
620
- T extends SelectInputTriggerButtonElementType = 'button',
621
- > = Merge<React.ComponentPropsWithoutRef<T>, { as?: T }>;
622
-
623
- export function SelectInputTriggerButton<T extends SelectInputTriggerButtonElementType = 'button'>({
624
- as = 'button' as T,
625
- ...restProps
626
- }: SelectInputTriggerButtonProps<T>) {
627
- const { ref, onClick, onKeyDown, ...interactionProps } = useContext(
628
- SelectInputTriggerButtonPropsContext,
629
- );
630
-
631
- return (
632
- <ListboxButton
633
- ref={ref}
634
- as={PolymorphicWithOverrides}
635
- role="combobox"
636
- __overrides={{ as, ...interactionProps }}
637
- {...mergeProps({ onClick, onKeyDown }, restProps)}
638
- />
639
- );
640
- }
641
-
642
- interface SelectInputOptionsContainerProps extends React.ComponentPropsWithRef<'div'> {
643
- onAriaActiveDescendantChange: (value: React.AriaAttributes['aria-activedescendant']) => void;
644
- }
645
-
646
- const SelectInputOptionsContainer = forwardRef(function SelectInputOptionsContainer(
647
- {
648
- 'aria-orientation': ariaOrientation,
649
- 'aria-activedescendant': ariaActiveDescendant,
650
- role,
651
- tabIndex,
652
- onAriaActiveDescendantChange,
653
- onKeyDown,
654
- ...restProps
655
- }: SelectInputOptionsContainerProps,
656
- ref: React.ForwardedRef<HTMLDivElement | null>,
657
- ) {
658
- const handleAriaActiveDescendantChange = useEffectEvent(onAriaActiveDescendantChange);
659
- useEffect(() => {
660
- handleAriaActiveDescendantChange(ariaActiveDescendant);
661
- }, [ariaActiveDescendant, handleAriaActiveDescendantChange]);
662
-
663
- return (
664
- <div
665
- ref={ref}
666
- role="none"
667
- onKeyDown={(event) => {
668
- // Prevent confirmation close without an active item
669
- if (event.key === 'Enter' && ariaActiveDescendant == null) {
670
- return;
671
- }
672
-
673
- // Required to make ListBox focusable
674
- if (event.key === 'Tab') {
675
- return;
676
- }
677
-
678
- // Prevent absorbing Escape early
679
- if (event.key === 'Escape') {
680
- onKeyDown?.({
681
- ...event,
682
- preventDefault: () => {},
683
- stopPropagation: () => {},
684
- });
685
- return;
686
- }
687
-
688
- onKeyDown?.(event);
689
- }}
690
- {...restProps}
691
- />
692
- );
693
- });
694
-
695
- interface SelectInputOptionsProps<T = string> extends Pick<
696
- SelectInputProps<T>,
697
- | 'items'
698
- | 'renderValue'
699
- | 'renderFooter'
700
- | 'filterable'
701
- | 'filterPlaceholder'
702
- | 'id'
703
- | 'parentId'
704
- | 'compareValues'
705
- | 'sortFilteredOptions'
706
- > {
707
- searchInputRef: React.MutableRefObject<HTMLInputElement | null>;
708
- listboxRef: React.MutableRefObject<HTMLDivElement | null>;
709
- filterQuery: string;
710
- onFilterChange: (query: string) => void;
711
- listBoxLabel?: string;
712
- listBoxLabelledBy?: string;
713
- autocomplete?: string;
714
- name?: string;
715
- onAutocompleteSelect?: (value: T) => void;
716
- }
717
-
718
- function SelectInputOptions<T = string>({
719
- id,
720
- parentId,
721
- items,
722
- compareValues: compareValuesProp,
723
- renderValue = String,
724
- renderFooter,
725
- filterable = false,
726
- filterPlaceholder,
727
- sortFilteredOptions,
728
- searchInputRef,
729
- listboxRef,
730
- filterQuery,
731
- onFilterChange,
732
- listBoxLabel,
733
- listBoxLabelledBy,
734
- autocomplete,
735
- name,
736
- onAutocompleteSelect,
737
- }: SelectInputOptionsProps<T>) {
738
- const intl = useIntl();
739
- const virtualiserHandlerRef = useRef<VirtualizerHandle>(null);
740
- const controllerRef = filterable ? searchInputRef : listboxRef;
741
- const [initialRender, setInitialRender] = useState(true);
742
-
743
- const needle = useMemo(() => {
744
- if (filterable) {
745
- return filterQuery ? searchableString(filterQuery) : null;
746
- }
747
- return undefined;
748
- }, [filterQuery, filterable]);
749
- useEffect(() => {
750
- if (needle) {
751
- // Ensure having an active option while filtering.
752
- // Without `requestAnimationFrame` upon which React depends for scheduling
753
- // updates, the active status would only show for a split second and then
754
- // disappear inadvertently.
755
- requestAnimationFrame(() => {
756
- if (
757
- controllerRef.current != null &&
758
- !controllerRef.current.hasAttribute('aria-activedescendant')
759
- ) {
760
- // Activate first option via synthetic key press
761
- controllerRef.current.dispatchEvent(
762
- new KeyboardEvent('keydown', { key: 'Home', bubbles: true }),
763
- );
764
- }
765
- });
766
- }
767
- }, [controllerRef, needle]);
768
-
769
- const compareValues = useMemo(() => {
770
- if (!compareValuesProp) {
771
- return undefined;
772
- }
773
-
774
- if (typeof compareValuesProp === 'function') {
775
- return (a: NonNullable<T>, b: NonNullable<T>) => compareValuesProp(a, b);
776
- }
777
-
778
- const key = compareValuesProp;
779
- return (a: NonNullable<T>, b: NonNullable<T>) => {
780
- if (typeof a === 'object' && a != null && typeof b === 'object' && b != null) {
781
- return (a as Record<string, unknown>)[key] === (b as Record<string, unknown>)[key];
782
- }
783
- return a === b;
784
- };
785
- }, [compareValuesProp]);
786
-
787
- const filteredItems: readonly SelectInputItem<NonNullable<T> | undefined>[] = useMemo(() => {
788
- if (needle == null) {
789
- return items;
790
- }
791
-
792
- const dedupedItems = dedupeSelectInputItems(items, compareValues);
793
-
794
- if (sortFilteredOptions) {
795
- // When sorting, filter out non-matching items completely to avoid ghost items
796
- const filtered = dedupedItems.map((item) => {
797
- if (item.type === 'option') {
798
- return selectInputOptionItemIncludesNeedle(item, needle)
799
- ? item
800
- : { ...item, value: undefined };
801
- }
802
- if (item.type === 'group') {
803
- return {
804
- ...item,
805
- options: item.options.map((option) =>
806
- selectInputOptionItemIncludesNeedle(option, needle)
807
- ? option
808
- : { ...option, value: undefined },
809
- ),
810
- };
811
- }
812
- return item;
813
- });
814
-
815
- return sortSelectInputItems(filtered, sortFilteredOptions, filterQuery);
816
- }
817
-
818
- return filterSelectInputItems(dedupedItems, (item) =>
819
- selectInputOptionItemIncludesNeedle(item, needle),
820
- );
821
- // eslint-disable-next-line react-hooks/exhaustive-deps
822
- }, [needle, items, compareValues]);
823
- const resultsEmpty = needle != null && filteredItems.length === 0;
824
-
825
- const virtualized = filteredItems.length > MAX_ITEMS_WITHOUT_VIRTUALIZATION;
826
-
827
- // Items shown once shall be kept mounted until the needle changes, otherwise
828
- // the scroll position may jump around inadvertently. Pattern adopted from:
829
- // https://inokawa.github.io/virtua/?path=/story/advanced-keep-offscreen-items--append-only
830
- const [mountedIndexes, setMountedIndexes] = useState<number[]>([]);
831
- const prevNeedleRef = useRef(needle);
832
-
833
- useEffect(() => {
834
- const needleChanged = prevNeedleRef.current !== needle;
835
- prevNeedleRef.current = needle;
836
-
837
- if (needleChanged) {
838
- // Reset mounted indexes when search changes to avoid stale scroll positions
839
- setMountedIndexes([]);
840
- return;
841
- }
842
-
843
- // Ensure the 'End' key works as intended by keeping the last item mounted.
844
- // Skipped on needle change to prevent auto-scrolling on search.
845
- if (filteredItems.length > 0) {
846
- setMountedIndexes((prevMountedIndexes) => {
847
- const indexes = new Set(prevMountedIndexes);
848
- indexes.add(filteredItems.length - 1);
849
- return [...indexes]; // Sorting is redundant by nature here
850
- });
851
- }
852
- }, [needle, filteredItems.length]);
853
-
854
- const listboxContainerRef = useRef<HTMLDivElement>(null);
855
- useEffect(() => {
856
- if (listboxContainerRef.current != null) {
857
- listboxContainerRef.current.style.setProperty(
858
- '--initial-height',
859
- `${listboxContainerRef.current.offsetHeight}px`,
860
- );
861
- }
862
- }, []);
863
-
864
- useEffect(() => {
865
- setInitialRender(false);
866
- }, []);
867
-
868
- const showStatus = resultsEmpty;
869
- const statusId = useId();
870
- const listboxId = useId();
871
-
872
- const getItemNode = (index: number) => {
873
- const item = filteredItems[index];
874
- return (
875
- <SelectInputItemView key={index} item={item} renderValue={renderValue} needle={needle} />
876
- );
877
- };
878
-
879
- const findMatchingItem = (autocompleteValue: string): T | null => {
880
- const flatOptions = items
881
- .flatMap((item) =>
882
- item.type === 'group' ? item.options : item.type === 'option' ? [item] : [],
883
- )
884
- .filter(
885
- (item): item is SelectInputOptionItem<NonNullable<T>> =>
886
- item.type === 'option' && item.value != null,
887
- );
888
-
889
- const exactMatch = flatOptions.find(
890
- (option) =>
891
- String(option.value) === autocompleteValue ||
892
- option.filterMatchers?.some((matcher) => matcher === autocompleteValue),
893
- );
894
-
895
- if (exactMatch) {
896
- return exactMatch.value;
897
- }
898
-
899
- const fuzzyMatch = flatOptions.find((option) =>
900
- option.filterMatchers?.some((matcher) =>
901
- matcher.toLowerCase().includes(autocompleteValue.toLowerCase()),
902
- ),
903
- );
904
-
905
- return fuzzyMatch ? fuzzyMatch.value : null;
906
- };
907
-
908
- return (
909
- <ListboxOptions
910
- modal
911
- as={SelectInputOptionsContainer}
912
- static
913
- className="np-select-input-options-container"
914
- onAriaActiveDescendantChange={(value: React.AriaAttributes['aria-activedescendant']) => {
915
- if (controllerRef.current != null) {
916
- if (!initialRender && value != null) {
917
- controllerRef.current.setAttribute('aria-activedescendant', value);
918
- } else {
919
- controllerRef.current.removeAttribute('aria-activedescendant');
920
- }
921
- }
922
- }}
923
- >
924
- {filterable ? (
925
- <div className="np-select-input-query-container">
926
- <SearchInput
927
- ref={searchInputRef}
928
- id={id}
929
- name={name}
930
- autoComplete={autocomplete}
931
- role="combobox"
932
- shape="rectangle"
933
- placeholder={filterPlaceholder}
934
- aria-label={filterPlaceholder}
935
- defaultValue={filterQuery}
936
- aria-autocomplete="list"
937
- aria-expanded
938
- aria-controls={listboxId}
939
- aria-describedby={showStatus ? statusId : undefined}
940
- onKeyDown={(event) => {
941
- // Prevent interfering with the matcher of Headless UI
942
- // https://mathiasbynens.be/notes/javascript-unicode#regex
943
- if (/^.$/u.test(event.key)) {
944
- event.stopPropagation();
945
- }
946
- }}
947
- onChange={(event) => {
948
- // Free up resources and ensure not to go out of bounds when the
949
- // resulting item count is less than before
950
- const inputValue = event.currentTarget.value;
951
-
952
- // Free up resources and ensure not to go out of bounds
953
- setMountedIndexes([]);
954
- onFilterChange(inputValue);
955
- }}
956
- onInput={(event) => {
957
- const inputValue = event.currentTarget.value;
958
- const inputElement = event.currentTarget;
959
-
960
- if (autocomplete && onAutocompleteSelect && inputValue) {
961
- setTimeout(() => {
962
- if (inputElement.value === inputValue && inputValue.length > 2) {
963
- const matchedValue = findMatchingItem(inputValue);
964
- if (matchedValue !== null) {
965
- onAutocompleteSelect(matchedValue);
966
- }
967
- }
968
- }, 50);
969
- }
970
- }}
971
- />
972
- </div>
973
- ) : null}
974
-
975
- <section
976
- ref={listboxContainerRef}
977
- tabIndex={-1}
978
- className={clsx(
979
- 'np-select-input-listbox-container',
980
- virtualized && 'np-select-input-listbox-container--virtualized',
981
- needle == null && // Groups aren't shown when filtering
982
- items.some((item) => item.type === 'group') &&
983
- 'np-select-input-listbox-container--has-group',
984
- )}
985
- data-wds-parent={parentId ?? undefined}
986
- >
987
- {resultsEmpty ? (
988
- <div id={statusId} className="np-select-input-options-status">
989
- <CrossCircle size={16} className="np-select-input-options-status-icon" />
990
- {intl.formatMessage(messages.noResultsFound)}
991
- </div>
992
- ) : null}
993
-
994
- <div
995
- ref={listboxRef}
996
- id={listboxId}
997
- role="listbox"
998
- aria-orientation="vertical"
999
- aria-label={listBoxLabel}
1000
- aria-labelledby={listBoxLabelledBy}
1001
- tabIndex={0}
1002
- className="np-select-input-listbox"
1003
- >
1004
- {!virtualized ? (
1005
- filteredItems.map((_, index) => getItemNode(index))
1006
- ) : (
1007
- <Virtualizer
1008
- ref={virtualiserHandlerRef}
1009
- data={filteredItems}
1010
- keepMounted={mountedIndexes}
1011
- scrollRef={listboxRef} // `VList` doesn't expose this
1012
- onScroll={async () => {
1013
- if (!virtualiserHandlerRef.current) return;
1014
-
1015
- const startIndex = virtualiserHandlerRef.current.findItemIndex(
1016
- virtualiserHandlerRef.current.scrollOffset,
1017
- );
1018
- const endIndex = virtualiserHandlerRef.current.findItemIndex(
1019
- virtualiserHandlerRef.current.scrollOffset +
1020
- virtualiserHandlerRef.current.viewportSize,
1021
- );
1022
-
1023
- setMountedIndexes((prevMountedIndexes) => {
1024
- const indexes = new Set(prevMountedIndexes);
1025
-
1026
- for (let index = startIndex; index <= endIndex; index += 1) {
1027
- indexes.add(index);
1028
- }
1029
-
1030
- return [...indexes].sort((a, b) => a - b);
1031
- });
1032
- }}
1033
- >
1034
- {(item, index) => (
1035
- // The position of each item can't be inferred by browsers when
1036
- // virtualizing, as some of the items may not be in the DOM
1037
- <SelectInputItemsCountContext.Provider value={filteredItems.length}>
1038
- <SelectInputItemPositionContext.Provider value={index + 1}>
1039
- {getItemNode(index)}
1040
- </SelectInputItemPositionContext.Provider>
1041
- </SelectInputItemsCountContext.Provider>
1042
- )}
1043
- </Virtualizer>
1044
- )}
1045
- </div>
1046
-
1047
- {renderFooter != null ? (
1048
- <footer className="np-select-input-footer">
1049
- <div
1050
- role="none"
1051
- onKeyDown={(event) => {
1052
- // Prevent interfering with Headless UI
1053
- if (event.key !== 'Escape') {
1054
- event.stopPropagation();
1055
- }
1056
- }}
1057
- >
1058
- {renderFooter({
1059
- resultsEmpty,
1060
- queryNormalized: needle,
1061
- })}
1062
- </div>
1063
- </footer>
1064
- ) : null}
1065
- </section>
1066
- </ListboxOptions>
1067
- );
1068
- }
1069
-
1070
- interface SelectInputItemViewProps<
1071
- T = string,
1072
- I extends SelectInputItem<T | undefined> = SelectInputItem<T | undefined>,
1073
- > extends Required<Pick<SelectInputProps<T>, 'renderValue'>> {
1074
- item: I;
1075
- needle: string | null | undefined;
1076
- }
1077
-
1078
- function SelectInputItemView<T = string>({
1079
- item,
1080
- renderValue,
1081
- needle,
1082
- }: SelectInputItemViewProps<T>) {
1083
- switch (item.type) {
1084
- case 'option': {
1085
- if (
1086
- item.value != null &&
1087
- (needle == null || selectInputOptionItemIncludesNeedle(item, needle))
1088
- ) {
1089
- return (
1090
- <SelectInputOption value={item.value} disabled={item.disabled}>
1091
- {renderValue(item.value, false)}
1092
- </SelectInputOption>
1093
- );
1094
- }
1095
- break;
1096
- }
1097
- case 'group': {
1098
- return <SelectInputGroupItemView item={item} renderValue={renderValue} needle={needle} />;
1099
- }
1100
- case 'separator': {
1101
- if (needle == null) {
1102
- return <hr className="np-select-input-separator-item" />;
1103
- }
1104
- break;
1105
- }
1106
- }
1107
- return null;
1108
- }
1109
-
1110
- interface SelectInputGroupItemViewProps<T = string> extends SelectInputItemViewProps<
1111
- T,
1112
- SelectInputGroupItem<T | undefined>
1113
- > {}
1114
-
1115
- function SelectInputGroupItemView<T = string>({
1116
- item,
1117
- renderValue,
1118
- needle,
1119
- }: SelectInputGroupItemViewProps<T>) {
1120
- const headerId = useId();
1121
-
1122
- const header = (
1123
- <Header
1124
- as="header"
1125
- role="none"
1126
- id={headerId}
1127
- title={item.label}
1128
- // @ts-expect-error when we migrate ActionButton to new Button this should be sorted
1129
- action={
1130
- item.action && {
1131
- text: item.action.label,
1132
- onClick: item.action.onClick,
1133
- }
1134
- }
1135
- className="np-select-input-group-item-header p-x-1"
1136
- />
1137
- );
1138
-
1139
- return (
1140
- // An empty container may be rendered when no options match `needle`
1141
- // However, pre-filtering would result in worse performance overall
1142
- <Section
1143
- as="section"
1144
- role="group"
1145
- aria-labelledby={headerId}
1146
- className={clsx('m-y-0', needle === null && 'np-select-input-group-item--without-needle')}
1147
- >
1148
- {needle == null ? header : null}
1149
- {item.options.map((option, index) => (
1150
- <SelectInputItemView
1151
- // eslint-disable-next-line react/no-array-index-key
1152
- key={index}
1153
- item={option}
1154
- renderValue={renderValue}
1155
- needle={needle}
1156
- />
1157
- ))}
1158
- </Section>
1159
- );
1160
- }
1161
-
1162
- const SelectInputItemsCountContext = createContext<number | undefined>(undefined);
1163
- const SelectInputItemPositionContext = createContext<number | undefined>(undefined);
1164
-
1165
- interface SelectInputOptionProps<T = string> {
1166
- value: T;
1167
- disabled?: boolean;
1168
- children?: React.ReactNode;
1169
- }
1170
-
1171
- function SelectInputOption<T = string>({ value, disabled, children }: SelectInputOptionProps<T>) {
1172
- const itemsCount = useContext(SelectInputItemsCountContext);
1173
- const itemPosition = useContext(SelectInputItemPositionContext);
1174
- return (
1175
- <ListboxOption
1176
- as="div"
1177
- value={value}
1178
- aria-setsize={itemsCount}
1179
- aria-posinset={itemPosition}
1180
- disabled={disabled}
1181
- className={({ active, disabled: uiDisabled }) =>
1182
- clsx(
1183
- 'np-select-input-option-container np-text-body-large',
1184
- active && 'np-select-input-option-container--active',
1185
- uiDisabled && 'np-select-input-option-container--disabled',
1186
- )
1187
- }
1188
- >
1189
- {({ selected }) => (
1190
- <>
1191
- <div className="np-select-input-option">{children}</div>
1192
- <Check
1193
- size={16}
1194
- className={clsx(
1195
- 'np-select-input-option-check',
1196
- !selected && 'np-select-input-option-check--not-selected',
1197
- )}
1198
- />
1199
- </>
1200
- )}
1201
- </ListboxOption>
1202
- );
1203
- }
1204
-
1205
- const SelectInputOptionContentWithinTriggerContext = createContext(false);
1206
-
1207
- export interface SelectInputOptionContentProps {
1208
- title: React.ReactNode;
1209
- note?: string;
1210
- description?: string;
1211
- icon?: React.ReactNode;
1212
- }
1213
-
1214
- export function SelectInputOptionContent({
1215
- title,
1216
- note,
1217
- description,
1218
- icon,
1219
- }: SelectInputOptionContentProps) {
1220
- const withinTrigger = useContext(SelectInputOptionContentWithinTriggerContext);
1221
-
1222
- return (
1223
- <div
1224
- className={clsx(
1225
- 'np-select-input-option-content-container',
1226
- (note || description) && 'np-text-body-large',
1227
- )}
1228
- >
1229
- {icon ? (
1230
- <div
1231
- className={clsx(
1232
- 'np-select-input-option-content-icon',
1233
- !withinTrigger && 'np-select-input-option-content-icon--not-within-trigger',
1234
- )}
1235
- >
1236
- {icon}
1237
- </div>
1238
- ) : null}
1239
-
1240
- <div className="np-select-input-option-content-text">
1241
- <div
1242
- className={clsx(
1243
- 'np-select-input-option-content-text-line-1',
1244
- withinTrigger && 'np-select-input-option-content-text-within-trigger',
1245
- )}
1246
- >
1247
- <div className="d-inline">{title}</div>
1248
- {note ? (
1249
- <span className="np-select-input-option-content-text-secondary np-text-body-default">
1250
- {note}
1251
- </span>
1252
- ) : null}
1253
- </div>
1254
-
1255
- {description ? (
1256
- <div
1257
- className={clsx(
1258
- 'np-select-input-option-content-text-secondary np-text-body-default',
1259
- withinTrigger &&
1260
- 'np-select-input-option-content-text-within-trigger np-select-input-option-description-in-trigger',
1261
- )}
1262
- >
1263
- {description}
1264
- </div>
1265
- ) : null}
1266
- </div>
1267
- </div>
1268
- );
1269
- }