@astryxdesign/core 0.7.7 → 0.7.8-canary.30b1896

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 (540) 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 +46 -6
  4. package/dist/AppShell/AppShell.d.ts.map +1 -1
  5. package/dist/AppShell/AppShell.js +5 -0
  6. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  7. package/dist/BottomSheet/BottomSheet.js +1 -0
  8. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.js +25 -4
  10. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  11. package/dist/BottomSheet/BottomSheetSwitcher.js +5 -0
  12. package/dist/BottomSheet/useMobileKeyboard.d.ts.map +1 -1
  13. package/dist/BottomSheet/useMobileKeyboard.js +6 -0
  14. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  15. package/dist/BottomSheet/useSheetGestures.js +12 -1
  16. package/dist/Button/Button.d.ts.map +1 -1
  17. package/dist/Button/Button.js +17 -1
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +15 -16
  20. package/dist/Carousel/Carousel.d.ts.map +1 -1
  21. package/dist/Carousel/Carousel.js +2 -0
  22. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  23. package/dist/Chat/ChatComposerInput.js +4 -0
  24. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  25. package/dist/Chat/ChatMessageList.js +2 -1
  26. package/dist/Chat/useChatDictation.d.ts.map +1 -1
  27. package/dist/Chat/useChatDictation.js +2 -0
  28. package/dist/Chat/useChatNewMessages.js +1 -1
  29. package/dist/Chat/useChatStreamScroll.d.ts.map +1 -1
  30. package/dist/Chat/useChatStreamScroll.js +2 -0
  31. package/dist/Chat/useSpeechRecognition.d.ts.map +1 -1
  32. package/dist/Chat/useSpeechRecognition.js +3 -0
  33. package/dist/Chat/useTriggerMenu.js +1 -1
  34. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  35. package/dist/CheckboxInput/CheckboxInput.js +17 -12
  36. package/dist/CodeBlock/CodeBlock.d.ts +10 -1
  37. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  38. package/dist/CodeBlock/CodeBlock.js +28 -11
  39. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  40. package/dist/CommandPalette/CommandPalette.js +2 -0
  41. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  42. package/dist/CommandPalette/CommandPaletteItem.js +2 -0
  43. package/dist/ComplexSelector/ComplexSelector.d.ts +4 -1
  44. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  45. package/dist/ComplexSelector/ComplexSelector.js +14 -4
  46. package/dist/ContextMenu/ContextMenu.js +2 -2
  47. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  48. package/dist/DateInput/MonthScroller.js +5 -2
  49. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  50. package/dist/DateInput/TouchDateField.js +1 -0
  51. package/dist/DateInput/Wheel.d.ts.map +1 -1
  52. package/dist/DateInput/Wheel.js +4 -0
  53. package/dist/DateInput/useOwnScrollGesture.d.ts.map +1 -1
  54. package/dist/DateInput/useOwnScrollGesture.js +3 -0
  55. package/dist/DateInput/usePointerDragScroll.d.ts.map +1 -1
  56. package/dist/DateInput/usePointerDragScroll.js +2 -0
  57. package/dist/DateInput/useScrollSettle.d.ts.map +1 -1
  58. package/dist/DateInput/useScrollSettle.js +3 -0
  59. package/dist/Dialog/Dialog.d.ts.map +1 -1
  60. package/dist/Dialog/Dialog.js +9 -32
  61. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -1
  62. package/dist/Drawer/useDrawerDialogPresence.js +3 -0
  63. package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -1
  64. package/dist/DropdownMenu/useMenuOverflow.js +5 -0
  65. package/dist/Field/Field.js +2 -2
  66. package/dist/FieldStatus/useFieldStatusAnnouncement.d.ts.map +1 -1
  67. package/dist/FieldStatus/useFieldStatusAnnouncement.js +1 -0
  68. package/dist/FormLayout/FormLayout.d.ts +5 -1
  69. package/dist/FormLayout/FormLayout.d.ts.map +1 -1
  70. package/dist/HoverCard/HoverCard.d.ts +6 -4
  71. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  72. package/dist/HoverCard/HoverCard.js +48 -3
  73. package/dist/HoverCard/useHoverCard.d.ts +5 -4
  74. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  75. package/dist/HoverCard/useHoverCard.js +20 -33
  76. package/dist/Icon/Icon.d.ts +5 -1
  77. package/dist/Icon/Icon.d.ts.map +1 -1
  78. package/dist/Icon/Icon.js +5 -2
  79. package/dist/Item/useSwipeAction.d.ts.map +1 -1
  80. package/dist/Item/useSwipeAction.js +3 -0
  81. package/dist/Layer/modalOutlet.d.ts.map +1 -1
  82. package/dist/Layer/modalOutlet.js +2 -0
  83. package/dist/Layer/outsidePress.d.ts.map +1 -1
  84. package/dist/Layer/outsidePress.js +1 -0
  85. package/dist/Layer/useLayer.d.ts.map +1 -1
  86. package/dist/Layer/useLayer.js +3 -0
  87. package/dist/Layer/useLayerDismissal.d.ts.map +1 -1
  88. package/dist/Layer/useLayerDismissal.js +3 -0
  89. package/dist/Layer/usePressPin.d.ts +17 -5
  90. package/dist/Layer/usePressPin.d.ts.map +1 -1
  91. package/dist/Layer/usePressPin.js +62 -12
  92. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  93. package/dist/Layer/useTouchTrigger.js +3 -0
  94. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  95. package/dist/Lightbox/Lightbox.js +19 -0
  96. package/dist/MobileNav/MobileNav.d.ts +3 -2
  97. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  98. package/dist/MobileNav/MobileNav.js +9 -25
  99. package/dist/Outline/useOutlineFromDOM.d.ts.map +1 -1
  100. package/dist/Outline/useOutlineFromDOM.js +3 -0
  101. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  102. package/dist/Outline/useScrollSpy.js +3 -0
  103. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  104. package/dist/OverflowList/OverflowList.js +3 -0
  105. package/dist/Popover/Popover.d.ts.map +1 -1
  106. package/dist/Popover/Popover.js +6 -0
  107. package/dist/PowerSearch/PowerSearch.d.ts +43 -4
  108. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  109. package/dist/PowerSearch/PowerSearch.js +445 -134
  110. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +102 -0
  111. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -0
  112. package/dist/PowerSearch/PowerSearchBottomSheet.js +1919 -0
  113. package/dist/PowerSearch/PowerSearchEditPopover.d.ts +35 -2
  114. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  115. package/dist/PowerSearch/PowerSearchEditPopover.js +25 -6
  116. package/dist/PowerSearch/PowerSearchFilterEditor.d.ts +1 -1
  117. package/dist/PowerSearch/PowerSearchFilterEditor.d.ts.map +1 -1
  118. package/dist/PowerSearch/PowerSearchFilterEditor.js +4 -2
  119. package/dist/PowerSearch/PowerSearchToken.d.ts +1 -1
  120. package/dist/PowerSearch/PowerSearchToken.d.ts.map +1 -1
  121. package/dist/PowerSearch/PowerSearchToken.js +7 -2
  122. package/dist/PowerSearch/PowerSearchValueEditor.d.ts +38 -3
  123. package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
  124. package/dist/PowerSearch/PowerSearchValueEditor.js +193 -83
  125. package/dist/PowerSearch/formatFilterValue.d.ts +18 -6
  126. package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
  127. package/dist/PowerSearch/formatFilterValue.js +33 -12
  128. package/dist/PowerSearch/formatTokenValue.d.ts +28 -0
  129. package/dist/PowerSearch/formatTokenValue.d.ts.map +1 -0
  130. package/dist/PowerSearch/formatTokenValue.js +162 -0
  131. package/dist/PowerSearch/powerSearchDateZone.d.ts +21 -0
  132. package/dist/PowerSearch/powerSearchDateZone.d.ts.map +1 -0
  133. package/dist/PowerSearch/powerSearchDateZone.js +37 -0
  134. package/dist/PowerSearch/powerSearchSheetUtils.d.ts +65 -0
  135. package/dist/PowerSearch/powerSearchSheetUtils.d.ts.map +1 -0
  136. package/dist/PowerSearch/powerSearchSheetUtils.js +168 -0
  137. package/dist/PowerSearch/types.d.ts +2 -0
  138. package/dist/PowerSearch/types.d.ts.map +1 -1
  139. package/dist/ProgressBar/ProgressBarTouch.d.ts.map +1 -1
  140. package/dist/ProgressBar/ProgressBarTouch.js +2 -0
  141. package/dist/RadioList/RadioList.d.ts.map +1 -1
  142. package/dist/RadioList/RadioList.js +3 -2
  143. package/dist/RadioList/RadioListItem.d.ts +1 -1
  144. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  145. package/dist/RadioList/RadioListItem.js +5 -3
  146. package/dist/Resizable/ResizeHandle.d.ts +2 -1
  147. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  148. package/dist/Resizable/ResizeHandle.js +101 -77
  149. package/dist/Resizable/createResizable.d.ts +145 -0
  150. package/dist/Resizable/createResizable.d.ts.map +1 -0
  151. package/dist/Resizable/createResizable.js +575 -0
  152. package/dist/Resizable/useResizable.d.ts +3 -3
  153. package/dist/Resizable/useResizable.d.ts.map +1 -1
  154. package/dist/Resizable/useResizable.js +252 -459
  155. package/dist/SegmentedControl/useSegmentedSelectionMotion.d.ts.map +1 -1
  156. package/dist/SegmentedControl/useSegmentedSelectionMotion.js +3 -3
  157. package/dist/SelectableCard/SelectableCard.d.ts +2 -0
  158. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  159. package/dist/SelectableCard/SelectableCard.js +36 -11
  160. package/dist/SelectableCard/selectableCardInput.d.ts +12 -0
  161. package/dist/SelectableCard/selectableCardInput.d.ts.map +1 -0
  162. package/dist/SelectableCard/selectableCardInput.js +20 -0
  163. package/dist/Selector/Selector.d.ts.map +1 -1
  164. package/dist/Selector/Selector.js +5 -8
  165. package/dist/Slider/sliderTouchController.js +2 -2
  166. package/dist/Spinner/Spinner.d.ts +1 -1
  167. package/dist/Spinner/Spinner.d.ts.map +1 -1
  168. package/dist/Spinner/Spinner.js +60 -58
  169. package/dist/Switch/Switch.d.ts.map +1 -1
  170. package/dist/Switch/Switch.js +4 -10
  171. package/dist/Switch/useSwitchTouch.js +4 -4
  172. package/dist/Table/BaseTable.d.ts.map +1 -1
  173. package/dist/Table/BaseTable.js +29 -8
  174. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  175. package/dist/Table/plugins/columnResize/useTableColumnResize.js +3 -1
  176. package/dist/Table/plugins/filtering/useTableFiltering.d.ts.map +1 -1
  177. package/dist/Table/plugins/filtering/useTableFiltering.js +2 -1
  178. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  179. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +4 -0
  180. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  181. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +2 -0
  182. package/dist/TextInput/TextInput.d.ts +2 -2
  183. package/dist/TextInput/TextInput.d.ts.map +1 -1
  184. package/dist/TextInput/TextInput.js +10 -7
  185. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  186. package/dist/TimeInput/TimeInput.js +2 -0
  187. package/dist/Timer/Timer.d.ts.map +1 -1
  188. package/dist/Timer/Timer.js +2 -0
  189. package/dist/Timestamp/Timestamp.js +1 -1
  190. package/dist/Toast/Toast.d.ts +11 -1
  191. package/dist/Toast/Toast.d.ts.map +1 -1
  192. package/dist/Toast/Toast.js +25 -15
  193. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  194. package/dist/Toast/ToastViewport.js +97 -34
  195. package/dist/Toast/types.d.ts +9 -3
  196. package/dist/Toast/types.d.ts.map +1 -1
  197. package/dist/Toast/useToast.d.ts.map +1 -1
  198. package/dist/Toast/useToast.js +1 -0
  199. package/dist/Tokenizer/Tokenizer.d.ts +49 -37
  200. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  201. package/dist/Tokenizer/Tokenizer.js +22 -19
  202. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +1 -1
  203. package/dist/Tokenizer/TokenizerBottomSheet.js +1 -1
  204. package/dist/Tokenizer/index.d.ts +1 -1
  205. package/dist/Tokenizer/index.d.ts.map +1 -1
  206. package/dist/Tooltip/useTooltip.d.ts +19 -1
  207. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  208. package/dist/Tooltip/useTooltip.js +34 -53
  209. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  210. package/dist/Typeahead/BaseTypeahead.js +27 -18
  211. package/dist/Typeahead/TypeaheadItem.d.ts +2 -1
  212. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  213. package/dist/animator/createAnimator.d.ts +98 -0
  214. package/dist/animator/createAnimator.d.ts.map +1 -0
  215. package/dist/animator/createAnimator.js +259 -0
  216. package/dist/astryx.css +36 -6
  217. package/dist/gestures/PressTracker.d.ts.map +1 -1
  218. package/dist/gestures/PressTracker.js +4 -3
  219. package/dist/hooks/ModalAnnounceProvider.d.ts.map +1 -1
  220. package/dist/hooks/ModalAnnounceProvider.js +1 -0
  221. package/dist/hooks/actionGroupTracker.d.ts +4 -1
  222. package/dist/hooks/actionGroupTracker.d.ts.map +1 -1
  223. package/dist/hooks/actionGroupTracker.js +13 -3
  224. package/dist/hooks/index.d.ts +1 -0
  225. package/dist/hooks/index.d.ts.map +1 -1
  226. package/dist/hooks/index.js +1 -0
  227. package/dist/hooks/longPressDetector.d.ts +66 -0
  228. package/dist/hooks/longPressDetector.d.ts.map +1 -0
  229. package/dist/hooks/longPressDetector.js +200 -0
  230. package/dist/hooks/pressGroupContext.d.ts +6 -0
  231. package/dist/hooks/pressGroupContext.d.ts.map +1 -0
  232. package/dist/hooks/pressGroupContext.js +20 -0
  233. package/dist/hooks/rootScrollStyles.d.ts +48 -0
  234. package/dist/hooks/rootScrollStyles.d.ts.map +1 -0
  235. package/dist/hooks/rootScrollStyles.js +207 -0
  236. package/dist/hooks/scrollLockTouchGuard.d.ts +13 -0
  237. package/dist/hooks/scrollLockTouchGuard.d.ts.map +1 -0
  238. package/dist/hooks/scrollLockTouchGuard.js +134 -0
  239. package/dist/hooks/scrollbarGutter.d.ts +4 -4
  240. package/dist/hooks/scrollbarGutter.d.ts.map +1 -1
  241. package/dist/hooks/scrollbarGutter.js +36 -23
  242. package/dist/hooks/useActionGroupTracking.d.ts +53 -0
  243. package/dist/hooks/useActionGroupTracking.d.ts.map +1 -0
  244. package/dist/hooks/useActionGroupTracking.js +174 -0
  245. package/dist/hooks/useAnimator.d.ts +41 -0
  246. package/dist/hooks/useAnimator.d.ts.map +1 -0
  247. package/dist/hooks/useAnimator.js +70 -0
  248. package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
  249. package/dist/hooks/useAnnounceRenderedText.js +1 -0
  250. package/dist/hooks/useAttachment.d.ts +1 -1
  251. package/dist/hooks/useAttachment.js +1 -1
  252. package/dist/hooks/useAutoMediaMode.d.ts.map +1 -1
  253. package/dist/hooks/useAutoMediaMode.js +1 -0
  254. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
  255. package/dist/hooks/useBusyAnnouncement.js +2 -0
  256. package/dist/hooks/useDevWarning.d.ts.map +1 -1
  257. package/dist/hooks/useDevWarning.js +2 -0
  258. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  259. package/dist/hooks/useFocusTrap.js +2 -0
  260. package/dist/hooks/useGridFocus.d.ts +8 -5
  261. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  262. package/dist/hooks/useGridFocus.js +4 -3
  263. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  264. package/dist/hooks/useHotkeys.js +3 -0
  265. package/dist/hooks/useImageMode.d.ts.map +1 -1
  266. package/dist/hooks/useImageMode.js +3 -0
  267. package/dist/hooks/useInputStatusIcon.d.ts +1 -1
  268. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  269. package/dist/hooks/useInputStatusIcon.js +2 -2
  270. package/dist/hooks/useKeyboardHint.js +1 -1
  271. package/dist/hooks/useLongPress.d.ts +8 -9
  272. package/dist/hooks/useLongPress.d.ts.map +1 -1
  273. package/dist/hooks/useLongPress.js +23 -106
  274. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  275. package/dist/hooks/useMenuHover.js +2 -0
  276. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  277. package/dist/hooks/useMenuPress.js +3 -0
  278. package/dist/hooks/useOverflow.d.ts.map +1 -1
  279. package/dist/hooks/useOverflow.js +2 -1
  280. package/dist/hooks/usePressFeedback.d.ts.map +1 -1
  281. package/dist/hooks/usePressFeedback.js +1 -0
  282. package/dist/hooks/useResolvedRequired.d.ts +3 -0
  283. package/dist/hooks/useResolvedRequired.d.ts.map +1 -1
  284. package/dist/hooks/useResolvedRequired.js +3 -0
  285. package/dist/hooks/useScrollLock.d.ts +7 -9
  286. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  287. package/dist/hooks/useScrollLock.js +55 -48
  288. package/dist/hooks/useScrollableArea.d.ts.map +1 -1
  289. package/dist/hooks/useScrollableArea.js +5 -0
  290. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  291. package/dist/hooks/useStreamingText.js +3 -0
  292. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts +8 -0
  293. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -0
  294. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +42 -0
  295. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.d.ts.map +1 -1
  296. package/dist/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.js +6 -0
  297. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts +8 -0
  298. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.d.ts.map +1 -0
  299. package/dist/i18n/generated-english/PowerSearch/formatTokenValue.generated.js +14 -0
  300. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  301. package/dist/i18n/generated-locales/en.generated.js +30 -0
  302. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  303. package/dist/i18n/generated-locales/pseudo.generated.js +30 -0
  304. package/dist/index.d.ts +1 -0
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +4 -0
  307. package/dist/internal/indicatorPress.stylex.d.ts +20 -0
  308. package/dist/internal/indicatorPress.stylex.d.ts.map +1 -0
  309. package/dist/internal/indicatorPress.stylex.js +26 -0
  310. package/dist/internal/selectable-card.d.ts +21 -0
  311. package/dist/internal/selectable-card.d.ts.map +1 -0
  312. package/dist/internal/selectable-card.js +20 -0
  313. package/dist/internal/sheetDraft.d.ts +2 -2
  314. package/dist/internal/sheetDraft.js +2 -2
  315. package/dist/motion/animatedLayout.d.ts.map +1 -1
  316. package/dist/motion/animatedLayout.js +7 -7
  317. package/dist/motion/animator.d.ts +14 -14
  318. package/dist/motion/animator.d.ts.map +1 -1
  319. package/dist/motion/animator.js +45 -20
  320. package/dist/motion/progress.d.ts +88 -0
  321. package/dist/motion/progress.d.ts.map +1 -0
  322. package/dist/motion/progress.js +128 -0
  323. package/dist/motion/reducedMotion.d.ts.map +1 -1
  324. package/dist/motion/reducedMotion.js +3 -1
  325. package/dist/motion/uikit/alert.d.ts +41 -0
  326. package/dist/motion/uikit/alert.d.ts.map +1 -0
  327. package/dist/motion/uikit/alert.js +43 -0
  328. package/dist/privateAttributes/pressGroupItem.stylex.d.ts +22 -0
  329. package/dist/privateAttributes/pressGroupItem.stylex.d.ts.map +1 -0
  330. package/dist/privateAttributes/pressGroupItem.stylex.js +36 -0
  331. package/dist/privateAttributes/reducedMotion.stylex.d.ts +23 -0
  332. package/dist/privateAttributes/reducedMotion.stylex.d.ts.map +1 -0
  333. package/dist/privateAttributes/reducedMotion.stylex.js +40 -0
  334. package/dist/theme/Theme.d.ts.map +1 -1
  335. package/dist/theme/Theme.js +29 -4
  336. package/dist/theme/motionKnobs.d.ts +4 -3
  337. package/dist/theme/motionKnobs.d.ts.map +1 -1
  338. package/dist/theme/motionKnobs.js +3 -2
  339. package/dist/theme/themeMotion.d.ts +6 -0
  340. package/dist/theme/themeMotion.d.ts.map +1 -1
  341. package/dist/theme/themeMotion.js +7 -0
  342. package/dist/utils/dateParser.d.ts.map +1 -1
  343. package/dist/utils/dateParser.js +12 -0
  344. package/dist/utils/focusReturn.d.ts +15 -0
  345. package/dist/utils/focusReturn.d.ts.map +1 -1
  346. package/dist/utils/focusReturn.js +28 -0
  347. package/dist/utils/interactionModality.d.ts.map +1 -1
  348. package/dist/utils/interactionModality.js +4 -0
  349. package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
  350. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  351. package/dist/utils/interactionOverlay.stylex.js +3 -2
  352. package/dist/utils/plainDate.d.ts +8 -0
  353. package/dist/utils/plainDate.d.ts.map +1 -1
  354. package/dist/utils/plainDate.js +19 -0
  355. package/dist/utils/pressFeedback.d.ts.map +1 -1
  356. package/dist/utils/pressFeedback.js +20 -6
  357. package/dist/utils/triggerFocus.d.ts +36 -0
  358. package/dist/utils/triggerFocus.d.ts.map +1 -0
  359. package/dist/utils/triggerFocus.js +65 -0
  360. package/locales/en.json +120 -0
  361. package/locales/pseudo.json +90 -0
  362. package/package.json +14 -9
  363. package/src/AlertDialog/AlertDialog.doc.mjs +2 -2
  364. package/src/AlertDialog/AlertDialog.tsx +46 -5
  365. package/src/AppShell/AppShell.tsx +4 -0
  366. package/src/BottomSheet/BottomSheet.tsx +1 -0
  367. package/src/BottomSheet/BottomSheetPanel.tsx +39 -3
  368. package/src/BottomSheet/BottomSheetSwitcher.tsx +5 -0
  369. package/src/BottomSheet/useMobileKeyboard.ts +4 -0
  370. package/src/BottomSheet/useSheetGestures.ts +10 -1
  371. package/src/Button/Button.tsx +26 -4
  372. package/src/Calendar/Calendar.tsx +36 -25
  373. package/src/Carousel/Carousel.tsx +1 -0
  374. package/src/Chat/ChatComposerInput.tsx +2 -0
  375. package/src/Chat/ChatMessageList.tsx +2 -1
  376. package/src/Chat/useChatDictation.ts +1 -0
  377. package/src/Chat/useChatNewMessages.ts +1 -1
  378. package/src/Chat/useChatStreamScroll.ts +2 -0
  379. package/src/Chat/useSpeechRecognition.ts +2 -0
  380. package/src/Chat/useTriggerMenu.tsx +1 -1
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.tsx +38 -15
  383. package/src/CodeBlock/CodeBlock.doc.mjs +9 -1
  384. package/src/CodeBlock/CodeBlock.tsx +41 -4
  385. package/src/CommandPalette/CommandPalette.tsx +1 -0
  386. package/src/CommandPalette/CommandPaletteItem.tsx +1 -0
  387. package/src/ComplexSelector/ComplexSelector.tsx +20 -5
  388. package/src/ContextMenu/ContextMenu.tsx +2 -2
  389. package/src/DateInput/MonthScroller.tsx +8 -2
  390. package/src/DateInput/TouchDateField.tsx +1 -0
  391. package/src/DateInput/Wheel.tsx +4 -0
  392. package/src/DateInput/useOwnScrollGesture.ts +2 -0
  393. package/src/DateInput/usePointerDragScroll.ts +1 -0
  394. package/src/DateInput/useScrollSettle.ts +2 -0
  395. package/src/Dialog/Dialog.tsx +12 -32
  396. package/src/Drawer/useDrawerDialogPresence.ts +2 -0
  397. package/src/DropdownMenu/useMenuOverflow.ts +3 -0
  398. package/src/Field/Field.tsx +2 -2
  399. package/src/FieldStatus/useFieldStatusAnnouncement.ts +1 -0
  400. package/src/FormLayout/FormLayout.doc.mjs +181 -46
  401. package/src/FormLayout/FormLayout.tsx +5 -1
  402. package/src/HoverCard/HoverCard.doc.mjs +10 -8
  403. package/src/HoverCard/HoverCard.tsx +58 -6
  404. package/src/HoverCard/useHoverCard.doc.mjs +178 -36
  405. package/src/HoverCard/useHoverCard.tsx +49 -54
  406. package/src/Icon/Icon.doc.mjs +49 -8
  407. package/src/Icon/Icon.tsx +15 -7
  408. package/src/IconButton/IconButton.doc.mjs +141 -21
  409. package/src/Item/useSwipeAction.ts +2 -0
  410. package/src/Layer/modalOutlet.ts +2 -0
  411. package/src/Layer/outsidePress.ts +1 -0
  412. package/src/Layer/useLayer.tsx +2 -0
  413. package/src/Layer/useLayerDismissal.ts +2 -0
  414. package/src/Layer/usePressPin.ts +81 -15
  415. package/src/Layer/useTouchTrigger.ts +2 -0
  416. package/src/Lightbox/Lightbox.tsx +19 -0
  417. package/src/MobileNav/MobileNav.tsx +9 -30
  418. package/src/Outline/useOutlineFromDOM.ts +2 -0
  419. package/src/Outline/useScrollSpy.ts +2 -0
  420. package/src/OverflowList/OverflowList.tsx +2 -0
  421. package/src/Popover/Popover.tsx +4 -0
  422. package/src/PowerSearch/PowerSearch.doc.mjs +60 -8
  423. package/src/PowerSearch/PowerSearch.tsx +627 -157
  424. package/src/PowerSearch/PowerSearchBottomSheet.tsx +2525 -0
  425. package/src/PowerSearch/PowerSearchEditPopover.tsx +39 -6
  426. package/src/PowerSearch/PowerSearchFilterEditor.tsx +2 -0
  427. package/src/PowerSearch/PowerSearchToken.tsx +7 -0
  428. package/src/PowerSearch/PowerSearchValueEditor.tsx +244 -58
  429. package/src/PowerSearch/formatFilterValue.ts +55 -17
  430. package/src/PowerSearch/formatTokenValue.ts +222 -0
  431. package/src/PowerSearch/powerSearchDateZone.ts +64 -0
  432. package/src/PowerSearch/powerSearchSheetUtils.ts +224 -0
  433. package/src/PowerSearch/types.ts +2 -0
  434. package/src/ProgressBar/ProgressBarTouch.ts +2 -0
  435. package/src/RadioList/RadioList.tsx +3 -2
  436. package/src/RadioList/RadioListItem.tsx +5 -3
  437. package/src/Resizable/Resizable.doc.mjs +5 -4
  438. package/src/Resizable/ResizeHandle.tsx +169 -109
  439. package/src/Resizable/createResizable.ts +831 -0
  440. package/src/Resizable/useResizable.ts +292 -623
  441. package/src/SegmentedControl/useSegmentedSelectionMotion.ts +6 -3
  442. package/src/SelectableCard/SelectableCard.tsx +39 -10
  443. package/src/SelectableCard/selectableCardInput.ts +30 -0
  444. package/src/Selector/Selector.tsx +5 -12
  445. package/src/Slider/sliderTouchController.ts +2 -2
  446. package/src/Spinner/Spinner.tsx +77 -63
  447. package/src/Switch/Switch.tsx +2 -6
  448. package/src/Switch/useSwitchTouch.ts +4 -4
  449. package/src/Table/BaseTable.tsx +46 -12
  450. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +2 -1
  451. package/src/Table/plugins/filtering/useTableFiltering.tsx +1 -0
  452. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +4 -0
  453. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +2 -0
  454. package/src/TextInput/TextInput.tsx +11 -10
  455. package/src/TimeInput/TimeInput.tsx +2 -0
  456. package/src/Timer/Timer.tsx +1 -0
  457. package/src/Timestamp/Timestamp.tsx +1 -1
  458. package/src/Toast/Toast.doc.mjs +4 -3
  459. package/src/Toast/Toast.tsx +52 -32
  460. package/src/Toast/ToastViewport.tsx +123 -35
  461. package/src/Toast/types.ts +9 -3
  462. package/src/Toast/useToast.tsx +1 -0
  463. package/src/Tokenizer/Tokenizer.doc.mjs +19 -3
  464. package/src/Tokenizer/Tokenizer.tsx +73 -51
  465. package/src/Tokenizer/TokenizerBottomSheet.tsx +1 -1
  466. package/src/Tokenizer/index.ts +1 -0
  467. package/src/Tooltip/useTooltip.doc.mjs +24 -0
  468. package/src/Tooltip/useTooltip.tsx +61 -56
  469. package/src/Typeahead/BaseTypeahead.tsx +27 -17
  470. package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
  471. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  472. package/src/animator/createAnimator.doc.mjs +153 -0
  473. package/src/animator/createAnimator.ts +324 -0
  474. package/src/gestures/PressTracker.ts +4 -3
  475. package/src/gestures/README.md +10 -4
  476. package/src/hooks/ModalAnnounceProvider.tsx +1 -0
  477. package/src/hooks/actionGroupTracker.ts +20 -5
  478. package/src/hooks/index.ts +2 -0
  479. package/src/hooks/longPressDetector.ts +273 -0
  480. package/src/hooks/pressGroupContext.ts +21 -0
  481. package/src/hooks/rootScrollStyles.ts +247 -0
  482. package/src/hooks/scrollLockTouchGuard.ts +182 -0
  483. package/src/hooks/scrollbarGutter.ts +41 -26
  484. package/src/hooks/useActionGroupTracking.ts +224 -0
  485. package/src/hooks/useAnimator.doc.mjs +109 -0
  486. package/src/hooks/useAnimator.ts +67 -0
  487. package/src/hooks/useAnnounceRenderedText.ts +1 -0
  488. package/src/hooks/useAttachment.ts +1 -1
  489. package/src/hooks/useAutoMediaMode.ts +1 -0
  490. package/src/hooks/useBusyAnnouncement.ts +2 -0
  491. package/src/hooks/useDevWarning.ts +2 -0
  492. package/src/hooks/useFocusTrap.ts +2 -0
  493. package/src/hooks/useGridFocus.doc.mjs +6 -6
  494. package/src/hooks/useGridFocus.ts +10 -7
  495. package/src/hooks/useHotkeys.ts +2 -0
  496. package/src/hooks/useImageMode.ts +2 -0
  497. package/src/hooks/useInputStatusIcon.tsx +3 -2
  498. package/src/hooks/useKeyboardHint.tsx +1 -1
  499. package/src/hooks/useLongPress.doc.mjs +2 -2
  500. package/src/hooks/useLongPress.ts +32 -122
  501. package/src/hooks/useMenuHover.ts +1 -0
  502. package/src/hooks/useMenuPress.ts +2 -0
  503. package/src/hooks/useOverflow.ts +2 -1
  504. package/src/hooks/usePressFeedback.ts +1 -0
  505. package/src/hooks/useResolvedRequired.ts +3 -0
  506. package/src/hooks/useScrollLock.doc.mjs +51 -12
  507. package/src/hooks/useScrollLock.ts +57 -60
  508. package/src/hooks/useScrollableArea.ts +4 -0
  509. package/src/hooks/useStreamingText.ts +2 -0
  510. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +43 -0
  511. package/src/i18n/generated-english/PowerSearch/PowerSearchValueEditor.generated.ts +6 -0
  512. package/src/i18n/generated-english/PowerSearch/formatTokenValue.generated.ts +15 -0
  513. package/src/i18n/generated-locales/en.generated.ts +41 -0
  514. package/src/i18n/generated-locales/pseudo.generated.ts +42 -0
  515. package/src/index.ts +13 -0
  516. package/src/internal/indicatorPress.stylex.ts +24 -0
  517. package/src/internal/selectable-card.ts +21 -0
  518. package/src/internal/sheetDraft.ts +2 -2
  519. package/src/motion/README.md +9 -5
  520. package/src/motion/animatedLayout.ts +10 -5
  521. package/src/motion/animator.ts +66 -31
  522. package/src/motion/progress.ts +155 -0
  523. package/src/motion/reducedMotion.ts +3 -1
  524. package/src/motion/uikit/alert.ts +43 -0
  525. package/src/privateAttributes/pressGroupItem.stylex.ts +44 -0
  526. package/src/privateAttributes/reducedMotion.stylex.ts +45 -0
  527. package/src/theme/Theme.tsx +37 -3
  528. package/src/theme/motionKnobs.ts +4 -3
  529. package/src/theme/themeMotion.ts +7 -0
  530. package/src/utils/dateParser.ts +16 -0
  531. package/src/utils/focusReturn.ts +30 -0
  532. package/src/utils/interactionModality.ts +4 -0
  533. package/src/utils/interactionOverlay.stylex.ts +3 -2
  534. package/src/utils/plainDate.ts +21 -0
  535. package/src/utils/pressFeedback.ts +24 -6
  536. package/src/utils/triggerFocus.ts +80 -0
  537. package/dist/internal/touch-sheet.d.ts +0 -21
  538. package/dist/internal/touch-sheet.d.ts.map +0 -1
  539. package/dist/internal/touch-sheet.js +0 -19
  540. package/src/internal/touch-sheet.ts +0 -21
