@astryxdesign/core 0.7.7-canary.c0929c8 → 0.7.7-canary.c19ca12

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 (472) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +4 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +36 -6
  4. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts +13 -0
  5. package/dist/AppShell/useCloseMobileDrawerOnActivate.d.ts.map +1 -0
  6. package/dist/AppShell/useCloseMobileDrawerOnActivate.js +45 -0
  7. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheet.js +79 -9
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +23 -4
  11. package/dist/BottomSheet/bottomSheetStack.d.ts.map +1 -1
  12. package/dist/BottomSheet/bottomSheetStack.js +5 -4
  13. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  14. package/dist/BottomSheet/useSheetGestures.js +3 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +2 -1
  16. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  17. package/dist/Breadcrumbs/BreadcrumbItem.js +7 -3
  18. package/dist/Button/Button.d.ts.map +1 -1
  19. package/dist/Button/Button.js +47 -3
  20. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  21. package/dist/CheckboxInput/CheckboxInput.js +13 -2
  22. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  23. package/dist/CheckboxList/CheckboxList.js +7 -1
  24. package/dist/CheckboxList/CheckboxListContext.d.ts +6 -0
  25. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  26. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  27. package/dist/CheckboxList/CheckboxListItem.js +10 -2
  28. package/dist/ComplexSelector/ComplexSelector.d.ts +4 -1
  29. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  30. package/dist/ComplexSelector/ComplexSelector.js +14 -4
  31. package/dist/ContextMenu/ContextMenu.js +2 -2
  32. package/dist/DateInput/DateInput.d.ts.map +1 -1
  33. package/dist/DateInput/DateInput.js +5 -2
  34. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  35. package/dist/DateInput/MonthScroller.js +13 -27
  36. package/dist/DateInput/Wheel.d.ts.map +1 -1
  37. package/dist/DateInput/Wheel.js +22 -41
  38. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  39. package/dist/DateTimeInput/DateTimeInput.js +5 -2
  40. package/dist/Dialog/Dialog.d.ts.map +1 -1
  41. package/dist/Dialog/Dialog.js +23 -45
  42. package/dist/Drawer/Drawer.d.ts.map +1 -1
  43. package/dist/Drawer/Drawer.js +15 -11
  44. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  45. package/dist/Drawer/useDrawerDialogPresence.js +5 -1
  46. package/dist/Field/Field.js +2 -2
  47. package/dist/Field/PanelSearchInput.d.ts +9 -1
  48. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  49. package/dist/Field/PanelSearchInput.js +3 -1
  50. package/dist/HoverCard/HoverCard.d.ts +4 -3
  51. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  52. package/dist/HoverCard/HoverCard.js +27 -2
  53. package/dist/HoverCard/useHoverCard.d.ts +5 -4
  54. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  55. package/dist/HoverCard/useHoverCard.js +17 -31
  56. package/dist/Icon/Icon.d.ts +5 -1
  57. package/dist/Icon/Icon.d.ts.map +1 -1
  58. package/dist/Icon/Icon.js +5 -2
  59. package/dist/InfoTip/InfoTip.d.ts +63 -0
  60. package/dist/InfoTip/InfoTip.d.ts.map +1 -0
  61. package/dist/InfoTip/InfoTip.js +126 -0
  62. package/dist/InfoTip/index.d.ts +10 -0
  63. package/dist/InfoTip/index.d.ts.map +1 -0
  64. package/dist/InfoTip/index.js +12 -0
  65. package/dist/Layer/layerAnimations.stylex.d.ts +7 -8
  66. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  67. package/dist/Layer/layerAnimations.stylex.js +25 -20
  68. package/dist/Layer/useBackdropPress.d.ts +27 -0
  69. package/dist/Layer/useBackdropPress.d.ts.map +1 -0
  70. package/dist/Layer/useBackdropPress.js +53 -0
  71. package/dist/Layer/usePressPin.d.ts +20 -10
  72. package/dist/Layer/usePressPin.d.ts.map +1 -1
  73. package/dist/Layer/usePressPin.js +66 -18
  74. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  75. package/dist/Lightbox/Lightbox.js +18 -0
  76. package/dist/MobileNav/MobileNav.js +0 -1
  77. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  78. package/dist/MultiSelector/hooks.js +2 -4
  79. package/dist/Outline/Outline.d.ts +2 -4
  80. package/dist/Outline/Outline.d.ts.map +1 -1
  81. package/dist/Outline/Outline.js +4 -52
  82. package/dist/Outline/useOutlineFromMarkdown.d.ts.map +1 -1
  83. package/dist/Outline/useOutlineFromMarkdown.js +1 -0
  84. package/dist/Outline/useScrollSpy.js +3 -3
  85. package/dist/PowerSearch/PowerSearch.d.ts +36 -3
  86. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  87. package/dist/PowerSearch/PowerSearch.js +571 -302
  88. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +100 -0
  89. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -0
  90. package/dist/PowerSearch/PowerSearchBottomSheet.js +1913 -0
  91. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +32 -1
  92. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  93. package/dist/PowerSearch/PowerSearchEditPopover.js +17 -6
  94. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +38 -3
  95. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  96. package/dist/PowerSearch/PowerSearchValueEditor.js +170 -61
  97. package/dist/PowerSearch/formatFilterValue.d.ts +7 -0
  98. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  99. package/dist/PowerSearch/formatFilterValue.js +11 -1
  100. package/dist/PowerSearch/formatTokenValue.d.ts +28 -0
  101. package/dist/PowerSearch/formatTokenValue.d.ts.map +1 -0
  102. package/dist/PowerSearch/formatTokenValue.js +160 -0
  103. package/dist/PowerSearch/powerSearchSheetUtils.d.ts +65 -0
  104. package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +1 -0
  105. package/dist/PowerSearch/powerSearchSheetUtils.js +168 -0
  106. package/dist/RadioList/RadioList.d.ts.map +1 -1
  107. package/dist/RadioList/RadioList.js +8 -2
  108. package/dist/RadioList/RadioListItem.d.ts +1 -1
  109. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  110. package/dist/RadioList/RadioListItem.js +5 -3
  111. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  112. package/dist/Resizable/ResizeHandle.js +118 -260
  113. package/dist/Resizable/resizeGesture.d.ts +91 -0
  114. package/dist/Resizable/resizeGesture.d.ts.map +1 -0
  115. package/dist/Resizable/resizeGesture.js +251 -0
  116. package/dist/SegmentedControl/SegmentedControl.d.ts +3 -1
  117. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  118. package/dist/SegmentedControl/SegmentedControl.js +115 -8
  119. package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
  120. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  121. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -0
  122. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  123. package/dist/SegmentedControl/SegmentedControlItem.js +8 -1
  124. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts +357 -0
  125. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -0
  126. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +1349 -0
  127. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts +70 -0
  128. package/dist/SegmentedControl/useSegmentedTouchTracking.d.ts.map +1 -0
  129. package/dist/SegmentedControl/useSegmentedTouchTracking.js +264 -0
  130. package/dist/Selector/Selector.d.ts.map +1 -1
  131. package/dist/Selector/Selector.js +96 -67
  132. package/dist/Selector/optionShape.d.ts +24 -0
  133. package/dist/Selector/optionShape.d.ts.map +1 -0
  134. package/dist/Selector/optionShape.js +48 -0
  135. package/dist/Selector/selectableOptionEntries.d.ts +20 -0
  136. package/dist/Selector/selectableOptionEntries.d.ts.map +1 -0
  137. package/dist/Selector/selectableOptionEntries.js +38 -0
  138. package/dist/Selector/utils.d.ts +2 -17
  139. package/dist/Selector/utils.d.ts.map +1 -1
  140. package/dist/Selector/utils.js +3 -51
  141. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  142. package/dist/SideNav/SideNavItem.js +3 -9
  143. package/dist/Slider/sliderTouchController.js +2 -2
  144. package/dist/Spinner/Spinner.d.ts +1 -1
  145. package/dist/Spinner/Spinner.d.ts.map +1 -1
  146. package/dist/Spinner/Spinner.js +60 -58
  147. package/dist/Switch/switchTouch.d.ts +0 -7
  148. package/dist/Switch/switchTouch.d.ts.map +1 -1
  149. package/dist/Switch/switchTouch.js +2 -10
  150. package/dist/Switch/useSwitchTouch.d.ts +2 -11
  151. package/dist/Switch/useSwitchTouch.d.ts.map +1 -1
  152. package/dist/Switch/useSwitchTouch.js +35 -93
  153. package/dist/Table/BaseTable.d.ts.map +1 -1
  154. package/dist/Table/BaseTable.js +29 -8
  155. package/dist/TextInput/TextInput.d.ts +2 -2
  156. package/dist/TextInput/TextInput.d.ts.map +1 -1
  157. package/dist/TextInput/TextInput.js +10 -7
  158. package/dist/Toast/Toast.d.ts +11 -1
  159. package/dist/Toast/Toast.d.ts.map +1 -1
  160. package/dist/Toast/Toast.js +30 -15
  161. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  162. package/dist/Toast/ToastViewport.js +94 -34
  163. package/dist/Toast/types.d.ts +9 -3
  164. package/dist/Toast/types.d.ts.map +1 -1
  165. package/dist/Token/Token.d.ts +2 -2
  166. package/dist/Token/Token.d.ts.map +1 -1
  167. package/dist/Token/Token.js +13 -4
  168. package/dist/Token/TokenNamingContext.d.ts +14 -0
  169. package/dist/Token/TokenNamingContext.d.ts.map +1 -0
  170. package/dist/Token/TokenNamingContext.js +23 -0
  171. package/dist/Tokenizer/Tokenizer.d.ts +46 -59
  172. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  173. package/dist/Tokenizer/Tokenizer.js +40 -57
  174. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +25 -28
  175. package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
  176. package/dist/Tokenizer/TokenizerBottomSheet.js +157 -54
  177. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts +3 -0
  178. package/dist/Tokenizer/TokenizerItemRemovableContext.d.ts.map +1 -0
  179. package/dist/Tokenizer/TokenizerItemRemovableContext.js +19 -0
  180. package/dist/Tooltip/Tooltip.d.ts +25 -30
  181. package/dist/Tooltip/Tooltip.d.ts.map +1 -1
  182. package/dist/Tooltip/Tooltip.js +24 -5
  183. package/dist/Tooltip/index.d.ts +1 -1
  184. package/dist/Tooltip/index.d.ts.map +1 -1
  185. package/dist/Tooltip/useTooltip.d.ts +45 -28
  186. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  187. package/dist/Tooltip/useTooltip.js +52 -59
  188. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  189. package/dist/TopNav/TopNavItem.js +4 -7
  190. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  191. package/dist/TopNav/TopNavMegaMenuItem.js +4 -7
  192. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  193. package/dist/TopNav/TopNavMenu.js +4 -7
  194. package/dist/Typeahead/TypeaheadItem.d.ts +2 -1
  195. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  196. package/dist/animator/createAnimator.d.ts +98 -0
  197. package/dist/animator/createAnimator.d.ts.map +1 -0
  198. package/dist/animator/createAnimator.js +259 -0
  199. package/dist/astryx.css +96 -7
  200. package/dist/gestures/GestureHost.d.ts +27 -1
  201. package/dist/gestures/GestureHost.d.ts.map +1 -1
  202. package/dist/gestures/GestureHost.js +157 -21
  203. package/dist/gestures/PressTracker.d.ts.map +1 -1
  204. package/dist/gestures/PressTracker.js +4 -3
  205. package/dist/hooks/actionGroupTracker.d.ts +7 -4
  206. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  207. package/dist/hooks/actionGroupTracker.js +19 -56
  208. package/dist/hooks/index.d.ts +1 -0
  209. package/dist/hooks/index.d.ts.map +1 -1
  210. package/dist/hooks/index.js +1 -0
  211. package/dist/hooks/pressGroupContext.d.ts +6 -0
  212. package/dist/hooks/pressGroupContext.d.ts.map +1 -0
  213. package/dist/hooks/pressGroupContext.js +20 -0
  214. package/dist/hooks/useActionGroupTracking.d.ts +53 -0
  215. package/dist/hooks/useActionGroupTracking.d.ts.map +1 -0
  216. package/dist/hooks/useActionGroupTracking.js +174 -0
  217. package/dist/hooks/useAnimator.d.ts +41 -0
  218. package/dist/hooks/useAnimator.d.ts.map +1 -0
  219. package/dist/hooks/useAnimator.js +70 -0
  220. package/dist/hooks/useAttachment.d.ts +1 -1
  221. package/dist/hooks/useAttachment.js +1 -1
  222. package/dist/hooks/useHighlightedOptionScroll.d.ts +11 -1
  223. package/dist/hooks/useHighlightedOptionScroll.d.ts.map +1 -1
  224. package/dist/hooks/useHighlightedOptionScroll.js +112 -11
  225. package/dist/hooks/useInputStatusIcon.d.ts +1 -1
  226. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  227. package/dist/hooks/useInputStatusIcon.js +2 -2
  228. package/dist/hooks/useLongPress.d.ts +6 -2
  229. package/dist/hooks/useLongPress.d.ts.map +1 -1
  230. package/dist/hooks/useLongPress.js +95 -15
  231. package/dist/hooks/useMediaQuery.d.ts +2 -1
  232. package/dist/hooks/useMediaQuery.d.ts.map +1 -1
  233. package/dist/hooks/useMediaQuery.js +7 -4
  234. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  235. package/dist/hooks/useMenuPress.js +4 -2
  236. package/dist/i18n/generated-english/InfoTip/InfoTip.generated.d.ts +8 -0
  237. package/dist/i18n/generated-english/InfoTip/InfoTip.generated.d.ts.map +1 -0
  238. package/dist/i18n/generated-english/InfoTip/InfoTip.generated.js +12 -0
  239. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.d.ts.map +1 -1
  240. package/dist/i18n/generated-english/PowerSearch/PowerSearch.generated.js +1 -0
  241. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +8 -0
  242. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -0
  243. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +42 -0
  244. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
  245. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +6 -0
  246. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +8 -0
  247. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +1 -0
  248. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +14 -0
  249. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.d.ts.map +1 -1
  250. package/dist/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.js +7 -1
  251. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  252. package/dist/i18n/generated-locales/en.generated.js +33 -1
  253. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  254. package/dist/i18n/generated-locales/pseudo.generated.js +33 -1
  255. package/dist/index.d.ts +2 -0
  256. package/dist/index.d.ts.map +1 -1
  257. package/dist/index.js +5 -0
  258. package/dist/internal/SheetConfirmation.d.ts +11 -0
  259. package/dist/internal/SheetConfirmation.d.ts.map +1 -0
  260. package/dist/internal/SheetConfirmation.js +92 -0
  261. package/dist/internal/indicatorPress.stylex.d.ts +20 -0
  262. package/dist/internal/indicatorPress.stylex.d.ts.map +1 -0
  263. package/dist/internal/indicatorPress.stylex.js +26 -0
  264. package/dist/internal/sheetDraft.d.ts +35 -0
  265. package/dist/internal/sheetDraft.d.ts.map +1 -0
  266. package/dist/internal/sheetDraft.js +65 -0
  267. package/dist/internal/touch-sheet.d.ts +21 -0
  268. package/dist/internal/touch-sheet.d.ts.map +1 -0
  269. package/dist/internal/touch-sheet.js +19 -0
  270. package/dist/internal/useDismissSearchOnTouchDrag.d.ts +24 -0
  271. package/dist/internal/useDismissSearchOnTouchDrag.d.ts.map +1 -0
  272. package/dist/internal/useDismissSearchOnTouchDrag.js +56 -0
  273. package/dist/motion/animator.d.ts +14 -14
  274. package/dist/motion/animator.d.ts.map +1 -1
  275. package/dist/motion/animator.js +41 -18
  276. package/dist/motion/reducedMotion.d.ts +24 -0
  277. package/dist/motion/reducedMotion.d.ts.map +1 -1
  278. package/dist/motion/reducedMotion.js +78 -1
  279. package/dist/motion/roles.d.ts +7 -1
  280. package/dist/motion/roles.d.ts.map +1 -1
  281. package/dist/motion/roles.js +20 -1
  282. package/dist/motion/uikit/alert.d.ts +41 -0
  283. package/dist/motion/uikit/alert.d.ts.map +1 -0
  284. package/dist/motion/uikit/alert.js +43 -0
  285. package/dist/motion/uikit/index.d.ts +6 -5
  286. package/dist/motion/uikit/index.d.ts.map +1 -1
  287. package/dist/motion/uikit/index.js +6 -5
  288. package/dist/motion/uikit/segmented.d.ts +55 -0
  289. package/dist/motion/uikit/segmented.d.ts.map +1 -0
  290. package/dist/motion/uikit/segmented.js +77 -0
  291. package/dist/motion/uikit/springs.d.ts +0 -18
  292. package/dist/motion/uikit/springs.d.ts.map +1 -1
  293. package/dist/motion/uikit/springs.js +5 -21
  294. package/dist/privateAttributes/pressGroupItem.stylex.d.ts +15 -0
  295. package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +1 -0
  296. package/dist/privateAttributes/pressGroupItem.stylex.js +23 -0
  297. package/dist/privateAttributes/segmentTitle.stylex.d.ts +5 -0
  298. package/dist/privateAttributes/segmentTitle.stylex.d.ts.map +1 -0
  299. package/dist/privateAttributes/segmentTitle.stylex.js +22 -0
  300. package/dist/utils/browserClickWait.d.ts +35 -0
  301. package/dist/utils/browserClickWait.d.ts.map +1 -0
  302. package/dist/utils/browserClickWait.js +123 -0
  303. package/dist/utils/clickGuard.d.ts +14 -1
  304. package/dist/utils/clickGuard.d.ts.map +1 -1
  305. package/dist/utils/clickGuard.js +46 -1
  306. package/dist/utils/dateParser.d.ts.map +1 -1
  307. package/dist/utils/dateParser.js +12 -0
  308. package/dist/utils/focusReturn.d.ts +15 -0
  309. package/dist/utils/focusReturn.d.ts.map +1 -1
  310. package/dist/utils/focusReturn.js +28 -0
  311. package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
  312. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  313. package/dist/utils/interactionOverlay.stylex.js +3 -2
  314. package/dist/utils/pressFeedback.d.ts.map +1 -1
  315. package/dist/utils/pressFeedback.js +19 -6
  316. package/dist/utils/triggerFocus.d.ts +36 -0
  317. package/dist/utils/triggerFocus.d.ts.map +1 -0
  318. package/dist/utils/triggerFocus.js +65 -0
  319. package/locales/en.json +128 -0
  320. package/locales/pseudo.json +96 -0
  321. package/package.json +18 -8
  322. package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
  323. package/src/AlertDialog/AlertDialog.tsx +37 -5
  324. package/src/AppShell/useCloseMobileDrawerOnActivate.ts +52 -0
  325. package/src/BottomSheet/BottomSheet.tsx +95 -10
  326. package/src/BottomSheet/BottomSheetPanel.tsx +40 -5
  327. package/src/BottomSheet/bottomSheetStack.ts +8 -4
  328. package/src/BottomSheet/useSheetGestures.ts +3 -1
  329. package/src/Breadcrumbs/BreadcrumbItem.tsx +13 -3
  330. package/src/Button/Button.tsx +63 -5
  331. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  332. package/src/CheckboxInput/CheckboxInput.tsx +36 -9
  333. package/src/CheckboxList/CheckboxList.doc.mjs +4 -2
  334. package/src/CheckboxList/CheckboxList.tsx +6 -0
  335. package/src/CheckboxList/CheckboxListContext.tsx +6 -0
  336. package/src/CheckboxList/CheckboxListItem.tsx +11 -2
  337. package/src/ComplexSelector/ComplexSelector.tsx +20 -5
  338. package/src/ContextMenu/ContextMenu.tsx +2 -2
  339. package/src/DateInput/DateInput.tsx +8 -2
  340. package/src/DateInput/MonthScroller.tsx +18 -22
  341. package/src/DateInput/Wheel.tsx +30 -32
  342. package/src/DateTimeInput/DateTimeInput.tsx +8 -2
  343. package/src/Dialog/Dialog.tsx +54 -45
  344. package/src/Drawer/Drawer.tsx +28 -11
  345. package/src/Drawer/useDrawerDialogPresence.ts +5 -1
  346. package/src/Field/Field.tsx +2 -2
  347. package/src/Field/PanelSearchInput.tsx +11 -0
  348. package/src/HoverCard/HoverCard.doc.mjs +4 -2
  349. package/src/HoverCard/HoverCard.tsx +35 -4
  350. package/src/HoverCard/useHoverCard.doc.mjs +178 -36
  351. package/src/HoverCard/useHoverCard.tsx +46 -52
  352. package/src/Icon/Icon.doc.mjs +49 -8
  353. package/src/Icon/Icon.tsx +15 -7
  354. package/src/InfoTip/InfoTip.doc.mjs +192 -0
  355. package/src/InfoTip/InfoTip.tsx +186 -0
  356. package/src/InfoTip/index.ts +14 -0
  357. package/src/Layer/layerAnimations.stylex.ts +24 -16
  358. package/src/Layer/useBackdropPress.ts +81 -0
  359. package/src/Layer/usePressPin.ts +86 -26
  360. package/src/Lightbox/Lightbox.tsx +18 -0
  361. package/src/MobileNav/MobileNav.tsx +1 -1
  362. package/src/MultiSelector/hooks.ts +2 -4
  363. package/src/Outline/Outline.doc.mjs +177 -51
  364. package/src/Outline/Outline.tsx +5 -57
  365. package/src/Outline/useOutlineFromMarkdown.doc.mjs +157 -0
  366. package/src/Outline/useOutlineFromMarkdown.ts +1 -0
  367. package/src/Outline/useScrollSpy.ts +3 -3
  368. package/src/PowerSearch/PowerSearch.doc.mjs +55 -6
  369. package/src/PowerSearch/PowerSearch.tsx +798 -324
  370. package/src/PowerSearch/PowerSearchBottomSheet.tsx +2515 -0
  371. package/src/PowerSearch/PowerSearchEditPopover.tsx +28 -6
  372. package/src/PowerSearch/PowerSearchValueEditor.tsx +203 -38
  373. package/src/PowerSearch/formatFilterValue.ts +18 -1
  374. package/src/PowerSearch/formatTokenValue.ts +215 -0
  375. package/src/PowerSearch/powerSearchSheetUtils.ts +224 -0
  376. package/src/RadioList/RadioList.doc.mjs +4 -2
  377. package/src/RadioList/RadioList.tsx +7 -2
  378. package/src/RadioList/RadioListItem.tsx +5 -3
  379. package/src/Resizable/ResizeHandle.tsx +133 -328
  380. package/src/Resizable/resizeGesture.ts +368 -0
  381. package/src/SegmentedControl/SegmentedControl.doc.mjs +3 -3
  382. package/src/SegmentedControl/SegmentedControl.tsx +179 -5
  383. package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
  384. package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
  385. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +1640 -0
  386. package/src/SegmentedControl/useSegmentedTouchTracking.ts +308 -0
  387. package/src/Selector/Selector.tsx +136 -89
  388. package/src/Selector/optionShape.ts +64 -0
  389. package/src/Selector/selectableOptionEntries.ts +39 -0
  390. package/src/Selector/utils.ts +9 -68
  391. package/src/SideNav/SideNavItem.tsx +3 -7
  392. package/src/Slider/sliderTouchController.ts +2 -2
  393. package/src/Spinner/Spinner.tsx +77 -63
  394. package/src/Switch/switchTouch.ts +2 -10
  395. package/src/Switch/useSwitchTouch.ts +38 -104
  396. package/src/Table/BaseTable.tsx +46 -12
  397. package/src/TextInput/TextInput.tsx +11 -10
  398. package/src/Toast/Toast.doc.mjs +4 -3
  399. package/src/Toast/Toast.tsx +57 -32
  400. package/src/Toast/ToastViewport.tsx +121 -35
  401. package/src/Toast/types.ts +9 -3
  402. package/src/Token/Token.tsx +15 -4
  403. package/src/Token/TokenNamingContext.ts +28 -0
  404. package/src/Tokenizer/Tokenizer.doc.mjs +16 -0
  405. package/src/Tokenizer/Tokenizer.tsx +89 -85
  406. package/src/Tokenizer/TokenizerBottomSheet.tsx +219 -93
  407. package/src/Tokenizer/TokenizerItemRemovableContext.ts +24 -0
  408. package/src/Tooltip/Tooltip.doc.mjs +123 -54
  409. package/src/Tooltip/Tooltip.tsx +36 -39
  410. package/src/Tooltip/index.ts +0 -1
  411. package/src/Tooltip/useTooltip.doc.mjs +171 -33
  412. package/src/Tooltip/useTooltip.tsx +98 -86
  413. package/src/TopNav/TopNavItem.tsx +6 -5
  414. package/src/TopNav/TopNavMegaMenuItem.tsx +6 -5
  415. package/src/TopNav/TopNavMenu.tsx +6 -5
  416. package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
  417. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  418. package/src/animator/createAnimator.doc.mjs +153 -0
  419. package/src/animator/createAnimator.ts +324 -0
  420. package/src/gestures/GestureHost.ts +198 -20
  421. package/src/gestures/PressTracker.ts +4 -3
  422. package/src/gestures/README.md +7 -1
  423. package/src/hooks/actionGroupTracker.ts +32 -61
  424. package/src/hooks/index.ts +2 -0
  425. package/src/hooks/pressGroupContext.ts +21 -0
  426. package/src/hooks/useActionGroupTracking.ts +224 -0
  427. package/src/hooks/useAnimator.doc.mjs +109 -0
  428. package/src/hooks/useAnimator.ts +67 -0
  429. package/src/hooks/useAttachment.ts +1 -1
  430. package/src/hooks/useHighlightedOptionScroll.ts +123 -15
  431. package/src/hooks/useInputStatusIcon.tsx +3 -2
  432. package/src/hooks/useLongPress.doc.mjs +2 -2
  433. package/src/hooks/useLongPress.ts +136 -15
  434. package/src/hooks/useMediaQuery.doc.mjs +2 -2
  435. package/src/hooks/useMediaQuery.ts +13 -4
  436. package/src/hooks/useMenuPress.ts +8 -2
  437. package/src/i18n/generated-english/InfoTip/InfoTip.generated.ts +13 -0
  438. package/src/i18n/generated-english/PowerSearch/PowerSearch.generated.ts +1 -0
  439. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +43 -0
  440. package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +6 -0
  441. package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +15 -0
  442. package/src/i18n/generated-english/Tokenizer/TokenizerBottomSheet.generated.ts +7 -1
  443. package/src/i18n/generated-locales/en.generated.ts +43 -0
  444. package/src/i18n/generated-locales/pseudo.generated.ts +44 -0
  445. package/src/index.ts +14 -0
  446. package/src/internal/SheetConfirmation.tsx +101 -0
  447. package/src/internal/indicatorPress.stylex.ts +24 -0
  448. package/src/internal/sheetDraft.ts +80 -0
  449. package/src/internal/touch-sheet.ts +21 -0
  450. package/src/internal/useDismissSearchOnTouchDrag.ts +69 -0
  451. package/src/motion/MEASUREMENTS.md +7 -0
  452. package/src/motion/README.md +9 -5
  453. package/src/motion/animator.ts +61 -29
  454. package/src/motion/reducedMotion.ts +79 -1
  455. package/src/motion/roles.ts +20 -1
  456. package/src/motion/uikit/alert.ts +43 -0
  457. package/src/motion/uikit/index.ts +5 -5
  458. package/src/motion/uikit/segmented.ts +74 -0
  459. package/src/motion/uikit/springs.ts +5 -21
  460. package/src/privateAttributes/pressGroupItem.stylex.ts +23 -0
  461. package/src/privateAttributes/segmentTitle.stylex.ts +22 -0
  462. package/src/utils/browserClickWait.ts +133 -0
  463. package/src/utils/clickGuard.ts +64 -1
  464. package/src/utils/dateParser.ts +16 -0
  465. package/src/utils/focusReturn.ts +30 -0
  466. package/src/utils/interactionOverlay.stylex.ts +3 -2
  467. package/src/utils/pressFeedback.ts +19 -6
  468. package/src/utils/triggerFocus.ts +80 -0
  469. package/dist/internal/tokenizer-presentation.d.ts +0 -21
  470. package/dist/internal/tokenizer-presentation.d.ts.map +0 -1
  471. package/dist/internal/tokenizer-presentation.js +0 -21
  472. package/src/internal/tokenizer-presentation.ts +0 -25
