@passui/react 0.1.17 → 0.1.20

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 (408) hide show
  1. package/README.md +140 -1
  2. package/THIRD_PARTY_NOTICES.md +25 -0
  3. package/dist/types/src/appearance/index.d.ts +2 -0
  4. package/dist/types/src/appearance/platform.d.ts +9 -0
  5. package/dist/types/src/appearance/theme.d.ts +71 -0
  6. package/dist/types/src/components/accordion/accordion.d.ts +19 -0
  7. package/dist/types/src/components/ambient/ambient.d.ts +12 -0
  8. package/dist/types/src/components/ambient/index.d.ts +1 -0
  9. package/dist/types/src/components/aspect-ratio/aspect-ratio.d.ts +7 -0
  10. package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +16 -0
  11. package/dist/types/src/components/button/button-styles.d.ts +12 -0
  12. package/dist/types/src/components/button/button.d.ts +32 -0
  13. package/dist/types/src/components/button/icon-button.d.ts +10 -0
  14. package/dist/types/src/components/button/pending-spinner.d.ts +5 -0
  15. package/dist/types/src/components/button/use-pending-indicator.d.ts +2 -0
  16. package/dist/types/src/components/chip/chip.d.ts +7 -0
  17. package/dist/types/src/components/code-block/code-block.d.ts +10 -0
  18. package/dist/types/src/components/combo-box/combo-box.d.ts +9 -0
  19. package/dist/types/src/components/command/command.d.ts +18 -0
  20. package/dist/types/src/components/compact-date-picker/calendar-month.d.ts +25 -0
  21. package/dist/types/src/components/compact-date-picker/compact-date-picker.d.ts +27 -0
  22. package/dist/types/src/components/compact-time-picker/compact-time-picker.d.ts +26 -0
  23. package/dist/types/src/components/compact-time-picker/time-value.d.ts +18 -0
  24. package/dist/types/src/components/content-unavailable-view/content-unavailable-view.d.ts +9 -0
  25. package/dist/types/src/components/content-view/content-view.d.ts +48 -0
  26. package/dist/types/src/components/content-view/index.d.ts +1 -0
  27. package/dist/types/src/components/context-menu/conceal-menu-source.d.ts +2 -0
  28. package/dist/types/src/components/context-menu/context-menu-host.d.ts +2 -0
  29. package/dist/types/src/components/context-menu/context-menu-store.d.ts +57 -0
  30. package/dist/types/src/components/context-menu/menu-navigation.d.ts +6 -0
  31. package/dist/types/src/components/context-menu/menu-position.d.ts +21 -0
  32. package/dist/types/src/components/context-menu/menu-reveal.d.ts +17 -0
  33. package/dist/types/src/components/context-menu/use-context-menu.d.ts +14 -0
  34. package/dist/types/src/components/conversation/conversation.d.ts +43 -0
  35. package/dist/types/src/components/conversation/index.d.ts +1 -0
  36. package/dist/types/src/components/date-picker/date-picker.d.ts +16 -0
  37. package/dist/types/src/components/date-picker/date-value.d.ts +2 -0
  38. package/dist/types/src/components/dialog/dialog-host.d.ts +6 -0
  39. package/dist/types/src/components/dialog/dialog-store.d.ts +23 -0
  40. package/dist/types/src/components/dialog/dialog.d.ts +30 -0
  41. package/dist/types/src/components/dialog/index.d.ts +1 -0
  42. package/dist/types/src/components/disclosure-group/disclosure-group.d.ts +15 -0
  43. package/dist/types/src/components/divider/divider.d.ts +9 -0
  44. package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +15 -0
  45. package/dist/types/src/components/keyboard-shortcut/keyboard-shortcut.d.ts +5 -0
  46. package/dist/types/src/components/labeled-content/labeled-content.d.ts +19 -0
  47. package/dist/types/src/components/locale-switcher/index.d.ts +1 -0
  48. package/dist/types/src/components/locale-switcher/locale-switcher.d.ts +10 -0
  49. package/dist/types/src/components/menu/menu.d.ts +16 -0
  50. package/dist/types/src/components/one-time-code/one-time-code.d.ts +18 -0
  51. package/dist/types/src/components/picker/picker-trigger.d.ts +2 -0
  52. package/dist/types/src/components/picker/picker.d.ts +43 -0
  53. package/dist/types/src/components/popover/anchored-surface.d.ts +13 -0
  54. package/dist/types/src/components/popover/popover-fit.d.ts +21 -0
  55. package/dist/types/src/components/popover/popover.d.ts +18 -0
  56. package/dist/types/src/components/profile-image/profile-image.d.ts +4 -0
  57. package/dist/types/src/components/progress-view/progress-view.d.ts +13 -0
  58. package/dist/types/src/components/progress-view/use-liquid-progress.d.ts +3 -0
  59. package/dist/types/src/components/radio-group/radio-group.d.ts +17 -0
  60. package/dist/types/src/components/search-field/expanding-search-field.d.ts +5 -0
  61. package/dist/types/src/components/search-field/search-field.d.ts +6 -0
  62. package/dist/types/src/components/search-list/search-list.d.ts +19 -0
  63. package/dist/types/src/components/section/section.d.ts +15 -0
  64. package/dist/types/src/components/secure-field/secure-field.d.ts +4 -0
  65. package/dist/types/src/components/segmented-control/segmented-control.d.ts +20 -0
  66. package/dist/types/src/components/sheet/modal-environment.d.ts +2 -0
  67. package/dist/types/src/components/sheet/sheet-portal.d.ts +5 -0
  68. package/dist/types/src/components/sheet/sheet.d.ts +21 -0
  69. package/dist/types/src/components/sheet/use-sheet-focus.d.ts +3 -0
  70. package/dist/types/src/components/sidebar/sidebar.d.ts +19 -0
  71. package/dist/types/src/components/skeleton/skeleton.d.ts +6 -0
  72. package/dist/types/src/components/slider/slider.d.ts +14 -0
  73. package/dist/types/src/components/slosh-slider/liquid-step.d.ts +6 -0
  74. package/dist/types/src/components/slosh-slider/slosh-slider.d.ts +18 -0
  75. package/dist/types/src/components/stat-tile/index.d.ts +1 -0
  76. package/dist/types/src/components/stat-tile/stat-tile.d.ts +15 -0
  77. package/dist/types/src/components/status-indicator/status-indicator.d.ts +14 -0
  78. package/dist/types/src/components/stepper/rolling-digit.d.ts +11 -0
  79. package/dist/types/src/components/stepper/stepper.d.ts +19 -0
  80. package/dist/types/src/components/swatch-picker/swatch-picker.d.ts +17 -0
  81. package/dist/types/src/components/tab-bar/tab-bar.d.ts +40 -0
  82. package/dist/types/src/components/tab-view/tab-view.d.ts +24 -0
  83. package/dist/types/src/components/table/table.d.ts +10 -0
  84. package/dist/types/src/components/text-editor/text-editor.d.ts +2 -0
  85. package/dist/types/src/components/text-field/text-field.d.ts +20 -0
  86. package/dist/types/src/components/toast/toast-store.d.ts +47 -0
  87. package/dist/types/src/components/toast/toaster.d.ts +10 -0
  88. package/dist/types/src/components/toggle/toggle.d.ts +17 -0
  89. package/dist/types/src/components/tooltip/tooltip-host.d.ts +6 -0
  90. package/dist/types/src/components/view-controller/index.d.ts +1 -0
  91. package/dist/types/src/components/view-controller/view-controller.d.ts +21 -0
  92. package/dist/types/src/components/wake-slider/wake-slider.d.ts +12 -0
  93. package/dist/types/src/hooks/scroll/minimize-decision.d.ts +2 -0
  94. package/dist/types/src/hooks/use-anchored-disclosure.d.ts +9 -0
  95. package/dist/types/src/hooks/use-anchored-placement.d.ts +7 -0
  96. package/dist/types/src/hooks/use-dismiss.d.ts +9 -0
  97. package/dist/types/src/hooks/use-floating-dismiss.d.ts +3 -0
  98. package/dist/types/src/hooks/use-media-query.d.ts +21 -0
  99. package/dist/types/src/hooks/use-minimize-on-scroll.d.ts +6 -0
  100. package/dist/types/src/hooks/use-visual-viewport.d.ts +4 -0
  101. package/dist/types/src/hooks/viewport/types.d.ts +19 -0
  102. package/dist/types/src/hooks/viewport/viewport-metrics.d.ts +3 -0
  103. package/dist/types/src/icons/icons.d.ts +612 -0
  104. package/dist/types/src/index.d.ts +71 -0
  105. package/dist/types/src/lib/anchored-placement.d.ts +39 -0
  106. package/dist/types/src/lib/class-names.d.ts +2 -0
  107. package/dist/types/src/lib/clipboard.d.ts +6 -0
  108. package/dist/types/src/lib/date.d.ts +50 -0
  109. package/dist/types/src/lib/floating-layers.d.ts +8 -0
  110. package/dist/types/src/lib/haptics.d.ts +8 -0
  111. package/dist/types/src/lib/language.d.ts +4 -0
  112. package/dist/types/src/lib/range-value.d.ts +4 -0
  113. package/dist/types/src/lib/screen-edges.d.ts +12 -0
  114. package/dist/types/src/lib/shapes.d.ts +17 -0
  115. package/dist/types/src/motion/transitions.d.ts +97 -0
  116. package/dist/types/src/tokens.d.ts +1 -0
  117. package/package.json +248 -40
  118. package/src/appearance/platform.tsx +10 -47
  119. package/src/appearance/theme.tsx +42 -142
  120. package/src/components/accordion/accordion.tsx +48 -0
  121. package/src/components/ambient/ambient.tsx +2 -10
  122. package/src/components/aspect-ratio/aspect-ratio.tsx +22 -0
  123. package/src/components/breadcrumb/breadcrumb.tsx +53 -0
  124. package/src/components/button/button-styles.ts +13 -31
  125. package/src/components/button/button.tsx +24 -15
  126. package/src/components/button/icon-button.tsx +14 -56
  127. package/src/components/button/use-pending-indicator.ts +2 -7
  128. package/src/components/chip/chip.tsx +8 -13
  129. package/src/components/code-block/code-block.tsx +41 -0
  130. package/src/components/combo-box/combo-box.tsx +13 -0
  131. package/src/components/command/command.tsx +119 -0
  132. package/src/components/compact-date-picker/calendar-month.ts +13 -57
  133. package/src/components/compact-date-picker/compact-date-picker.tsx +72 -195
  134. package/src/components/compact-time-picker/compact-time-picker.tsx +74 -195
  135. package/src/components/compact-time-picker/time-value.ts +4 -16
  136. package/src/components/content-unavailable-view/content-unavailable-view.tsx +37 -0
  137. package/src/components/content-view/content-view.tsx +13 -56
  138. package/src/components/context-menu/conceal-menu-source.ts +1 -4
  139. package/src/components/context-menu/context-menu-host.tsx +132 -198
  140. package/src/components/context-menu/context-menu-store.ts +5 -14
  141. package/src/components/context-menu/menu-navigation.ts +29 -0
  142. package/src/components/context-menu/menu-position.ts +6 -27
  143. package/src/components/context-menu/menu-reveal.ts +14 -52
  144. package/src/components/context-menu/use-context-menu.ts +6 -30
  145. package/src/components/conversation/conversation.tsx +26 -72
  146. package/src/components/date-picker/date-picker.tsx +8 -37
  147. package/src/components/date-picker/date-value.ts +5 -30
  148. package/src/components/dialog/dialog-host.tsx +37 -76
  149. package/src/components/dialog/dialog-store.ts +16 -7
  150. package/src/components/dialog/dialog.tsx +23 -80
  151. package/src/components/disclosure-group/disclosure-group.tsx +79 -0
  152. package/src/components/divider/divider.tsx +31 -0
  153. package/src/components/inline-disclosure/inline-disclosure.tsx +49 -0
  154. package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +17 -0
  155. package/src/components/labeled-content/labeled-content.tsx +24 -18
  156. package/src/components/locale-switcher/locale-switcher.tsx +20 -70
  157. package/src/components/menu/menu.tsx +68 -0
  158. package/src/components/one-time-code/one-time-code.tsx +168 -0
  159. package/src/components/picker/picker-trigger.ts +10 -0
  160. package/src/components/picker/picker.tsx +165 -283
  161. package/src/components/popover/anchored-surface.tsx +223 -0
  162. package/src/components/popover/popover-fit.ts +69 -0
  163. package/src/components/popover/popover.tsx +209 -63
  164. package/src/components/profile-image/profile-image.tsx +6 -28
  165. package/src/components/progress-view/progress-view.tsx +70 -0
  166. package/src/components/progress-view/use-liquid-progress.ts +43 -0
  167. package/src/components/radio-group/radio-group.tsx +63 -0
  168. package/src/components/search-field/expanding-search-field.tsx +183 -0
  169. package/src/components/search-field/search-field.tsx +25 -7
  170. package/src/components/search-list/search-list.tsx +58 -75
  171. package/src/components/section/section.tsx +18 -17
  172. package/src/components/secure-field/secure-field.tsx +5 -7
  173. package/src/components/segmented-control/segmented-control.tsx +20 -29
  174. package/src/components/sheet/modal-environment.ts +16 -54
  175. package/src/components/sheet/sheet-portal.tsx +10 -42
  176. package/src/components/sheet/sheet.tsx +17 -25
  177. package/src/components/sheet/use-sheet-focus.ts +15 -51
  178. package/src/components/sidebar/sidebar.tsx +44 -38
  179. package/src/components/skeleton/skeleton.tsx +19 -0
  180. package/src/components/slider/slider.tsx +76 -0
  181. package/src/components/slosh-slider/liquid-step.ts +42 -0
  182. package/src/components/slosh-slider/slosh-slider.tsx +251 -0
  183. package/src/components/stat-tile/stat-tile.tsx +9 -30
  184. package/src/components/status-indicator/status-indicator.tsx +12 -19
  185. package/src/components/stepper/rolling-digit.tsx +77 -0
  186. package/src/components/stepper/stepper.tsx +101 -0
  187. package/src/components/swatch-picker/swatch-picker.tsx +19 -15
  188. package/src/components/tab-bar/tab-bar.tsx +59 -59
  189. package/src/components/tab-view/tab-view.tsx +52 -57
  190. package/src/components/table/table.tsx +14 -62
  191. package/src/components/text-editor/text-editor.tsx +2 -12
  192. package/src/components/text-field/text-field.tsx +53 -44
  193. package/src/components/toast/toast-store.ts +9 -30
  194. package/src/components/toast/toaster.tsx +90 -104
  195. package/src/components/toggle/toggle.tsx +165 -44
  196. package/src/components/tooltip/tooltip-host.tsx +70 -67
  197. package/src/components/view-controller/view-controller.tsx +4 -21
  198. package/src/components/wake-slider/wake-slider.tsx +237 -0
  199. package/src/hooks/scroll/minimize-decision.ts +1 -5
  200. package/src/hooks/use-anchored-disclosure.ts +23 -0
  201. package/src/hooks/use-anchored-placement.ts +7 -14
  202. package/src/hooks/use-dismiss.ts +5 -23
  203. package/src/hooks/use-floating-dismiss.ts +28 -0
  204. package/src/hooks/use-media-query.ts +12 -31
  205. package/src/hooks/use-minimize-on-scroll.ts +10 -34
  206. package/src/hooks/use-visual-viewport.ts +5 -14
  207. package/src/hooks/viewport/viewport-metrics.ts +4 -16
  208. package/src/icons/icons.tsx +229 -183
  209. package/src/index.ts +27 -1
  210. package/src/lib/anchored-placement.ts +12 -18
  211. package/src/lib/class-names.ts +1 -2
  212. package/src/lib/clipboard.ts +14 -0
  213. package/src/lib/date.ts +4 -15
  214. package/src/lib/floating-layers.ts +18 -8
  215. package/src/lib/haptics.ts +1 -3
  216. package/src/lib/language.ts +2 -7
  217. package/src/lib/range-value.ts +32 -0
  218. package/src/lib/screen-edges.ts +2 -6
  219. package/src/lib/shapes.ts +5 -20
  220. package/src/motion/transitions.ts +18 -12
  221. package/src/styles/code-block.css +33 -0
  222. package/src/styles/motion-controls.css +189 -0
  223. package/src/styles/styles.css +3 -0
  224. package/src/components/ambient/ambient.docs.tsx +0 -68
  225. package/src/components/ambient/examples/ambient-images.tsx +0 -30
  226. package/src/components/ambient/examples/ambient-now-playing.tsx +0 -55
  227. package/src/components/ambient/examples/ambient-showcase.tsx +0 -33
  228. package/src/components/ambient/examples/ambient-usage.tsx +0 -19
  229. package/src/components/button/button.docs.tsx +0 -116
  230. package/src/components/button/examples/button-disabled.tsx +0 -19
  231. package/src/components/button/examples/button-icon.tsx +0 -39
  232. package/src/components/button/examples/button-link.tsx +0 -12
  233. package/src/components/button/examples/button-pending.tsx +0 -27
  234. package/src/components/button/examples/button-showcase.tsx +0 -115
  235. package/src/components/button/examples/button-sizes.tsx +0 -12
  236. package/src/components/button/examples/button-usage.tsx +0 -12
  237. package/src/components/button/examples/button-variants.tsx +0 -15
  238. package/src/components/button/examples/button-with-icon.tsx +0 -32
  239. package/src/components/button/examples/icon-button-player.tsx +0 -38
  240. package/src/components/button/examples/icon-button-showcase.tsx +0 -83
  241. package/src/components/button/examples/icon-button-usage.tsx +0 -11
  242. package/src/components/button/examples/icon-button-variants.tsx +0 -39
  243. package/src/components/button/icon-button.docs.tsx +0 -68
  244. package/src/components/chip/chip.docs.tsx +0 -37
  245. package/src/components/chip/examples/chip-in-rows.tsx +0 -19
  246. package/src/components/chip/examples/chip-showcase.tsx +0 -51
  247. package/src/components/chip/examples/chip-tones.tsx +0 -13
  248. package/src/components/chip/examples/chip-usage.tsx +0 -5
  249. package/src/components/compact-date-picker/compact-date-picker.docs.tsx +0 -81
  250. package/src/components/compact-date-picker/examples/compact-date-picker-showcase.tsx +0 -35
  251. package/src/components/compact-date-picker/examples/compact-date-picker-usage.tsx +0 -22
  252. package/src/components/compact-time-picker/compact-time-picker.docs.tsx +0 -80
  253. package/src/components/compact-time-picker/examples/compact-time-picker-showcase.tsx +0 -31
  254. package/src/components/compact-time-picker/examples/compact-time-picker-usage.tsx +0 -21
  255. package/src/components/content-view/content-view.docs.tsx +0 -61
  256. package/src/components/content-view/examples/content-view-header-action.tsx +0 -33
  257. package/src/components/content-view/examples/content-view-showcase.tsx +0 -65
  258. package/src/components/content-view/examples/content-view-stats.tsx +0 -35
  259. package/src/components/content-view/examples/content-view-usage.tsx +0 -14
  260. package/src/components/context-menu/context-menu.docs.tsx +0 -92
  261. package/src/components/context-menu/examples/context-menu-checked.tsx +0 -37
  262. package/src/components/context-menu/examples/context-menu-list.tsx +0 -85
  263. package/src/components/context-menu/examples/context-menu-showcase.tsx +0 -56
  264. package/src/components/context-menu/examples/context-menu-toolbar.tsx +0 -63
  265. package/src/components/context-menu/examples/context-menu-usage.tsx +0 -24
  266. package/src/components/conversation/conversation.docs.tsx +0 -88
  267. package/src/components/conversation/examples/conversation-composer.tsx +0 -36
  268. package/src/components/conversation/examples/conversation-days.tsx +0 -48
  269. package/src/components/conversation/examples/conversation-showcase.tsx +0 -71
  270. package/src/components/conversation/examples/conversation-usage.tsx +0 -28
  271. package/src/components/date-picker/date-picker.docs.tsx +0 -88
  272. package/src/components/date-picker/examples/date-picker-locales.tsx +0 -41
  273. package/src/components/date-picker/examples/date-picker-past-only.tsx +0 -29
  274. package/src/components/date-picker/examples/date-picker-showcase.tsx +0 -49
  275. package/src/components/date-picker/examples/date-picker-usage.tsx +0 -22
  276. package/src/components/dialog/dialog.docs.tsx +0 -114
  277. package/src/components/dialog/examples/dialog-confirm.tsx +0 -24
  278. package/src/components/dialog/examples/dialog-form.tsx +0 -46
  279. package/src/components/dialog/examples/dialog-inline.tsx +0 -48
  280. package/src/components/dialog/examples/dialog-showcase.tsx +0 -53
  281. package/src/components/dialog/examples/dialog-usage.tsx +0 -31
  282. package/src/components/labeled-content/examples/labeled-content-descriptions.tsx +0 -37
  283. package/src/components/labeled-content/examples/labeled-content-showcase.tsx +0 -54
  284. package/src/components/labeled-content/examples/labeled-content-usage.tsx +0 -22
  285. package/src/components/labeled-content/examples/labeled-content-values.tsx +0 -26
  286. package/src/components/labeled-content/examples/labeled-content-wide.tsx +0 -46
  287. package/src/components/labeled-content/labeled-content.docs.tsx +0 -69
  288. package/src/components/locale-switcher/examples/locale-switcher-native-names.tsx +0 -30
  289. package/src/components/locale-switcher/examples/locale-switcher-showcase.tsx +0 -46
  290. package/src/components/locale-switcher/examples/locale-switcher-translated-page.tsx +0 -35
  291. package/src/components/locale-switcher/examples/locale-switcher-usage.tsx +0 -20
  292. package/src/components/locale-switcher/locale-switcher.docs.tsx +0 -91
  293. package/src/components/picker/examples/picker-details.tsx +0 -44
  294. package/src/components/picker/examples/picker-searchable.tsx +0 -48
  295. package/src/components/picker/examples/picker-showcase.tsx +0 -66
  296. package/src/components/picker/examples/picker-usage.tsx +0 -20
  297. package/src/components/picker/picker.docs.tsx +0 -68
  298. package/src/components/popover/examples/popover-help.tsx +0 -57
  299. package/src/components/popover/examples/popover-menu.tsx +0 -51
  300. package/src/components/popover/examples/popover-showcase.tsx +0 -61
  301. package/src/components/popover/examples/popover-usage.tsx +0 -31
  302. package/src/components/popover/popover.docs.tsx +0 -84
  303. package/src/components/profile-image/examples/profile-image-fallbacks.tsx +0 -29
  304. package/src/components/profile-image/examples/profile-image-group.tsx +0 -34
  305. package/src/components/profile-image/examples/profile-image-showcase.tsx +0 -30
  306. package/src/components/profile-image/examples/profile-image-usage.tsx +0 -14
  307. package/src/components/profile-image/profile-image.docs.tsx +0 -81
  308. package/src/components/search-field/examples/search-field-filter.tsx +0 -53
  309. package/src/components/search-field/examples/search-field-showcase.tsx +0 -56
  310. package/src/components/search-field/examples/search-field-toolbar.tsx +0 -27
  311. package/src/components/search-field/examples/search-field-usage.tsx +0 -18
  312. package/src/components/search-field/search-field.docs.tsx +0 -58
  313. package/src/components/search-list/examples/search-list-dropdown.tsx +0 -53
  314. package/src/components/search-list/examples/search-list-groups.tsx +0 -64
  315. package/src/components/search-list/examples/search-list-showcase.tsx +0 -67
  316. package/src/components/search-list/examples/search-list-usage.tsx +0 -27
  317. package/src/components/search-list/search-list.docs.tsx +0 -87
  318. package/src/components/section/examples/section-actions.tsx +0 -35
  319. package/src/components/section/examples/section-groups.tsx +0 -41
  320. package/src/components/section/examples/section-showcase.tsx +0 -63
  321. package/src/components/section/examples/section-usage.tsx +0 -19
  322. package/src/components/section/section.docs.tsx +0 -51
  323. package/src/components/secure-field/examples/secure-field-new-password.tsx +0 -56
  324. package/src/components/secure-field/examples/secure-field-reveal.tsx +0 -20
  325. package/src/components/secure-field/examples/secure-field-showcase.tsx +0 -62
  326. package/src/components/secure-field/examples/secure-field-usage.tsx +0 -19
  327. package/src/components/secure-field/secure-field.docs.tsx +0 -72
  328. package/src/components/segmented-control/examples/segmented-control-overflow.tsx +0 -28
  329. package/src/components/segmented-control/examples/segmented-control-showcase.tsx +0 -59
  330. package/src/components/segmented-control/examples/segmented-control-usage.tsx +0 -20
  331. package/src/components/segmented-control/examples/segmented-control-views.tsx +0 -32
  332. package/src/components/segmented-control/segmented-control.docs.tsx +0 -61
  333. package/src/components/sheet/examples/sheet-choices.tsx +0 -58
  334. package/src/components/sheet/examples/sheet-share.tsx +0 -51
  335. package/src/components/sheet/examples/sheet-showcase.tsx +0 -74
  336. package/src/components/sheet/examples/sheet-usage.tsx +0 -32
  337. package/src/components/sheet/sheet.docs.tsx +0 -75
  338. package/src/components/sidebar/examples/sidebar-groups.tsx +0 -133
  339. package/src/components/sidebar/examples/sidebar-showcase.tsx +0 -123
  340. package/src/components/sidebar/examples/sidebar-unread.tsx +0 -66
  341. package/src/components/sidebar/examples/sidebar-usage.tsx +0 -52
  342. package/src/components/sidebar/sidebar.docs.tsx +0 -135
  343. package/src/components/stat-tile/examples/stat-tile-filters.tsx +0 -32
  344. package/src/components/stat-tile/examples/stat-tile-grid.tsx +0 -21
  345. package/src/components/stat-tile/examples/stat-tile-showcase.tsx +0 -52
  346. package/src/components/stat-tile/examples/stat-tile-usage.tsx +0 -13
  347. package/src/components/stat-tile/stat-tile.docs.tsx +0 -60
  348. package/src/components/status-indicator/examples/status-indicator-live.tsx +0 -34
  349. package/src/components/status-indicator/examples/status-indicator-showcase.tsx +0 -39
  350. package/src/components/status-indicator/examples/status-indicator-states.tsx +0 -17
  351. package/src/components/status-indicator/examples/status-indicator-usage.tsx +0 -9
  352. package/src/components/status-indicator/status-indicator.docs.tsx +0 -43
  353. package/src/components/swatch-picker/examples/swatch-picker-accents.tsx +0 -42
  354. package/src/components/swatch-picker/examples/swatch-picker-labels.tsx +0 -45
  355. package/src/components/swatch-picker/examples/swatch-picker-showcase.tsx +0 -70
  356. package/src/components/swatch-picker/examples/swatch-picker-usage.tsx +0 -39
  357. package/src/components/swatch-picker/swatch-picker.docs.tsx +0 -60
  358. package/src/components/tab-bar/examples/tab-bar-five-tabs.tsx +0 -66
  359. package/src/components/tab-bar/examples/tab-bar-minimize.tsx +0 -71
  360. package/src/components/tab-bar/examples/tab-bar-showcase.tsx +0 -82
  361. package/src/components/tab-bar/examples/tab-bar-usage.tsx +0 -54
  362. package/src/components/tab-bar/tab-bar.docs.tsx +0 -136
  363. package/src/components/tab-view/examples/tab-view-keyboard.tsx +0 -39
  364. package/src/components/tab-view/examples/tab-view-settings.tsx +0 -56
  365. package/src/components/tab-view/examples/tab-view-showcase.tsx +0 -53
  366. package/src/components/tab-view/examples/tab-view-usage.tsx +0 -23
  367. package/src/components/tab-view/tab-view.docs.tsx +0 -77
  368. package/src/components/table/examples/table-empty.tsx +0 -53
  369. package/src/components/table/examples/table-invoices.tsx +0 -102
  370. package/src/components/table/examples/table-selection.tsx +0 -150
  371. package/src/components/table/examples/table-showcase.tsx +0 -115
  372. package/src/components/table/examples/table-usage.tsx +0 -67
  373. package/src/components/table/examples/table-wide.tsx +0 -75
  374. package/src/components/table/table.docs.tsx +0 -131
  375. package/src/components/text-editor/examples/text-editor-comment.tsx +0 -48
  376. package/src/components/text-editor/examples/text-editor-count.tsx +0 -38
  377. package/src/components/text-editor/examples/text-editor-showcase.tsx +0 -58
  378. package/src/components/text-editor/examples/text-editor-usage.tsx +0 -25
  379. package/src/components/text-editor/text-editor.docs.tsx +0 -61
  380. package/src/components/text-field/examples/text-field-icons.tsx +0 -72
  381. package/src/components/text-field/examples/text-field-showcase.tsx +0 -50
  382. package/src/components/text-field/examples/text-field-usage.tsx +0 -18
  383. package/src/components/text-field/examples/text-field-validation.tsx +0 -27
  384. package/src/components/text-field/text-field.docs.tsx +0 -118
  385. package/src/components/toast/examples/toast-action.tsx +0 -32
  386. package/src/components/toast/examples/toast-promise.tsx +0 -45
  387. package/src/components/toast/examples/toast-showcase.tsx +0 -49
  388. package/src/components/toast/examples/toast-usage.tsx +0 -13
  389. package/src/components/toast/toast.docs.tsx +0 -79
  390. package/src/components/toggle/examples/toggle-checkboxes.tsx +0 -43
  391. package/src/components/toggle/examples/toggle-settings.tsx +0 -31
  392. package/src/components/toggle/examples/toggle-showcase.tsx +0 -59
  393. package/src/components/toggle/examples/toggle-usage.tsx +0 -13
  394. package/src/components/toggle/toggle.docs.tsx +0 -69
  395. package/src/components/tooltip/examples/tooltip-icon-buttons.tsx +0 -27
  396. package/src/components/tooltip/examples/tooltip-shortcuts.tsx +0 -38
  397. package/src/components/tooltip/examples/tooltip-showcase.tsx +0 -44
  398. package/src/components/tooltip/examples/tooltip-usage.tsx +0 -13
  399. package/src/components/tooltip/tooltip.docs.tsx +0 -56
  400. package/src/components/view-controller/examples/view-controller-action.tsx +0 -27
  401. package/src/components/view-controller/examples/view-controller-settings.tsx +0 -31
  402. package/src/components/view-controller/examples/view-controller-showcase.tsx +0 -45
  403. package/src/components/view-controller/examples/view-controller-usage.tsx +0 -19
  404. package/src/components/view-controller/view-controller.docs.tsx +0 -71
  405. package/src/docs/index.ts +0 -3
  406. package/src/docs/registry.ts +0 -113
  407. package/src/docs/snippet.ts +0 -17
  408. package/src/docs/types.ts +0 -180