@@ -4,19 +4,25 @@
4
4
 
5
5
  /**
6
6
  * @file PowerSearch.tsx
7
- * @input PowerSearchConfig, filters, onChange
7
+ * @input PowerSearchConfig, filters, onChange; Tokenizer, Field parts, Token,
8
+ * useAdaptivePresentation, and PowerSearchBottomSheet (lazy)
8
9
  * @output Structured filter bar with token-based filter management
9
10
  * @position Main component; forwards DOM ref and exposes tokenizer control via
10
- * handleRef
11
+ * handleRef. The experimental `presentation` option swaps the tokenizer
12
+ * field and editor popover for one trigger that opens PowerSearchBottomSheet.
11
13
  *
12
14
  * SYNC: When modified, update:
13
15
  * - /packages/core/src/PowerSearch/index.ts
16
+ * - /packages/core/src/PowerSearch/PowerSearchBottomSheet.tsx (sheet presentation)
14
17
  * - /packages/cli/assets/templates/blocks/components/PowerSearch/ (showcase blocks)
15
18
  */
16
19
 
17
20
  import React, {
21
+ lazy,
22
+ Suspense,
18
23
  useCallback,
19
24
  useEffect,
25
+ useId,
20
26
  useImperativeHandle,
21
27
  useMemo,
22
28
  useRef,
@@ -37,30 +43,45 @@ import {Token} from '../Token';
37
43
  import {TokenNamingContext} from '../Token/TokenNamingContext';
38
44
  import {Avatar} from '../Avatar';
39
45
  import {layerAnimations} from '../Layer/layerAnimations.stylex';
40
- import {Icon} from '../Icon';
46
+ import {Icon, renderIconSlot} from '../Icon';
41
47
  import type {IconType} from '../Icon';
42
48
  import type {IconName} from '../Icon/globalIconRegistry';
43
49
  import type {InputStatus} from '../Field';
50
+ import {Field} from '../Field/Field';
51
+ import {
52
+ inputStatusBorderStyles,
53
+ inputStatusFocusWithinStyles,
54
+ inputStatusHoverShadowStyles,
55
+ inputWrapperStyles,
56
+ } from '../Field/inputStyles.stylex';
44
57
  import {usePopover} from '../Popover/usePopover';
58
+ import {useTooltip} from '../Tooltip/useTooltip';
45
59
  import {layerViewportInset} from '../Layer/layerViewportInset.stylex';
46
60
  import {useAnnounce} from '../hooks/useAnnounce';
61
+ import {useAdaptivePresentation} from '../hooks/useAdaptivePresentation';
62
+ import {useDevWarning} from '../hooks/useDevWarning';
63
+ import {useIsHydrating} from '../hooks/useIsHydrating';
47
64
  import {
48
65
  spacingVars,
49
66
  colorVars,
67
+ sizeVars,
50
68
  typeScaleVars,
69
+ typographyVars,
51
70
  fontWeightVars,
52
71
  } from '../theme/tokens.stylex';
53
72
  import {useSize} from '../SizeContext/SizeContext';
73
+ import {isFocusDetached} from '../utils/focusReturn';
74
+ import {isRenderable} from '../utils/isRenderable';
54
75
  import {useInternalConfig} from './useInternalConfig';
55
76
  import {usePowerSearchSource} from './usePowerSearchSource';
77
+ import {formatFilterValue} from './formatFilterValue';
78
+ import {NestedEditor, PowerSearchEditPopover} from './PowerSearchEditPopover';
56
79
  import {
57
- formatFilterValue,
58
- formatDateAbsoluteCompact,
59
- } from './formatFilterValue';
60
- import {PowerSearchEditPopover} from './PowerSearchEditPopover';
80
+ buildRelativeDateOptions,
81
+ PowerSearchValueEditor,
82
+ } from './PowerSearchValueEditor';
61
83
  import {resolveOperatorLabel} from './resolveOperatorLabel';
