@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
@@ -5,7 +5,7 @@
5
5
  /**
6
6
  * @file useLayer.tsx
7
7
  * @input Uses React hooks, Popover API, CSS anchor positioning, typography tokens
8
- * @output Exports useLayer hook for layer positioning and visibility
8
+ * @output Exports the public useLayer hook plus internal trigger helpers.
9
9
  * @position Core layer utility; used by useHoverCard, useTooltip, etc.
10
10
  *
11
11
  * SYNC: When modified, update:
@@ -18,6 +18,7 @@ import React, {
18
18
  useCallback,
19
19
  useEffect,
20
20
  useId,
21
+ useMemo,
21
22
  useRef,
22
23
  useState,
23
24
  type ReactNode,
@@ -27,6 +28,7 @@ import * as stylex from '@stylexjs/stylex';
27
28
  import type {StyleXStyles} from '@stylexjs/stylex';
28
29
  import {createPortal} from 'react-dom';
29
30
  import {addAnchorName, removeAnchorName} from './anchorName';
31
+ import {currentGesture, currentGestureHasClicked} from './gestureCounter';
30
32
  import {resolveLayerPortalTarget} from './layerHost';
31
33
  import {typeScaleVars, typographyVars} from '../theme/tokens.stylex';
32
34
  import {overlayPaddingReset} from '../Layout/padding.stylex';
@@ -72,6 +74,18 @@ const styles = stylex.create({
72
74
  }),
73
75
  });
74
76
 
77
+ /**
78
+ * Props for a control that sits on the trigger but must not dismiss the layer.
79
+ */
80
+ interface KeepLayerOpenProps {
81
+ onPointerDown: React.PointerEventHandler<HTMLElement>;
82
+ /**
83
+ * Capture phase, so spreading these props never collides with the control's
84
+ * own `onClick`.
85
+ */
86
+ onClickCapture: React.MouseEventHandler<HTMLElement>;
87
+ }
88
+
75
89
  /**
76
90
  * Position placement relative to anchor.
77
91
  * Logical: start/end resolve against the popover's own inherited direction
@@ -326,6 +340,13 @@ export interface FixedLayerReturn {
326
340
  render: (children: ReactNode, props: FixedRenderProps) => ReactNode;
327
341
  }
328
342
 
343
+ interface InternalLayerFields {
344
+ wasJustDismissed: () => boolean;
345
+ }
346
+
347
+ type InternalContextLayerReturn = ContextLayerReturn & InternalLayerFields;
348
+ type InternalFixedLayerReturn = FixedLayerReturn & InternalLayerFields;
349
+
329
350
  function toCssLength(value: number | string): string {
330
351
  return typeof value === 'number' ? `${value}px` : value;
331
352
  }
@@ -437,6 +458,51 @@ export function getPositionTryFallbacks(
437
458
  return `${flips}, ${same} span-top, ${same} span-bottom, ${opposite} span-top, ${opposite} span-bottom`;
438
459
  }
439
460
 
461
+ /**
462
+ * Internal props for controls that sit beside an open layer. This stays out of
463
+ * the public useLayer/usePopover return types until the input family adopts it.
464
+ */
465
+ export function useKeepLayerOpenProps(
466
+ id: string,
467
+ isOpen: boolean,
468
+ ): KeepLayerOpenProps {
469
+ const isOpenRef = useRef(isOpen);
470
+ isOpenRef.current = isOpen;
471
+
472
+ return useMemo(
473
+ () => ({
474
+ onPointerDown: (event: React.PointerEvent<HTMLElement>) => {
475
+ if (!isOpenRef.current) {
476
+ return;
477
+ }
478
+ // The browser reads the invoker relationship at both ends of the press.
479
+ const control = event.currentTarget;
480
+ const doc = control.ownerDocument;
481
+ control.setAttribute('popovertarget', id);
482
+ const onPressEnd = () => {
483
+ doc.removeEventListener('pointerup', onPressEnd);
484
+ doc.removeEventListener('pointercancel', onPressEnd);
485
+ // Light dismiss runs as the pointerup default action after listeners.
486
+ doc.defaultView?.setTimeout(() => {
487
+ control.removeAttribute('popovertarget');
488
+ }, 0);
489
+ };
490
+ doc.addEventListener('pointerup', onPressEnd);
491
+ doc.addEventListener('pointercancel', onPressEnd);
492
+ },
493
+ onClickCapture: (event: React.MouseEvent<HTMLElement>) => {
494
+ const control = event.currentTarget;
495
+ if (control.hasAttribute('popovertarget')) {
496
+ // Being an invoker would otherwise toggle the layer shut.
497
+ event.preventDefault();
498
+ control.removeAttribute('popovertarget');
499
+ }
500
+ },
501
+ }),
502
+ [id],
503
+ );
504
+ }
505
+
440
506
  /**
441
507
  * Core layer hook that handles popover behavior and positioning.
442
508
  *
@@ -451,11 +517,9 @@ export function getPositionTryFallbacks(
451
517
  * {layer.render(<Content />, { placement: 'above', alignment: 'center' })}
452
518
  * ```
453
519
  */
