@astryxdesign/core 0.5.0 → 0.5.1-canary.70169cd

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 (622) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +45 -20
  5. package/dist/AspectRatio/AspectRatio.d.ts +6 -6
  6. package/dist/Avatar/Avatar.d.ts +25 -1
  7. package/dist/Avatar/Avatar.d.ts.map +1 -1
  8. package/dist/Avatar/Avatar.js +43 -9
  9. package/dist/Avatar/index.d.ts +2 -2
  10. package/dist/Avatar/index.d.ts.map +1 -1
  11. package/dist/Avatar/index.js +1 -1
  12. package/dist/AvatarGroup/AvatarGroup.d.ts +9 -2
  13. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroup.js +5 -2
  15. package/dist/AvatarGroup/AvatarGroupContext.d.ts +2 -1
  16. package/dist/AvatarGroup/AvatarGroupContext.d.ts.map +1 -1
  17. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  18. package/dist/AvatarGroup/AvatarGroupOverflow.js +11 -7
  19. package/dist/Badge/Badge.d.ts.map +1 -1
  20. package/dist/Badge/Badge.js +26 -2
  21. package/dist/Banner/Banner.d.ts +10 -1
  22. package/dist/Banner/Banner.d.ts.map +1 -1
  23. package/dist/Banner/Banner.js +11 -2
  24. package/dist/Button/Button.d.ts.map +1 -1
  25. package/dist/Button/Button.js +8 -7
  26. package/dist/Calendar/Calendar.d.ts +4 -2
  27. package/dist/Calendar/Calendar.d.ts.map +1 -1
  28. package/dist/Calendar/Calendar.js +15 -21
  29. package/dist/Calendar/hooks/useCalendarDays.js +4 -6
  30. package/dist/Calendar/hooks/useCalendarNavigation.js +4 -6
  31. package/dist/Calendar/styles.d.ts +5 -20
  32. package/dist/Calendar/styles.d.ts.map +1 -1
  33. package/dist/Calendar/styles.js +15 -17
  34. package/dist/Card/Card.d.ts +12 -12
  35. package/dist/Card/Card.d.ts.map +1 -1
  36. package/dist/Card/Card.js +17 -9
  37. package/dist/Card/index.d.ts +35 -1
  38. package/dist/Card/index.d.ts.map +1 -1
  39. package/dist/Card/index.js +21 -1
  40. package/dist/Carousel/Carousel.d.ts.map +1 -1
  41. package/dist/Carousel/Carousel.js +108 -7
  42. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  43. package/dist/CheckboxInput/CheckboxInput.js +7 -1
  44. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  45. package/dist/CodeBlock/CodeBlock.js +15 -4
  46. package/dist/CodeBlock/highlightRanges.js +1 -1
  47. package/dist/CodeBlock/highlightStyles.js +13 -13
  48. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  49. package/dist/CommandPalette/CommandPaletteItem.js +2 -7
  50. package/dist/ComplexSelector/ComplexSelector.d.ts +10 -2
  51. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  52. package/dist/ComplexSelector/ComplexSelector.js +14 -7
  53. package/dist/DateInput/DateInput.d.ts +43 -0
  54. package/dist/DateInput/DateInput.d.ts.map +1 -1
  55. package/dist/DateInput/DateInput.js +22 -5
  56. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  57. package/dist/DateInput/MonthScroller.js +5 -3
  58. package/dist/DateInput/MonthYearWheels.d.ts.map +1 -1
  59. package/dist/DateInput/MonthYearWheels.js +6 -4
  60. package/dist/DateInput/NativeDateField.d.ts +10 -0
  61. package/dist/DateInput/NativeDateField.d.ts.map +1 -0
  62. package/dist/DateInput/NativeDateField.js +494 -0
  63. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  64. package/dist/DateInput/TouchDateField.js +52 -25
  65. package/dist/DateInput/Wheel.d.ts.map +1 -1
  66. package/dist/DateInput/Wheel.js +1 -0
  67. package/dist/DateInput/index.d.ts +1 -1
  68. package/dist/DateInput/index.d.ts.map +1 -1
  69. package/dist/DateInput/nativeDateSegments.d.ts +22 -0
  70. package/dist/DateInput/nativeDateSegments.d.ts.map +1 -0
  71. package/dist/DateInput/nativeDateSegments.js +111 -0
  72. package/dist/DateRangeInput/DateRangeInput.d.ts +4 -2
  73. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  74. package/dist/DateRangeInput/DateRangeInput.js +3 -5
  75. package/dist/DateTimeInput/DateTimeInput.d.ts +23 -24
  76. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  77. package/dist/DateTimeInput/DateTimeInput.js +35 -10
  78. package/dist/DateTimeInput/TouchDateTimeField.d.ts +6 -0
  79. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -0
  80. package/dist/DateTimeInput/TouchDateTimeField.js +1021 -0
  81. package/dist/Dialog/Dialog.d.ts +16 -2
  82. package/dist/Dialog/Dialog.d.ts.map +1 -1
  83. package/dist/Dialog/Dialog.js +28 -7
  84. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  85. package/dist/DropdownMenu/DropdownMenu.js +4 -13
  86. package/dist/Field/FieldLabel.d.ts.map +1 -1
  87. package/dist/Field/FieldLabel.js +9 -1
  88. package/dist/Field/InputClearButton.d.ts +9 -3
  89. package/dist/Field/InputClearButton.d.ts.map +1 -1
  90. package/dist/Field/InputClearButton.js +14 -3
  91. package/dist/Heading/Heading.d.ts.map +1 -1
  92. package/dist/Heading/Heading.js +0 -1
  93. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  94. package/dist/HoverCard/HoverCard.js +104 -3
  95. package/dist/HoverCard/useHoverCard.d.ts +14 -2
  96. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  97. package/dist/HoverCard/useHoverCard.js +7 -1
  98. package/dist/Icon/Icon.d.ts +4 -2
  99. package/dist/Icon/Icon.d.ts.map +1 -1
  100. package/dist/Icon/defaultIcons.d.ts +3 -2
  101. package/dist/Icon/defaultIcons.d.ts.map +1 -1
  102. package/dist/Icon/defaultIcons.js +7 -0
  103. package/dist/Icon/globalIconRegistry.d.ts +13 -0
  104. package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
  105. package/dist/Icon/globalIconRegistry.js +34 -6
  106. package/dist/Icon/index.d.ts +1 -1
  107. package/dist/Icon/index.d.ts.map +1 -1
  108. package/dist/Icon/useIcon.d.ts +5 -2
  109. package/dist/Icon/useIcon.d.ts.map +1 -1
  110. package/dist/Icon/useIcon.js +3 -0
  111. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  112. package/dist/Indicator/CheckboxIndicator.js +6 -2
  113. package/dist/Item/Item.d.ts.map +1 -1
  114. package/dist/Item/Item.js +2 -2
  115. package/dist/Kbd/Kbd.d.ts.map +1 -1
  116. package/dist/Kbd/Kbd.js +2 -26
  117. package/dist/Layer/gestureCounter.d.ts +8 -0
  118. package/dist/Layer/gestureCounter.d.ts.map +1 -0
  119. package/dist/Layer/gestureCounter.js +53 -0
  120. package/dist/Layer/useLayer.d.ts +23 -13
  121. package/dist/Layer/useLayer.d.ts.map +1 -1
  122. package/dist/Layer/useLayer.js +146 -24
  123. package/dist/List/List.d.ts +1 -1
  124. package/dist/List/List.d.ts.map +1 -1
  125. package/dist/List/List.js +15 -4
  126. package/dist/Markdown/Markdown.d.ts +1 -1
  127. package/dist/Markdown/Markdown.d.ts.map +1 -1
  128. package/dist/Markdown/Markdown.js +20 -7
  129. package/dist/Markdown/parser.d.ts.map +1 -1
  130. package/dist/Markdown/parser.js +5 -3
  131. package/dist/MetadataList/MetadataListItem.d.ts +1 -1
  132. package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
  133. package/dist/MetadataList/MetadataListItem.js +4 -1
  134. package/dist/MultiSelector/MultiSelector.d.ts +18 -1
  135. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  136. package/dist/MultiSelector/MultiSelector.js +58 -11
  137. package/dist/MultiSelector/hooks.d.ts +7 -1
  138. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  139. package/dist/MultiSelector/hooks.js +3 -2
  140. package/dist/NavIcon/NavIcon.d.ts.map +1 -1
  141. package/dist/NavIcon/NavIcon.js +5 -1
  142. package/dist/NavItem/navItemStyles.stylex.d.ts +0 -8
  143. package/dist/NavItem/navItemStyles.stylex.d.ts.map +1 -1
  144. package/dist/NavItem/navItemStyles.stylex.js +0 -2
  145. package/dist/NavMenu/NavHeadingMenu.d.ts +1 -1
  146. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  147. package/dist/NavMenu/NavHeadingMenu.js +7 -4
  148. package/dist/NumberInput/NumberInput.d.ts +6 -1
  149. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  150. package/dist/NumberInput/NumberInput.js +95 -126
  151. package/dist/NumberInput/numberInputCommit.d.ts +34 -0
  152. package/dist/NumberInput/numberInputCommit.d.ts.map +1 -0
  153. package/dist/NumberInput/numberInputCommit.js +71 -0
  154. package/dist/NumberInput/numberParser.d.ts +64 -0
  155. package/dist/NumberInput/numberParser.d.ts.map +1 -0
  156. package/dist/NumberInput/numberParser.js +350 -0
  157. package/dist/Outline/Outline.d.ts.map +1 -1
  158. package/dist/Outline/Outline.js +2 -3
  159. package/dist/OverflowList/OverflowList.d.ts +41 -1
  160. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  161. package/dist/OverflowList/OverflowList.js +43 -2
  162. package/dist/Popover/Popover.d.ts.map +1 -1
  163. package/dist/Popover/Popover.js +8 -14
  164. package/dist/Popover/usePopover.d.ts +15 -41
  165. package/dist/Popover/usePopover.d.ts.map +1 -1
  166. package/dist/Popover/usePopover.js +26 -7
  167. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  168. package/dist/PowerSearch/PowerSearchValueEditor.js +91 -18
  169. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  170. package/dist/PowerSearch/formatFilterValue.js +4 -2
  171. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  172. package/dist/ProgressBar/ProgressBar.js +18 -5
  173. package/dist/Resizable/useResizable.d.ts +19 -1
  174. package/dist/Resizable/useResizable.d.ts.map +1 -1
  175. package/dist/Resizable/useResizable.js +104 -38
  176. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  177. package/dist/SelectableCard/SelectableCard.js +9 -0
  178. package/dist/Selector/Selector.d.ts +18 -1
  179. package/dist/Selector/Selector.d.ts.map +1 -1
  180. package/dist/Selector/Selector.js +92 -21
  181. package/dist/Selector/hooks.d.ts +7 -1
  182. package/dist/Selector/hooks.d.ts.map +1 -1
  183. package/dist/Selector/hooks.js +3 -2
  184. package/dist/SideNav/SideNav.d.ts +6 -1
  185. package/dist/SideNav/SideNav.d.ts.map +1 -1
  186. package/dist/SideNav/SideNav.js +57 -27
  187. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  188. package/dist/SideNav/SideNavHeading.js +5 -4
  189. package/dist/SideNav/SideNavItem.d.ts +29 -4
  190. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  191. package/dist/SideNav/SideNavItem.js +75 -20
  192. package/dist/Slider/Slider.d.ts.map +1 -1
  193. package/dist/Slider/Slider.js +34 -2
  194. package/dist/Spinner/Spinner.d.ts +3 -1
  195. package/dist/Spinner/Spinner.d.ts.map +1 -1
  196. package/dist/Spinner/Spinner.js +199 -20
  197. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  198. package/dist/StatusDot/StatusDot.js +6 -1
  199. package/dist/Stepper/Step.d.ts.map +1 -1
  200. package/dist/Stepper/Step.js +7 -6
  201. package/dist/Switch/Switch.d.ts.map +1 -1
  202. package/dist/Switch/Switch.js +5 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +6 -1
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +3 -6
  207. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  208. package/dist/Table/plugins/filtering/useTableFiltering.js +73 -4
  209. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +26 -0
  210. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  211. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +34 -3
  212. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  213. package/dist/Table/plugins/selection/useTableSelection.js +25 -1
  214. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  215. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +3 -4
  216. package/dist/Text/Text.d.ts.map +1 -1
  217. package/dist/Text/Text.js +0 -1
  218. package/dist/TextArea/TextArea.d.ts.map +1 -1
  219. package/dist/TextArea/TextArea.js +13 -8
  220. package/dist/Timestamp/Timestamp.d.ts +1 -1
  221. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  222. package/dist/Timestamp/Timestamp.js +10 -18
  223. package/dist/Timestamp/formatInstant.d.ts.map +1 -1
  224. package/dist/Timestamp/formatInstant.js +10 -5
  225. package/dist/Toast/Toast.d.ts +17 -2
  226. package/dist/Toast/Toast.d.ts.map +1 -1
  227. package/dist/Toast/Toast.js +77 -11
  228. package/dist/Toast/ToastViewport.d.ts +1 -0
  229. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  230. package/dist/Toast/ToastViewport.js +76 -21
  231. package/dist/Toast/index.d.ts +1 -1
  232. package/dist/Toast/index.d.ts.map +1 -1
  233. package/dist/Toast/types.d.ts +38 -0
  234. package/dist/Toast/types.d.ts.map +1 -1
  235. package/dist/Toast/useToastGesture.d.ts +26 -0
  236. package/dist/Toast/useToastGesture.d.ts.map +1 -0
  237. package/dist/Toast/useToastGesture.js +223 -0
  238. package/dist/Token/Token.d.ts +1 -1
  239. package/dist/Token/Token.d.ts.map +1 -1
  240. package/dist/Token/Token.js +23 -12
  241. package/dist/Token/TokenLink.d.ts +1 -1
  242. package/dist/Token/TokenLink.d.ts.map +1 -1
  243. package/dist/Token/TokenLink.js +5 -2
  244. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  245. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  246. package/dist/Tokenizer/Tokenizer.js +31 -21
  247. package/dist/TopNav/TopNavHeading.js +2 -2
  248. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  249. package/dist/TopNav/TopNavItem.js +3 -3
  250. package/dist/TopNav/TopNavMegaMenu.d.ts +1 -1
  251. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  252. package/dist/TopNav/TopNavMegaMenu.js +30 -9
  253. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  254. package/dist/TopNav/TopNavMegaMenuItem.js +3 -3
  255. package/dist/TopNav/TopNavMenu.d.ts +1 -1
  256. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  257. package/dist/TopNav/TopNavMenu.js +21 -6
  258. package/dist/TreeList/TreeList.d.ts +1 -1
  259. package/dist/TreeList/TreeList.d.ts.map +1 -1
  260. package/dist/TreeList/TreeList.js +7 -2
  261. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  262. package/dist/TreeList/TreeListItem.js +2 -2
  263. package/dist/Typeahead/BaseTypeahead.d.ts +33 -0
  264. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  265. package/dist/Typeahead/BaseTypeahead.js +51 -8
  266. package/dist/Typeahead/Typeahead.d.ts +8 -1
  267. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  268. package/dist/Typeahead/Typeahead.js +2 -0
  269. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  270. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  271. package/dist/Typeahead/TypeaheadItem.js +7 -2
  272. package/dist/astryx.css +87 -22
  273. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  274. package/dist/hooks/useHotkeys.js +3 -25
  275. package/dist/hooks/useOverflow.d.ts.map +1 -1
  276. package/dist/hooks/useOverflow.js +14 -4
  277. package/dist/i18n/InternationalizationContext.d.ts +2 -0
  278. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  279. package/dist/i18n/InternationalizationContext.js +3 -1
  280. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  281. package/dist/i18n/InternationalizationProvider.js +3 -1
  282. package/dist/i18n/resolve.d.ts +1 -1
  283. package/dist/i18n/resolve.d.ts.map +1 -1
  284. package/dist/i18n/resolve.js +37 -38
  285. package/dist/i18n/useTranslator.d.ts.map +1 -1
  286. package/dist/i18n/useTranslator.js +2 -3
  287. package/dist/index.d.ts +1 -1
  288. package/dist/index.d.ts.map +1 -1
  289. package/dist/theme/Theme.d.ts.map +1 -1
  290. package/dist/theme/Theme.js +46 -10
  291. package/dist/theme/defineTheme.d.ts +9 -3
  292. package/dist/theme/defineTheme.d.ts.map +1 -1
  293. package/dist/theme/defineTheme.js +5 -0
  294. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  295. package/dist/theme/derivedVarRegistry.js +24 -4
  296. package/dist/theme/generateThemeRules.d.ts +21 -0
  297. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  298. package/dist/theme/generateThemeRules.js +26 -0
  299. package/dist/theme/syntax/tokens.d.ts +1 -1
  300. package/dist/theme/syntax/tokens.d.ts.map +1 -1
  301. package/dist/theme/syntax/tokens.js +5 -2
  302. package/dist/theme/useTheme.d.ts.map +1 -1
  303. package/dist/theme/useTheme.js +6 -7
  304. package/dist/utils/dateParser.d.ts +5 -8
  305. package/dist/utils/dateParser.d.ts.map +1 -1
  306. package/dist/utils/dateParser.js +10 -11
  307. package/dist/utils/focusOutline.stylex.d.ts +25 -0
  308. package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
  309. package/dist/utils/focusOutline.stylex.js +15 -0
  310. package/dist/utils/interactionOverlay.stylex.d.ts +26 -0
  311. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -0
  312. package/dist/utils/interactionOverlay.stylex.js +42 -0
  313. package/dist/utils/isApplePlatform.d.ts +18 -0
  314. package/dist/utils/isApplePlatform.d.ts.map +1 -0
  315. package/dist/utils/isApplePlatform.js +37 -0
  316. package/dist/utils/plainDate.d.ts +11 -0
  317. package/dist/utils/plainDate.d.ts.map +1 -1
  318. package/dist/utils/plainDate.js +20 -4
  319. package/locales/af-ZA.json +7 -7
  320. package/locales/ar-SA.json +7 -7
  321. package/locales/ca-ES.json +6 -6
  322. package/locales/cs-CZ.json +7 -7
  323. package/locales/da-DK.json +7 -7
  324. package/locales/de-DE.json +7 -7
  325. package/locales/el-GR.json +7 -7
  326. package/locales/en.json +178 -6
  327. package/locales/es-ES.json +6 -6
  328. package/locales/fi-FI.json +7 -7
  329. package/locales/fr-FR.json +7 -7
  330. package/locales/he-IL.json +7 -7
  331. package/locales/hu-HU.json +7 -7
  332. package/locales/it-IT.json +7 -7
  333. package/locales/ja-JP.json +7 -7
  334. package/locales/ko-KR.json +7 -7
  335. package/locales/nl-NL.json +7 -7
  336. package/locales/no-NO.json +7 -7
  337. package/locales/pl-PL.json +7 -7
  338. package/locales/pseudo.json +130 -1
  339. package/locales/pt-BR.json +7 -7
  340. package/locales/pt-PT.json +7 -7
  341. package/locales/ro-RO.json +7 -7
  342. package/locales/ru-RU.json +7 -7
  343. package/locales/sr-SP.json +7 -7
  344. package/locales/sv-SE.json +7 -7
  345. package/locales/tr-TR.json +7 -7
  346. package/locales/uk-UA.json +7 -7
  347. package/locales/vi-VN.json +7 -7
  348. package/locales/zh-CN.json +7 -7
  349. package/locales/zh-TW.json +7 -7
  350. package/package.json +4 -5
  351. package/src/AlertDialog/AlertDialog.doc.mjs +114 -30
  352. package/src/AlertDialog/AlertDialog.test.tsx +211 -2
  353. package/src/AlertDialog/AlertDialog.tsx +62 -20
  354. package/src/AspectRatio/AspectRatio.doc.mjs +3 -3
  355. package/src/AspectRatio/AspectRatio.tsx +6 -6
  356. package/src/Avatar/Avatar.doc.mjs +12 -2
  357. package/src/Avatar/Avatar.test.tsx +80 -0
  358. package/src/Avatar/Avatar.tsx +64 -14
  359. package/src/Avatar/index.ts +2 -2
  360. package/src/AvatarGroup/AvatarGroup.doc.mjs +8 -2
  361. package/src/AvatarGroup/AvatarGroup.test.tsx +24 -0
  362. package/src/AvatarGroup/AvatarGroup.tsx +12 -4
  363. package/src/AvatarGroup/AvatarGroupContext.ts +5 -3
  364. package/src/AvatarGroup/AvatarGroupOverflow.tsx +12 -12
  365. package/src/Badge/Badge.doc.mjs +3 -2
  366. package/src/Badge/Badge.test.tsx +32 -3
  367. package/src/Badge/Badge.tsx +42 -1
  368. package/src/Banner/Banner.doc.mjs +9 -0
  369. package/src/Banner/Banner.test.tsx +76 -11
  370. package/src/Banner/Banner.tsx +21 -2
  371. package/src/Button/Button.doc.mjs +2 -1
  372. package/src/Button/Button.test.tsx +19 -4
  373. package/src/Button/Button.tsx +9 -29
  374. package/src/ButtonGroup/ButtonGroup.doc.mjs +2 -2
  375. package/src/ButtonGroup/ButtonGroup.test.tsx +13 -0
  376. package/src/Calendar/Calendar.doc.mjs +4 -4
  377. package/src/Calendar/Calendar.test.tsx +112 -0
  378. package/src/Calendar/Calendar.tsx +18 -16
  379. package/src/Calendar/hooks/useCalendarDays.ts +4 -4
  380. package/src/Calendar/hooks/useCalendarNavigation.ts +4 -4
  381. package/src/Calendar/styles.ts +48 -42
  382. package/src/Card/Card.doc.mjs +10 -7
  383. package/src/Card/Card.test.tsx +113 -19
  384. package/src/Card/Card.tsx +67 -89
  385. package/src/Card/index.ts +36 -1
  386. package/src/Carousel/Carousel.doc.mjs +3 -0
  387. package/src/Carousel/Carousel.test.tsx +360 -0
  388. package/src/Carousel/Carousel.tsx +116 -15
  389. package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -1
  390. package/src/CheckboxInput/CheckboxInput.test.tsx +15 -0
  391. package/src/CheckboxInput/CheckboxInput.tsx +5 -0
  392. package/src/CheckboxList/CheckboxList.doc.mjs +1 -1
  393. package/src/CodeBlock/CodeBlock.doc.mjs +10 -4
  394. package/src/CodeBlock/CodeBlock.test.tsx +37 -0
  395. package/src/CodeBlock/CodeBlock.tsx +20 -4
  396. package/src/CodeBlock/highlightRanges.ts +1 -1
  397. package/src/CodeBlock/highlightStyles.ts +13 -13
  398. package/src/CommandPalette/CommandPaletteItem.tsx +2 -12
  399. package/src/ComplexSelector/ComplexSelector.doc.mjs +9 -2
  400. package/src/ComplexSelector/ComplexSelector.test.tsx +183 -0
  401. package/src/ComplexSelector/ComplexSelector.tsx +22 -12
  402. package/src/DateInput/DateInput.doc.mjs +17 -1
  403. package/src/DateInput/DateInput.test.tsx +9 -4
  404. package/src/DateInput/DateInput.tsx +54 -3
  405. package/src/DateInput/DateInputTouch.test.tsx +146 -32
  406. package/src/DateInput/MonthScroller.tsx +5 -1
  407. package/src/DateInput/MonthYearWheels.tsx +7 -4
  408. package/src/DateInput/NativeDateField.test.tsx +635 -0
  409. package/src/DateInput/NativeDateField.tsx +641 -0
  410. package/src/DateInput/TouchDateField.tsx +83 -33
  411. package/src/DateInput/Wheel.tsx +1 -0
  412. package/src/DateInput/index.ts +1 -0
  413. package/src/DateInput/nativeDateSegments.ts +127 -0
  414. package/src/DateRangeInput/DateRangeInput.doc.mjs +3 -3
  415. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -0
  416. package/src/DateRangeInput/DateRangeInput.tsx +8 -13
  417. package/src/DateTimeInput/DateTimeInput.doc.mjs +39 -22
  418. package/src/DateTimeInput/DateTimeInput.test.tsx +76 -0
  419. package/src/DateTimeInput/DateTimeInput.tsx +54 -20
  420. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +980 -0
  421. package/src/DateTimeInput/TouchDateTimeField.tsx +1437 -0
  422. package/src/Dialog/Dialog.test.tsx +185 -18
  423. package/src/Dialog/Dialog.tsx +35 -11
  424. package/src/DropdownMenu/DropdownMenu.test.tsx +44 -10
  425. package/src/DropdownMenu/DropdownMenu.tsx +4 -13
  426. package/src/Field/Field.doc.mjs +1 -1
  427. package/src/Field/FieldLabel.test.tsx +29 -0
  428. package/src/Field/FieldLabel.tsx +7 -0
  429. package/src/Field/InputClearButton.test.tsx +15 -1
  430. package/src/Field/InputClearButton.tsx +28 -4
  431. package/src/FieldStatus/FieldStatus.doc.mjs +1 -1
  432. package/src/FileInput/FileInput.doc.mjs +1 -1
  433. package/src/Heading/Heading.tsx +0 -1
  434. package/src/HoverCard/HoverCard.doc.mjs +8 -2
  435. package/src/HoverCard/HoverCard.test.tsx +154 -4
  436. package/src/HoverCard/HoverCard.tsx +137 -3
  437. package/src/HoverCard/useHoverCard.tsx +23 -3
  438. package/src/Icon/Icon.test.tsx +55 -1
  439. package/src/Icon/Icon.tsx +5 -3
  440. package/src/Icon/defaultIcons.tsx +11 -2
  441. package/src/Icon/globalIconRegistry.test.tsx +16 -3
  442. package/src/Icon/globalIconRegistry.tsx +37 -6
  443. package/src/Icon/index.ts +1 -0
  444. package/src/Icon/useIcon.ts +9 -2
  445. package/src/Indicator/CheckboxIndicator.tsx +14 -8
  446. package/src/Indicator/Indicator.doc.mjs +3 -1
  447. package/src/InputGroup/InputGroup.doc.mjs +1 -1
  448. package/src/Item/Item.tsx +2 -7
  449. package/src/Kbd/Kbd.test.tsx +0 -17
  450. package/src/Kbd/Kbd.tsx +2 -26
  451. package/src/Layer/gestureCounter.ts +57 -0
  452. package/src/Layer/useLayer.test.tsx +368 -16
  453. package/src/Layer/useLayer.tsx +208 -35
  454. package/src/Layout/__tests__/edgeCompensation.test.tsx +1 -1
  455. package/src/List/List.test.tsx +38 -0
  456. package/src/List/List.tsx +8 -1
  457. package/src/Markdown/Markdown.test.tsx +37 -0
  458. package/src/Markdown/Markdown.tsx +16 -5
  459. package/src/Markdown/parser.test.ts +33 -0
  460. package/src/Markdown/parser.ts +9 -3
  461. package/src/MetadataList/MetadataList.test.tsx +75 -2
  462. package/src/MetadataList/MetadataListItem.tsx +5 -2
  463. package/src/MultiSelector/MultiSelector.doc.mjs +23 -2
  464. package/src/MultiSelector/MultiSelector.test.tsx +205 -0
  465. package/src/MultiSelector/MultiSelector.tsx +103 -16
  466. package/src/MultiSelector/hooks.ts +9 -2
  467. package/src/NavIcon/NavIcon.doc.mjs +8 -2
  468. package/src/NavIcon/NavIcon.test.tsx +9 -0
  469. package/src/NavIcon/NavIcon.tsx +5 -1
  470. package/src/NavItem/navItemStyles.stylex.ts +0 -8
  471. package/src/NavMenu/NavHeadingMenu.test.tsx +30 -0
  472. package/src/NavMenu/NavHeadingMenu.tsx +7 -4
  473. package/src/NumberInput/NumberInput.doc.mjs +27 -8
  474. package/src/NumberInput/NumberInput.test.tsx +563 -8
  475. package/src/NumberInput/NumberInput.tsx +77 -131
  476. package/src/NumberInput/numberInputCommit.test.ts +72 -0
  477. package/src/NumberInput/numberInputCommit.ts +103 -0
  478. package/src/NumberInput/numberParser.docblock.test.ts +234 -0
  479. package/src/NumberInput/numberParser.test.ts +222 -0
  480. package/src/NumberInput/numberParser.ts +403 -0
  481. package/src/Outline/Outline.tsx +2 -4
  482. package/src/OverflowList/OverflowList.doc.mjs +102 -21
  483. package/src/OverflowList/OverflowList.test.tsx +550 -4
  484. package/src/OverflowList/OverflowList.tsx +100 -2
  485. package/src/Pagination/Pagination.test.tsx +3 -2
  486. package/src/Popover/Popover.doc.mjs +2 -2
  487. package/src/Popover/Popover.test.tsx +38 -0
  488. package/src/Popover/Popover.tsx +28 -66
  489. package/src/Popover/usePopover.tsx +44 -5
  490. package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
  491. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +87 -0
  492. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +64 -0
  493. package/src/PowerSearch/PowerSearchValueEditor.tsx +109 -10
  494. package/src/PowerSearch/formatFilterValue.test.ts +16 -1
  495. package/src/PowerSearch/formatFilterValue.ts +2 -0
  496. package/src/ProgressBar/ProgressBar.doc.mjs +42 -6
  497. package/src/ProgressBar/ProgressBar.test.tsx +24 -0
  498. package/src/ProgressBar/ProgressBar.tsx +28 -8
  499. package/src/RadioList/RadioList.doc.mjs +1 -1
  500. package/src/Resizable/Resizable.doc.mjs +2 -2
  501. package/src/Resizable/useResizable.doc.mjs +21 -2
  502. package/src/Resizable/useResizable.test.ts +421 -0
  503. package/src/Resizable/useResizable.ts +133 -48
  504. package/src/SelectableCard/SelectableCard.doc.mjs +3 -0
  505. package/src/SelectableCard/SelectableCard.tsx +16 -0
  506. package/src/Selector/Selector.doc.mjs +15 -1
  507. package/src/Selector/Selector.source-build.test.mjs +65 -0
  508. package/src/Selector/Selector.test.tsx +301 -0
  509. package/src/Selector/Selector.tsx +145 -32
  510. package/src/Selector/hooks.ts +17 -2
  511. package/src/SideNav/SideNav.doc.mjs +2 -2
  512. package/src/SideNav/SideNav.test.tsx +1069 -6
  513. package/src/SideNav/SideNav.tsx +109 -38
  514. package/src/SideNav/SideNavHeading.tsx +5 -2
  515. package/src/SideNav/SideNavItem.doc.mjs +24 -3
  516. package/src/SideNav/SideNavItem.tsx +134 -44
  517. package/src/Slider/Slider.doc.mjs +2 -2
  518. package/src/Slider/Slider.test.tsx +106 -1
  519. package/src/Slider/Slider.tsx +52 -1
  520. package/src/Spinner/Spinner.doc.mjs +17 -5
  521. package/src/Spinner/Spinner.test.tsx +164 -0
  522. package/src/Spinner/Spinner.tsx +252 -22
  523. package/src/StatusDot/StatusDot.doc.mjs +8 -2
  524. package/src/StatusDot/StatusDot.test.tsx +9 -0
  525. package/src/StatusDot/StatusDot.tsx +7 -1
  526. package/src/Stepper/Step.doc.mjs +1 -1
  527. package/src/Stepper/Step.tsx +7 -14
  528. package/src/Switch/Switch.doc.mjs +3 -1
  529. package/src/Switch/Switch.test.tsx +10 -0
  530. package/src/Switch/Switch.tsx +3 -0
  531. package/src/Table/BaseTable.tsx +6 -1
  532. package/src/Table/Table.doc.mjs +5 -3
  533. package/src/Table/Table.tsx +3 -8
  534. package/src/Table/TableBody.doc.mjs +2 -2
  535. package/src/Table/TableFooter.doc.mjs +1 -1
  536. package/src/Table/TableHeader.doc.mjs +1 -1
  537. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +44 -6
  538. package/src/Table/plugins/filtering/useTableFiltering.tsx +100 -4
  539. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +50 -4
  540. package/src/Table/plugins/selection/useTableSelection.test.tsx +62 -0
  541. package/src/Table/plugins/selection/useTableSelection.tsx +25 -2
  542. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  543. package/src/Table/useTableGroupedRows.doc.mjs +3 -3
  544. package/src/Table/useTablePagination.doc.mjs +1 -1
  545. package/src/Table/useTableSelection.doc.mjs +1 -1
  546. package/src/Text/Text.test.tsx +44 -0
  547. package/src/Text/Text.tsx +0 -1
  548. package/src/TextArea/TextArea.doc.mjs +13 -3
  549. package/src/TextArea/TextArea.test.tsx +11 -0
  550. package/src/TextArea/TextArea.tsx +34 -22
  551. package/src/TextInput/TextInput.doc.mjs +1 -1
  552. package/src/TimeInput/TimeInput.doc.mjs +1 -1
  553. package/src/Timestamp/Timestamp.test.tsx +89 -0
  554. package/src/Timestamp/Timestamp.tsx +8 -8
  555. package/src/Timestamp/formatInstant.ts +5 -0
  556. package/src/Toast/Toast.doc.mjs +213 -44
  557. package/src/Toast/Toast.tsx +118 -23
  558. package/src/Toast/ToastViewport.test.tsx +1434 -8
  559. package/src/Toast/ToastViewport.tsx +85 -14
  560. package/src/Toast/index.ts +2 -0
  561. package/src/Toast/types.ts +43 -5
  562. package/src/Toast/useToastGesture.ts +361 -0
  563. package/src/Token/Token.test.tsx +95 -0
  564. package/src/Token/Token.tsx +19 -17
  565. package/src/Token/TokenLink.tsx +12 -3
  566. package/src/Tokenizer/Tokenizer.doc.mjs +17 -4
  567. package/src/Tokenizer/Tokenizer.test.tsx +161 -0
  568. package/src/Tokenizer/Tokenizer.tsx +49 -24
  569. package/src/TopNav/TopNavHeading.tsx +2 -2
  570. package/src/TopNav/TopNavItem.tsx +3 -7
  571. package/src/TopNav/TopNavMegaMenu.test.tsx +104 -0
  572. package/src/TopNav/TopNavMegaMenu.tsx +47 -4
  573. package/src/TopNav/TopNavMegaMenuItem.tsx +10 -9
  574. package/src/TopNav/TopNavMenu.test.tsx +133 -1
  575. package/src/TopNav/TopNavMenu.tsx +38 -5
  576. package/src/TreeList/TreeList.test.tsx +49 -0
  577. package/src/TreeList/TreeList.tsx +7 -2
  578. package/src/TreeList/TreeListItem.tsx +2 -7
  579. package/src/Typeahead/BaseTypeahead.doc.mjs +13 -0
  580. package/src/Typeahead/BaseTypeahead.tsx +96 -6
  581. package/src/Typeahead/Typeahead.doc.mjs +7 -1
  582. package/src/Typeahead/Typeahead.test.tsx +154 -0
  583. package/src/Typeahead/Typeahead.tsx +9 -0
  584. package/src/Typeahead/TypeaheadItem.test.tsx +43 -0
  585. package/src/Typeahead/TypeaheadItem.tsx +8 -1
  586. package/src/hooks/useAnnounce.doc.mjs +96 -0
  587. package/src/hooks/useDevWarning.doc.mjs +96 -0
  588. package/src/hooks/useHotkeys.test.ts +0 -18
  589. package/src/hooks/useHotkeys.ts +3 -25
  590. package/src/hooks/useIndicatorFocusRing.doc.mjs +104 -0
  591. package/src/hooks/useInputStatusIcon.doc.mjs +142 -0
  592. package/src/hooks/useInteractiveRole.doc.mjs +119 -0
  593. package/src/hooks/useLongPress.doc.mjs +123 -0
  594. package/src/hooks/useOverflow.doc.mjs +58 -17
  595. package/src/hooks/useOverflow.ts +16 -4
  596. package/src/hooks/useTypeahead.doc.mjs +153 -0
  597. package/src/i18n/InternationalizationContext.ts +3 -0
  598. package/src/i18n/InternationalizationProvider.tsx +8 -1
  599. package/src/i18n/__tests__/resolve.test.ts +23 -1
  600. package/src/i18n/resolve.ts +46 -49
  601. package/src/i18n/useTranslator.ts +2 -7
  602. package/src/index.ts +2 -0
  603. package/src/raw-import.d.ts +14 -0
  604. package/src/theme/Theme.test.tsx +104 -2
  605. package/src/theme/Theme.tsx +49 -14
  606. package/src/theme/defineTheme.test.ts +23 -0
  607. package/src/theme/defineTheme.ts +12 -3
  608. package/src/theme/derivedVarRegistry.test.ts +87 -3
  609. package/src/theme/derivedVarRegistry.ts +23 -4
  610. package/src/theme/generateThemeRules.test.ts +99 -1
  611. package/src/theme/generateThemeRules.ts +28 -0
  612. package/src/theme/syntax/tokens.ts +5 -2
  613. package/src/theme/useTheme.test.tsx +10 -0
  614. package/src/theme/useTheme.ts +16 -10
  615. package/src/utils/dateParser.test.ts +34 -2
  616. package/src/utils/dateParser.ts +11 -14
  617. package/src/utils/focusOutline.stylex.ts +50 -0
  618. package/src/utils/interactionOverlay.stylex.ts +74 -0
  619. package/src/utils/isApplePlatform.test.ts +61 -0
  620. package/src/utils/isApplePlatform.ts +37 -0
  621. package/src/utils/plainDate.test.ts +46 -0
  622. package/src/utils/plainDate.ts +18 -3
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
- import {describe, it, expect} from 'vitest';
4
- import {render, screen} from '@testing-library/react';
3
+ import {describe, it, expect, vi} from 'vitest';
4
+ import {render, screen, fireEvent} from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
  import {MetadataList} from './MetadataList';