62
84
  import {themeProps} from '../utils/themeProps';
63
- import {truncateCharacters} from '../utils/characters';
64
85
  import {useLocale} from '../i18n/useLocale';
65
86
  import type {Locale} from '../i18n/types';
66
87
  import type {SearchableItem} from '../Typeahead/types';
@@ -74,10 +95,14 @@ import type {
74
95
  PowerSearchHandle,
75
96
  FilterValue,
76
97
  OperatorValue,
77
- EnumItem,
78
98
  PowerSearchComponents,
79
99
  PowerSearchOperator,
80
100
  } from './types';
101
+ import type {
102
+ PowerSearchBottomSheetProps,
103
+ PowerSearchSheetEditors,
104
+ } from './PowerSearchBottomSheet';
105
+ import {formatTokenValueText} from './formatTokenValue';
81
106
 
82
107
  import {useMergedRefs} from '../hooks/useMergedRefs';
83
108
  import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement';
@@ -150,130 +175,127 @@ const resultCountStyles = stylex.create({
150
175
  },
151
176
  });
152
177
 
153
- function truncateString(value: string, limit: number): string {
154
- // Same semantics as before — strings within limit + 3 pass through, longer
155
- // ones cut to limit + '...' — but counted in characters, not code units.
156
- return truncateCharacters(value, limit + 3, '...');
157
- }
178
+ // The sheet presentation's trigger field: the same control box as the
179
+ // tokenizer field, with display-only tokens and one button as its only tab
180
+ // stop.
181
+ const sheetTriggerSizeStyles = stylex.create({
182
+ sm: {minHeight: sizeVars['--size-element-sm']},
183
+ md: {minHeight: sizeVars['--size-element-md']},
184
+ lg: {minHeight: sizeVars['--size-element-lg']},
185
+ });
158
186
 
