@astryxdesign/core 0.5.0 → 0.5.1-canary.046f6ea

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
@@ -0,0 +1,421 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file useResizable.test.ts
5
+ * @input Uses vitest, @testing-library/react renderHook, useResizable
6
+ * @output Unit tests for useResizable persistence and collapse state
7
+ * @position Testing; validates useResizable implementation
8
+ *
9
+ * Persistence coverage originated in #4824 by @AKnassa.
10
+ *
11
+ * SYNC: When useResizable changes, update tests to match new behavior
12
+ */
13
+
14
+ import {useLayoutEffect} from 'react';
15
+ import {describe, it, expect, beforeEach, vi} from 'vitest';
16
+ import {renderHook, act} from '@testing-library/react';
17
+ import {useResizable} from './useResizable';
18
+
19
+ const AUTO_SAVE_ID = 'test-panel';
20
+ const KEY = `astryx-resizable:${AUTO_SAVE_ID}`;
21
+
22
+ const BASE_CONFIG = {
23
+ defaultSize: 260,
24
+ minSizePx: 180,
25
+ maxSizePx: 480,
26
+ autoSaveId: AUTO_SAVE_ID,
27
+ };
28
+
29
+ type StoredEntry = {size: number; isCollapsed: boolean} | number | null;
30
+
31
+ function readStored(): StoredEntry {
32
+ return JSON.parse(localStorage.getItem(KEY) ?? 'null') as StoredEntry;
33
+ }
34
+
35
+ beforeEach(() => {
36
+ localStorage.clear();
37
+ });
38
+
39
+ describe('useResizable persistence', () => {
40
+ it('restores a persisted width (legacy plain-number format)', () => {
41
+ localStorage.setItem(KEY, '320');
42
+ const {result} = renderHook(() =>
43
+ useResizable({...BASE_CONFIG, collapsible: true}),
44
+ );
45
+ expect(result.current.size).toBe(320);
46
+ expect(result.current.isCollapsed).toBe(false);
47
+ });
48
+
49
+ it('clamps a persisted width below the minimum', () => {
50
+ localStorage.setItem(KEY, '20');
51
+ const {result} = renderHook(() => useResizable(BASE_CONFIG));
52
+ expect(result.current.size).toBe(180);
53
+ });
54
+
55
+ it('ignores a non-finite persisted value', () => {
56
+ // JSON.parse('1e999') yields Infinity, which passes a typeof check.
57
+ localStorage.setItem(KEY, '1e999');
58
+ const {result} = renderHook(() => useResizable(BASE_CONFIG));
59
+ expect(result.current.size).toBe(260);
60
+ });
61
+
62
+ it('ignores corrupt storage content', () => {
63
+ localStorage.setItem(KEY, 'not json');
64
+ const {result} = renderHook(() => useResizable(BASE_CONFIG));
65
+ expect(result.current.size).toBe(260);
66
+ });
67
+
68
+ it('persists the expanded size together with the collapse flag', () => {
69
+ const {result} = renderHook(() =>
70
+ useResizable({...BASE_CONFIG, collapsible: true}),
71
+ );
72
+ act(() => result.current.resize(340));
73
+ expect(readStored()).toEqual({size: 340, isCollapsed: false});
74
+ });
75
+
76
+ it('keeps the expanded width in storage when collapsing', () => {
77
+ const {result} = renderHook(() =>
78
+ useResizable({...BASE_CONFIG, collapsible: true}),
79
+ );
80
+ act(() => result.current.resize(300));
81
+ act(() => result.current.collapse());
82
+
83
+ expect(result.current.size).toBe(0);
84
+ expect(result.current.isCollapsed).toBe(true);
85
+ expect(readStored()).toEqual({size: 300, isCollapsed: true});
86
+ });
87
+
88
+ it('restores the collapsed state and the pre-collapse width on remount', () => {
89
+ localStorage.setItem(KEY, JSON.stringify({size: 300, isCollapsed: true}));
90
+ const {result} = renderHook(() =>
91
+ useResizable({...BASE_CONFIG, collapsible: true}),
92
+ );
93
+
94
+ expect(result.current.isCollapsed).toBe(true);
95
+ expect(result.current.size).toBe(0);
96
+
97
+ act(() => result.current.expand());
98
+ expect(result.current.size).toBe(300);
99
+ expect(readStored()).toEqual({size: 300, isCollapsed: false});
100
+ });
101
+
102
+ it('treats a legacy persisted 0 as collapsed when collapsible', () => {
103
+ localStorage.setItem(KEY, '0');
104
+ const {result} = renderHook(() =>
105
+ useResizable({...BASE_CONFIG, collapsible: true}),
106
+ );
107
+
108
+ expect(result.current.isCollapsed).toBe(true);
109
+ expect(result.current.size).toBe(0);
110
+
111
+ // The legacy entry lost the real width, so expanding falls back to the
112
+ // default rather than to the minimum.
113
+ act(() => result.current.expand());
114
+ expect(result.current.size).toBe(260);
115
+ });
116
+
117
+ it('treats a legacy persisted 0 as the default width when not collapsible', () => {
118
+ localStorage.setItem(KEY, '0');
119
+ const {result} = renderHook(() => useResizable(BASE_CONFIG));
120
+ expect(result.current.isCollapsed).toBe(false);
121
+ expect(result.current.size).toBe(260);
122
+ });
123
+
124
+ it('ignores a persisted collapse flag when not collapsible', () => {
125
+ localStorage.setItem(KEY, JSON.stringify({size: 300, isCollapsed: true}));
126
+ const {result} = renderHook(() => useResizable(BASE_CONFIG));
127
+ expect(result.current.isCollapsed).toBe(false);
128
+ expect(result.current.size).toBe(300);
129
+ });
130
+
131
+ it('keeps the saved width across repeated collapse calls', () => {
132
+ const {result} = renderHook(() =>
133
+ useResizable({...BASE_CONFIG, collapsible: true}),
134
+ );
135
+ act(() => result.current.resize(300));
136
+ act(() => result.current.collapse());
137
+ act(() => result.current.collapse());
138
+
139
+ expect(readStored()).toEqual({size: 300, isCollapsed: true});
140
+ act(() => result.current.expand());
141
+ expect(result.current.size).toBe(300);
142
+ });
143
+
144
+ it('persists the pre-drag width when dragging to collapse', () => {
145
+ const {result} = renderHook(() =>
146
+ useResizable({...BASE_CONFIG, collapsible: true}),
147
+ );
148
+ act(() => result.current.resize(300));
149
+ act(() => result.current.props._onResizeStart());
150
+ act(() => result.current.props._onResizeMove(-270));
151
+
152
+ expect(result.current.isCollapsed).toBe(true);
153
+ expect(readStored()).toEqual({size: 300, isCollapsed: true});
154
+
155
+ // A continued drag below the threshold must not clobber the saved width.
156
+ act(() => result.current.props._onResizeMove(-275));
157
+ act(() => result.current.expand());
158
+ expect(result.current.size).toBe(300);
159
+ });
160
+
161
+ it('does not touch storage without an autoSaveId', () => {
162
+ const {result} = renderHook(() =>
163
+ useResizable({defaultSize: 260, collapsible: true}),
164
+ );
165
+ act(() => result.current.collapse());
166
+ expect(localStorage.length).toBe(0);
167
+ });
168
+ });
169
+
170
+ describe('useResizable uncontrolled collapse', () => {
171
+ it('starts collapsed from defaultIsCollapsed', () => {
172
+ const {result} = renderHook(() =>
173
+ useResizable({
174
+ defaultSize: 260,
175
+ collapsible: true,
176
+ defaultIsCollapsed: true,
177
+ }),
178
+ );
179
+ expect(result.current.isCollapsed).toBe(true);
180
+ expect(result.current.size).toBe(0);
181
+
182
+ act(() => result.current.expand());
183
+ expect(result.current.isCollapsed).toBe(false);
184
+ expect(result.current.size).toBe(260);
185
+ });
186
+
187
+ it('ignores defaultIsCollapsed when not collapsible', () => {
188
+ const {result} = renderHook(() =>
189
+ useResizable({defaultSize: 260, defaultIsCollapsed: true}),
190
+ );
191
+ expect(result.current.isCollapsed).toBe(false);
192
+ expect(result.current.size).toBe(260);
193
+ });
194
+
195
+ it('lets a persisted collapse flag win over defaultIsCollapsed', () => {
196
+ localStorage.setItem(KEY, JSON.stringify({size: 300, isCollapsed: false}));
197
+ const {result} = renderHook(() =>
198
+ useResizable({
199
+ ...BASE_CONFIG,
200
+ collapsible: true,
201
+ defaultIsCollapsed: true,
202
+ }),
203
+ );
204
+ expect(result.current.isCollapsed).toBe(false);
205
+ expect(result.current.size).toBe(300);
206
+ });
207
+
208
+ it('keeps defaultIsCollapsed when a legacy width entry says nothing about collapse', () => {
209
+ localStorage.setItem(KEY, '300');
210
+ const {result} = renderHook(() =>
211
+ useResizable({
212
+ ...BASE_CONFIG,
213
+ collapsible: true,
214
+ defaultIsCollapsed: true,
215
+ }),
216
+ );
217
+ expect(result.current.isCollapsed).toBe(true);
218
+ // The width is still restored — it is only collapse that is unknown.
219
+ act(() => result.current.expand());
220
+ expect(result.current.size).toBe(300);
221
+ });
222
+
223
+ it('notifies onCollapseChange once per collapse and expand', () => {
224
+ const onCollapseChange = vi.fn();
225
+ const {result} = renderHook(() =>
226
+ useResizable({defaultSize: 260, collapsible: true, onCollapseChange}),
227
+ );
228
+
229
+ act(() => result.current.collapse());
230
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(true);
231
+
232
+ onCollapseChange.mockClear();
233
+ act(() => result.current.expand());
234
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(false);
235
+ });
236
+
237
+ it('collapses on a drag past the threshold', () => {
238
+ const onCollapseChange = vi.fn();
239
+ const {result} = renderHook(() =>
240
+ useResizable({...BASE_CONFIG, collapsible: true, onCollapseChange}),
241
+ );
242
+
243
+ act(() => result.current.props._onResizeStart());
244
+ act(() => result.current.props._onResizeMove(-240));
245
+
246
+ expect(result.current.isCollapsed).toBe(true);
247
+ expect(onCollapseChange).toHaveBeenCalledWith(true);
248
+ });
249
+ });
250
+
251
+ describe('useResizable controlled collapse', () => {
252
+ it('renders the controlled value rather than its own state', () => {
253
+ const {result, rerender} = renderHook(
254
+ ({isCollapsed}: {isCollapsed: boolean}) =>
255
+ useResizable({defaultSize: 260, collapsible: true, isCollapsed}),
256
+ {initialProps: {isCollapsed: true}},
257
+ );
258
+
259
+ expect(result.current.isCollapsed).toBe(true);
260
+ expect(result.current.size).toBe(0);
261
+
262
+ rerender({isCollapsed: false});
263
+ expect(result.current.isCollapsed).toBe(false);
264
+ expect(result.current.size).toBe(260);
265
+ });
266
+
267
+ it('makes collapse() and expand() intents that change nothing locally', () => {
268
+ const onCollapseChange = vi.fn();
269
+ const {result} = renderHook(() =>
270
+ useResizable({
271
+ defaultSize: 260,
272
+ collapsible: true,
273
+ isCollapsed: false,
274
+ onCollapseChange,
275
+ }),
276
+ );
277
+
278
+ act(() => result.current.collapse());
279
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(true);
280
+ // The owner said expanded and never changed its mind.
281
+ expect(result.current.isCollapsed).toBe(false);
282
+ expect(result.current.size).toBe(260);
283
+ });
284
+
285
+ it('reports a drag past the threshold instead of collapsing itself', () => {
286
+ const onCollapseChange = vi.fn();
287
+ const {result} = renderHook(() =>
288
+ useResizable({
289
+ ...BASE_CONFIG,
290
+ collapsible: true,
291
+ isCollapsed: false,
292
+ onCollapseChange,
293
+ }),
294
+ );
295
+
296
+ act(() => result.current.props._onResizeStart());
297
+ act(() => result.current.props._onResizeMove(-240));
298
+
299
+ expect(onCollapseChange).toHaveBeenCalledWith(true);
300
+ expect(result.current.isCollapsed).toBe(false);
301
+ });
302
+
303
+ it('persists the collapse state the owner holds', () => {
304
+ const {result} = renderHook(() =>
305
+ useResizable({...BASE_CONFIG, collapsible: true, isCollapsed: true}),
306
+ );
307
+ expect(result.current.isCollapsed).toBe(true);
308
+ expect(readStored()).toEqual({size: 260, isCollapsed: true});
309
+ });
310
+ });
311
+
312
+ // The fix under test is @AKnassa's, from #5118.
313
+ describe('useResizable live collapse state', () => {
314
+ it('reads the current controlled value before consumer layout effects', () => {
315
+ const onCollapseChange = vi.fn();
316
+ const {rerender} = renderHook(
317
+ ({isCollapsed, shouldCollapse}) => {
318
+ const {collapse} = useResizable({
319
+ defaultSize: 260,
320
+ collapsible: true,
321
+ isCollapsed,
322
+ onCollapseChange,
323
+ });
324
+ useLayoutEffect(() => {
325
+ if (shouldCollapse) {
326
+ collapse();
327
+ }
328
+ }, [collapse, shouldCollapse]);
329
+ return collapse;
330
+ },
331
+ {initialProps: {isCollapsed: true, shouldCollapse: false}},
332
+ );
333
+
334
+ rerender({isCollapsed: false, shouldCollapse: true});
335
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(true);
336
+ });
337
+
338
+ // ResizeHandle registers its pointermove listener once at pointer down, so
339
+ // a whole gesture runs against the props object captured there. These tests
340
+ // hold that snapshot instead of re-reading result.current between moves —
341
+ // re-reading hands the callbacks a fresh render and hides the bug.
342
+ it('reports one collapse for a drag that stays below the threshold', () => {
343
+ const onCollapseChange = vi.fn();
344
+ const {result} = renderHook(() =>
345
+ useResizable({...BASE_CONFIG, collapsible: true, onCollapseChange}),
346
+ );
347
+
348
+ act(() => result.current.props._onResizeStart());
349
+ const gesture = result.current.props;
350
+ for (const delta of [-240, -245, -250, -255, -260]) {
351
+ act(() => gesture._onResizeMove(delta));
352
+ }
353
+
354
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(true);
355
+ expect(result.current.isCollapsed).toBe(true);
356
+ });
357
+
358
+ it('re-expands mid-gesture when the drag crosses back above the threshold', () => {
359
+ const onCollapseChange = vi.fn();
360
+ const {result} = renderHook(() =>
361
+ useResizable({...BASE_CONFIG, collapsible: true, onCollapseChange}),
362
+ );
363
+
364
+ act(() => result.current.props._onResizeStart());
365
+ const gesture = result.current.props;
366
+ act(() => gesture._onResizeMove(-240));
367
+ act(() => gesture._onResizeMove(-20));
368
+
369
+ expect(onCollapseChange.mock.calls).toEqual([[true], [false]]);
370
+ expect(result.current.isCollapsed).toBe(false);
371
+ expect(result.current.size).toBe(240);
372
+ });
373
+
374
+ it('notifies when resize() lifts the panel out of collapse', () => {
375
+ const onCollapseChange = vi.fn();
376
+ const {result} = renderHook(() =>
377
+ useResizable({...BASE_CONFIG, collapsible: true, onCollapseChange}),
378
+ );
379
+
380
+ act(() => result.current.collapse());
381
+ onCollapseChange.mockClear();
382
+ act(() => result.current.resize(300));
383
+
384
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(false);
385
+ expect(result.current.isCollapsed).toBe(false);
386
+ expect(result.current.size).toBe(300);
387
+ });
388
+
389
+ it('reports one collapse when collapse() runs twice in a tick', () => {
390
+ const onCollapseChange = vi.fn();
391
+ const {result} = renderHook(() =>
392
+ useResizable({...BASE_CONFIG, collapsible: true, onCollapseChange}),
393
+ );
394
+
395
+ act(() => {
396
+ result.current.collapse();
397
+ result.current.collapse();
398
+ });
399
+
400
+ expect(onCollapseChange).toHaveBeenCalledExactlyOnceWith(true);
401
+ });
402
+ });
403
+
404
+ describe('useResizable identity', () => {
405
+ it('keeps callbacks stable when optional snaps are omitted', () => {
406
+ const {result, rerender} = renderHook(() =>
407
+ useResizable({defaultSize: 200, minSizePx: 100, maxSizePx: 400}),
408
+ );
409
+ const first = {
410
+ expand: result.current.expand,
411
+ resize: result.current.resize,
412
+ snaps: result.current.props._snaps,
413
+ };
414
+
415
+ rerender();
416
+
417
+ expect(result.current.expand).toBe(first.expand);
418
+ expect(result.current.resize).toBe(first.resize);
419
+ expect(result.current.props._snaps).toBe(first.snaps);
420
+ });
421
+ });
@@ -4,7 +4,8 @@
4
4
 