454
- export function useLayer(options: ContextLayerOptions): ContextLayerReturn;
455
- export function useLayer(options: FixedLayerOptions): FixedLayerReturn;
456
- export function useLayer(
520
+ function useLayerImplementation(
457
521
  options: ContextLayerOptions | FixedLayerOptions,
458
- ): ContextLayerReturn | FixedLayerReturn {
522
+ ): InternalContextLayerReturn | InternalFixedLayerReturn {
459
523
  const {mode, onShow, onHide, lightDismiss = false} = options;
460
524
  const lazyMount = mode === 'context' ? (options.lazyMount ?? false) : false;
461
525
  const id = useId();
@@ -485,6 +549,17 @@ export function useLayer(
485
549
  // stale-closure reads of the previous isOpen value.
486
550
  const isOpenRef = useRef(false);
487
551
 
552
+ // The gesture during which the browser last closed this layer on its own.
553
+ // Read through wasJustDismissed by triggers deciding whether a click is
554
+ // theirs to act on.
555
+ const dismissedByGestureRef = useRef<number | null>(null);
556
+ const forgetDismissalRef = useRef<(() => void) | null>(null);
557
+
558
+ const wasJustDismissed = useCallback(() => {
559
+ const gesture = currentGesture();
560
+ return dismissedByGestureRef.current === gesture;
561
+ }, []);
562
+
488
563
  const showPopoverElement = useCallback((popover: HTMLElement) => {
489
564
  // Finding infra-4: the Popover API is unsupported on Safari <17 and
490
565
  // Firefox <125. On those browsers `showPopover` does not exist, so fall
@@ -548,6 +623,11 @@ export function useLayer(
548
623
  }, [mode, lazyMount]);
549
624
 
550
625
  const show = useCallback(() => {
626
+ // Every caller lands here, so this is where the dismissing press is
627
+ // absorbed: opening now would reopen the popup that same press closed.
628
+ if (wasJustDismissed()) {
629
+ return;
630
+ }
551
631
  // A context popover left over until React commits a previous hide must not
552
632
  // be reopened. The synchronous mount ref is the source of truth.
553
633
  const candidate = popoverRef.current;
@@ -569,12 +649,18 @@ export function useLayer(
569
649
  requestContextMount,
570
650
  showPopoverElement,
571
651
  isCurrentContextPopover,
652
+ wasJustDismissed,
572
653
  ]);
573
654
 
574
655
  const hide = useCallback(() => {
575
656
  pendingShowRef.current = false;
576
657
  if (isOpenRef.current) {
577
658
  const el = popoverRef.current;
659
+ // Clear the open state BEFORE hiding: hidePopover fires `toggle`, and
660
+ // the reconciler below reads this ref to tell the browser's own
661
+ // dismissals from ours.
662
+ openedPopoverRef.current = null;
663
+ isOpenRef.current = false;
578
664
  // See finding infra-4 note in `show`: mirror the same guard on hide so
579
665
  // unsupported browsers degrade gracefully instead of throwing.
580
666
  if (el) {
@@ -584,31 +670,74 @@ export function useLayer(
584
670
  el.style.display = 'none';
585
671
  }
586
672
  }
587
- openedPopoverRef.current = null;
588
- isOpenRef.current = false;
589
673
  setIsOpen(false);
590
674
  onHide?.();
591
675
  }
592
676
  clearContextMount();
593
677
  }, [onHide, clearContextMount]);
594
678
 
595
- // Ref for trigger element (context mode only)
596
- const ref: RefCallback<HTMLElement> | undefined =
597
- mode === 'context'
598
- ? (el: HTMLElement | null) => {
599
- // Remove only THIS layer's anchor name from the previous element so
600
- // other layers sharing the same trigger keep their anchors.
601
- if (triggerRef.current && triggerRef.current !== el) {
602
- removeAnchorName(triggerRef.current, anchorId);
603
- }
679
+ // Stable ref for the trigger element (context mode only).
680
+ const contextRef = useCallback(
681
+ (el: HTMLElement | null) => {
682
+ // Remove only THIS layer's anchor name from the previous element so
683
+ // other layers sharing the same trigger keep their anchors.
684
+ if (triggerRef.current && triggerRef.current !== el) {
685
+ removeAnchorName(triggerRef.current, anchorId);
686
+ }
687
+
688
+ if (el) {
689
+ addAnchorName(el, anchorId);
690
+ }
604
691
 
605
- if (el) {
606
- addAnchorName(el, anchorId);
692
+ triggerRef.current = el;
693
+ },
694
+ [anchorId],
695
+ );
696
+
697
+ // Arm only when the dismissing gesture's click is still ahead of us. Some
698
+ // engines deliver click first; in that order there is nothing left to absorb.
699
+ const rememberDismissal = useCallback((doc: Document) => {
700
+ forgetDismissalRef.current?.();
701
+ if (currentGestureHasClicked()) {
702
+ return;
703
+ }
704
+ dismissedByGestureRef.current = currentGesture();
705
+ const view = doc.defaultView;
706
+ let forgetTimer: number | null = null;
707
+ const forget = () => {
708
+ dismissedByGestureRef.current = null;
709
+ doc.removeEventListener('click', scheduleForget, true);
710
+ if (forgetTimer !== null) {
711
+ view?.clearTimeout(forgetTimer);
712
+ forgetTimer = null;
713
+ }
714
+ if (forgetDismissalRef.current === forget) {
715
+ forgetDismissalRef.current = null;
716
+ }
717
+ };
718
+ const scheduleForget = () => {
719
+ doc.removeEventListener('click', scheduleForget, true);
720
+ // The next task keeps the dismissal armed through React's click handler.
721
+ if (view) {
722
+ forgetTimer = view.setTimeout(() => {
723
+ forgetTimer = null;
724
+ if (forgetDismissalRef.current === forget) {
725
+ forget();
607
726
  }
727
+ }, 0);
728
+ } else {
729
+ forget();
730
+ }
731
+ };
732
+ doc.addEventListener('click', scheduleForget, true);
733
+ forgetDismissalRef.current = forget;
734
+ }, []);
608
735
 
609
- triggerRef.current = el;
610
- }
611
- : undefined;
736
+ useEffect(() => {
737
+ // Install capture-phase gesture tracking before the first interaction.
738
+ currentGesture();
739
+ return () => forgetDismissalRef.current?.();
740
+ }, []);
612
741
 
613
742
  // Reconcile browser-initiated closes (light-dismiss, popover="auto" stack
614
743
  // eviction). These are the only cases where the DOM mutates without going
@@ -626,12 +755,15 @@ export function useLayer(
626
755
  if (toggleEvent.newState === 'closed' && isOpenRef.current) {
627
756
  openedPopoverRef.current = null;
628
757
  isOpenRef.current = false;
758
+ rememberDismissal(
759
+ (e.currentTarget as HTMLElement | null)?.ownerDocument ?? document,
760
+ );
629
761
  setIsOpen(false);
630
762
  onHide?.();
631
763
  clearContextMount();
632
764
  }
633
765
  },
634
- [onHide, clearContextMount],
766
+ [onHide, clearContextMount, rememberDismissal],
635
767
  );
636
768
 
637
769
  // Ref callback for popover element — sets up the `toggle` listener.
@@ -865,24 +997,65 @@ export function useLayer(
865
997
  [popoverRefCallback, id, lightDismiss],
866
998
  );
867
999
 
868
- if (mode === 'context') {
869
- return {
870
- ref: ref as RefCallback<HTMLElement>,
1000
+ const contextResult = useMemo<InternalContextLayerReturn>(
1001
+ () => ({
1002
+ ref: contextRef,
871
1003
  anchorId,
872
1004
  show,
873
1005
  hide,
874
1006
  isOpen,
1007
+ wasJustDismissed,
875
1008
  id,
876
1009
  render: renderContext,
877
- };
878
- }
1010
+ }),
1011
+ [
1012
+ contextRef,
1013
+ anchorId,
1014
+ show,
1015
+ hide,
1016
+ isOpen,
1017
+ wasJustDismissed,
1018
+ id,
1019
+ renderContext,
1020
+ ],
1021
+ );
1022
+ const fixedResult = useMemo<InternalFixedLayerReturn>(
1023
+ () => ({
1024
+ ref: undefined,
1025
+ show,
1026
+ hide,
1027
+ isOpen,
1028
+ wasJustDismissed,
1029
+ id,
1030
+ render: renderFixed,
1031
+ }),
1032
+ [show, hide, isOpen, wasJustDismissed, id, renderFixed],
1033
+ );
879
1034
 
880
- return {
881
- ref: undefined,
882
- show,
883
- hide,
884
- isOpen,
885
- id,
886
- render: renderFixed,
887
- };
1035
+ return mode === 'context' ? contextResult : fixedResult;
1036
+ }
1037
+
1038
+ export function useLayer(options: ContextLayerOptions): ContextLayerReturn;
1039
+ export function useLayer(options: FixedLayerOptions): FixedLayerReturn;
1040
+ export function useLayer(
1041
+ options: ContextLayerOptions | FixedLayerOptions,
1042
+ ): ContextLayerReturn | FixedLayerReturn {
1043
+ const internalLayer = useLayerImplementation(options);
1044
+ return useMemo(() => {
1045
+ const {wasJustDismissed: _, ...layer} = internalLayer;
1046
+ return layer;
1047
+ }, [internalLayer]);
1048
+ }
1049
+
1050
+ /** @internal Shared with usePopover; not exported from the package barrel. */
1051
+ export function useLayerInternal(
1052
+ options: ContextLayerOptions,
1053
+ ): InternalContextLayerReturn;
1054
+ export function useLayerInternal(
1055
+ options: FixedLayerOptions,
1056
+ ): InternalFixedLayerReturn;
1057
+ export function useLayerInternal(
1058
+ options: ContextLayerOptions | FixedLayerOptions,
1059
+ ): InternalContextLayerReturn | InternalFixedLayerReturn {
1060
+ return useLayerImplementation(options);
888
1061
  }
@@ -112,7 +112,7 @@ describe('Edge Compensation', () => {
112
112
  describe('Banner container compensation', () => {
113
113
  it('renders dismissable banner with ghost dismiss button', () => {
114
114
  render(<Banner status="info" title="Test" isDismissable />);
115
- const dismissButton = screen.getByRole('button', {name: 'Dismiss'});
115
+ const dismissButton = screen.getByRole('button', {name: 'Dismiss Test'});
116
116
  expect(dismissButton).toHaveAttribute(EDGE_COMP_ATTR);
117
117
  });
118
118
  });
@@ -681,6 +681,44 @@ describe('List', () => {
681
681
  expect(screen.getByText('Simple text')).toBeInTheDocument();
682
682
  });
683
683
 
684
+ it('forwards data and aria attributes onto the list element', () => {
685
+ // BaseProps keeps data-*/aria-*; the list element dropped them.
686
+ const {container} = render(
687
+ <List listStyle="decimal" data-delimiter=")" aria-label="Steps">
688
+ <ListItem label="First" />
689
+ </List>,
690
+ );
691
+ const list = container.querySelector('ol')!;
692
+ expect(list.getAttribute('data-delimiter')).toBe(')');
693
+ expect(list.getAttribute('aria-label')).toBe('Steps');
694
+ });
695
+
696
+ it('keeps an aria-labelledby pointing outside the component', () => {
697
+ // With no header of its own there is nothing to associate, so the
698
+ // consumer's label must survive.
699
+ const {container} = render(
700
+ <List aria-labelledby="external-heading">
701
+ <ListItem label="First" />
702
+ </List>,
703
+ );
704
+ expect(container.querySelector('ul')!.getAttribute('aria-labelledby')).toBe(
705
+ 'external-heading',
706
+ );
707
+ });
708
+
709
+ it('keeps its own list role and header association', () => {
710
+ // The rest spread comes first so a consumer prop cannot drop the
711
+ // Safari/VoiceOver role workaround or the generated header association.
712
+ const {container} = render(
713
+ <List header="Steps" role="presentation" aria-labelledby="elsewhere">
714
+ <ListItem label="First" />
715
+ </List>,
716
+ );
717
+ const list = container.querySelector('ul')!;
718
+ expect(list.getAttribute('role')).toBe('list');
719
+ expect(list.getAttribute('aria-labelledby')).not.toBe('elsewhere');
720
+ });
721
+
684
722
  it('accepts number as description (ReactNode)', () => {
685
723
  render(
686
724
  <List>
package/src/List/List.tsx CHANGED
@@ -152,6 +152,7 @@ export function List({
152
152
  style,
153
153
  'data-testid': testId,
154
154
  ref,
155
+ ...props
155
156
  }: ListProps) {
156
157
  const headerId = useId();
157
158
  const isOrdered = listStyle === 'decimal';
@@ -165,8 +166,14 @@ export function List({
165
166
  const listElement = (
166
167
  <Tag
167
168
  ref={ref as React.Ref<HTMLUListElement & HTMLOListElement>}
169
+ // Consumer props first: everything the component sets for itself below
170
+ // is part of its contract and wins on conflict.
171
+ {...props}
168
172
  data-testid={testId}
169
- aria-labelledby={header != null ? headerId : undefined}
173
+ // Only when this component renders the header — writing `undefined`
174
+ // unconditionally would erase a label a consumer pointed at their own
175
+ // heading.
176
+ {...(header != null ? {'aria-labelledby': headerId} : null)}
170
177
  {...(isOrdered && start != null && start !== 1 ? {start} : {})}
171
178
  // The base list style always sets list-style-type: none (markers are
172
179
  // custom-rendered by ListItem), and Safari/VoiceOver drops implicit
@@ -124,6 +124,43 @@ describe('Markdown', () => {
124
124
  expect(second.className).toContain('astryx-markdown-paragraph');
125
125
  });
126
126
 
127
+ describe('base props', () => {
128
+ // BaseProps documents that data-*, aria-* and role are kept; the root
129
+ // dropped everything but data-testid.
130
+ it('forwards data and aria attributes to the block root', () => {
131
+ const {container} = render(
132
+ <Markdown data-source="turn-7" aria-label="Answer">
133
+ Hello
134
+ </Markdown>,
135
+ );
136
+ const root = container.firstElementChild!;
137
+ expect(root.getAttribute('data-source')).toBe('turn-7');
138
+ expect(root.getAttribute('aria-label')).toBe('Answer');
139
+ });
140
+
141
+ it('keeps its own role when a consumer passes one', () => {
142
+ // The rest spread comes first precisely so the component's own
143
+ // semantics survive a consumer prop.
144
+ const {container} = render(
145
+ <Markdown role="presentation">Hello</Markdown>,
146
+ );
147
+ expect(container.firstElementChild!.getAttribute('role')).toBe(
148
+ 'document',
149
+ );
150
+ });
151
+
152
+ it('forwards them on the inline root too', () => {
153
+ const {container} = render(
154
+ <Markdown display="inline" data-source="turn-7">
155
+ Hello
156
+ </Markdown>,
157
+ );
158
+ expect(container.firstElementChild!.getAttribute('data-source')).toBe(
159
+ 'turn-7',
160
+ );
161
+ });
162
+ });
163
+
127
164
  describe('block spacing theme targets', () => {
128
165
  // Every block type renders a stable astryx-markdown-<block> class so a
129
166
  // theme can tune the gap around it (marginBlockStart/marginBlockEnd) via
@@ -561,14 +561,19 @@ const headingStyles = {
561
561
  const DANGEROUS_URL_PATTERN = /^(javascript|data|vbscript):/i;
562
562
 
563
563
  function sanitizeUrl(url: string): string | null {
564
- const trimmed = url.trim();
565
- if (trimmed.length === 0) {
564
+ // Strip control characters before testing, the same normalization the
565
+ // parser's isSafeUrl applies — the anchored pattern must see the URL the
566
+ // way a browser will. Return the normalized value so the stripped
567
+ // characters don't ride along into an attribute or component override.
568
+ // eslint-disable-next-line no-control-regex -- control chars are the bypass
569
+ const normalized = url.replace(/[\x00-\x1f\x7f]/g, '').trim();
570
+ if (normalized.length === 0) {
566
571
  return null;
567
572
  }
568
- if (DANGEROUS_URL_PATTERN.test(trimmed)) {
573
+ if (DANGEROUS_URL_PATTERN.test(normalized)) {
569
574
  return null;
570
575
  }
571
- return trimmed;
576
+ return normalized;
572
577
  }
573
578
 
574
579
  // ---------------------------------------------------------------------------
@@ -1622,6 +1627,7 @@ export function Markdown({
1622
1627
  className,
1623
1628
  style,
1624
1629
  'data-testid': testId,
1630
+ ...props
1625
1631
  }: MarkdownProps): React.ReactElement {
1626
1632
  const t = useTranslator();
1627
1633
  const LinkComponent = useLinkComponent();
@@ -1744,6 +1750,8 @@ export function Markdown({
1744
1750
  const renderedInline = (
1745
1751
  <span
1746
1752
  ref={ref}
1753
+ // Consumer props first: what the component sets for itself wins.
1754
+ {...props}
1747
1755
  data-testid={testId}
1748
1756
  {...mergeProps(
1749
1757
  themeProps('markdown', {density}),
@@ -1775,8 +1783,11 @@ export function Markdown({
1775
1783
 
1776
1784
  const rendered = (
1777
1785
  <div
1778
- role="document"
1779
1786
  ref={ref as React.Ref<HTMLDivElement>}
1787
+ // Consumer props first: what the component sets for itself — the
1788
+ // document role included — wins.
1789
+ {...props}
1790
+ role="document"
1780
1791
  data-testid={testId}
1781
1792
  {...mergeProps(
1782
1793
  themeProps('markdown', {density}),
@@ -454,6 +454,14 @@ describe('parseMarkdown', () => {
454
454
  }
455
455
  });
456
456
 
457
+ it('rejects a standalone image with an unsafe scheme as literal text (XSS prevention)', () => {
458
+ // Same rule as inline images: the line falls through to the paragraph
459
+ // path and stays literal text instead of becoming an image node.
460
+ const result = parseMarkdown('![alt](vbscript:msgbox)');
461
+ expect(result[0].type).toBe('paragraph');
462
+ expect(JSON.stringify(result)).not.toContain('"type":"image"');
463
+ });
464
+
457
465
  it('parses complex AI response', () => {
458
466
  const input = [
459
467
  '# Analysis',
@@ -1150,6 +1158,31 @@ describe('link reference definitions', () => {
1150
1158
  expect(links).toEqual([{type: 'image', src: '/logo.png', text: 'logo'}]);
1151
1159
  });
1152
1160
 
1161
+ it('rejects a reference image whose definition has an unsafe scheme (XSS prevention)', () => {
1162
+ const {links} = paragraphLinks('![logo][l]\n\n[l]: <javascript:alert(1)>');
1163
+ expect(links).toEqual([]);
1164
+ });
1165
+
1166
+ it('rejects a shortcut reference image with an unsafe definition (XSS prevention)', () => {
1167
+ const {links} = paragraphLinks('![logo]\n\n[logo]: <javascript:alert(1)>');
1168
+ expect(links).toEqual([]);
1169
+ });
1170
+
1171
+ it('rejects definitions that hide the scheme behind control chars (XSS prevention)', () => {
1172
+ // isSafeUrl strips control characters before testing; the angle-bracket
1173
+ // destination form is the only definition shape that can contain them.
1174
+ // Both consumers of a definition — reference images and reference links —
1175
+ // must apply it.
1176
+ const {links: imageLinks} = paragraphLinks(
1177
+ '![logo][l]\n\n[l]: <java\tscript:alert(1)>',
1178
+ );
1179
+ expect(imageLinks).toEqual([]);
1180
+ const {links: refLinks} = paragraphLinks(
1181
+ '[click][l]\n\n[l]: <java\tscript:alert(1)>',
1182
+ );
1183
+ expect(refLinks).toEqual([]);
1184
+ });
1185
+
1153
1186
  it('does not treat a whitespace-only second label as collapsed', () => {
1154
1187
  // `[foo][ ]` is a full reference to the empty (normalized) label and matches
1155
1188
  // nothing; `[foo]` still resolves as a shortcut and `[ ]` stays literal.
@@ -369,7 +369,7 @@ function matchReferenceImage(
369
369
  const rawLabel = text.slice(altClose + 2, labelClose);
370
370
  const label = rawLabel === '' ? alt : rawLabel;
371
371
  const src = linkDefs.get(normalizeLinkLabel(label));
372
- if (src != null) {
372
+ if (src != null && isSafeUrl(src)) {
373
373
  return {node: {type: 'image', src, alt}, end: labelClose + 1};
374
374
  }
375
375
  // No match — fall back to a shortcut `![alt]`.
@@ -379,7 +379,7 @@ function matchReferenceImage(
379
379
  return null;
380
380
  }
381
381
  const src = linkDefs.get(normalizeLinkLabel(alt));
382
- if (src == null) {
382
+ if (src == null || !isSafeUrl(src)) {
383
383
  return null;
384
384
  }
385
385
  return {node: {type: 'image', src, alt}, end: altClose + 1};
@@ -1416,8 +1416,14 @@ function parseMarkdownImpl(
1416
1416
  }
1417
1417
 
1418
1418
  // --- Standalone image ---
1419
+ // An unsafe src falls through to the paragraph path and renders as
1420
+ // literal text, the same rule the inline image path applies.
1419
1421
  const imageMatch = line.match(/^!\[([^\]]*)\]\(([^)]+)\)/);
1420
- if (imageMatch && line.trim() === imageMatch[0]) {
1422
+ if (
1423
+ imageMatch &&
1424
+ line.trim() === imageMatch[0] &&
1425
+ isSafeUrl(imageMatch[2])
1426
+ ) {
1421
1427
  pushBlock({type: 'image', alt: imageMatch[1], src: imageMatch[2]});
1422
1428
  index++;
1423
1429
  continue;