159
- function getEnumLabel(values: ReadonlyArray<EnumItem>, value: string): string {
160
- return values.find(v => v.value === value)?.label ?? value;
161
- }
187
+ const sheetTriggerStyles = stylex.create({
188
+ wrapper: {
189
+ position: 'relative',
190
+ flexWrap: 'nowrap',
191
+ gap: spacingVars['--spacing-1'],
192
+ height: 'auto',
193
+ cursor: {
194
+ default: 'pointer',
195
+ ':is([aria-disabled="true"])': 'default',
196
+ },
197
+ },
198
+ wrapperWithTokens: {
199
+ paddingBlock: `calc(${spacingVars['--spacing-1']} - 1px)`,
200
+ paddingInline: `calc(${spacingVars['--spacing-1']} - 1px)`,
201
+ },
202
+ contentSection: {
203
+ position: 'relative',
204
+ minWidth: 0,
205
+ flexGrow: 1,
206
+ display: 'flex',
207
+ alignItems: 'center',
208
+ flexWrap: 'wrap',
209
+ columnGap: spacingVars['--spacing-1'],
210
+ rowGap: `calc(${spacingVars['--spacing-1']} - 1px)`,
211
+ },
212
+ startIconWithTokens: {
213
+ marginInlineStart: `calc(${spacingVars['--spacing-2']} - ${spacingVars['--spacing-1']} + 1px)`,
214
+ },
215
+ // The only tab stop in the field. It stays visually borderless while the
216
+ // wrapper delegates taps on tokens and empty space to it.
217
+ trigger: {
218
+ appearance: 'none',
219
+ flex: '1 1 40px',
220
+ minWidth: '40px',
221
+ alignSelf: 'stretch',
222
+ display: 'flex',
223
+ alignItems: 'center',
224
+ paddingBlock: 0,
225
+ paddingInline: spacingVars['--spacing-1'],
226
+ margin: 0,
227
+ borderWidth: 0,
228
+ borderStyle: 'none',
229
+ backgroundColor: 'transparent',
230
+ color: colorVars['--color-text-secondary'],
231
+ fontFamily: typographyVars['--font-family-body'],
232
+ fontSize: typeScaleVars['--text-body-size'],
233
+ lineHeight: typeScaleVars['--text-body-leading'],
234
+ textAlign: 'start',
235
+ whiteSpace: 'nowrap',
236
+ cursor: {
237
+ default: 'pointer',
238
+ ':is([aria-disabled="true"])': 'default',
239
+ },
240
+ outline: 'none',
241
+ },
242
+ // Once a token is present the empty trigger covers the token lane instead
243
+ // of reserving 40px or wrapping onto an otherwise blank row.
244
+ triggerWithTokens: {
245
+ position: 'absolute',
246
+ inset: 0,
247
+ minWidth: 0,
248
+ padding: 0,
249
+ },
250
+ endSection: {
251
+ display: 'flex',
252
+ alignItems: 'center',
253
+ gap: spacingVars['--spacing-2'],
254
+ flexShrink: 0,
255
+ paddingInlineEnd: spacingVars['--spacing-1'],
256
+ },
257
+ });
258
+
259
+ // The sheet flow (three standalone sheets, the sheet editors) only
260
+ // renders for the bottom-sheet presentation. Loading it on demand keeps it out
261
+ // of pages whose PowerSearch stays a tokenizer field.
262
+ // One import promise, shared by the preload and the lazy element: the sheet
263
+ // presentation loads the flow's chunk as soon as it is active, so the first
264
+ // press does not wait for the network, while nothing of the flow is rendered
265
+ // until a press.
266
+ const loadPowerSearchBottomSheet = async () =>
267
+ import('./PowerSearchBottomSheet');
268
+ const LazyPowerSearchBottomSheet = lazy(async () =>
269
+ loadPowerSearchBottomSheet().then(module => ({
270
+ default: module.PowerSearchBottomSheet,
271
+ })),
272
+ );
273
+ // The value step reuses the popover presentation's editors, which this
274
+ // module already loads; see PowerSearchSheetEditors.
275
+ const SHEET_EDITORS: PowerSearchSheetEditors = {
276
+ NestedEditor,
277
+ PowerSearchValueEditor,
278
+ buildRelativeDateOptions,
279
+ };
162
280
 