5
5
  /**
6
6
  * @file useResizable.ts
7
- * @input Resize configuration (defaultSize, minSizePx, maxSizePx, collapsible, snaps)
7
+ * @input Resize configuration (defaultSize, minSizePx, maxSizePx, snaps) and
8
+ * collapse configuration (collapsible, defaultIsCollapsed, isCollapsed)
8
9
  * @output Hook return: size, isCollapsed, collapse/expand/resize methods, props for handle
9
10
  * @position Public hook; consumed by layout components via `resizable` prop
10
11
  */
@@ -44,15 +45,33 @@ export interface ResizableConfig {
44
45
  minWidth?: number;
45
46
  /** Maximum width in pixels. @default 480 */
46
47
  maxWidth?: number;
47
- /** localStorage key for persisting width. */
48
+ /** localStorage key for persisting width and collapse state. */
48
49
  autoSaveId?: string;
49
50
  /** Called when the width changes (on drag end). */
50
51
  onWidthChange?: (width: number) => void;
52
+ /** Start collapsed (uncontrolled). A persisted entry wins over this. */
53
+ defaultIsCollapsed?: boolean;
54
+ /** Controlled collapse state. Pair with `onCollapseChange`. */
55
+ isCollapsed?: boolean;
56
+ /** Called when collapse state changes (via drag or programmatic). */
57
+ onCollapseChange?: (isCollapsed: boolean) => void;
51
58
  }