@@ -1,25 +1,15 @@
1
- import {
2
- AnimatePresence,
3
- motion,
4
- useIsPresent,
5
- useReducedMotion,
6
- } from "motion/react"
7
- import {
8
- useEffect,
9
- useLayoutEffect,
10
- useRef,
11
- useState,
12
- useSyncExternalStore,
13
- } from "react"
1
+ import { AnimatePresence, motion, useIsPresent, useReducedMotion } from "motion/react"
2
+ import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react"
14
3
  import { FLOATING_LAYER } from "../../hooks/use-dismiss"
15
4
  import { CheckIcon } from "../../icons/icons"
16
5
  import { cn } from "../../lib/class-names"
17
- import { registerFloatingLayer } from "../../lib/floating-layers"
6
+ import { belongsToFloatingSurface, registerFloatingLayer } from "../../lib/floating-layers"
18
7
  import { screenEdges } from "../../lib/screen-edges"
19
8
  import { spring } from "../../motion/transitions"
20
9
  import { concealMenuSource } from "./conceal-menu-source"
10
+ import { findMenuTypeaheadIndex, isMenuNavigationKey, nextMenuIndex } from "./menu-navigation"
21
11
  import { menuPosition } from "./menu-position"
22
- import { menuReveal, toolbarMenuReveal } from "./menu-reveal"
12
+ import { menuSurfaceReveal } from "./menu-reveal"
23
13
  import {
24
14
  closeContextMenu,
25
15
  getContextMenu,
@@ -29,22 +19,15 @@ import {
29
19
 
30
20
  /** Renders the open context menu. Mount once, near the app root. */
31
21
  export function ContextMenuHost() {
32
- const menu = useSyncExternalStore(
33
- subscribeContextMenu,
34
- getContextMenu,
35
- () => null
36
- )
37
- return (
38
- <AnimatePresence>
39
- {menu && <Menu key={menu.id} menu={menu} />}
40
- </AnimatePresence>
41
- )
22
+ const menu = useSyncExternalStore(subscribeContextMenu, getContextMenu, () => null)
23
+ return <AnimatePresence>{menu && <Menu key={menu.id} menu={menu} />}</AnimatePresence>
42
24
  }
43
25
 
44
26
  function Menu({ menu }: { menu: OpenMenu }) {
45
27
  const present = useIsPresent()
46
28
  const reducedMotion = useReducedMotion()
47
29
  const ref = useRef<HTMLDivElement>(null)
30
+ const content = useRef<HTMLDivElement>(null)
48
31
  const [position, setPosition] = useState({
49
32
  left: menu.x,
50
33
  top: menu.y,
@@ -53,9 +36,11 @@ function Menu({ menu }: { menu: OpenMenu }) {
53
36
  maxHeight: undefined as number | undefined,
54
37
  width: 0,
55
38
  height: 0,
39
+ radius: 0,
56
40
  ready: false,
57
41
  })
58
42
  const [focused, setFocused] = useState(-1)
43
+ const typeahead = useRef({ query: "", at: 0 })
59
44
 
60
45
  useLayoutEffect(() => {
61
46
  if (
@@ -66,10 +51,7 @@ function Menu({ menu }: { menu: OpenMenu }) {
66
51
  return
67
52
  const anchor = menu.toolbar ?? menu.anchor
68
53
  const surface = ref.current
69
- const restore = concealMenuSource(
70
- anchor,
71
- Boolean(menu.toolbar)
72
- )
54
+ const restore = concealMenuSource(anchor, Boolean(menu.toolbar))
73
55
  return () => {
74
56
  const restored = restore()
75
57
  if (
@@ -87,9 +69,7 @@ function Menu({ menu }: { menu: OpenMenu }) {
87
69
  useLayoutEffect(() => {
88
70
  const surface = ref.current
89
71
  if (!surface || !present || !position.ready) return
90
- const unregister = menu.anchor
91
- ? registerFloatingLayer(surface, menu.anchor)
92
- : undefined
72
+ const unregister = menu.anchor ? registerFloatingLayer(surface, menu.anchor) : undefined
93
73
  surface.focus()
94
74
  return unregister
95
75
  }, [menu.anchor, present, position.ready])
@@ -106,33 +86,39 @@ function Menu({ menu }: { menu: OpenMenu }) {
106
86
  const edges = screenEdges()
107
87
  const left = viewport?.offsetLeft ?? 0
108
88
  const top = viewport?.offsetTop ?? 0
109
- setPosition({
89
+ const next = {
110
90
  ...menuPosition(
111
91
  menu,
112
92
  {
113
93
  width: element.offsetWidth,
114
94
  height: Math.max(
115
95
  element.offsetHeight,
116
- element.scrollHeight
96
+ (content.current?.scrollHeight ?? 0) + 12
117
97
  ),
118
98
  },
119
99
  {
120
100
  left: left + edges.left,
121
101
  top: top + edges.top,
122
- right:
123
- left +
124
- (viewport?.width ??
125
- window.innerWidth) -
126
- edges.right,
127
- bottom:
128
- top +
129
- (viewport?.height ??
130
- window.innerHeight) -
131
- edges.bottom,
102
+ right: left + (viewport?.width ?? window.innerWidth) - edges.right,
103
+ bottom: top + (viewport?.height ?? window.innerHeight) - edges.bottom,
132
104
  }
133
105
  ),
106
+ radius: Number.parseFloat(getComputedStyle(element).borderTopLeftRadius) || 0,
134
107
  ready: true,
135
- })
108
+ }
109
+ setPosition((current) =>
110
+ current.left === next.left &&
111
+ current.top === next.top &&
112
+ current.originX === next.originX &&
113
+ current.originY === next.originY &&
114
+ current.maxHeight === next.maxHeight &&
115
+ current.width === next.width &&
116
+ current.height === next.height &&
117
+ current.radius === next.radius &&
118
+ current.ready === next.ready
119
+ ? current
120
+ : next
121
+ )
136
122
  }
137
123
  const schedule = () => {
138
124
  cancelAnimationFrame(frame)
@@ -153,7 +139,7 @@ function Menu({ menu }: { menu: OpenMenu }) {
153
139
  useEffect(() => {
154
140
  if (!present) return
155
141
  const onPointer = (event: PointerEvent) => {
156
- if (!ref.current?.contains(event.target as Node))
142
+ if (!ref.current || !belongsToFloatingSurface(ref.current, event.target as Element))
157
143
  closeContextMenu(false)
158
144
  }
159
145
  const close = () => closeContextMenu(false)
@@ -163,16 +149,8 @@ function Menu({ menu }: { menu: OpenMenu }) {
163
149
  }
164
150
  const onScrollKey = (event: KeyboardEvent) => {
165
151
  if (
166
- !ref.current?.contains(
167
- event.target as Node
168
- ) &&
169
- [
170
- "PageDown",
171
- "PageUp",
172
- "Home",
173
- "End",
174
- " ",
175
- ].includes(event.key)
152
+ !ref.current?.contains(event.target as Node) &&
153
+ ["PageDown", "PageUp", "Home", "End", " "].includes(event.key)
176
154
  )
177
155
  markScroll()
178
156
  }
@@ -185,11 +163,7 @@ function Menu({ menu }: { menu: OpenMenu }) {
185
163
  }
186
164
  // Opened by a long-press: ignore the finger lifting off.
187
165
  const timer = setTimeout(() => {
188
- window.addEventListener(
189
- "pointerdown",
190
- onPointer,
191
- true
192
- )
166
+ window.addEventListener("pointerdown", onPointer, true)
193
167
  }, 0)
194
168
  window.addEventListener("blur", close)
195
169
  window.addEventListener("wheel", markScroll, {
@@ -204,51 +178,22 @@ function Menu({ menu }: { menu: OpenMenu }) {
204
178
  window.addEventListener("scroll", onScroll, true)
205
179
  return () => {
206
180
  clearTimeout(timer)
207
- window.removeEventListener(
208
- "pointerdown",
209
- onPointer,
210
- true
211
- )
181
+ window.removeEventListener("pointerdown", onPointer, true)
212
182
  window.removeEventListener("blur", close)
213
- window.removeEventListener(
214
- "wheel",
215
- markScroll,
216
- true
217
- )
218
- window.removeEventListener(
219
- "touchmove",
220
- markScroll,
221
- true
222
- )
223
- window.removeEventListener(
224
- "keydown",
225
- onScrollKey,
226
- true
227
- )
228
- window.removeEventListener(
229
- "scroll",
230
- onScroll,
231
- true
232
- )
183
+ window.removeEventListener("wheel", markScroll, true)
184
+ window.removeEventListener("touchmove", markScroll, true)
185
+ window.removeEventListener("keydown", onScrollKey, true)
186
+ window.removeEventListener("scroll", onScroll, true)
233
187
  }
234
188
  }, [present])
235
189
 
236
- const source = menuReveal(
237
- position.ready ? menu.source : undefined,
190
+ const source = menuSurfaceReveal(
191
+ position.ready ? (menu.toolbarRect ?? menu.source) : undefined,
238
192
  position
239
193
  )
240
- const toolbarReveal =
241
- menu.source && menu.toolbarRect
242
- ? toolbarMenuReveal(
243
- menu.source,
244
- menu.toolbarRect,
245
- position
246
- )
247
- : undefined
248
194
  let actionIndex = -1
249
195
  return (
250
- <motion.div
251
- key={position.ready ? "surface" : "measure"}
196
+ <div
252
197
  ref={ref}
253
198
  role="menu"
254
199
  tabIndex={-1}
@@ -264,41 +209,48 @@ function Menu({ menu }: { menu: OpenMenu }) {
264
209
  closeContextMenu()
265
210
  return
266
211
  }
267
- const keys = [
268
- "ArrowDown",
269
- "ArrowUp",
270
- "Home",
271
- "End",
272
- ]
273
- if (!keys.includes(event.key)) return
274
- event.preventDefault()
275
212
  const controls = Array.from(
276
213
  ref.current?.querySelectorAll<HTMLButtonElement>(
277
214
  "[role=menuitem]:not(:disabled)"
278
215
  ) ?? []
279
216
  )
217
+ if (!isMenuNavigationKey(event.key)) {
218
+ if (
219
+ event.key.length !== 1 ||
220
+ !event.key.trim() ||
221
+ event.altKey ||
222
+ event.ctrlKey ||
223
+ event.metaKey ||
224
+ event.nativeEvent.isComposing
225
+ )
226
+ return
227
+ const now = performance.now()
228
+ const query =
229
+ (now - typeahead.current.at < 700 ? typeahead.current.query : "") +
230
+ event.key
231
+ typeahead.current = { query, at: now }
232
+ const labels = controls.map((control) => control.textContent ?? "")
233
+ const current = controls.indexOf(document.activeElement as HTMLButtonElement)
234
+ let next = findMenuTypeaheadIndex(labels, query, current)
235
+ if (next < 0) {
236
+ typeahead.current.query = event.key
237
+ next = findMenuTypeaheadIndex(labels, event.key, current)
238
+ }
239
+ if (next >= 0) {
240
+ event.preventDefault()
241
+ controls[next]?.focus()
242
+ }
243
+ return
244
+ }
245
+ event.preventDefault()
280
246
  if (!controls.length) return
281
- const current = controls.indexOf(
282
- document.activeElement as HTMLButtonElement
283
- )
284
- const next =
285
- event.key === "Home"
286
- ? 0
287
- : event.key === "End"
288
- ? controls.length - 1
289
- : event.key === "ArrowDown"
290
- ? (current + 1) % controls.length
291
- : current < 0
292
- ? controls.length - 1
293
- : (current -
294
- 1 +
295
- controls.length) %
296
- controls.length
247
+ const current = controls.indexOf(document.activeElement as HTMLButtonElement)
248
+ const next = nextMenuIndex(event.key, current, controls.length)
297
249
  controls[next]?.focus()
298
250
  }}
299
251
  {...{ [FLOATING_LAYER]: "" }}
300
252
  className={cn(
301
- "glass-menu fixed z-75 flex min-w-50 max-w-[min(280px,calc(100vw-24px))] flex-col overflow-y-auto overscroll-contain rounded-3xl p-1.5",
253
+ "fixed z-75 isolate flex min-w-50 max-w-[min(280px,calc(100vw-24px))] flex-col rounded-3xl p-1.5 outline-none",
302
254
  menu.touch && "min-w-60",
303
255
  !present && "pointer-events-none"
304
256
  )}
@@ -307,65 +259,60 @@ function Menu({ menu }: { menu: OpenMenu }) {
307
259
  top: position.top,
308
260
  maxHeight: position.maxHeight,
309
261
  minWidth: menu.toolbarRect
310
- ? Math.min(
311
- Math.max(
312
- 200,
313
- menu.toolbarRect.width
314
- ),
315
- window.innerWidth - 24
316
- )
262
+ ? Math.min(Math.max(200, menu.toolbarRect.width), window.innerWidth - 24)
317
263
  : undefined,
318
- transformOrigin: menu.source
319
- ? "50% 50%"
320
- : `${position.originX * 100}% ${position.originY * 100}%`,
321
- visibility: position.ready
322
- ? undefined
323
- : "hidden",
264
+ visibility: position.ready ? undefined : "hidden",
324
265
  }}
325
- initial={reducedMotion ? false : source}
326
- animate={
327
- toolbarReveal && !reducedMotion
328
- ? toolbarReveal.enter
329
- : {
330
- opacity: 1,
331
- x: 0,
332
- y: 0,
333
- scaleX: 1,
334
- scaleY: 1,
335
- }
336
- }
337
- exit={
338
- reducedMotion
339
- ? { opacity: 0 }
340
- : (toolbarReveal?.exit ?? source)
341
- }
342
- transition={
343
- reducedMotion
344
- ? { duration: 0 }
345
- : toolbarReveal
346
- ? {
347
- duration: 0.34,
348
- times: present
349
- ? [0, 0.35, 1]
350
- : [0, 0.65, 1],
351
- ease: [0.22, 1, 0.36, 1],
352
- }
353
- : spring.smooth
354
- }
355
266
  onContextMenu={(event) => event.preventDefault()}
356
267
  >
268
+ {position.ready && (
269
+ <motion.div
270
+ aria-hidden="true"
271
+ className="glass-menu pointer-events-none absolute top-0 left-0 -z-10 rounded-3xl"
272
+ style={{ width: "100%", height: "100%" }}
273
+ initial={reducedMotion || !position.ready ? false : source}
274
+ animate={
275
+ position.ready
276
+ ? {
277
+ x: 0,
278
+ y: 0,
279
+ width: position.width,
280
+ height: position.height,
281
+ opacity: 1,
282
+ borderRadius: position.radius,
283
+ }
284
+ : undefined
285
+ }
286
+ exit={reducedMotion ? { opacity: 0 } : source}
287
+ transition={reducedMotion ? { duration: 0 } : spring.smooth}
288
+ />
289
+ )}
357
290
  <motion.div
358
- className="flex flex-col"
359
- initial={{ opacity: reducedMotion ? 1 : 0 }}
360
- animate={{ opacity: 1 }}
291
+ ref={content}
292
+ className="flex flex-col overflow-y-auto overscroll-contain"
293
+ style={{
294
+ maxHeight: position.maxHeight
295
+ ? Math.max(0, position.maxHeight - 12)
296
+ : undefined,
297
+ }}
298
+ initial={
299
+ reducedMotion
300
+ ? false
301
+ : {
302
+ opacity: 0,
303
+ y: menu.source && menu.source.top > position.top ? 4 : -4,
304
+ }
305
+ }
306
+ animate={{ opacity: 1, y: 0 }}
307
+ exit={{
308
+ opacity: 0,
309
+ transition: {
310
+ duration: reducedMotion ? 0 : 0.06,
311
+ },
312
+ }}
361
313
  transition={{
362
- duration: reducedMotion ? 0 : 0.12,
363
- delay:
364
- reducedMotion || !menu.source
365
- ? 0
366
- : menu.toolbar
367
- ? 0.14
368
- : 0.08,
314
+ duration: reducedMotion ? 0 : 0.16,
315
+ delay: reducedMotion || !menu.source ? 0 : 0.06,
369
316
  }}
370
317
  >
371
318
  {menu.items.map((item, index) => {
@@ -380,7 +327,7 @@ function Menu({ menu }: { menu: OpenMenu }) {
380
327
  if ("heading" in item) {
381
328
  return (
382
329
  <span
383
- key={`heading-${item.heading}`}
330
+ key={`heading-${index}`}
384
331
  className="px-2.5 pt-1.5 pb-0.5 text-caption2 font-semibold text-label-tertiary"
385
332
  >
386
333
  {item.heading}
@@ -391,35 +338,24 @@ function Menu({ menu }: { menu: OpenMenu }) {
391
338
  const current = actionIndex
392
339
  return (
393
340
  <button
394
- key={item.label}
341
+ key={`item-${index}`}
395
342
  type="button"
396
343
  role="menuitem"
397
344
  tabIndex={-1}
398
345
  disabled={item.disabled}
399
- onFocus={() =>
400
- setFocused(current)
401
- }
402
- onPointerEnter={() =>
403
- !item.disabled &&
404
- setFocused(current)
405
- }
346
+ onFocus={() => setFocused(current)}
347
+ onPointerEnter={() => !item.disabled && setFocused(current)}
406
348
  onClick={() => {
407
349
  closeContextMenu()
408
350
  item.onSelect()
409
351
  }}
410
352
  className={cn(
411
353
  "flex items-center gap-2.5 rounded-lg px-2.5 text-start text-subheadline transition-colors disabled:opacity-40",
412
- menu.touch
413
- ? "py-2.5"
414
- : "py-1.5",
415
- item.destructive
416
- ? "text-danger"
417
- : "text-label",
354
+ menu.touch ? "py-2.5" : "py-1.5",
355
+ item.destructive ? "text-danger" : "text-label",
418
356
  focused === current &&
419
357
  !item.disabled &&
420
- (item.destructive
421
- ? "bg-danger/10"
422
- : "bg-fill")
358
+ (item.destructive ? "bg-danger/10" : "bg-fill")
423
359
  )}
424
360
  >
425
361
  <span className="flex w-4 shrink-0 justify-center text-label-secondary">
@@ -433,13 +369,11 @@ function Menu({ menu }: { menu: OpenMenu }) {
433
369
  item.icon
434
370
  )}
435
371
  </span>
436
- <span className="grow truncate">
437
- {item.label}
438
- </span>
372
+ <span className="grow truncate">{item.label}</span>
439
373
  </button>
440
374
  )
441
375
  })}
442
376
  </motion.div>
443
- </motion.div>
377
+ </div>
444
378
  )
445
379
  }
@@ -52,9 +52,7 @@ export function openContextMenu(
52
52
  y: number,
53
53
  items: ContextMenuItem[],
54
54
  touch = false,
55
- anchor: Element | null = typeof document === "undefined"
56
- ? null
57
- : document.activeElement
55
+ anchor: Element | null = typeof document === "undefined" ? null : document.activeElement
58
56
  ) {
59
57
  menu = {
60
58
  id: nextId++,
@@ -83,8 +81,8 @@ export function openFollowUpMenu(items: ContextMenuItem[]) {
83
81
  }
84
82
 
85
83
  /**
86
- * Opens a menu from a control, the way a SwiftUI `Menu` does: under it (or
87
- * above it), lined up with its leading or trailing edge. Leading and
84
+ * Expands a control into a menu, lined up with its leading or trailing
85
+ * edge and growing down (or up) from the control's bounds. Leading and
88
86
  * trailing follow the text direction, so right-to-left languages mirror.
89
87
  * The menu's real size decides where it goes; nothing is guessed.
90
88
  */
@@ -107,18 +105,11 @@ export function openMenu(
107
105
  const rtl = getComputedStyle(anchor).direction === "rtl"
108
106
  // Trailing is the right edge in left-to-right text, the left edge in right-to-left.
109
107
  const right = (edge === "trailing") !== rtl
110
- const gap = 6
111
108
  menu = {
112
109
  id: nextId++,
113
110
  anchor,
114
- x: right
115
- ? (toolbarRect ?? rect).right
116
- : (toolbarRect ?? rect).left,
117
- y: toolbar
118
- ? rect.top
119
- : above
120
- ? rect.top - gap
121
- : rect.bottom + gap,
111
+ x: right ? (toolbarRect ?? rect).right : (toolbarRect ?? rect).left,
112
+ y: toolbar ? rect.top : above ? rect.bottom : rect.top,
122
113
  items,
123
114
  touch: false,
124
115
  alignX: right ? "end" : "start",
@@ -0,0 +1,29 @@
1
+ export type MenuNavigationKey = "ArrowDown" | "ArrowUp" | "Home" | "End"
2
+
3
+ export function isMenuNavigationKey(key: string): key is MenuNavigationKey {
4
+ return key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End"
5
+ }
6
+
7
+ /** Index of the next enabled action in a menu, with wrapping arrow navigation. */
8
+ export function nextMenuIndex(key: MenuNavigationKey, current: number, count: number): number {
9
+ if (count <= 0) return -1
10
+ if (key === "Home") return 0
11
+ if (key === "End") return count - 1
12
+ if (key === "ArrowDown") return (current + 1) % count
13
+ return current < 0 ? count - 1 : (current - 1 + count) % count
14
+ }
15
+
16
+ /** Finds a matching label after the current action, wrapping at the end. */
17
+ export function findMenuTypeaheadIndex(
18
+ labels: ReadonlyArray<string>,
19
+ query: string,
20
+ current: number
21
+ ): number {
22
+ if (!labels.length || !query.trim()) return -1
23
+ const needle = query.toLocaleLowerCase()
24
+ for (let offset = 1; offset <= labels.length; offset++) {
25
+ const index = (current + offset + labels.length) % labels.length
26
+ if (labels[index]?.trim().toLocaleLowerCase().startsWith(needle)) return index
27
+ }
28
+ return -1
29
+ }
@@ -1,10 +1,7 @@
1
1
  import type { OpenMenu } from "./context-menu-store"
2
2
  import type { MenuRect } from "./menu-reveal"
3
3
 
4
- type MenuAnchor = Pick<
5
- OpenMenu,
6
- "x" | "y" | "alignX" | "alignY"
7
- >
4
+ type MenuAnchor = Pick<OpenMenu, "x" | "y" | "alignX" | "alignY">
8
5
  type MenuSize = Pick<MenuRect, "width" | "height">
9
6
  type MenuBounds = {
10
7
  left: number
@@ -14,38 +11,20 @@ type MenuBounds = {
14
11
  }
15
12
 
16
13
  /** Fit within the current visible viewport, including a keyboard or Safari pan. */
17
- export function menuPosition(
18
- menu: MenuAnchor,
19
- size: MenuSize,
20
- bounds: MenuBounds
21
- ) {
14
+ export function menuPosition(menu: MenuAnchor, size: MenuSize, bounds: MenuBounds) {
22
15
  const { width, height } = size
23
16
  const { left, top, right, bottom } = bounds
24
17
  const fitsAfterX = menu.x + width <= right
25
18
  const fitsBeforeX = menu.x - width >= left
26
- const endX =
27
- menu.alignX === "end"
28
- ? fitsBeforeX || !fitsAfterX
29
- : !fitsAfterX
19
+ const endX = menu.alignX === "end" ? fitsBeforeX || !fitsAfterX : !fitsAfterX
30
20
  const fitsBelow = menu.y + height <= bottom
31
21
  const fitsAbove = menu.y - height >= top
32
- const endY =
33
- menu.alignY === "bottom"
34
- ? fitsAbove || !fitsBelow
35
- : !fitsBelow
22
+ const endY = menu.alignY === "bottom" ? fitsAbove || !fitsBelow : !fitsBelow
36
23
  const clamp = (value: number, min: number, max: number) =>
37
24
  Math.min(Math.max(value, min), Math.max(min, max))
38
25
  return {
39
- left: clamp(
40
- endX ? menu.x - width : menu.x,
41
- left,
42
- right - width
43
- ),
44
- top: clamp(
45
- endY ? menu.y - height : menu.y,
46
- top,
47
- bottom - height
48
- ),
26
+ left: clamp(endX ? menu.x - width : menu.x, left, right - width),
27
+ top: clamp(endY ? menu.y - height : menu.y, top, bottom - height),
49
28
  originX: endX ? 1 : 0,
50
29
  originY: endY ? 1 : 0,
51
30
  maxHeight: Math.max(0, bottom - top),