@@ -0,0 +1,308 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file useSegmentedTouchTracking.ts
7
+ * @input The radiogroup element, the selection motion controller and whether
8
+ * the control is enabled
9
+ * @output useSegmentedTouchTracking (a press of any pointer, landing to
10
+ * lift, and its ClickWait), and segmentTargetAt, measureSegments and
11
+ * liftTargetAt, the segment a pointer counts for
12
+ * @position Internal to SegmentedControl; drives useSegmentedSelectionMotion.ts
13
+ *
14
+ * SYNC: When modified, update:
15
+ * - /packages/core/src/SegmentedControl/useSegmentedSelectionMotion.ts
16
+ * - /packages/core/src/SegmentedControl/__tests__/SegmentedControl.interaction.browser.spec.ts
17
+ */
18
+
19
+ import {useRef, useState, type RefObject} from 'react';
20
+ import {PanGestureRecognizer} from '../gestures/PanGestureRecognizer';
21
+ import {useGestureRecognizer} from '../gestures/useGestureRecognizer';
22
+ import {usePressTracker} from '../gestures/usePressTracker';
23
+ import {
24
+ createActionGroupTracker,
25
+ type ActionGroupTarget,
26
+ type ActionGroupTracker,
27
+ } from '../hooks/actionGroupTracker';
28
+ import {lockSelection} from '../gestures/selectionLock';
29
+ import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
30
+ import {awaitBrowserClick, clickForLift} from '../utils/browserClickWait';
31
+ import {
32
+ SEGMENT_SELECTOR,
33
+ type SegmentedSelectionMotion,
34
+ } from './useSegmentedSelectionMotion';
35
+
36
+ /**
37
+ * A tap's click still to come. Until it runs, a focus move the tap causes
38
+ * selects nothing. A key press, the next press or a cancel ends the wait.
39
+ */
40
+ export interface ClickWait {
41
+ readonly isAwaiting: () => boolean;
42
+ readonly end: () => void;
43
+ }
44
+
45
+ /** A ref to nothing: hooks given it attach nothing. */
46
+ const DETACHED: RefObject<HTMLElement | null> = {current: null};
47
+
48
+ /** The control's box as measured when the finger landed: viewport px. */
49
+ export interface ControlBounds {
50
+ readonly left: number;
51
+ readonly top: number;
52
+ readonly right: number;
53
+ readonly bottom: number;
54
+ }
55
+
56
+ /** A segment as measured when the finger landed: its viewport span, px. */
57
+ export interface SegmentSpan {
58
+ readonly element: HTMLElement;
59
+ readonly left: number;
60
+ readonly right: number;
61
+ }
62
+
63
+ /** Every segment of `track`, disabled ones included, measured once. */
64
+ export function measureSegments(track: HTMLElement): SegmentSpan[] {
65
+ return Array.from(
66
+ track.querySelectorAll<HTMLElement>(SEGMENT_SELECTOR),
67
+ element => {
68
+ const rect = element.getBoundingClientRect();
69
+ return {element, left: rect.left, right: rect.right};
70
+ },
71
+ );
72
+ }
73
+
74
+ function isDisabled(element: HTMLElement): boolean {
75
+ return (
76
+ element.hasAttribute('disabled') ||
77
+ element.getAttribute('aria-disabled') === 'true'
78
+ );
79
+ }
80
+
81
+ /**
82
+ * The segment a finger at viewport `x` counts for: the one under it, else the
83
+ * nearest. A gap between two segments splits at its middle, as UIKit's
84
+ * segments share an edge. Past the ends, the end segment counts. The result is
85
+ * null when that segment is disabled.
86
+ */
87
+ export function segmentTargetAt(
88
+ x: number,
89
+ segments: ReadonlyArray<SegmentSpan>,
90
+ ): HTMLElement | null {
91
+ let best: SegmentSpan | null = null;
92
+ let distance = Infinity;
93
+ for (const segment of segments) {
94
+ const d = Math.max(segment.left - x, 0, x - segment.right);
95
+ if (d < distance) {
96
+ distance = d;
97
+ best = segment;
98
+ }
99
+ }
100
+ return best != null && !isDisabled(best.element) ? best.element : null;
101
+ }
102
+
103
+ /**
104
+ * The segment a lift at viewport (`x`, `y`) commits: inside the control's
105
+ * `bounds`, the one `segmentTargetAt` counts it for; outside them, none. A
106
+ * press that slides off cancels, because the control selects rather than
107
+ * toggles: a second press would not undo it.
108
+ */
109
+ export function liftTargetAt(
110
+ x: number,
111
+ y: number,
112
+ bounds: ControlBounds,
113
+ segments: ReadonlyArray<SegmentSpan>,
114
+ ): HTMLElement | null {
115
+ if (
116
+ x < bounds.left ||
117
+ x > bounds.right ||
118
+ y < bounds.top ||
119
+ y > bounds.bottom
120
+ ) {
121
+ return null;
122
+ }
123
+ return segmentTargetAt(x, segments);
124
+ }
125
+
126
+ const NO_BOUNDS: ControlBounds = {left: 0, top: 0, right: -1, bottom: -1};
127
+
128
+ /**
129
+ * Tracks a press on the control as UIKit's segmented control does. The press
130
+ * tracker reports the press from landing to lift. It never claims the
131
+ * pointer, so a vertical drag still scrolls the page. A horizontal pan
132
+ * carries the lens. It claims the pointer only past its slop, and the lift
133
+ * still ends the press.
134
+ *
135
+ * The lift decides the segment, wherever the press began. Inside the control,
136
+ * the segment under the pointer counts, else the nearest one. Outside the
137
+ * control, nothing counts.
138
+ *
139
+ * A tap gets the browser's own click. A drag gets no click from the browser,
140
+ * so the tracker clicks the segment at the lift. Either way the item's
141
+ * `onClick` runs once and can cancel the selection. A mouse and a pen are
142
+ * tracked as a finger is. Keyboard and assistive technology keep the click.
143
+ */
144
+ export function useSegmentedTouchTracking(
145
+ trackRef: RefObject<HTMLElement | null>,
146
+ motion: SegmentedSelectionMotion,
147
+ isEnabled: boolean,
148
+ ): ClickWait {
149
+ /** The horizontal pan began for this touch: it is a drag, not a tap. */
150
+ const isDraggingRef = useRef(false);
151
+ /** The segments as the finger found them (disabled ones too). */
152
+ const segmentsRef = useRef<SegmentSpan[]>([]);
153
+ /** The control's box as the finger found it: a lift outside it cancels. */
154
+ const boundsRef = useRef<ControlBounds>(NO_BOUNDS);
155
+ const trackerRef = useRef<ActionGroupTracker | null>(null);
156
+ const tracker = (trackerRef.current ??= createActionGroupTracker(() => ({
157
+ roots: () => [trackRef.current],
158
+ itemSelector: SEGMENT_SELECTOR,
159
+ highlightExpansion: 0,
160
+ // The press controller writes the shared press paint on the touched
161
+ // segment, as everywhere under a finger. The tracker paints nothing of its
162
+ // own. A held lens shows the selected face.
163
+ paintsHighlight: false,
164
+ resolveTarget: (
165
+ x: number,
166
+ y: number,
167
+ _targets: ReadonlyArray<ActionGroupTarget>,
168
+ ) => liftTargetAt(x, y, boundsRef.current, segmentsRef.current),
169
+ onActivated: (item, cancelled) => {
170
+ const track = trackRef.current;
171
+ const value = item.dataset.value;
172
+ if (track != null && value != null) {
173
+ motion.clicked(track, value, cancelled);
174
+ }
175
+ },
176
+ swallowsNativeClick: true,
177
+ })));
178
+ /** Click waits still running: an unmount ends them. */
179
+ const pendingClicksRef = useRef<Set<() => void>>(new Set());
180
+ /**
181
+ * A tap lifted and its click has not run. After a direct click, the
182
+ * browser's late copy can still move focus, so the wait then lasts until the
183
+ * next key or press.
184
+ */
185
+ const clickToComeRef = useRef(false);
186
+ const [clickWait] = useState<ClickWait>(() => ({
187
+ isAwaiting: () => clickToComeRef.current,
188
+ end: () => {
189
+ clickToComeRef.current = false;
190
+ },
191
+ }));
192
+ // Ends the tracker's window listeners and any click wait on unmount.
193
+ useIsomorphicLayoutEffect(() => {
194
+ const pendingClicks = pendingClicksRef.current;
195
+ return () => {
196
+ tracker.dispose();
197
+ clickWait.end();
198
+ for (const end of [...pendingClicks]) {
199
+ end();
200
+ }
201
+ };
202
+ }, [tracker, clickWait]);
203
+
204
+ // Every pointer type is tracked. A disabled control attaches nothing.
205
+ const attachRef = isEnabled ? trackRef : DETACHED;
206
+ usePressTracker(attachRef, {
207
+ isEnabled: () => isEnabled,
208
+ onControlEvent: (event, touch) => {
209
+ const track = trackRef.current;
210
+ if (track == null) {
211
+ return;
212
+ }
213
+ switch (event) {
214
+ case 'touchDown': {
215
+ clickWait.end();
216
+ isDraggingRef.current = false;
217
+ segmentsRef.current = measureSegments(track);
218
+ const rect = track.getBoundingClientRect();
219
+ boundsRef.current = {
220
+ left: rect.left,
221
+ top: rect.top,
222
+ right: rect.right,
223
+ bottom: rect.bottom,
224
+ };
225
+ tracker.begin(touch.x, touch.y);
226
+ motion.begin(track, tracker.beganOn, touch.pointerType === 'touch');
227
+ break;
228
+ }
229
+ case 'touchUpInside':
230
+ case 'touchUpOutside': {
231
+ // The lift decides, wherever the press began. A lift outside the
232
+ // control commits nothing.
233
+ tracker.move(touch.x, touch.y);
234
+ const lifted = tracker.highlighted;
235
+ const isTap =
236
+ event === 'touchUpInside' &&
237
+ !isDraggingRef.current &&
238
+ lifted != null &&
239
+ lifted === tracker.beganOn;
240
+ const value = motion.release(lifted);
241
+ if (value != null && isTap && lifted != null) {
242
+ // A tap: the browser's own click commits it.
243
+ tracker.cancel();
244
+ clickToComeRef.current = true;
245
+ const pending = pendingClicksRef.current;
246
+ const endWait = awaitBrowserClick(lifted, {
247
+ onClicked: isCancelled => {
248
+ pending.delete(endWait);
249
+ clickWait.end();
250
+ motion.clicked(track, value, isCancelled);
251
+ },
252
+ onNoClick: () => {
253
+ pending.delete(endWait);
254
+ motion.clicked(track, value, clickForLift(lifted));
255
+ },
256
+ });
257
+ pending.add(endWait);
258
+ } else if (value != null) {
259
+ // No click comes from the browser for a drag: the segment's own
260
+ // click, inside the lift's event; its onClick may veto it.
261
+ tracker.end();
262
+ } else {
263
+ tracker.cancel();
264
+ }
265
+ break;
266
+ }
267
+ case 'touchCancel':
268
+ clickWait.end();
269
+ tracker.cancel();
270
+ motion.cancel();
271
+ break;
272
+ case 'touchDragInside':
273
+ case 'touchDragOutside':
274
+ case 'touchDragEnter':
275
+ case 'touchDragExit':
276
+ // The horizontal pan carries the lens.
277
+ break;
278
+ }
279
+ },
280
+ });
281
+ useGestureRecognizer(
282
+ attachRef,
283
+ () =>
284
+ new PanGestureRecognizer({
285
+ name: 'segmented-control-drag',
286
+ direction: 'horizontal',
287
+ cancelsTouchesInView: false,
288
+ // No title is selectable under a held finger, as in UIKit. A hold on
289
+ // the track does something: a drag carries the lens and commits where
290
+ // it lifts. So this recognizer brings the lock, and the host turns off
291
+ // text selection and the callout on the track while a finger is down.
292
+ selectionLock: lockSelection,
293
+ }),
294
+ recognizer => {
295
+ if (recognizer.state === 'began') {
296
+ isDraggingRef.current = true;
297
+ }
298
+ if (recognizer.state === 'began' || recognizer.state === 'changed') {
299
+ const {x, y} = recognizer.location();
300
+ // The lens chases the finger, then the tracker moves the highlight.
301
+ motion.drag(x);
302
+ tracker.move(x, y);
303
+ }
304
+ },
305
+ {isEnabled},
306
+ );
307
+ return clickWait;
308
+ }
@@ -20,6 +20,7 @@
20
20
  */