7
7
  import {MetadataListItem} from './MetadataListItem';
@@ -223,3 +223,76 @@ describe('MetadataListItem', () => {
223
223
  expect(wrapper?.querySelector('dd')).toBeInTheDocument();
224
224
  });
225
225
  });
226
+
227
+ describe('MetadataListItem pass-through props', () => {
228
+ it('forwards pass-through props to the item element', () => {
229
+ render(
230
+ <MetadataList>
231
+ <MetadataListItem
232
+ label="Owner"
233
+ aria-label="Owner field"
234
+ id="owner"
235
+ data-source="crm"
236
+ data-testid="item">
237
+ Alice
238
+ </MetadataListItem>
239
+ </MetadataList>,
240
+ );
241
+ const item = screen.getByTestId('item-label');
242
+ expect(item).toHaveAttribute('aria-label', 'Owner field');
243
+ expect(item).toHaveAttribute('id', 'owner');
244
+ expect(item).toHaveAttribute('data-source', 'crm');
245
+ });
246
+
247
+ it('forwards pass-through props in the stacked layout', () => {
248
+ render(
249
+ <MetadataList orientation="horizontal">
250
+ <MetadataListItem label="Owner" id="owner-stacked" data-testid="item">
251
+ Alice
252
+ </MetadataListItem>
253
+ </MetadataList>,
254
+ );
255
+ expect(screen.getByTestId('item')).toHaveAttribute('id', 'owner-stacked');
256
+ });
257
+ });
258
+
259
+ describe('MetadataListItem pass-through target', () => {
260
+ it('lands pass-through props on the <dt> in the inline layout, where the ref also goes', () => {
261
+ const handleClick = vi.fn();
262
+ render(
263
+ <MetadataList>
264
+ <MetadataListItem
265
+ label="Owner"
266
+ onClick={handleClick}
267
+ data-testid="item">
268
+ Alice
269
+ </MetadataListItem>
270
+ </MetadataList>,
271
+ );
272
+
273
+ const label = screen.getByTestId('item-label');
274
+ expect(label.tagName).toBe('DT');
275
+ fireEvent.click(label);
276
+ expect(handleClick).toHaveBeenCalledOnce();
277
+
278
+ fireEvent.click(screen.getByTestId('item-value'));
279
+ expect(handleClick).toHaveBeenCalledOnce();
280
+ });
281
+
282
+ it('lands them on the wrapper in the stacked layout, so both halves are covered', () => {
283
+ const handleClick = vi.fn();
284
+ render(
285
+ <MetadataList orientation="horizontal">
286
+ <MetadataListItem
287
+ label="Owner"
288
+ onClick={handleClick}
289
+ data-testid="item">
290
+ Alice
291
+ </MetadataListItem>
292
+ </MetadataList>,
293
+ );
294
+
295
+ fireEvent.click(screen.getByText('Alice'));
296
+ expect(handleClick).toHaveBeenCalledOnce();
297
+ });
298
+ });
@@ -144,6 +144,7 @@ export function MetadataListItem({
144
144
  style,
145
145
  'data-testid': testId,
146
146
  ref,
147
+ ...rest
147
148
  }: MetadataListItemProps) {
148
149
  const ctx = use(MetadataListContext);
149
150
  const labelPosition = ctx?.labelConfig.position ?? 'start';
@@ -170,7 +171,8 @@ export function MetadataListItem({
170
171
  stylex.props(styles.stackedWrapper, xstyle),
171
172
  className,
172
173
  style,
173
- )}>
174
+ )}
175
+ {...rest}>
174
176
  <dt {...stylex.props(styles.stackedLabel)}>{labelContent}</dt>