52
59
 
53
60
  export interface UseResizableSingleConfig extends ResizableRegionConfig {
54
61
  /** Unique key for localStorage persistence. */
55
62
  autoSaveId?: string;
63
+ /**
64
+ * Initial collapse state (uncontrolled). A persisted entry wins over it,
65
+ * so a restored session keeps the state the user left behind.
66
+ * Only honored when `collapsible` is true.
67
+ */
68
+ defaultIsCollapsed?: boolean;
69
+ /**
70
+ * Controlled collapse state. When set, the caller owns collapse:
71
+ * `collapse()`, `expand()` and a drag past the collapse threshold report
72
+ * through `onCollapseChange` instead of changing state here.
73
+ */
74
+ isCollapsed?: boolean;
56
75
  /** Called when size changes during drag. */
57
76
  onSizeChange?: (size: number) => void;
58
77
  /** Called when collapse state changes (via drag or programmatic). */
@@ -138,16 +157,55 @@ function clampSize(
138
157
  return clamped;
139
158
  }
140
159
 
141
- function loadPersistedSize(key: string): number | null {
160
+ interface PersistedResizableState {
161
+ /** Expanded size in px, or null when the entry carries no usable size. */
162
+ size: number | null;
163
+ /**
164
+ * Collapse state when the entry was written, or null when the entry
165
+ * predates collapse persistence and says nothing about it.
166
+ */
167
+ isCollapsed: boolean | null;
168
+ }
169
+
170
+ /**
171
+ * Reads a persisted entry. Three formats exist in storage:
172
+ * - `{size, isCollapsed}` — the current format; `size` is the expanded size,
173
+ * so the pre-collapse width survives a collapsed session
174
+ * - a plain non-zero number — a legacy width-only entry that never recorded
175
+ * collapse, so `isCollapsed` is null (unknown)
176
+ * - a plain `0` — written by legacy collapse, which restored the region as a
177
+ * zero-width expanded panel (#4790); read as "collapsed, no saved size"
178
+ */
179
+ function loadPersistedState(key: string): PersistedResizableState | null {
142
180
  if (typeof window === 'undefined') {
143
181
  return null;
144
182
  }
145
183
  try {
146
184
  const raw = localStorage.getItem(STORAGE_PREFIX + key);
147
- if (raw != null) {
148
- const parsed = JSON.parse(raw);
149
- if (typeof parsed === 'number') {
150
- return parsed;
185
+ if (raw == null) {
186
+ return null;
187
+ }
188
+ const parsed: unknown = JSON.parse(raw);
189
+ if (typeof parsed === 'number') {
190
+ if (!Number.isFinite(parsed)) {
191
+ return null;
192
+ }
193
+ return parsed === 0
194
+ ? {size: null, isCollapsed: true}
195
+ : {size: parsed, isCollapsed: null};
196
+ }
197
+ if (typeof parsed === 'object' && parsed != null) {
198
+ const {size, isCollapsed} = parsed as {
199
+ size?: unknown;
200
+ isCollapsed?: unknown;
201
+ };
202
+ const hasSize =
203
+ typeof size === 'number' && Number.isFinite(size) && size > 0;
204
+ if (hasSize || isCollapsed === true) {
205
+ return {
206
+ size: hasSize ? size : null,
207
+ isCollapsed: isCollapsed === true,
208
+ };
151
209
  }
152
210
  }
153
211
  } catch {
@@ -156,12 +214,12 @@ function loadPersistedSize(key: string): number | null {
156
214
  return null;
157
215
  }
158
216
 
159
- function persistSize(key: string, size: number): void {
217
+ function persistState(key: string, state: PersistedResizableState): void {
160
218
  if (typeof window === 'undefined') {
161
219
  return;
162
220
  }
163
221
  try {
164
- localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(size));
222
+ localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(state));
165
223
  } catch {
166
224
  /* ignore */
167
225
  }
@@ -195,87 +253,115 @@ function useSingleResizable(config: UseResizableSingleConfig): ResizableRegion {
195
253
  maxSizePx = Infinity,
196
254
  collapsible = false,
197
255
  collapsedSize = DEFAULT_COLLAPSED_SIZE,
198
- snaps = [],
256
+ snaps: configuredSnaps,
199
257
  autoSaveId,
258
+ defaultIsCollapsed,
259
+ isCollapsed: controlledIsCollapsed,
200
260
  onSizeChange,
201
261
  onCollapseChange,
202
262
  } = config;
263
+ const emptySnapsRef = useRef<number[]>([]);
264
+ const snaps = configuredSnaps ?? emptySnapsRef.current;
203
265
 
204
266
  const resolvedDefault = resolveDefaultSize(defaultSize);
205
- const persisted = autoSaveId ? loadPersistedSize(autoSaveId) : null;
206
- const initial = persisted ?? resolvedDefault;
267
+ const persisted = autoSaveId ? loadPersistedState(autoSaveId) : null;
268
+ const initial = persisted?.size ?? resolvedDefault;
207
269
 
270
+ // `size` is always the *expanded* size: collapse hides it behind the
271
+ // isCollapsed gate below rather than zeroing it, so the width the user had
272
+ // survives a collapse, a persist round-trip, and an expand.
208
273
  const [size, setSize] = useState(() =>
209
274
  clampSize(initial, minSizePx, maxSizePx, snaps),
210
275
  );
211
- const [isCollapsed, setIsCollapsed] = useState(
212
- () => persisted === 0 && collapsible,
276
+ const [uncontrolledIsCollapsed, setUncontrolledIsCollapsed] = useState(
277
+ () => persisted?.isCollapsed ?? defaultIsCollapsed ?? false,
213
278
  );
214
- const preCollapseSizeRef = useRef(size);
279
+
280
+ const isControlled = controlledIsCollapsed !== undefined;
281
+ const isCollapsed =
282
+ collapsible &&
283
+ (isControlled ? controlledIsCollapsed : uncontrolledIsCollapsed);
215
284
  const dragStartSizeRef = useRef(size);
216
285
 
286
+ // Mirrors isCollapsed so the callbacks below read the live value instead of
287
+ // the one their last render captured. Two cases reach them from a stale
288
+ // closure: two imperative calls in one tick, and a drag — ResizeHandle
289
+ // registers its pointermove listener once at pointer down, so a whole
290
+ // gesture runs against one props snapshot.
291
+ const isCollapsedRef = useRef(isCollapsed);
292
+ isCollapsedRef.current = isCollapsed;
293
+
294
+ // Controlled callers own the state; collapse() and a drag past the
295
+ // threshold report through onCollapseChange and change nothing here.
296
+ const setCollapsed = useCallback(
297
+ (value: boolean) => {
298
+ if (!isControlled) {
299
+ isCollapsedRef.current = value;
300
+ setUncontrolledIsCollapsed(value);
301
+ }
302
+ },
303
+ [isControlled],
304
+ );
305
+
217
306
  useEffect(() => {
218
307
  if (autoSaveId) {
219
- persistSize(autoSaveId, isCollapsed ? 0 : size);
308
+ persistState(autoSaveId, {size, isCollapsed});
220
309
  }
221
310
  }, [size, isCollapsed, autoSaveId]);
222
311
 
223
312
  const collapse = useCallback(() => {
224
- if (!collapsible) {
313
+ // The already-collapsed guard keeps a repeated call from re-notifying a
314
+ // transition that already happened.
315
+ if (!collapsible || isCollapsedRef.current) {
225
316
  return;
226
317
  }
227
- preCollapseSizeRef.current = size;
228
- setIsCollapsed(true);
229
- setSize(0);
318
+ setCollapsed(true);
230
319
  onCollapseChange?.(true);
231
320
  onSizeChange?.(0);
232
- }, [collapsible, size, onCollapseChange, onSizeChange]);
321
+ }, [collapsible, setCollapsed, onCollapseChange, onSizeChange]);
233
322
 
234
323
  const expand = useCallback(() => {
235
- setIsCollapsed(false);
236
- const restored = preCollapseSizeRef.current || resolvedDefault;
237
- const newSize = clampSize(restored, minSizePx, maxSizePx, snaps);
238
- setSize(newSize);
239
- onCollapseChange?.(false);
240
- onSizeChange?.(newSize);
241
- }, [
242
- resolvedDefault,
243
- minSizePx,
244
- maxSizePx,
245
- snaps,
246
- onCollapseChange,
247
- onSizeChange,
248
- ]);
324
+ const wasCollapsed = isCollapsedRef.current;
325
+ setCollapsed(false);
326
+ if (wasCollapsed) {
327
+ onCollapseChange?.(false);
328
+ }
329
+ onSizeChange?.(size);
330
+ }, [setCollapsed, size, onCollapseChange, onSizeChange]);
249
331
 
250
332
  const resize = useCallback(
251
333
  (newSize: number) => {
252
334
  const clamped = clampSize(newSize, minSizePx, maxSizePx, snaps);
335
+ const wasCollapsed = isCollapsedRef.current;
253
336
  setSize(clamped);
254
- setIsCollapsed(false);
337
+ setCollapsed(false);
338
+ // Resizing out of the collapsed state is an implicit expand — notify
339
+ // like the drag path does when it crosses back over the threshold.
340
+ if (wasCollapsed) {
341
+ onCollapseChange?.(false);
342
+ }
255
343
  onSizeChange?.(clamped);
256
344
  },
257
- [minSizePx, maxSizePx, snaps, onSizeChange],
345
+ [minSizePx, maxSizePx, snaps, setCollapsed, onCollapseChange, onSizeChange],
258
346
  );
259
347
 
260
348
  const onResizeStart = useCallback(() => {
261
- dragStartSizeRef.current = isCollapsed ? 0 : size;
262
- }, [size, isCollapsed]);
349
+ dragStartSizeRef.current = isCollapsedRef.current ? 0 : size;
350
+ }, [size]);
263
351
 
264
352
  const onResizeMove = useCallback(
265
353
  (delta: number) => {
266
354
  const raw = dragStartSizeRef.current + delta;
267
355
  if (collapsible && raw < collapsedSize) {
268
- if (!isCollapsed) {
269
- preCollapseSizeRef.current = size;
356
+ if (!isCollapsedRef.current) {
357
+ setCollapsed(true);
270
358
  onCollapseChange?.(true);
359
+ onSizeChange?.(0);
271
360
  }
272
- setIsCollapsed(true);
273
- setSize(0);
274
- onSizeChange?.(0);
275
361
  return;
276
362
  }
277
- if (isCollapsed && raw >= collapsedSize) {
278
- setIsCollapsed(false);
363
+ if (isCollapsedRef.current && raw >= collapsedSize) {
364
+ setCollapsed(false);
279
365
  onCollapseChange?.(false);
280
366
  }
281
367
  const clamped = clampSize(raw, minSizePx, maxSizePx, snaps);
@@ -285,8 +371,7 @@ function useSingleResizable(config: UseResizableSingleConfig): ResizableRegion {
285
371
  [
286
372
  collapsible,
287
373
  collapsedSize,
288
- isCollapsed,
289
- size,
374
+ setCollapsed,
290
375
  minSizePx,
291
376
  maxSizePx,
292
377
  snaps,