21
21
 
22
22
  import React, {
23
+ memo,
23
24
  use,
24
25
  useCallback,
25
26
  useEffect,
@@ -37,6 +38,7 @@ import {useTooltip} from '../Tooltip';
37
38
  import {Icon, renderIconSlot, type IconType} from '../Icon';
38
39
  import {useIndicator} from '../Indicator';
39
40
  import type {IndicatorPosition} from '../Indicator';
41
+ import type {IndicatorComponent, IndicatorMap} from '../Indicator/types';
40
42
  import type {IconName} from '../Icon';
41
43
  import {
42
44
  Field,
@@ -66,13 +68,8 @@ import {
66
68
  borderVars,
67
69
  } from '../theme/tokens.stylex';
68
70
  import type {SelectorOptionType, SelectorOptionData} from './types';
69
- import {
70
- isOptionData,
71
- isDivider,
72
- isSection,
73
- normalizeOption,
74
- getSelectableOptions,
75
- } from './utils';
71
+ import {isOptionData, isDivider, isSection, normalizeOption} from './utils';
72
+ import {getSelectableOptionEntries} from './selectableOptionEntries';
76
73
  import {useCombobox, useSelectedItemOffset} from './hooks';
77
74
  import {useMenuPress} from '../hooks/useMenuPress';
78
75
  import {useMenuOverflow} from '../DropdownMenu/useMenuOverflow';
@@ -917,6 +914,108 @@ function DefaultOption({option}: {option: SelectorOptionData}) {
917
914
  );
918
915
  }
919
916
 
917
+ interface SelectorOptionRowProps {
918
+ item: SelectorOptionData;
919
+ flatIndex: number;
920
+ id: string;
921
+ isHighlighted: boolean;
922
+ isSelected: boolean;
923
+ size: SelectorSize;
924
+ indicatorPosition: IndicatorPosition;
925
+ renderOption: ((option: SelectorOptionData) => ReactNode) | undefined;
926
+ SelectionMark: IndicatorComponent<IndicatorMap['check']>;
927
+ onSelect: (item: SelectorOptionData) => void;
928
+ onHover: (item: SelectorOptionData, flatIndex: number) => void;
929
+ }
930
+
931
+ /**
932
+ * Keep each prop stable across renders that do not change this row. A new
933
+ * `onSelect` or `renderOption` re-renders every row.
934
+ */
935
+ const SelectorOptionRow = memo(function SelectorOptionRow({
936
+ item,
937
+ flatIndex,
938
+ id,
939
+ isHighlighted,
940
+ isSelected,
941
+ size,
942
+ indicatorPosition,
943
+ renderOption,
944
+ SelectionMark,
945
+ onSelect,
946
+ onHover,
947
+ }: SelectorOptionRowProps) {
948
+ /*
949
+ * Rendered UNCONDITIONALLY, with the state passed down: the default
950
+ * check draws nothing when unchecked, but a theme that replaces the
951
+ * `check` indicator with a radio needs the unselected state to draw
952
+ * its empty circle. `{isSelected && …}` would make that impossible.
953
+ *
954
+ * `selector-check` stays the stable target for the mark's position
955
+ * in the row; the indicator owns what the mark looks like.
956
+ */
957
+ const mark = (
958
+ <span {...stylex.props(styles.itemMarkColumn)}>
959
+ <SelectionMark
960
+ state={isSelected ? 'checked' : 'unchecked'}
961
+ size="sm"
962
+ isDisabled={item.disabled ?? false}
963
+ {...themeProps('selector-check')}
964
+ />
965
+ </span>
966
+ );
967
+
968
+ const optionContent = (
969
+ <span {...stylex.props(styles.itemContent)}>
970
+ {renderOption ? renderOption(item) : <DefaultOption option={item} />}
971
+ </span>
972
+ );
973
+
974
+ return (
975
+ <div
976
+ id={id}
977
+ role="option"
978
+ aria-selected={isSelected}
979
+ aria-disabled={item.disabled}
980
+ onClick={() => onSelect(item)}
981
+ onMouseEnter={() => onHover(item, flatIndex)}
982
+ {...mergeProps(
983
+ // Stable theme target on the option row itself, mirroring
984
+ // `multi-selector-option`: it carries the row's size and runtime
985
+ // state so a theme can express "selected option at large" or
986
+ // restyle a given row density without structural selectors. The
987
+ // row's padding is split across a base and a per-size override (the
988
+ // default `md` trims the block axis), so `size` is what a theme
989
+ // needs to reach it. Named `-option-row` because `selector-option`
990
+ // is the public SelectorOption content primitive, not this row.
991
+ themeProps('selector-option-row', {
992
+ size,
993
+ selected: isSelected ? 'selected' : null,
994
+ disabled: item.disabled ? 'disabled' : null,
995
+ }),
996
+ stylex.props(
997
+ styles.item,
998
+ itemSizeStyles[size],
999
+ isHighlighted && styles.itemHighlighted,
1000
+ isSelected && styles.itemSelected,
1001
+ item.disabled && styles.itemDisabled,
1002
+ ),
1003
+ )}>
1004
+ {indicatorPosition === 'start' ? (
1005
+ <>
1006
+ {mark}
1007
+ {optionContent}
1008
+ </>
1009
+ ) : (
1010
+ <>
1011
+ {optionContent}
1012
+ {mark}
1013
+ </>
1014
+ )}
1015
+ </div>
1016
+ );
1017
+ });
1018
+
920
1019
  // Case-insensitive substring match for a single option. The one predicate used
921
1020
  // by both the flat filter (count + keyboard nav) and the grouped renderer, so
922
1021
  // what is shown while searching stays in lockstep with the announced count.
@@ -1118,10 +1217,19 @@ export function Selector<T extends SelectorOptionType>(
1118
1217
  : ariaLabelledBy;
1119
1218
 
1120
1219
  // Flatten options for keyboard navigation
1121
- const selectableItems = useMemo(
1122
- () => getSelectableOptions(options),
1220
+ const selectableEntries = useMemo(
1221
+ () => getSelectableOptionEntries(options),
1123
1222
  [options],
1124
1223
  );
1224
+ const selectableItems = useMemo(
1225
+ () => selectableEntries.map(entry => entry.item),
1226
+ [selectableEntries],
1227
+ );
1228
+ // Rows need the same item object on every render, or their memo fails.
1229
+ const normalizedOf = useMemo(
1230
+ () => new Map(selectableEntries.map(entry => [entry.source, entry.item])),
1231
+ [selectableEntries],
1232
+ );
1125
1233
 
1126
1234
  // `action` is declared on the shared option type for `MultiSelector`;
1127
1235
  // this listbox has not adopted it, so it renders the option without the
@@ -1613,84 +1721,22 @@ export function Selector<T extends SelectorOptionType>(
1613
1721
 
1614
1722
  // Render an individual item
1615
1723
  const renderItem = useCallback(
1616
- (item: SelectorOptionData, flatIndex: number) => {
1617
- const isHighlighted = flatIndex === highlightedIndex;
1618
- const isSelected = item.value === normalizedValue;
1619
-
1620
- /*
1621
- * Rendered UNCONDITIONALLY, with the state passed down: the default
1622
- * check draws nothing when unchecked, but a theme that replaces the
1623
- * `check` indicator with a radio needs the unselected state to draw
1624
- * its empty circle. `{isSelected && …}` would make that impossible.
1625
- *
1626
- * `selector-check` stays the stable target for the mark's position
1627
- * in the row; the indicator owns what the mark looks like.
1628
- */
1629
- const mark = (
1630
- <span {...stylex.props(styles.itemMarkColumn)}>
1631
- <SelectionMark
1632
- state={isSelected ? 'checked' : 'unchecked'}
1633
- size="sm"
1634
- isDisabled={item.disabled ?? false}
1635
- {...themeProps('selector-check')}
1636
- />
1637
- </span>
1638
- );
1639
-
1640
- const optionContent = (
1641
- <span {...stylex.props(styles.itemContent)}>
1642
- {renderOption ? renderOption(item) : <DefaultOption option={item} />}
1643
- </span>
1644
- );
1645
-
1646
- const content =
1647
- indicatorPosition === 'start' ? (
1648
- <>
1649
- {mark}
1650
- {optionContent}
1651
- </>
1652
- ) : (
1653
- <>
1654
- {optionContent}
1655
- {mark}
1656
- </>
1657
- );
1658
-
1659
- return (
1660
- <div
1661
- key={item.value}
1662
- id={getItemId(flatIndex)}
1663
- role="option"
1664
- aria-selected={isSelected}
1665
- aria-disabled={item.disabled}
1666
- onClick={() => onItemSelect(item)}
1667
- onMouseEnter={() => onItemMouseEnter(item, flatIndex)}
1668
- {...mergeProps(
1669
- // Stable theme target on the option row itself, mirroring
1670
- // `multi-selector-option`: it carries the row's size and runtime
1671
- // state so a theme can express "selected option at large" or
1672
- // restyle a given row density without structural selectors. The
1673
- // row's padding is split across a base and a per-size override (the
1674
- // default `md` trims the block axis), so `size` is what a theme
1675
- // needs to reach it. Named `-option-row` because `selector-option`
1676
- // is the public SelectorOption content primitive, not this row.
1677
- themeProps('selector-option-row', {
1678
- size,
1679
- selected: isSelected ? 'selected' : null,
1680
- disabled: item.disabled ? 'disabled' : null,
1681
- }),
1682
- stylex.props(
1683
- styles.item,
1684
- itemSizeStyles[size],
1685
- isHighlighted && styles.itemHighlighted,
1686
- isSelected && styles.itemSelected,
1687
- item.disabled && styles.itemDisabled,
1688
- ),
1689
- )}>
1690
- {content}
1691
- </div>
1692
- );
1693
- },
1724
+ (item: SelectorOptionData, flatIndex: number) => (
1725
+ <SelectorOptionRow
1726
+ key={item.value}
1727
+ item={item}
1728
+ flatIndex={flatIndex}
1729
+ id={getItemId(flatIndex)}
1730
+ isHighlighted={flatIndex === highlightedIndex}
1731
+ isSelected={item.value === normalizedValue}
1732
+ size={size}
1733
+ indicatorPosition={indicatorPosition}
1734
+ renderOption={renderOption}
1735
+ SelectionMark={SelectionMark}
1736
+ onSelect={onItemSelect}
1737
+ onHover={onItemMouseEnter}
1738
+ />
1739
+ ),
1694
1740
  [
1695
1741
  renderOption,
1696
1742
  indicatorPosition,
@@ -1757,7 +1803,7 @@ export function Selector<T extends SelectorOptionType>(
1757
1803
  } else if (isSection(option)) {
1758
1804
  const sectionItems: ReactNode[] = [];
1759
1805
  for (const opt of option.options) {
1760
- const normalized = normalizeOption(opt);
1806
+ const normalized = normalizedOf.get(opt) ?? normalizeOption(opt);
1761
1807
  if (isSearching && !optionMatchesQuery(normalized, searchQuery)) {
1762
1808
  continue;
1763
1809
  }
@@ -1790,7 +1836,7 @@ export function Selector<T extends SelectorOptionType>(
1790
1836
  </div>,
1791
1837
  );
1792
1838
  } else if (isOptionData(option)) {
1793
- const normalized = normalizeOption(option);
1839
+ const normalized = normalizedOf.get(option) ?? normalizeOption(option);
1794
1840
  if (isSearching && !optionMatchesQuery(normalized, searchQuery)) {
1795
1841
  continue;
1796
1842
  }
@@ -1802,6 +1848,7 @@ export function Selector<T extends SelectorOptionType>(
1802
1848
  return elements;
1803
1849
  }, [
1804
1850
  options,
1851
+ normalizedOf,
1805
1852
  renderItem,
1806
1853
  hasSearch,
1807
1854
  searchQuery,
@@ -0,0 +1,64 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file optionShape.ts
5
+ * @input Selector option values
6
+ * @output The option type guards and `normalizeOption`
7
+ * @position Internal; re-exported by utils.ts, the public entry point
8
+ */
9
+
10
+ import type {
11
+ SelectorOptionType,
12
+ SelectorOptionData,
13
+ SelectorDivider,
14
+ SelectorSection,
15
+ } from './types';
16
+
17
+ /**
18
+ * Type guard: check if option is a selectable option (string or OptionData)
19
+ */
20
+ export function isOptionData(
21
+ option: SelectorOptionType,
22
+ ): option is SelectorOptionData | string {
23
+ if (typeof option === 'string') {
24
+ return true;
25
+ }
26
+ return !('type' in option);
27
+ }
28
+
29
+ /**
30
+ * Type guard: check if option is a divider
31
+ */
32
+ export function isDivider(
33
+ option: SelectorOptionType,
34
+ ): option is SelectorDivider {
35
+ return (
36
+ typeof option === 'object' && 'type' in option && option.type === 'divider'
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Type guard: check if option is a section
42
+ */
43
+ export function isSection(
44
+ option: SelectorOptionType,
45
+ ): option is SelectorSection {
46
+ return (
47
+ typeof option === 'object' && 'type' in option && option.type === 'section'
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Normalize string or option to consistent shape
53
+ */
54
+ export function normalizeOption(
55
+ option: string | SelectorOptionData,
56
+ ): SelectorOptionData {
57
+ if (typeof option === 'string') {
58
+ return {value: option, label: option};
59
+ }
60
+ return {
61
+ ...option,
62
+ label: option.label ?? option.value,
63
+ };
64
+ }
@@ -0,0 +1,39 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file selectableOptionEntries.ts
5
+ * @input Selector options, with sections and dividers
6
+ * @output `getSelectableOptionEntries`: each selectable item with the option
7
+ * it was given as
8
+ * @position Internal; the one walk behind utils.ts's `getSelectableOptions`
9
+ * and Selector's rows
10
+ */
11
+
12
+ import type {SelectorOptionType, SelectorOptionData} from './types';
13
+ import {isOptionData, isSection, normalizeOption} from './optionShape';
14
+
15
+ /** A selectable option as given, and its normalized data. */
16
+ export interface SelectableOptionEntry {
17
+ readonly source: string | SelectorOptionData;
18
+ readonly item: SelectorOptionData;
19
+ }
20
+
21
+ /**
22
+ * Every selectable option (sections flattened, dividers skipped), each with
23
+ * the option it was given as.
24
+ */
25
+ export function getSelectableOptionEntries(
26
+ options: SelectorOptionType[],
27
+ ): SelectableOptionEntry[] {
28
+ const result: SelectableOptionEntry[] = [];
29
+ for (const option of options) {
30
+ if (isOptionData(option)) {
31
+ result.push({source: option, item: normalizeOption(option)});
32
+ } else if (isSection(option)) {
33
+ for (const opt of option.options) {
34
+ result.push({source: opt, item: normalizeOption(opt)});
35
+ }
36
+ }
37
+ }
38
+ return result;
39
+ }