175
177
  <dd {...stylex.props(styles.stackedValue)}>{children}</dd>
176
178
  </div>
@@ -188,7 +190,8 @@ export function MetadataListItem({
188
190
  stylex.props(styles.label, xstyle),
189
191
  className,
190
192
  style,
191
- )}>
193
+ )}
194
+ {...rest}>
192
195
  {labelContent}
193
196
  </dt>
194
197
  <dd
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'MultiSelector',
7
7
  displayName: 'Multi Selector',
8
8
  group: 'Selector',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: [
11
11
  'multiselect',
12
12
  'checkbox',
@@ -25,7 +25,10 @@ export const docs = {
25
25
  visualProps: ['variant', 'size', 'status'],
26
26
  states: ['disabled'],
27
27
  },
28
- {className: 'astryx-multi-selector-clear-icon', deprecatedFor: 'input-clear-icon'},
28
+ {
29
+ className: 'astryx-multi-selector-clear-icon',
30
+ deprecatedFor: 'input-clear-icon',
31
+ },
29
32
  {className: 'astryx-multi-selector-empty-state'},
30
33
  {className: 'astryx-multi-selector-search'},
31
34
  {className: 'astryx-multi-selector-section-heading'},
@@ -139,6 +142,20 @@ export const docs = {
139
142
  description: 'Placeholder text for the search input.',
140
143
  default: "'Search...'",
141
144
  },
145
+ {
146
+ name: 'emptyText',
147
+ type: 'ReactNode',
148
+ description:
149
+ 'Content shown in the dropdown panel when there are no options to show, and announced in a polite live region when the panel opens (a string override is announced verbatim; a richer node falls back to the default text). Not shown while isLoading.',
150
+ default: "'No options'",
151
+ },
152
+ {
153
+ name: 'emptySearchText',
154
+ type: 'ReactNode',
155
+ description:
156
+ 'Content shown in the dropdown panel when a search query matches no options, and announced in a polite live region at the same time (a string override is announced verbatim; a richer node falls back to the default text).',
157
+ default: "'No results found'",
158
+ },
142
159
  {
143
160
  name: 'isDisabled',
144
161
  type: 'boolean',
@@ -315,6 +332,8 @@ export const docsZh = {
315
332
  selectAllLabel: '全选复选框的标签。',
316
333
  hasSearch: '是否显示用于过滤选项的搜索输入。',
317
334
  searchPlaceholder: '搜索输入的占位文本。',
335
+ emptyText: '没有可显示的选项时,下拉面板中显示的内容。',
336
+ emptySearchText: '搜索查询未匹配到任何选项时,下拉面板中显示的内容。',
318
337
  isDisabled: '禁用选择器。',
319
338
  htmlName:
320
339
  '用于表单提交的 HTML name 属性。为每个已选值渲染一个隐藏输入,类似原生多选。',
@@ -452,6 +471,8 @@ export const docsDense = {
452
471
  selectAllLabel: 'select-all label',
453
472
  hasSearch: 'show search input',
454
473
  searchPlaceholder: 'search placeholder',
474
+ emptyText: 'panel content when there are no options',
475
+ emptySearchText: 'panel content when the query matches nothing',
455
476
  isDisabled: 'disables selector',
456
477
  htmlName: 'HTML name attr; one hidden input per selected value.',
457
478
  disabledMessage:
@@ -11,6 +11,7 @@
11
11
 
12
12
  import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
13
13
  import {
14
+ act,
14
15
  render,
15
16
  screen,
16
17
  fireEvent,
@@ -911,6 +912,176 @@ describe('MultiSelector', () => {
911
912
  expect(empty).toHaveAttribute('role', 'presentation');
912
913
  });
913
914
 
915
+ it('renders emptySearchText in place of the default message', async () => {
916
+ const user = userEvent.setup();
917
+ render(
918
+ <MultiSelector
919
+ label="Fruit"
920
+ options={defaultOptions}
921
+ value={[]}
922
+ onChange={() => {}}
923
+ hasSearch
924
+ emptySearchText="Nothing like that here"
925
+ />,
926
+ );
927
+
928
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
929
+ await user.type(screen.getByRole('combobox', h), 'xyz');
930
+
931
+ const listbox = screen.getByRole('listbox', h);
932
+ expect(
933
+ within(listbox).getByText('Nothing like that here'),
934
+ ).toBeInTheDocument();
935
+ expect(
936
+ within(listbox).queryByText('No results found'),
937
+ ).not.toBeInTheDocument();
938
+ });
939
+
940
+ it('renders emptyText, not emptySearchText, with no options and no query', async () => {
941
+ const user = userEvent.setup();
942
+ render(
943
+ <MultiSelector
944
+ label="Fruit"
945
+ options={[]}
946
+ value={[]}
947
+ onChange={() => {}}
948
+ hasSearch
949
+ emptySearchText="Nothing like that here"
950
+ />,
951
+ );
952
+
953
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
954
+
955
+ const listbox = screen.getByRole('listbox', h);
956
+ expect(within(listbox).getByText('No options')).toBeInTheDocument();
957
+ expect(
958
+ within(listbox).queryByText('Nothing like that here'),
959
+ ).not.toBeInTheDocument();
960
+ });
961
+
962
+ it('announces emptySearchText, not the catalog default', async () => {
963
+ const user = userEvent.setup();
964
+ render(
965
+ <MultiSelector
966
+ label="Fruit"
967
+ options={defaultOptions}
968
+ value={[]}
969
+ onChange={() => {}}
970
+ hasSearch
971
+ emptySearchText="Nothing like that here"
972
+ />,
973
+ );
974
+
975
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
976
+ await user.type(screen.getByRole('combobox', h), 'xyz');
977
+
978
+ // The panel message is role="presentation", so the live region is the
979
+ // only thing a screen reader gets — it has to say the same words.
980
+ await waitFor(() =>
981
+ expect(politeRegion()?.textContent).toBe('Nothing like that here'),
982
+ );
983
+ });
984
+
985
+ it('announces the empty state when opened with no options', async () => {
986
+ const user = userEvent.setup();
987
+ render(
988
+ <MultiSelector
989
+ label="Fruit"
990
+ options={[]}
991
+ value={[]}
992
+ onChange={() => {}}
993
+ emptyText="Add a fruit first"
994
+ />,
995
+ );
996
+
997
+ await user.click(screen.getByRole('combobox', {name: 'Fruit'}));
998
+
999
+ await waitFor(() =>
1000
+ expect(politeRegion()?.textContent).toBe('Add a fruit first'),
1001
+ );
1002
+ });
1003
+
1004
+ it('shows and announces nothing while isLoading', async () => {
1005
+ const user = userEvent.setup();
1006
+ render(
1007
+ <MultiSelector
1008
+ label="Fruit"
1009
+ options={[]}
1010
+ value={[]}
1011
+ onChange={() => {}}
1012
+ isLoading
1013
+ />,
1014
+ );
1015
+
1016
+ await user.click(screen.getByRole('combobox', {name: 'Fruit'}));
1017
+
1018
+ // useAnnounce writes on the next animation frame, so an immediate read
1019
+ // would pass whether or not anything was announced.
1020
+ await act(
1021
+ async () =>
1022
+ void (await new Promise(resolve => requestAnimationFrame(resolve))),
1023
+ );
1024
+
1025
+ // The options have not arrived, so "No options" would be a claim the
1026
+ // component cannot make; the trigger's spinner carries the state.
1027
+ const listbox = screen.getByRole('listbox', h);
1028
+ expect(within(listbox).queryByText('No options')).not.toBeInTheDocument();
1029
+ expect(politeRegion()?.textContent ?? '').toBe('');
1030
+ });
1031
+
1032
+ it('announces nothing while isLoading, matching the silent panel', async () => {
1033
+ const user = userEvent.setup();
1034
+ render(
1035
+ <MultiSelector
1036
+ label="Fruit"
1037
+ options={defaultOptions}
1038
+ value={[]}
1039
+ onChange={() => {}}
1040
+ hasSearch
1041
+ isLoading
1042
+ emptySearchText="Nothing like that here"
1043
+ />,
1044
+ );
1045
+
1046
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
1047
+ await user.type(screen.getByRole('combobox', h), 'xyz');
1048
+
1049
+ // useAnnounce writes on the next animation frame, so an immediate read
1050
+ // would pass whether or not anything was announced.
1051
+ await act(
1052
+ async () =>
1053
+ void (await new Promise(resolve => requestAnimationFrame(resolve))),
1054
+ );
1055
+
1056
+ // The panel is deliberately blank while loading; the live region is the
1057
+ // only channel left, so a result there would be a claim the screen
1058
+ // refuses to make.
1059
+ const listbox = screen.getByRole('listbox', h);
1060
+ expect(
1061
+ within(listbox).queryByText('Nothing like that here'),
1062
+ ).not.toBeInTheDocument();
1063
+ expect(politeRegion()?.textContent ?? '').toBe('');
1064
+ });
1065
+
1066
+ it('renders emptyText when there are no options and no search input', async () => {
1067
+ const user = userEvent.setup();
1068
+ render(
1069
+ <MultiSelector
1070
+ label="Fruit"
1071
+ options={[]}
1072
+ value={[]}
1073
+ onChange={() => {}}
1074
+ emptyText="Add a fruit first"
1075
+ />,
1076
+ );
1077
+
1078
+ // Without hasSearch the trigger itself is the combobox.
1079
+ await user.click(screen.getByRole('combobox', {name: 'Fruit'}));
1080
+
1081
+ const listbox = screen.getByRole('listbox', h);
1082
+ expect(within(listbox).getByText('Add a fruit first')).toBeInTheDocument();
1083
+ });
1084
+
914
1085
  describe('result announcements', () => {
915
1086
  it('announces the match count politely while searching', async () => {
916
1087
  const user = userEvent.setup();
@@ -2489,4 +2660,38 @@ describe('MultiSelector popup theme target', () => {
2489
2660
  expect(popup).not.toBe(layer);
2490
2661
  expect(layer.contains(popup)).toBe(true);
2491
2662
  });
2663
+
2664
+ it('stays closed when the trigger click follows its own light dismiss (#5004)', async () => {
2665
+ const user = userEvent.setup();
2666
+ render(
2667
+ <MultiSelector
2668
+ label="Fruit"
2669
+ options={['Apple', 'Banana', 'Cherry']}
2670
+ value={[]}
2671
+ onChange={() => {}}
2672
+ />,
2673
+ );
2674
+ const trigger = screen.getByRole('combobox');
2675
+ await user.click(trigger);
2676
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
2677
+
2678
+ // The browser dismissed the popup on pointerup and queued the toggle. When
2679
+ // that event lands before the click — WebKit, or any engine under load —
2680
+ // the click used to read a closed popup and reopen it.
2681
+ fireEvent.pointerDown(trigger);
2682
+ const popover = document.querySelector('[popover]') as HTMLElement;
2683
+ act(() => {
2684
+ popover.dispatchEvent(
2685
+ Object.assign(new Event('toggle'), {
2686
+ oldState: 'open',
2687
+ newState: 'closed',
2688
+ }),
2689
+ );
2690
+ });
2691
+ // Synchronously: the click falls inside the one gesture the guard covers,
2692
+ // as it does in a browser a few milliseconds behind the dismissal.
2693
+ fireEvent.click(trigger);
2694
+
2695
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
2696
+ });
2492
2697
  });
@@ -28,18 +28,19 @@ import React, {
28
28
  type ReactNode,
29
29
  } from 'react';
30
30
  import * as stylex from '@stylexjs/stylex';
31
- import {usePopover} from '../Popover/usePopover';
31
+ import {usePopoverInternal} from '../Popover/usePopover';
32
32
  import {useTooltip} from '../Tooltip';
33
33
  import {Icon, renderIconSlot, type IconType} from '../Icon';
34
34
  import type {IconName} from '../Icon';
35
35
  import {
36
36
  Field,
37
- InputClearButton,
38
37
  inputStatusBorderStyles,
39
38
  inputStatusHoverShadowStyles,
40
39
  inputWrapperStyles,
41
40
  type FieldStatusVariant,
42
41
  } from '../Field';
42
+ import {useKeepLayerOpenProps} from '../Layer/useLayer';
43
+ import {InternalInputClearButton} from '../Field/InputClearButton';
43
44
  import {Divider} from '../Divider';
44
45
  import {Spinner} from '../Spinner';
45
46
  import {PanelSearchInput} from '../Field/PanelSearchInput';
@@ -79,6 +80,7 @@ import type {SizeValue} from '../utils/types';
79
80
  import {useSize} from '../SizeContext/SizeContext';
80
81
  import {themeProps} from '../utils/themeProps';
81
82
  import {focusOutlineStyles} from '../utils/focusOutline.stylex';
83
+ import {interactionOverlayStyles} from '../utils/interactionOverlay.stylex';
82
84
  import {stableClassName} from '../naming';
83
85
  import {groupStyles} from '../InputGroup/groupStyles';
84
86
  import {useInputGroup} from '../InputGroup/InputGroupContext';
@@ -194,13 +196,6 @@ const styles = stylex.create({
194
196
  width: 'auto',
195
197
  borderWidth: 0,
196
198
  backgroundColor: 'transparent',
197
- backgroundImage: {
198
- default: null,
199
- ':hover:where(:not(:disabled,[aria-disabled="true"]))': {
200
- '@media (hover: hover)': `linear-gradient(${colorVars['--color-overlay-hover']}, ${colorVars['--color-overlay-hover']})`,
201
- },
202
- ':active': `linear-gradient(${colorVars['--color-overlay-pressed']}, ${colorVars['--color-overlay-pressed']})`,
203
- },
204
199
  boxShadow: {
205
200
  default: 'none',
206
201
  ':hover:not(:focus-within):where(:not(:disabled,[aria-disabled="true"]))':
@@ -604,6 +599,25 @@ export interface MultiSelectorProps<
604
599
  */
605
600
  searchPlaceholder?: string;
606
601
 
602
+ /**
603
+ * Content shown in the panel when there are no options to show, and
604
+ * announced in a polite live region when the panel opens. Not shown while
605
+ * `isLoading` — the options have not arrived yet.
606
+ * @default 'No options'
607
+ */
608
+ emptyText?: ReactNode;
609
+
610
+ /**
611
+ * Content shown in the panel when a search query matches no options, and
612
+ * announced in a polite live region at the same time.
613
+ *
614
+ * The panel message is `role="presentation"`, so the live region is the only
615
+ * route to assistive tech: a string is announced verbatim, a richer node
616
+ * falls back to the default text since it cannot be spoken.
617
+ * @default 'No results found'
618
+ */
619
+ emptySearchText?: ReactNode;
620
+
607
621
  /**
608
622
  * How to display selected items in the trigger.
609
623
  * - 'count': "3 selected"
@@ -726,6 +740,8 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
726
740
  selectAllLabel: selectAllLabelFromProps,
727
741
  hasSearch = false,
728
742
  searchPlaceholder: searchPlaceholderFromProps,
743
+ emptyText: emptyTextFromProps,
744
+ emptySearchText: emptySearchTextFromProps,
729
745
  triggerDisplay = 'count',
730
746
  formatValue,
731
747
  maxBadges = 3,
@@ -747,6 +763,9 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
747
763
  selectAllLabelFromProps ?? t('@astryx.multiSelector.selectAll');
748
764
  const searchPlaceholder =
749
765
  searchPlaceholderFromProps ?? t('@astryx.multiSelector.searchPlaceholder');
766
+ const emptyText = emptyTextFromProps ?? t('@astryx.multiSelector.empty');
767
+ const emptySearchText =
768
+ emptySearchTextFromProps ?? t('@astryx.multiSelector.emptySearchResults');
750
769
  const size = useSize(sizeProp, 'md');
751
770
  const effectiveStatusVariant =
752
771
  variant === 'ghost' && statusVariant === 'attached'
@@ -821,6 +840,19 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
821
840
  // Announce selection-count changes politely (comboboxes-7 announce path).
822
841
  // Toggling options / select-all previously produced no audible feedback.
823
842
  const announce = useAnnounce();
843
+
844
+ // The panel's empty message is role="presentation" and reaches assistive tech
845
+ // only through this live region, so the region has to speak whatever the
846
+ // panel shows. A ReactNode override cannot be spoken; fall back to the
847
+ // catalog copy for that case rather than announcing nothing.
848
+ const emptyAnnouncement =
849
+ typeof emptyText === 'string'
850
+ ? emptyText
851
+ : t('@astryx.multiSelector.empty');
852
+ const emptySearchAnnouncement =
853
+ typeof emptySearchText === 'string'
854
+ ? emptySearchText
855
+ : t('@astryx.multiSelector.emptySearchResults');
824
856
  const announceSelection = useCallback(
825
857
  (nextValue: string[]) => {
826
858
  const total = selectableItems.length;
@@ -906,7 +938,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
906
938
  triggerRef.current?.focus();
907
939
  }, [announce]);
908
940
 
909
- const popover = usePopover({
941
+ const popover = usePopoverInternal({
910
942
  hasLightDismiss: true,
911
943
  onHide: handleLayerHide,
912
944
  hasCloseButton: false,
@@ -918,6 +950,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
918
950
  // `usePopover` owns — not on the scrolling list inside it.
919
951
  surfaceTarget: 'multi-selector-popup',
920
952
  });
953
+ const keepOpenProps = useKeepLayerOpenProps(popover.id, popover.isOpen);
921
954
 
922
955
  // Open dropdown on mount when isDefaultOpen is true
923
956
  useEffect(() => {
@@ -940,16 +973,54 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
940
973
  announce('');
941
974
  return;
942
975
  }
976
+ // While isLoading the panel deliberately shows nothing, so announcing a
977
+ // result would put a claim in the one channel the screen has gone quiet
978
+ // for.
979
+ if (isLoading) {
980
+ announce('');
981
+ return;
982
+ }
943
983
  const count = filterOptionsByQuery(selectableItems, nextQuery).length;
944
984
  announce(
945
985
  count === 0
946
- ? t('@astryx.multiSelector.emptySearchResults')
986
+ ? emptySearchAnnouncement
947
987
  : t('@astryx.multiSelector.resultCount', {count}),
948
988
  );
949
989
  },
950
- [announce, selectableItems, t],
990
+ [announce, isLoading, selectableItems, emptySearchAnnouncement, t],
951
991
  );
952
992
 
993
+ // The panel's empty message is role="presentation", so this region is the
994
+ // only route to assistive tech. It has to watch the STATE rather than the
995
+ // open event: the panel can become empty either on open or when a fetch
996
+ // lands with nothing in it, and an open-only announcement leaves the second
997
+ // case silent while the message sits on screen. The ref makes it fire once
998
+ // per arrival at that state rather than on every re-render.
999
+ const announcedEmptyRef = useRef<string | null>(null);
1000
+ useEffect(() => {
1001
+ const isPanelEmpty =
1002
+ popover.isOpen &&
1003
+ !isLoading &&
1004
+ searchQuery === '' &&
1005
+ selectableItems.length === 0;
1006
+ if (!isPanelEmpty) {
1007
+ announcedEmptyRef.current = null;
1008
+ return;
1009
+ }
1010
+ if (announcedEmptyRef.current === emptyAnnouncement) {
1011
+ return;
1012
+ }
1013
+ announcedEmptyRef.current = emptyAnnouncement;
1014
+ announce(emptyAnnouncement);
1015
+ }, [
1016
+ popover.isOpen,
1017
+ isLoading,
1018
+ searchQuery,
1019
+ selectableItems.length,
1020
+ emptyAnnouncement,
1021
+ announce,
1022
+ ]);
1023
+
953
1024
  // Handle toggle
954
1025
  // Clear all selected values. Shared by the clear button and the keyboard
955
1026
  // Delete/Backspace path so clearing is reachable without a mouse.
@@ -1087,6 +1158,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1087
1158
  onKeyDown,
1088
1159
  onItemMouseEnter,
1089
1160
  } = useMultiCombobox({
1161
+ wasJustDismissed: popover.wasJustDismissed,
1090
1162
  selectableItems: sortedItems,
1091
1163
  isDisabled,
1092
1164
  isOpen: popover.isOpen,
@@ -1416,7 +1488,10 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1416
1488
  // message out of the listbox's accessibility tree (role="listbox" only
1417
1489
  // permits option/group children); the no-results outcome is announced
1418
1490
  // via the result-count live region instead.
1419
- if (realItemCount === 0) {
1491
+ // While isLoading the options have not arrived yet, so asserting either
1492
+ // message would be a claim the component cannot make; the trigger's
1493
+ // spinner covers it.
1494
+ if (realItemCount === 0 && !isLoading) {
1420
1495
  elements.push(
1421
1496
  <div
1422
1497
  key="empty"
@@ -1425,7 +1500,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1425
1500
  themeProps('multi-selector-empty-state'),
1426
1501
  stylex.props(styles.emptyState),
1427
1502
  )}>
1428
- No results found
1503
+ {searchQuery ? emptySearchText : emptyText}
1429
1504
  </div>,
1430
1505
  );
1431
1506
  return elements;
@@ -1515,7 +1590,16 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1515
1590
  flushPending();
1516
1591
 
1517
1592
  return elements;
1518
- }, [options, renderItem, sortedItems, searchQuery, hasSelectAll]);
1593
+ }, [
1594
+ options,
1595
+ renderItem,
1596
+ sortedItems,
1597
+ searchQuery,
1598
+ hasSelectAll,
1599
+ isLoading,
1600
+ emptyText,
1601
+ emptySearchText,
1602
+ ]);
1519
1603
 
1520
1604
  // The detached message box renders its own leading status icon, so the
1521
1605
  // on-field icon would duplicate it — keep the chevron indicator instead.
@@ -1548,6 +1632,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1548
1632
  styles.triggerContainer,
1549
1633
  sizeStyles[size],
1550
1634
  variant === 'ghost' && styles.triggerGhost,
1635
+ variant === 'ghost' && interactionOverlayStyles.backgroundImage,
1551
1636
  variant === 'ghost' && focusOutlineStyles.focusWithin,
1552
1637
  isDisabled && inputWrapperStyles.disabled,
1553
1638
  variant === 'ghost' && isDisabled && styles.triggerGhostDisabled,
@@ -1617,7 +1702,8 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1617
1702
  ))}
1618
1703
  {isBusy && <Spinner size="sm" />}
1619
1704
  {hasClear && value.length > 0 && !isDisabled && (
1620
- <InputClearButton
1705
+ <InternalInputClearButton
1706
+ {...keepOpenProps}
1621
1707
  label={t('@astryx.multiSelector.clearAll', {label})}
1622
1708
  onClick={handleClear}
1623
1709
  iconClassName={stableClassName('multi-selector-clear-icon')}
@@ -1636,6 +1722,7 @@ export function MultiSelector<T extends MultiSelectorOptionType>({
1636
1722
  type="button"
1637
1723
  aria-label={t(STATUS_BUTTON_LABEL_KEY[status.type])}
1638
1724
  aria-describedby={statusTooltip.describedBy}
1725
+ {...keepOpenProps}
1639
1726
  onClick={e => e.stopPropagation()}
1640
1727
  {...stylex.props(
1641
1728
  focusOutlineStyles.focusVisible,
@@ -34,6 +34,12 @@ interface UseMultiComboboxOptions {
34
34
  * The Delete/Backspace clear path is skipped when false.
35
35
  */
36
36
  hasValue?: boolean;
37
+ /**
38
+ * Whether the browser's light dismiss just closed the popup. The trigger
39
+ * click that follows belongs to that same press, so acting on it would
40
+ * reopen the popup the user just closed.
41
+ */
42
+ wasJustDismissed?: () => boolean;
37
43
  listboxId: string;
38
44
  }
39
45
 
@@ -62,6 +68,7 @@ export function useMultiCombobox({
62
68
  onToggle,
63
69
  onClear,
64
70
  hasValue = false,
71
+ wasJustDismissed,
65
72
  listboxId,
66
73
  }: UseMultiComboboxOptions): UseMultiComboboxResult {
67
74
  const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);
@@ -87,7 +94,7 @@ export function useMultiCombobox({
87
94
  }, [onClose]);
88
95
 
89
96
  const onTriggerClick = useCallback(() => {
90
- if (isDisabled) {
97
+ if (isDisabled || wasJustDismissed?.()) {
91
98
  return;
92
99
  }
93
100
  if (isOpen) {
@@ -98,7 +105,7 @@ export function useMultiCombobox({
98
105
  setHighlightedIndex(0);
99
106
  }
100
107
  }
101
- }, [isDisabled, isOpen, onOpen, closeAndReset, hasSearch]);
108
+ }, [isDisabled, wasJustDismissed, isOpen, onOpen, closeAndReset, hasSearch]);
102
109
 
103
110
  const onItemMouseEnter = useCallback(
104
111
  (item: MultiSelectorOptionData, index: number) => {