163
281
  /**
164
- * The visible text of a filter token's value. The token renders it, and the
165
- * token's remove button and editor dialog are named by the same text.
282
+ * Hosts the lazily loaded sheet flow. Renders nothing on the server or in the
283
+ * render that hydrates server HTML: a lazy component that has not loaded yet
284
+ * would leave server markup incomplete and abort hydration. The closed flow
285
+ * has no visible markup, so it first renders right after hydration.
166
286
  */
167
- function tokenValueText(
168
- operatorValue: OperatorValue,
169
- filterValue: FilterValue,
170
- maxLength: number,
171
- locale: Locale,
172
- ): string | null {
173
- switch (filterValue.type) {
174
- case 'empty':
175
- return null;
176
-
177
- case 'string':
178
- return truncateString(filterValue.value, maxLength);
179
-
180
- case 'integer':
181
- case 'float':
182
- return String(filterValue.value);
183
-
184
- case 'enum':
185
- if (operatorValue.type === 'enum') {
186
- return truncateString(
187
- getEnumLabel(operatorValue.values, filterValue.value),
188
- maxLength,
189
- );
190
- }
191
- return truncateString(filterValue.value, maxLength);
192
-
193
- case 'string_list': {
194
- const items = filterValue.value;
195
- if (items.length === 0) {
196
- return null;
197
- }
198
- if (items.length === 1) {
199
- return truncateString(items[0], maxLength);
200
- }
201
- const totalLength = items.reduce((sum, s) => sum + s.length, 0);
202
- if (totalLength > maxLength) {
203
- return `${items.length} items`;
204
- }
205
- return items.join(', ');
206
- }
207
-
208
- case 'enum_list': {
209
- const items = filterValue.value;
210
- if (items.length === 0) {
211
- return null;
212
- }
213
- if (operatorValue.type === 'enum_list') {
214
- const labels = items.map(v => getEnumLabel(operatorValue.values, v));
215
- if (labels.length === 1) {
216
- return truncateString(labels[0], maxLength);
217
- }
218
- const totalLength = labels.reduce((sum, s) => sum + s.length, 0);
219
- if (totalLength > maxLength) {
220
- return `${labels.length} items`;
221
- }
222
- return labels.join(', ');
223
- }
224
- if (items.length === 1) {
225
- return truncateString(items[0], maxLength);
226
- }
227
- return `${items.length} items`;
228
- }
229
-
230
- case 'entity_list': {
231
- const entities = filterValue.value;
232
- if (entities.length === 0) {
233
- return null;
234
- }
235
- if (entities.length === 1) {
236
- return truncateString(entities[0].label, maxLength);
237
- }
238
- const totalLength = entities.reduce((sum, e) => sum + e.label.length, 0);
239
- if (totalLength > maxLength) {
240
- return `${entities.length} items`;
241
- }
242
- return entities.map(e => e.label).join(', ');
243
- }
244
-
245
- case 'time':
246
- return filterValue.value;
247
-
248
- case 'date_absolute':
249
- return truncateString(
250
- formatDateAbsoluteCompact(filterValue.unixSeconds, locale),
251
- maxLength,
252
- );
253
-
254
- case 'date_relative':
255
- return truncateString(filterValue.value, maxLength);
256
-
257
- case 'date_range':
258
- return 'date range';
259
-
260
- case 'custom':
261
- if (operatorValue.type === 'custom') {
262
- return truncateString(
263
- operatorValue.getString(filterValue.value),
264
- maxLength,
265
- );
266
- }
267
- return filterValue.value;
268
-
269
- case 'nested': {
270
- const count = filterValue.value.length;
271
- return count === 1 ? '1 filter' : `${count} filters`;
272
- }
273
-
274
- default:
275
- return null;
287
+ function PowerSearchSheetHost(
288
+ props: Omit<PowerSearchBottomSheetProps, 'editors'>,
289
+ ) {
290
+ const isHydrating = useIsHydrating();
291
+ if (isHydrating) {
292
+ return null;
276
293
  }
294
+ return (
295
+ <Suspense fallback={null}>
296
+ <LazyPowerSearchBottomSheet {...props} editors={SHEET_EDITORS} />
297
+ </Suspense>
298
+ );
277
299
  }
278
300
 
279
301
  function PowerSearchTokenValue({text}: {text: string}) {
@@ -356,7 +378,13 @@ export interface PowerSearchProps extends Omit<
356
378
  maxSearchResults?: number;
357
379
  /** Label for the save button in edit popover. @default 'Apply' */
358
380
  popoverSaveButtonLabel?: string;
359
- /** Timezone ID for date formatting. */
381
+ /**
382
+ * IANA zone a date-only `date_absolute` value, and each absolute part of a
383
+ * `date_range`, is read and written in: the token, the label and the editor
384
+ * show its day there, and a picked day stores as that midnight. Unset, a
385
+ * day is its UTC day. A `date_absolute` with `isDateOnly: false` formats
386
+ * its label in it.
387
+ */
360
388
  timezoneID?: string;
361
389
  /**
362
390
  * Controls how tokens overflow when the container is too narrow.
@@ -388,6 +416,37 @@ export interface PowerSearchProps extends Omit<
388
416
  * Keys are operator value types (e.g. 'string', 'enum', 'date_absolute').
389
417
  */
390
418
  components?: PowerSearchComponents;
419
+
420
+ /**
421
+ * Experimental options, outside the stable compatibility promise: anything
422
+ * in this object may change or be removed in a patch release. Omit it for
423
+ * the stable behavior.
424
+ * @experimental
425
+ */
426
+ experimental?: {
427
+ /**
428
+ * How the field presents filter editing.
429
+ * - `'popover'`: the tokenizer field with its typeahead and the editor
430
+ * popover — the stable behavior.
431
+ * - `'bottom-sheet'`: the whole field is one trigger showing display-only
432
+ * filter tokens; it opens a stack of sheets. Filters lists the filters
433
+ * as rows that edit or remove one, with Add filter, Done, and Clear
434
+ * all, which asks first and keeps read-only filters. Add filter opens
435
+ * above it with the fields (or the content search with inline
436
+ * suggestions), and a value step opens above that, stages operator →
437
+ * value, and confirms from its footer. Dismissing a step reveals the
438
+ * one beneath. Suited to compact touch screens, where the editor
439
+ * popover competes with the software keyboard.
440
+ * - `'adaptive'`: the bottom sheet on compact coarse-pointer screens,
441
+ * otherwise the popover — the same resolution Selector uses.
442
+ *
443
+ * In the sheet presentation `menuWidth` and `tokenOverflowBehavior` have
444
+ * no surface to act on and warn in development.
445
+ * @experimental
446
+ * @default 'popover'
447
+ */
448
+ presentation?: 'popover' | 'bottom-sheet' | 'adaptive';
449
+ };
391
450
  }
392
451
 
393
452
  // =============================================================================
@@ -524,6 +583,7 @@ export function PowerSearch({
524
583
  className,
525
584
  style,
526
585
  components: componentOverrides,
586
+ experimental,
527
587
  }: PowerSearchProps) {
528
588
  const size = useSize(sizeProp, 'md');
529
589
  const config = useInternalConfig(configProp);
@@ -537,6 +597,105 @@ export function PowerSearch({
537
597
  popoverSaveButtonLabelFromProps ?? t('@astryx.powersearch.editor.apply');
538
598
  const tokenizerRef = useRef<TokenizerHandle>(null);
539
599
 
600
+ // Experimental presentation. `adaptive`
601
+ // resolves through the policy the selection controls share; an open flow
602
+ // keeps its presentation until it closes, so a resolution change mid-task
603
+ // cannot pull the surface out from under the person.
604
+ const presentation = experimental?.presentation ?? 'popover';
605
+ const resolvedPresentation = useAdaptivePresentation(presentation);
606
+ const [isSheetOpen, setIsSheetOpen] = useState(false);
607
+ // Latched for the flow's lifetime: the flow stays presented after it closes
608
+ // until the sheets have run their exit and handed focus back to the trigger.
609
+ // Without the latch, `adaptive` resolving to `popover` mid-session (a phone
610
+ // turned to landscape) would unmount the sheets and the trigger they return
611
+ // focus to in the same render, dropping focus to the page body.
612
+ const [isSheetPresented, setIsSheetPresented] = useState(false);
613
+ const isSheetPresentation =
614
+ isSheetOpen || isSheetPresented || resolvedPresentation === 'bottom-sheet';
615
+ const triggerRef = useRef<HTMLButtonElement>(null);
616
+ // `hasAutoFocus` applies only to the control the field first mounts. A
617
+ // presentation swap remounts the control (combobox input or trigger), and
618
+ // React's `autoFocus` on the new one would pull focus from wherever on the
619
+ // page it was; the roots' callback refs hand over the focus the field held
620
+ // instead. A field that never swaps keeps `autoFocus` on every render, so a
621
+ // Dialog that keeps it mounted still finds its `data-autofocus` on open.
622
+ const [autoFocusState, setAutoFocusState] = useState({
623
+ presentation: isSheetPresentation,
624
+ isFirstControl: true,
625
+ });
626
+ if (autoFocusState.presentation !== isSheetPresentation) {
627
+ setAutoFocusState({
628
+ presentation: isSheetPresentation,
629
+ isFirstControl: false,
630
+ });
631
+ }
632
+ const autoFocusOnMount =
633
+ hasAutoFocus &&
634
+ autoFocusState.isFirstControl &&
635
+ autoFocusState.presentation === isSheetPresentation;
636
+ // A focus the swap moves continues the same visit to the field: the
637
+ // control that held focus is gone (no blur fires for a removed element), so
638
+ // the caller is told of neither a blur nor a new focus.
639
+ const isSwapFocusRef = useRef(false);
640
+ const focusAfterSwap = useCallback((control: {focus(): void} | null) => {
641
+ if (control == null) {
642
+ return;
643
+ }
644
+ isSwapFocusRef.current = true;
645
+ try {
646
+ control.focus();
647
+ } finally {
648
+ isSwapFocusRef.current = false;
649
+ }
650
+ }, []);
651
+ const handleFieldFocus = useCallback(
652
+ (event: React.FocusEvent) => {
653
+ if (!isSwapFocusRef.current) {
654
+ onFocus?.(event);
655
+ }
656
+ },
657
+ [onFocus],
658
+ );
659
+ const handleFieldBlur = useCallback(
660
+ (event: React.FocusEvent) => {
661
+ onBlur?.(event);
662
+ },
663
+ [onBlur],
664
+ );
665
+ // While the flow is open, the sheet announces its own content from inside
666
+ // its dialog; this component announces only with the flow closed, and
667
+ // `useAnnounce` routes those messages by where the field is — into a Dialog
668
+ // or modal Drawer that holds it, or the shared channel when no modal does.
669
+ // A result count that changed during the flow's exit is spoken once the
670
+ // flow has fully closed and focus is back on the trigger: the closing
671
+ // dialog stays modal through its exit, so neither its region nor the
672
+ // page-body channel is heard before.
673
+ const afterCloseResultCountRef = useRef<string | null>(null);
674
+ const triggerId = useId();
675
+ const triggerLabelId = useId();
676
+ const triggerContentId = useId();
677
+ const statusMessageId = useId();
678
+ // A field that becomes disabled cannot keep its flow open, and must not
679
+ // reopen it when re-enabled. Adjusted in the
680
+ // render that sees the change, not an effect, so no frame shows both.
681
+ const [wasDisabled, setWasDisabled] = useState(isDisabled);
682
+ if (isDisabled !== wasDisabled) {
683
+ setWasDisabled(isDisabled);
684
+ if (isDisabled && isSheetOpen) {
685
+ setIsSheetOpen(false);
686
+ }
687
+ }
688
+ useDevWarning(
689
+ 'PowerSearch',
690
+ '`menuWidth` has no surface in the bottom-sheet presentation: the sheet flow has no typeahead popover to size. Scope it to the popover presentation or leave it out.',
691
+ isSheetPresentation && menuWidth != null,
692
+ );
693
+ useDevWarning(
694
+ 'PowerSearch',
695
+ '`tokenOverflowBehavior` has no surface in the bottom-sheet presentation: the trigger wraps its display-only tokens. Scope it to the popover presentation or leave it out.',
696
+ isSheetPresentation && tokenOverflowBehavior != null,
697
+ );
698
+
540
699
  const [popoverState, setPopoverStateRaw] = useState<PopoverState>({
541
700
  type: 'idle',
542
701
  });
@@ -552,7 +711,7 @@ export function PowerSearch({
552
711
  const operatorLabel = operator ? resolveOperatorLabel(operator, t) : '';
553
712
  const label = `${fieldLabel}: ${operatorLabel}`.trim();
554
713
  const value = operator
555
- ? tokenValueText(
714
+ ? formatTokenValueText(
556
715
  operator.value,
557
716
  filter.value,
558
717
  Math.max(
@@ -560,11 +719,14 @@ export function PowerSearch({
560
719
  10,
561
720
  ),
562
721
  locale,
722
+ undefined,
723
+ undefined,
724
+ timezoneID,
563
725
  )
564
726
  : null;
565
727
  return {label, value, full: value != null ? `${label} ${value}` : label};
566
728
  },
567
- [config, maxTokenLength, t, locale],
729
+ [config, maxTokenLength, t, locale, timezoneID],
568
730
  );
569
731
  // The built-in token being edited. A token from a `components` override
570
732
  // names itself, so its editor keeps the plain popover.
@@ -581,9 +743,21 @@ export function PowerSearch({
581
743
  componentOverrides?.[editedOperator.value.type]?.Token == null
582
744
  ? editedFilter
583
745
  : undefined;
746
+ // Whether the editor popover is open, for the hand-off below, which runs
747
+ // in the commit that swaps presentations.
748
+ const isPopoverOpenRef = useRef(false);
749
+ // When `adaptive` flips to the sheet while the editor popover is open (a
750
+ // phone rotated), the editing ends with the popover: its state returns to
751
+ // idle (through the raw setter, since the tokenizer input is gone), in the
752
+ // render that sees the swap, so a later switch back does not reopen a stale
753
+ // editor.
754
+ if (isSheetPresentation && popoverState.type !== 'idle') {
755
+ setPopoverStateRaw({type: 'idle'});
756
+ }
584
757
 
585
758
  // Layer for positioning the edit popover anchored to the tokenizer
586
759
  const handleLayerHide = useCallback(() => {
760
+ isPopoverOpenRef.current = false;
587
761
  setPopoverStateRaw({type: 'idle'});
588
762
  }, []);
589
763
 
@@ -604,9 +778,54 @@ export function PowerSearch({
604
778
  : undefined,
605
779
  });
606
780
 
781
+ // A presentation swap removes the focused control, so the other
782
+ // presentation's primary control (the combobox input or the trigger) takes
783
+ // focus and focus never falls to the page body. Each presentation keys its
784
+ // root, so a swap mounts a new root. The detaching root notes that it held
785
+ // focus, which also covers an input focused by server-rendered `autofocus`
786
+ // before hydration. The editor popover is portaled outside the root, so an
787
+ // open editor counts as held focus. The attaching root gives focus to its
788
+ // primary control when focus has fallen to the page body; the sheet root
789
+ // also hides a popover layer left open.
790
+ const isFocusPendingHandOffRef = useRef(false);
791
+ const hidePopover = popover.hide;
792
+ const handOffFocusRef = useCallback(
793
+ (node: HTMLDivElement | null) => {
794
+ if (node == null) {
795
+ return;
796
+ }
797
+ const trigger = triggerRef.current;
798
+ const isSheetRoot = trigger != null && node.contains(trigger);
799
+ // The sheet presentation preloads the flow's chunk, so the first press
800
+ // opens without waiting for the network; nothing renders.
801
+ if (isSheetRoot) {
802
+ void loadPowerSearchBottomSheet();
803
+ }
804
+ if (isSheetRoot && isPopoverOpenRef.current) {
805
+ isPopoverOpenRef.current = false;
806
+ hidePopover();
807
+ }
808
+ const takesFocus = isFocusPendingHandOffRef.current;
809
+ isFocusPendingHandOffRef.current = false;
810
+ if (takesFocus && isFocusDetached(node.ownerDocument)) {
811
+ focusAfterSwap(isSheetRoot ? trigger : tokenizerRef.current);
812
+ }
813
+ return () => {
814
+ if (
815
+ node.contains(node.ownerDocument.activeElement) ||
816
+ isPopoverOpenRef.current
817
+ ) {
818
+ isFocusPendingHandOffRef.current = true;
819
+ }
820
+ };
821
+ },
822
+ [focusAfterSwap, hidePopover],
823
+ );
824
+
607
825
  // Wrapper that manages layer visibility and tokenizer focus alongside state
608
826
  const setPopoverState = useCallback(
609
827
  (state: PopoverState) => {
828
+ isPopoverOpenRef.current = state.type !== 'idle';
610
829
  setPopoverStateRaw(state);
611
830
  if (state.type !== 'idle') {
612
831
  // Schedule after the current frame so React can render the popover
@@ -623,13 +842,22 @@ export function PowerSearch({
623
842
  [popover],
624
843
  );
625
844
 
626
- // Expose imperative handle
845
+ // Expose imperative handle; in the sheet presentation the trigger is the
846
+ // field's focusable control.
627
847
  useImperativeHandle(handleRef, () => ({
628
848
  focusTypeahead() {
629
- tokenizerRef.current?.focus();
849
+ if (isSheetPresentation) {
850
+ triggerRef.current?.focus();
851
+ } else {
852
+ tokenizerRef.current?.focus();
853
+ }
630
854
  },
631
855
  blurTypeahead() {
632
- tokenizerRef.current?.blur();
856
+ if (isSheetPresentation) {
857
+ triggerRef.current?.blur();
858
+ } else {
859
+ tokenizerRef.current?.blur();
860
+ }
633
861
  },
634
862
  }));
635
863
 
@@ -833,10 +1061,12 @@ export function PowerSearch({
833
1061
  setPopoverState({type: 'idle'});
834
1062
  }, [setPopoverState]);
835
1063
 
836
- // Custom token renderer
837
- // Custom token renderer
838
- const renderToken = useCallback(
839
- (item: PowerSearchItem, onRemove: () => void) => {
1064
+ // Token renderer for both presentations. Given `onRemove`, the token is
1065
+ // the tokenizer field's: click edits, × removes. Without it (the sheet
1066
+ // presentation's trigger) the token is display-only; the field as a whole
1067
+ // opens the flow.
1068
+ const renderFilterToken = useCallback(
1069
+ (item: PowerSearchItem, onRemove: (() => void) | undefined) => {
840
1070
  const auxData = item.auxiliaryData;
841
1071
  const filterIndex = auxData?.filterIndex ?? -1;
842
1072
  const filter = filterIndex >= 0 ? filters[filterIndex] : undefined;
@@ -846,7 +1076,8 @@ export function PowerSearch({
846
1076
  ? config.getOperator(auxData.fieldKey, auxData.operatorKey)
847
1077
  : undefined;
848
1078
 
849
- const canInteract = !isReadOnly && !isDisabled && !filter?.isReadOnly;
1079
+ const canInteract =
1080
+ onRemove != null && !isReadOnly && !isDisabled && !filter?.isReadOnly;
850
1081
  const handleClick = canInteract
851
1082
  ? () => handleTokenClick(filterIndex)
852
1083
  : undefined;
@@ -868,6 +1099,7 @@ export function PowerSearch({
868
1099
  onClick={handleClick}
869
1100
  onRemove={handleRemove}
870
1101
  isDisabled={isDisabled}
1102
+ timezoneID={timezoneID}
871
1103
  />
872
1104
  );
873
1105
  }
@@ -944,6 +1176,7 @@ export function PowerSearch({
944
1176
  );
945
1177
  },
946
1178
  [
1179
+ timezoneID,
947
1180
  filters,
948
1181
  config,
949
1182
  configProp,
@@ -959,6 +1192,12 @@ export function PowerSearch({
959
1192
  ],
960
1193
  );
961
1194
 
1195
+ const renderToken = useCallback(
1196
+ (item: PowerSearchItem, onRemove: () => void) =>
1197
+ renderFilterToken(item, onRemove),
1198
+ [renderFilterToken],
1199
+ );
1200
+
962
1201
  // Custom typeahead item renderer — adds field icon and description
963
1202
  const renderItem = useCallback(
964
1203
  (item: PowerSearchItem) => {
@@ -1032,6 +1271,7 @@ export function PowerSearch({
1032
1271
  onCancel={handlePopoverCancel}
1033
1272
  saveButtonLabel={popoverSaveButtonLabel}
1034
1273
  isReadOnly={isReadOnly}
1274
+ timezoneID={timezoneID}
1035
1275
  />
1036
1276
  );
1037
1277
  }
@@ -1047,9 +1287,11 @@ export function PowerSearch({
1047
1287
  saveButtonLabel={popoverSaveButtonLabel}
1048
1288
  maxMenuItems={maxOperatorMenuItems}
1049
1289
  isReadOnly={isReadOnly}
1290
+ timezoneID={timezoneID}
1050
1291
  />
1051
1292
  );
1052
1293
  }, [
1294
+ timezoneID,
1053
1295
  popoverPartialFilter,
1054
1296
  popoverState,
1055
1297
  EditorOverride,
@@ -1076,13 +1318,18 @@ export function PowerSearch({
1076
1318
  return resultCount;
1077
1319
  }, [resultCount, t]);
1078
1320
 
1079
- // Announce result-count changes to screen readers through a polite live
1080
- // region, mirroring the way Typeahead announces its dropdown result count.
1081
- // The count is otherwise only shown visually and stays silent to assistive
1082
- // tech. Only a change after mount is spoken: the guard compares against the
1083
- // last announced text rather than a mounted flag, so StrictMode's effect
1084
- // replay (which resets a flag and would speak the count present on mount
1085
- // unprompted) announces nothing until the count changes.
1321
+ // This Effect synchronizes a polite live region with the result count,
1322
+ // mirroring the way Typeahead announces its dropdown result count: the count
1323
+ // is otherwise only shown visually and stays silent to assistive tech. The
1324
+ // region follows where the field is: the modal that holds the field (a
1325
+ // Dialog, a modal Drawer), else the shared channel. While a sheet of the flow is open the sheet itself announces the
1326
+ // count from inside its dialog, and this Effect stays quiet. Only a
1327
+ // change after mount is spoken — the count present on mount is not
1328
+ // announced unprompted — and the guard compares against the last announced
1329
+ // text rather than a mounted flag, so StrictMode's effect replay announces
1330
+ // nothing either. A change that lands while the flow is closing waits for
1331
+ // the trigger focus, since the dialog is still modal through its exit. The
1332
+ // announcement acquires no resource, so there is no cleanup.
1086
1333
  const announce = useAnnounce();
1087
1334
  // PowerSearch words its Clear all as its record does; the field announces
1088
1335
  // these words instead of Tokenizer's (component:Tokenizer AR7).
@@ -1095,15 +1342,20 @@ export function PowerSearch({
1095
1342
  [hasReadOnlyFilter, t],
1096
1343
  );
1097
1344
  const announcedResultCountRef = useRef(resultCountText);
1345
+ const isSheetExiting = isSheetPresented && !isSheetOpen;
1098
1346
  useEffect(() => {
1099
1347
  if (resultCountText === announcedResultCountRef.current) {
1100
1348
  return;
1101
1349
  }
1102
1350
  announcedResultCountRef.current = resultCountText;
1103
1351
  if (resultCountText != null) {
1104
- announce(resultCountText);
1352
+ if (isSheetExiting) {
1353
+ afterCloseResultCountRef.current = resultCountText;
1354
+ } else if (!isSheetOpen) {
1355
+ announce(resultCountText);
1356
+ }
1105
1357
  }
1106
- }, [resultCountText, announce]);
1358
+ }, [resultCountText, announce, isSheetExiting, isSheetOpen]);
1107
1359
 
1108
1360
  // Build combined endContent from resultCount + endContent props
1109
1361
  const combinedEndContent = useMemo((): React.ReactNode => {
@@ -1124,14 +1376,231 @@ export function PowerSearch({
1124
1376
  return resultCountNode || endContent || undefined;
1125
1377
  }, [resultCountText, endContent]);
1126
1378
 
1379
+ // ------------------------------------------------------- sheet trigger --
1380
+
1381
+ // Disabled-reason tooltip for the sheet trigger. Disabled controls swallow
1382
+ // pointer events, so the listeners attach to the field's control box; the
1383
+ // trigger stays perceivable through aria-disabled.
1384
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
1385
+ const disabledMessageTooltip = useTooltip({
1386
+ placement: 'above',
1387
+ focusTrigger: 'always',
1388
+ isEnabled: isSheetPresentation && showsDisabledMessage,
1389
+ });
1390
+ const rootRef = useMergedRefs(
1391
+ ref,
1392
+ popover.triggerRef as React.Ref<HTMLDivElement>,
1393
+ handOffFocusRef,
1394
+ );
1395
+
1396
+ const openSheet = useCallback(() => {
1397
+ if (isDisabled) {
1398
+ return;
1399
+ }
1400
+ // The flow restores focus to the element that was active when it opened;
1401
+ // make sure that is the trigger even for a tap on a token.
1402
+ triggerRef.current?.focus({preventScroll: true});
1403
+ setIsSheetPresented(true);
1404
+ setIsSheetOpen(true);
1405
+ }, [isDisabled]);
1406
+
1407
+ // The field's end lane — the result count and `endContent` — sits outside
1408
+ // the trigger button: a caller's action button there keeps its own tab stop
1409
+ // and taps, and an interactive control must not nest in a component
1410
+ // button, so a tap on it must not open the flow.
1411
+ const endLaneRef = useRef<HTMLDivElement>(null);
1412
+ const handleTriggerWrapperClick = useCallback(
1413
+ (event: React.MouseEvent<HTMLDivElement>) => {
1414
+ // Tokens are display-only; a tap on them or the empty space around them
1415
+ // opens the flow. The trigger button handles its own click, and the end
1416
+ // lane handles its own.
1417
+ if (
1418
+ event.target instanceof Node &&
1419
+ (triggerRef.current?.contains(event.target) ||
1420
+ endLaneRef.current?.contains(event.target))
1421
+ ) {
1422
+ return;
1423
+ }
1424
+ openSheet();
1425
+ },
1426
+ [openSheet],
1427
+ );
1428
+
1429
+ const handleTriggerFocusWithin = useCallback(
1430
+ (event: React.FocusEvent<HTMLDivElement>) => {
1431
+ // The flow focuses the trigger only after its exit completes; that focus
1432
+ // is the signal that the retained sheets can leave. If the resolution
1433
+ // moved to the popover meanwhile, the tokenizer input takes over the
1434
+ // focus once it mounts (the hand-off above).
1435
+ if (!isSheetOpen && isSheetPresented) {
1436
+ setIsSheetPresented(false);
1437
+ // The dialogs have closed: the page-body channel is exposed again.
1438
+ const resultCount = afterCloseResultCountRef.current;
1439
+ afterCloseResultCountRef.current = null;
1440
+ if (resultCount != null) {
1441
+ announce(resultCount);
1442
+ }
1443
+ }
1444
+ if (
1445
+ !(event.relatedTarget instanceof Node) ||
1446
+ !event.currentTarget.contains(event.relatedTarget)
1447
+ ) {
1448
+ handleFieldFocus(event);
1449
+ }
1450
+ },
1451
+ [announce, handleFieldFocus, isSheetOpen, isSheetPresented],
1452
+ );
1453
+ const handleTriggerBlurWithin = useCallback(
1454
+ (event: React.FocusEvent<HTMLDivElement>) => {
1455
+ if (
1456
+ !(event.relatedTarget instanceof Node) ||
1457
+ !event.currentTarget.contains(event.relatedTarget)
1458
+ ) {
1459
+ handleFieldBlur(event);
1460
+ }
1461
+ },
1462
+ [handleFieldBlur],
1463
+ );
1464
+
1465
+ if (isSheetPresentation) {
1466
+ const triggerDescribedBy =
1467
+ [
1468
+ status?.message ? statusMessageId : null,
1469
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
1470
+ ]
1471
+ .filter(Boolean)
1472
+ .join(' ') || undefined;
1473
+
1474
+ return (
1475
+ <div key="sheet" ref={rootRef} {...themeProps('power-search')}>
1476
+ <Field
1477
+ label={label}
1478
+ isLabelHidden={isLabelHidden}
1479
+ inputID={triggerId}
1480
+ labelID={triggerLabelId}
1481
+ isDisabled={isDisabled}
1482
+ status={
1483
+ status
1484
+ ? {
1485
+ type: status.type,
1486
+ message: status.message,
1487
+ messageID: status.message ? statusMessageId : undefined,
1488
+ }
1489
+ : undefined
1490
+ }
1491
+ statusVariant={statusVariant}
1492
+ xstyle={xstyle}
1493
+ className={className}
1494
+ style={style}>
1495
+ <div
1496
+ ref={disabledMessageTooltip.ref}
1497
+ onClick={handleTriggerWrapperClick}
1498
+ onFocus={handleTriggerFocusWithin}
1499
+ onBlur={handleTriggerBlurWithin}
1500
+ aria-disabled={isDisabled ? true : undefined}
1501
+ {...stylex.props(
1502
+ inputWrapperStyles.base,
1503
+ sheetTriggerStyles.wrapper,
1504
+ filters.length > 0 && sheetTriggerStyles.wrapperWithTokens,
1505
+ sheetTriggerSizeStyles[size],
1506
+ isDisabled && inputWrapperStyles.disabled,
1507
+ status && inputStatusBorderStyles[status.type],
1508
+ status &&
1509
+ !isDisabled &&
1510
+ inputStatusHoverShadowStyles[status.type],
1511
+ status && inputStatusFocusWithinStyles[status.type],
1512
+ )}>
1513
+ {/* The trigger's name is the label plus everything visible here:
1514
+ the placeholder, or the tokens, so speech input can say what
1515
+ it sees and assistive technology hears the selected filters
1516
+ (WCAG 2.5.3). */}
1517
+ <div
1518
+ id={triggerContentId}
1519
+ {...stylex.props(sheetTriggerStyles.contentSection)}>
1520
+ {isRenderable(startIcon as React.ReactNode) && (
1521
+ <span
1522
+ {...stylex.props(
1523
+ filters.length > 0 &&
1524
+ sheetTriggerStyles.startIconWithTokens,
1525
+ )}>
1526
+ {renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
1527
+ </span>
1528
+ )}
1529
+ {tokenizerValue.map(item => (
1530
+ <React.Fragment key={item.id}>
1531
+ {renderFilterToken(item, undefined)}
1532
+ </React.Fragment>
1533
+ ))}
1534
+ <button
1535
+ type="button"
1536
+ id={triggerId}
1537
+ ref={triggerRef}
1538
+ // The field's primary control: `data-testid` lands here, as it
1539
+ // lands on the combobox input in the popover presentation.
1540
+ data-testid={testId}
1541
+ autoFocus={autoFocusOnMount}
1542
+ onClick={openSheet}
1543
+ aria-disabled={isDisabled ? true : undefined}
1544
+ aria-haspopup="dialog"
1545
+ aria-expanded={isSheetOpen}
1546
+ aria-labelledby={`${triggerLabelId} ${triggerContentId}`}
1547
+ aria-describedby={triggerDescribedBy}
1548
+ {...stylex.props(
1549
+ sheetTriggerStyles.trigger,
1550
+ filters.length > 0 && sheetTriggerStyles.triggerWithTokens,
1551
+ )}>
1552
+ {filters.length === 0 ? placeholder : null}
1553
+ </button>
1554
+ </div>
1555
+ {(endContent || isRenderable(resultCountText)) && (
1556
+ <div
1557
+ ref={endLaneRef}
1558
+ {...stylex.props(sheetTriggerStyles.endSection)}>
1559
+ {isRenderable(resultCountText) && (
1560
+ <span {...stylex.props(resultCountStyles.text)}>
1561
+ {resultCountText}
1562
+ </span>
1563
+ )}
1564
+ {endContent}
1565
+ </div>
1566
+ )}
1567
+ </div>
1568
+ {showsDisabledMessage &&
1569
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
1570
+ </Field>
1571
+ {/* Mounted from the press until the flow's exit has completed and
1572
+ focus is back on the trigger (`isSheetPresented`): at rest the
1573
+ field renders its trigger alone — no closed dialogs, rows, search
1574
+ field or editors in the DOM. */}
1575
+ {(isSheetOpen || isSheetPresented) && (
1576
+ <PowerSearchSheetHost
1577
+ isOpen={isSheetOpen}
1578
+ onOpenChange={setIsSheetOpen}
1579
+ finalFocusRef={triggerRef}
1580
+ resultCountText={resultCountText}
1581
+ config={config}
1582
+ configProp={configProp}
1583
+ filters={filters}
1584
+ onChange={onChange}
1585
+ searchSource={searchSource}
1586
+ placeholder={placeholder}
1587
+ isReadOnly={isReadOnly}
1588
+ isDisabled={isDisabled}
1589
+ hasClear={hasClear}
1590
+ maxOperatorMenuItems={maxOperatorMenuItems}
1591
+ timezoneID={timezoneID}
1592
+ maxSearchResults={maxSearchResults}
1593
+ saveButtonLabel={popoverSaveButtonLabelFromProps}
1594
+ components={componentOverrides}
1595
+ />
1596
+ )}
1597
+ </div>
1598
+ );
1599
+ }
1600
+
1127
1601
  return (
1128
1602
  <>
1129
- <div
1130
- ref={useMergedRefs(
1131
- ref,
1132
- popover.triggerRef as React.Ref<HTMLDivElement>,
1133
- )}
1134
- {...themeProps('power-search')}>
1603
+ <div key="popover" ref={rootRef} {...themeProps('power-search')}>
1135
1604
  {/* The field's Clear all announces PowerSearch's own words
1136
1605
  (component:PowerSearch; component:Tokenizer AR7). */}
1137
1606
  <TokenizerClearAllMessageContext value={clearAllMessage}>
@@ -1148,7 +1617,7 @@ export function PowerSearch({
1148
1617
  maxMenuItems={MAX_BROWSE_MENU_ITEMS}
1149
1618
  menuWidth={menuWidth}
1150
1619
  placeholder={filters.length === 0 ? placeholder : ''}
1151
- hasAutoFocus={hasAutoFocus}
1620
+ hasAutoFocus={autoFocusOnMount}
1152
1621
  hasClear={hasClear && !isReadOnly}
1153
1622
  startIcon={startIcon}
1154
1623
  endContent={combinedEndContent}
@@ -1160,12 +1629,13 @@ export function PowerSearch({
1160
1629
  debounceMs={0}
1161
1630
  status={status}
1162
1631
  statusVariant={statusVariant}
1163
- onFocus={onFocus}
1164
- onBlur={onBlur}
1632
+ onFocus={handleFieldFocus}
1633
+ onBlur={handleFieldBlur}
1165
1634
  containerXstyle={xstyle}
1166
1635
  containerClassName={className}
1167
1636
  containerStyle={style}
1168
1637
  data-testid={testId}
1638
+ presentation="popover"
1169
1639
  />
1170
1640
  </TokenizerItemRemovableContext>
1171
1641
  </TokenizerClearAllMessageContext>