@remix-run/ui 0.3.0 → 0.4.0

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 (308) hide show
  1. package/README.md +50 -164
  2. package/dist/accordion/index.d.ts +46 -0
  3. package/dist/accordion/index.js +142 -0
  4. package/dist/accordion/index.js.map +1 -0
  5. package/dist/accordion/primitives.d.ts +104 -0
  6. package/dist/accordion/primitives.js +312 -0
  7. package/dist/accordion/primitives.js.map +1 -0
  8. package/dist/{components/anchor/anchor.js → anchor/index.js} +1 -1
  9. package/dist/anchor/index.js.map +1 -0
  10. package/dist/animation/demos/reordering.js +9 -6
  11. package/dist/animation/demos/reordering.js.map +1 -1
  12. package/dist/{components/breadcrumbs/breadcrumbs.js → breadcrumbs/index.js} +19 -19
  13. package/dist/breadcrumbs/index.js.map +1 -0
  14. package/dist/button/index.d.ts +15 -0
  15. package/dist/button/index.js +128 -0
  16. package/dist/button/index.js.map +1 -0
  17. package/dist/checkbox/index.d.ts +14 -0
  18. package/dist/checkbox/index.js +125 -0
  19. package/dist/checkbox/index.js.map +1 -0
  20. package/dist/combobox/index.d.ts +21 -0
  21. package/dist/combobox/index.js +58 -0
  22. package/dist/combobox/index.js.map +1 -0
  23. package/dist/{components/combobox/combobox.d.ts → combobox/primitives.d.ts} +3 -7
  24. package/dist/{components/combobox/combobox.js → combobox/primitives.js} +8 -61
  25. package/dist/combobox/primitives.js.map +1 -0
  26. package/dist/input/index.d.ts +13 -0
  27. package/dist/input/index.js +121 -0
  28. package/dist/input/index.js.map +1 -0
  29. package/dist/listbox/flash-attribute.js.map +1 -0
  30. package/dist/{components/listbox/listbox.d.ts → listbox/index.d.ts} +2 -6
  31. package/dist/{components/listbox/listbox.js → listbox/index.js} +4 -82
  32. package/dist/listbox/index.js.map +1 -0
  33. package/dist/menu/hover-aim.js.map +1 -0
  34. package/dist/menu/index.d.ts +42 -0
  35. package/dist/menu/index.js +250 -0
  36. package/dist/menu/index.js.map +1 -0
  37. package/dist/{components/menu/menu.d.ts → menu/primitives.d.ts} +3 -32
  38. package/dist/{components/menu/menu.js → menu/primitives.js} +97 -314
  39. package/dist/menu/primitives.js.map +1 -0
  40. package/dist/{components/popover/popover.d.ts → popover/index.d.ts} +2 -4
  41. package/dist/{components/popover/popover.js → popover/index.js} +5 -48
  42. package/dist/popover/index.js.map +1 -0
  43. package/dist/{interactions/outside-click/outside-click-mixin.js → popover/outside-click.js} +1 -1
  44. package/dist/popover/outside-click.js.map +1 -0
  45. package/dist/popover/scroll-lock.d.ts +1 -0
  46. package/dist/{utils → popover}/scroll-lock.js +0 -18
  47. package/dist/popover/scroll-lock.js.map +1 -0
  48. package/dist/radio/index.d.ts +12 -0
  49. package/dist/radio/index.js +77 -0
  50. package/dist/radio/index.js.map +1 -0
  51. package/dist/runtime/reconcile.js +11 -5
  52. package/dist/runtime/reconcile.js.map +1 -1
  53. package/dist/runtime/vdom.js +3 -0
  54. package/dist/runtime/vdom.js.map +1 -1
  55. package/dist/select/index.d.ts +19 -0
  56. package/dist/select/index.js +95 -0
  57. package/dist/select/index.js.map +1 -0
  58. package/dist/select/keydown.js.map +1 -0
  59. package/dist/{components/select/select.d.ts → select/primitives.d.ts} +4 -6
  60. package/dist/{components/select/select.js → select/primitives.js} +11 -63
  61. package/dist/select/primitives.js.map +1 -0
  62. package/dist/server/stream.js +2 -2
  63. package/dist/server/stream.js.map +1 -1
  64. package/dist/shared/focus-styles.d.ts +1 -0
  65. package/dist/shared/focus-styles.js +2 -0
  66. package/dist/shared/focus-styles.js.map +1 -0
  67. package/dist/shared/icons.d.ts +9 -0
  68. package/dist/shared/icons.js +42 -0
  69. package/dist/shared/icons.js.map +1 -0
  70. package/dist/shared/listbox-popover-styles.d.ts +6 -0
  71. package/dist/shared/listbox-popover-styles.js +137 -0
  72. package/dist/shared/listbox-popover-styles.js.map +1 -0
  73. package/dist/shared/style-values.d.ts +78 -0
  74. package/dist/{theme/presets/rmx-01/index.js → shared/style-values.js} +3 -34
  75. package/dist/shared/style-values.js.map +1 -0
  76. package/dist/{interactions/typeahead/typeahead-mixin.js → shared/typeahead.js} +1 -1
  77. package/dist/shared/typeahead.js.map +1 -0
  78. package/dist/shared/wait-for-css-transition.js.map +1 -0
  79. package/dist/shared/wait.js.map +1 -0
  80. package/dist/tabs/index.d.ts +32 -0
  81. package/dist/tabs/index.js +157 -0
  82. package/dist/tabs/index.js.map +1 -0
  83. package/dist/tabs/primitives.d.ts +57 -0
  84. package/dist/tabs/primitives.js +238 -0
  85. package/dist/tabs/primitives.js.map +1 -0
  86. package/dist/toggle/index.d.ts +9 -0
  87. package/dist/toggle/index.js +108 -0
  88. package/dist/toggle/index.js.map +1 -0
  89. package/dist/toggle/primitives.d.ts +29 -0
  90. package/dist/toggle/primitives.js +127 -0
  91. package/dist/toggle/primitives.js.map +1 -0
  92. package/package.json +73 -44
  93. package/src/{components/accordion → accordion}/README.md +50 -14
  94. package/src/accordion/index.tsx +285 -0
  95. package/src/accordion/primitives.tsx +512 -0
  96. package/src/{components/anchor → anchor}/README.md +37 -2
  97. package/src/animation/demos/reordering.tsx +9 -6
  98. package/src/{components/breadcrumbs → breadcrumbs}/README.md +4 -4
  99. package/src/{components/breadcrumbs/breadcrumbs.tsx → breadcrumbs/index.tsx} +18 -18
  100. package/src/button/README.md +44 -0
  101. package/src/button/index.ts +168 -0
  102. package/src/checkbox/README.md +59 -0
  103. package/src/checkbox/index.ts +162 -0
  104. package/src/{components/combobox → combobox}/README.md +58 -9
  105. package/src/combobox/index.tsx +112 -0
  106. package/src/{components/combobox/combobox.tsx → combobox/primitives.tsx} +5 -91
  107. package/src/input/README.md +52 -0
  108. package/src/input/index.ts +147 -0
  109. package/src/{components/listbox → listbox}/README.md +9 -41
  110. package/src/{components/listbox/listbox.ts → listbox/index.ts} +2 -87
  111. package/src/{components/menu → menu}/README.md +55 -14
  112. package/src/menu/index.tsx +354 -0
  113. package/src/{components/menu/menu.tsx → menu/primitives.tsx} +99 -413
  114. package/src/{components/popover → popover}/README.md +20 -39
  115. package/src/{components/popover/popover.ts → popover/index.ts} +3 -57
  116. package/src/{utils → popover}/scroll-lock.ts +0 -28
  117. package/src/radio/README.md +53 -0
  118. package/src/radio/index.ts +100 -0
  119. package/src/runtime/demos/readme.demo.tsx +16 -14
  120. package/src/runtime/reconcile.ts +11 -5
  121. package/src/runtime/vdom.ts +3 -0
  122. package/src/{components/select → select}/README.md +29 -19
  123. package/src/select/index.tsx +159 -0
  124. package/src/{components/select/select.tsx → select/primitives.tsx} +10 -98
  125. package/src/server/stream.ts +3 -3
  126. package/src/shared/focus-styles.ts +2 -0
  127. package/src/shared/icons.tsx +62 -0
  128. package/src/shared/listbox-popover-styles.ts +146 -0
  129. package/src/shared/style-values.ts +165 -0
  130. package/src/tabs/README.md +141 -0
  131. package/src/tabs/index.tsx +244 -0
  132. package/src/tabs/primitives.ts +365 -0
  133. package/src/toggle/README.md +56 -0
  134. package/src/toggle/index.tsx +134 -0
  135. package/src/toggle/primitives.ts +192 -0
  136. package/dist/animation/demos/animation.demo.d.ts +0 -5
  137. package/dist/animation/demos/animation.demo.js +0 -92
  138. package/dist/animation/demos/animation.demo.js.map +0 -1
  139. package/dist/animation/demos/spring.demo.d.ts +0 -6
  140. package/dist/animation/demos/spring.demo.js +0 -304
  141. package/dist/animation/demos/spring.demo.js.map +0 -1
  142. package/dist/components/accordion/accordion.d.ts +0 -92
  143. package/dist/components/accordion/accordion.js +0 -335
  144. package/dist/components/accordion/accordion.js.map +0 -1
  145. package/dist/components/accordion/demos/card.demo.d.ts +0 -7
  146. package/dist/components/accordion/demos/card.demo.js +0 -59
  147. package/dist/components/accordion/demos/card.demo.js.map +0 -1
  148. package/dist/components/accordion/demos/multiple.demo.d.ts +0 -7
  149. package/dist/components/accordion/demos/multiple.demo.js +0 -20
  150. package/dist/components/accordion/demos/multiple.demo.js.map +0 -1
  151. package/dist/components/accordion/demos/overview.demo.d.ts +0 -7
  152. package/dist/components/accordion/demos/overview.demo.js +0 -20
  153. package/dist/components/accordion/demos/overview.demo.js.map +0 -1
  154. package/dist/components/anchor/anchor.js.map +0 -1
  155. package/dist/components/breadcrumbs/basic.demo.d.ts +0 -6
  156. package/dist/components/breadcrumbs/basic.demo.js +0 -15
  157. package/dist/components/breadcrumbs/basic.demo.js.map +0 -1
  158. package/dist/components/breadcrumbs/breadcrumbs.js.map +0 -1
  159. package/dist/components/breadcrumbs/separator.demo.d.ts +0 -6
  160. package/dist/components/breadcrumbs/separator.demo.js +0 -15
  161. package/dist/components/breadcrumbs/separator.demo.js.map +0 -1
  162. package/dist/components/button/button.d.ts +0 -105
  163. package/dist/components/button/button.js +0 -207
  164. package/dist/components/button/button.js.map +0 -1
  165. package/dist/components/button/demos/aliases.demo.d.ts +0 -7
  166. package/dist/components/button/demos/aliases.demo.js +0 -19
  167. package/dist/components/button/demos/aliases.demo.js.map +0 -1
  168. package/dist/components/button/demos/basic.demo.d.ts +0 -7
  169. package/dist/components/button/demos/basic.demo.js +0 -20
  170. package/dist/components/button/demos/basic.demo.js.map +0 -1
  171. package/dist/components/button/demos/states.demo.d.ts +0 -7
  172. package/dist/components/button/demos/states.demo.js +0 -30
  173. package/dist/components/button/demos/states.demo.js.map +0 -1
  174. package/dist/components/combobox/combobox.js.map +0 -1
  175. package/dist/components/combobox/overview.demo.d.ts +0 -7
  176. package/dist/components/combobox/overview.demo.js +0 -154
  177. package/dist/components/combobox/overview.demo.js.map +0 -1
  178. package/dist/components/glyph/glyph.d.ts +0 -14
  179. package/dist/components/glyph/glyph.js +0 -65
  180. package/dist/components/glyph/glyph.js.map +0 -1
  181. package/dist/components/listbox/listbox.js.map +0 -1
  182. package/dist/components/listbox/overview.demo.d.ts +0 -7
  183. package/dist/components/listbox/overview.demo.js +0 -65
  184. package/dist/components/listbox/overview.demo.js.map +0 -1
  185. package/dist/components/menu/demos/bubbling.demo.d.ts +0 -6
  186. package/dist/components/menu/demos/bubbling.demo.js +0 -16
  187. package/dist/components/menu/demos/bubbling.demo.js.map +0 -1
  188. package/dist/components/menu/demos/context-trigger.demo.d.ts +0 -7
  189. package/dist/components/menu/demos/context-trigger.demo.js +0 -87
  190. package/dist/components/menu/demos/context-trigger.demo.js.map +0 -1
  191. package/dist/components/menu/demos/overview.demo.d.ts +0 -7
  192. package/dist/components/menu/demos/overview.demo.js +0 -33
  193. package/dist/components/menu/demos/overview.demo.js.map +0 -1
  194. package/dist/components/menu/hover-aim.js.map +0 -1
  195. package/dist/components/menu/menu.js.map +0 -1
  196. package/dist/components/popover/overview.demo.d.ts +0 -7
  197. package/dist/components/popover/overview.demo.js +0 -65
  198. package/dist/components/popover/overview.demo.js.map +0 -1
  199. package/dist/components/popover/popover.js.map +0 -1
  200. package/dist/components/select/deconstructed.demo.d.ts +0 -7
  201. package/dist/components/select/deconstructed.demo.js +0 -54
  202. package/dist/components/select/deconstructed.demo.js.map +0 -1
  203. package/dist/components/select/overview.demo.d.ts +0 -6
  204. package/dist/components/select/overview.demo.js +0 -28
  205. package/dist/components/select/overview.demo.js.map +0 -1
  206. package/dist/components/select/select.js.map +0 -1
  207. package/dist/components/separator/separator.d.ts +0 -2
  208. package/dist/components/separator/separator.js +0 -15
  209. package/dist/components/separator/separator.js.map +0 -1
  210. package/dist/components/tabs/tabs.d.ts +0 -78
  211. package/dist/components/tabs/tabs.js +0 -339
  212. package/dist/components/tabs/tabs.js.map +0 -1
  213. package/dist/interactions/keydown/keydown.js.map +0 -1
  214. package/dist/interactions/outside-click/outside-click-mixin.js.map +0 -1
  215. package/dist/interactions/typeahead/typeahead-mixin.js.map +0 -1
  216. package/dist/runtime/demos/basic.demo.d.ts +0 -6
  217. package/dist/runtime/demos/basic.demo.js +0 -16
  218. package/dist/runtime/demos/basic.demo.js.map +0 -1
  219. package/dist/runtime/demos/controlled-uncontrolled-values.demo.d.ts +0 -6
  220. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js +0 -77
  221. package/dist/runtime/demos/controlled-uncontrolled-values.demo.js.map +0 -1
  222. package/dist/runtime/demos/keyed-list.demo.d.ts +0 -6
  223. package/dist/runtime/demos/keyed-list.demo.js +0 -62
  224. package/dist/runtime/demos/keyed-list.demo.js.map +0 -1
  225. package/dist/runtime/demos/readme.demo.d.ts +0 -5
  226. package/dist/runtime/demos/readme.demo.js +0 -396
  227. package/dist/runtime/demos/readme.demo.js.map +0 -1
  228. package/dist/theme/contract.d.ts +0 -61
  229. package/dist/theme/contract.js +0 -131
  230. package/dist/theme/contract.js.map +0 -1
  231. package/dist/theme/glyph-contract.d.ts +0 -11
  232. package/dist/theme/glyph-contract.js +0 -34
  233. package/dist/theme/glyph-contract.js.map +0 -1
  234. package/dist/theme/presets/rmx-01/glyphs.d.ts +0 -2
  235. package/dist/theme/presets/rmx-01/glyphs.js +0 -252
  236. package/dist/theme/presets/rmx-01/glyphs.js.map +0 -1
  237. package/dist/theme/presets/rmx-01/index.d.ts +0 -4
  238. package/dist/theme/presets/rmx-01/index.js.map +0 -1
  239. package/dist/theme/runtime.d.ts +0 -2
  240. package/dist/theme/runtime.js +0 -84
  241. package/dist/theme/runtime.js.map +0 -1
  242. package/dist/theme/theme.d.ts +0 -6
  243. package/dist/theme/theme.js +0 -5
  244. package/dist/theme/theme.js.map +0 -1
  245. package/dist/utils/flash-attribute.js.map +0 -1
  246. package/dist/utils/scroll-lock.d.ts +0 -3
  247. package/dist/utils/scroll-lock.js.map +0 -1
  248. package/dist/utils/wait-for-css-transition.js.map +0 -1
  249. package/dist/utils/wait.js.map +0 -1
  250. package/src/animation/demos/animation.demo.tsx +0 -194
  251. package/src/animation/demos/spring.demo.tsx +0 -385
  252. package/src/components/accordion/accordion.tsx +0 -565
  253. package/src/components/accordion/demos/card.demo.tsx +0 -114
  254. package/src/components/accordion/demos/multiple.demo.tsx +0 -57
  255. package/src/components/accordion/demos/overview.demo.tsx +0 -57
  256. package/src/components/breadcrumbs/basic.demo.tsx +0 -18
  257. package/src/components/breadcrumbs/separator.demo.tsx +0 -19
  258. package/src/components/button/README.md +0 -44
  259. package/src/components/button/button.tsx +0 -286
  260. package/src/components/button/demos/aliases.demo.tsx +0 -28
  261. package/src/components/button/demos/basic.demo.tsx +0 -31
  262. package/src/components/button/demos/states.demo.tsx +0 -53
  263. package/src/components/combobox/overview.demo.tsx +0 -200
  264. package/src/components/glyph/README.md +0 -72
  265. package/src/components/glyph/glyph.tsx +0 -110
  266. package/src/components/listbox/overview.demo.tsx +0 -102
  267. package/src/components/menu/demos/bubbling.demo.tsx +0 -35
  268. package/src/components/menu/demos/context-trigger.demo.tsx +0 -133
  269. package/src/components/menu/demos/overview.demo.tsx +0 -77
  270. package/src/components/popover/overview.demo.tsx +0 -144
  271. package/src/components/select/deconstructed.demo.tsx +0 -96
  272. package/src/components/select/overview.demo.tsx +0 -52
  273. package/src/components/separator/separator.ts +0 -19
  274. package/src/components/tabs/README.md +0 -105
  275. package/src/components/tabs/tabs.tsx +0 -504
  276. package/src/runtime/demos/basic.demo.tsx +0 -21
  277. package/src/runtime/demos/controlled-uncontrolled-values.demo.tsx +0 -177
  278. package/src/runtime/demos/keyed-list.demo.tsx +0 -94
  279. package/src/theme/README.md +0 -103
  280. package/src/theme/contract.ts +0 -215
  281. package/src/theme/glyph-contract.ts +0 -59
  282. package/src/theme/presets/rmx-01/glyphs.tsx +0 -308
  283. package/src/theme/presets/rmx-01/index.ts +0 -123
  284. package/src/theme/runtime.ts +0 -119
  285. package/src/theme/theme.ts +0 -15
  286. package/src/utils/scroll-lock/README.md +0 -33
  287. /package/dist/{components/anchor/anchor.d.ts → anchor/index.d.ts} +0 -0
  288. /package/dist/{components/breadcrumbs/breadcrumbs.d.ts → breadcrumbs/index.d.ts} +0 -0
  289. /package/dist/{utils → listbox}/flash-attribute.d.ts +0 -0
  290. /package/dist/{utils → listbox}/flash-attribute.js +0 -0
  291. /package/dist/{components/menu → menu}/hover-aim.d.ts +0 -0
  292. /package/dist/{components/menu → menu}/hover-aim.js +0 -0
  293. /package/dist/{interactions/outside-click/outside-click-mixin.d.ts → popover/outside-click.d.ts} +0 -0
  294. /package/dist/{interactions/keydown → select}/keydown.d.ts +0 -0
  295. /package/dist/{interactions/keydown → select}/keydown.js +0 -0
  296. /package/dist/{interactions/typeahead/typeahead-mixin.d.ts → shared/typeahead.d.ts} +0 -0
  297. /package/dist/{utils → shared}/wait-for-css-transition.d.ts +0 -0
  298. /package/dist/{utils → shared}/wait-for-css-transition.js +0 -0
  299. /package/dist/{utils → shared}/wait.d.ts +0 -0
  300. /package/dist/{utils → shared}/wait.js +0 -0
  301. /package/src/{components/anchor/anchor.ts → anchor/index.ts} +0 -0
  302. /package/src/{utils → listbox}/flash-attribute.ts +0 -0
  303. /package/src/{components/menu → menu}/hover-aim.ts +0 -0
  304. /package/src/{interactions/outside-click/outside-click-mixin.ts → popover/outside-click.ts} +0 -0
  305. /package/src/{interactions/keydown → select}/keydown.ts +0 -0
  306. /package/src/{interactions/typeahead/typeahead-mixin.ts → shared/typeahead.ts} +0 -0
  307. /package/src/{utils → shared}/wait-for-css-transition.ts +0 -0
  308. /package/src/{utils → shared}/wait.ts +0 -0
@@ -1,30 +1,23 @@
1
1
  import {
2
2
  attrs,
3
- createElement,
4
3
  createMixin,
5
- css,
6
4
  on,
7
5
  ref,
8
- type CSSMixinDescriptor,
9
6
  type Dispatched,
10
7
  type ElementProps,
11
8
  type Handle,
12
9
  type MixinFactory,
13
- type Props,
14
10
  type RemixNode,
15
11
  } from '@remix-run/ui'
16
- import { type AnchorOptions, type AnchorPoint } from '../anchor/anchor.ts'
17
- import * as button from '../button/button.tsx'
18
- import { Glyph } from '../glyph/glyph.tsx'
19
- import * as popover from '../popover/popover.ts'
20
- import { theme } from '../../theme/theme.ts'
12
+ import { type AnchorOptions, type AnchorPoint } from '../anchor/index.ts'
13
+ import * as popover from '../popover/index.ts'
21
14
  import {
22
15
  hiddenTypeahead,
23
16
  matchNextItemBySearchText,
24
17
  type SearchValue,
25
- } from '../../interactions/typeahead/typeahead-mixin.ts'
26
- import { waitForCssTransition } from '../../utils/wait-for-css-transition.ts'
27
- import { wait } from '../../utils/wait.ts'
18
+ } from '../shared/typeahead.ts'
19
+ import { waitForCssTransition } from '../shared/wait-for-css-transition.ts'
20
+ import { wait } from '../shared/wait.ts'
28
21
  import { createHoverAim } from './hover-aim.ts'
29
22
 
30
23
  const MENU_SELECT_EVENT = 'rmx:menu-select' as const
@@ -37,182 +30,6 @@ type MenuSelectHandler<target extends HTMLElement> = (
37
30
  signal: AbortSignal,
38
31
  ) => void | Promise<void>
39
32
 
40
- const menuButtonCss: CSSMixinDescriptor = css({
41
- display: 'grid',
42
- gridTemplateColumns: 'minmax(0, 1fr) auto',
43
- alignItems: 'center',
44
- borderRadius: theme.radius.md,
45
- paddingInlineEnd: theme.space.sm,
46
- textAlign: 'left',
47
- '&[aria-expanded="true"], &[aria-expanded="true"]:hover, &[aria-expanded="true"]:focus-visible': {
48
- backgroundColor: theme.surface.lvl3,
49
- color: theme.colors.text.primary,
50
- },
51
- })
52
-
53
- const menuPopoverCss: CSSMixinDescriptor = css({
54
- '&[data-menu-submenu="true"][data-anchor-placement^="right"]': {
55
- marginLeft: `calc(${theme.space.xs} * -1)`,
56
- },
57
- '&[data-menu-submenu="true"][data-anchor-placement^="left"]': {
58
- marginLeft: theme.space.xs,
59
- },
60
- '&[data-close-animation="none"]:not(:popover-open)': {
61
- transition: 'none',
62
- transitionBehavior: 'normal',
63
- },
64
- })
65
-
66
- const menuListCss: CSSMixinDescriptor = css({
67
- display: 'flex',
68
- flexDirection: 'column',
69
- flex: '1 1 auto',
70
- minHeight: 0,
71
- paddingBlock: theme.space.xs,
72
- paddingInline: theme.space.none,
73
- overflow: 'auto',
74
- overscrollBehavior: 'contain',
75
- outline: 'none',
76
- userSelect: 'none',
77
- WebkitUserSelect: 'none',
78
- '--rmx-ui-item-inset': `calc(${theme.space.sm} + ${theme.space.xs})`,
79
- '--rmx-ui-item-indicator-gap': 'var(--rmx-menu-item-slot-gap)',
80
- '--rmx-ui-item-indicator-width': 'var(--rmx-menu-item-slot-width)',
81
- '--rmx-menu-item-slot-gap': theme.space.none,
82
- '--rmx-menu-item-slot-width': theme.space.none,
83
- '&:has(> [role="menuitemcheckbox"])': {
84
- '--rmx-menu-item-slot-gap': theme.space.xs,
85
- '--rmx-menu-item-slot-width': theme.fontSize.md,
86
- },
87
- '&:has(> [role="menuitemradio"])': {
88
- '--rmx-menu-item-slot-gap': theme.space.xs,
89
- '--rmx-menu-item-slot-width': theme.fontSize.md,
90
- },
91
- })
92
-
93
- const menuItemCss: CSSMixinDescriptor = css({
94
- display: 'flex',
95
- alignItems: 'center',
96
- width: '100%',
97
- minWidth: 0,
98
- minHeight: theme.control.height.md,
99
- boxSizing: 'border-box',
100
- position: 'relative',
101
- isolation: 'isolate',
102
- paddingInline: `calc(${theme.space.sm} + ${theme.space.xs})`,
103
- color: theme.colors.text.primary,
104
- fontFamily: theme.fontFamily.sans,
105
- fontSize: theme.fontSize.sm,
106
- fontWeight: theme.fontWeight.normal,
107
- lineHeight: theme.lineHeight.normal,
108
- textAlign: 'left',
109
- userSelect: 'none',
110
- WebkitUserSelect: 'none',
111
- '--rmx-menu-item-indicator-opacity': '0',
112
- '&::before': {
113
- content: '""',
114
- position: 'absolute',
115
- insetBlock: 0,
116
- insetInline: theme.space.xs,
117
- borderRadius: theme.radius.md,
118
- backgroundColor: 'transparent',
119
- pointerEvents: 'none',
120
- zIndex: -1,
121
- },
122
- '&:focus': {
123
- outline: 'none',
124
- },
125
- '&[data-highlighted="true"]': {
126
- color: theme.colors.action.primary.foreground,
127
- },
128
- '&[data-highlighted="true"]::before': {
129
- backgroundColor: theme.colors.action.primary.background,
130
- },
131
- '&[aria-haspopup="menu"][aria-expanded="true"]:not(:focus)': {
132
- color: theme.colors.text.primary,
133
- },
134
- '&[aria-haspopup="menu"][aria-expanded="true"]:not(:focus)::before': {
135
- backgroundColor: theme.surface.lvl2,
136
- },
137
- '&[data-submenu-state="selecting"], &[data-submenu-state="dismissing"]': {
138
- color: theme.colors.text.primary,
139
- },
140
- '&[data-submenu-state="selecting"]::before, &[data-submenu-state="dismissing"]::before': {
141
- backgroundColor: theme.surface.lvl2,
142
- },
143
- '&[data-menu-flash="true"]': {
144
- color: theme.colors.text.primary,
145
- },
146
- '&[data-menu-flash="true"]::before': {
147
- backgroundColor: 'transparent',
148
- },
149
- '&[aria-disabled="true"]': {
150
- opacity: 0.5,
151
- },
152
- '&[aria-checked="true"], &[aria-selected="true"]': {
153
- '--rmx-menu-item-indicator-opacity': '1',
154
- },
155
- })
156
-
157
- const menuItemSlotCss: CSSMixinDescriptor = css({
158
- display: 'inline-flex',
159
- alignItems: 'center',
160
- justifyContent: 'center',
161
- flex: '0 0 var(--rmx-menu-item-slot-width)',
162
- width: 'var(--rmx-menu-item-slot-width)',
163
- minWidth: 0,
164
- marginInlineEnd: 'var(--rmx-menu-item-slot-gap)',
165
- overflow: 'hidden',
166
- })
167
-
168
- const menuItemLabelCss: CSSMixinDescriptor = css({
169
- display: 'inline-flex',
170
- alignItems: 'center',
171
- flex: '1 1 auto',
172
- minWidth: 0,
173
- })
174
-
175
- const menuItemGlyphCss: CSSMixinDescriptor = css({
176
- display: 'inline-flex',
177
- alignItems: 'center',
178
- justifyContent: 'center',
179
- width: theme.fontSize.md,
180
- height: theme.fontSize.md,
181
- color: 'currentColor',
182
- flexShrink: 0,
183
- '& > svg': {
184
- display: 'block',
185
- width: '100%',
186
- height: '100%',
187
- },
188
- opacity: 'var(--rmx-menu-item-indicator-opacity)',
189
- })
190
-
191
- const menuTriggerGlyphCss: CSSMixinDescriptor = css({
192
- display: 'inline-flex',
193
- alignItems: 'center',
194
- justifyContent: 'center',
195
- width: theme.fontSize.md,
196
- height: theme.fontSize.md,
197
- color: 'currentColor',
198
- flexShrink: 0,
199
- marginInlineStart: 'auto',
200
- '& > svg': {
201
- display: 'block',
202
- width: '100%',
203
- height: '100%',
204
- },
205
- })
206
-
207
- export const buttonStyle = menuButtonCss
208
- export const popoverStyle = menuPopoverCss
209
- export const listStyle = menuListCss
210
- export const itemStyle = menuItemCss
211
- export const itemSlotStyle = menuItemSlotCss
212
- export const itemLabelStyle = menuItemLabelCss
213
- export const itemGlyphStyle = menuItemGlyphCss
214
- export const triggerGlyphStyle = menuTriggerGlyphCss
215
-
216
33
  declare global {
217
34
  interface HTMLElementEventMap {
218
35
  [MENU_SELECT_EVENT]: MenuSelectEvent
@@ -964,50 +781,56 @@ const triggerMixin: MixinFactory<HTMLElement, [options?: MenuTriggerOptions], El
964
781
  createMixin<HTMLElement, [options?: MenuTriggerOptions], ElementProps>((handle) => {
965
782
  let context = handle.context.get(MenuProvider)
966
783
 
967
- return (options = {}) => [
968
- attrs({
969
- id: handle.id,
970
- 'aria-controls': context.listId,
971
- 'aria-expanded': context.isOpen ? 'true' : 'false',
972
- 'aria-haspopup': 'menu',
973
- }),
974
- ref((node: HTMLElement, signal) => {
975
- context.registerTrigger(node, handle.id)
976
- signal.addEventListener('abort', () => {
977
- context.unregisterTrigger(node)
978
- })
979
- }),
980
- popover.anchor({
981
- placement: 'bottom-start',
982
- ...options,
983
- }),
984
- on('click', () => {
985
- if (context.isOpen) {
986
- void context.closeAll()
987
- } else {
988
- void context.open({ strategy: 'list' })
989
- }
990
- }),
991
- on('keydown', (event) => {
992
- switch (event.key) {
993
- case 'ArrowDown':
994
- event.preventDefault()
995
- event.stopPropagation()
996
- void context.open({ strategy: 'first' })
997
- break
998
- case 'ArrowUp':
999
- event.preventDefault()
1000
- event.stopPropagation()
1001
- void context.open({ strategy: 'last' })
1002
- break
1003
- case 'Enter':
1004
- case ' ':
1005
- event.preventDefault()
1006
- event.stopPropagation()
784
+ return (options = {}, props = options as ElementProps) => {
785
+ if (props === options) {
786
+ options = {}
787
+ }
788
+
789
+ return [
790
+ attrs({
791
+ id: handle.id,
792
+ 'aria-controls': context.listId,
793
+ 'aria-expanded': context.isOpen ? 'true' : 'false',
794
+ 'aria-haspopup': 'menu',
795
+ }),
796
+ ref((node: HTMLElement, signal) => {
797
+ context.registerTrigger(node, handle.id)
798
+ signal.addEventListener('abort', () => {
799
+ context.unregisterTrigger(node)
800
+ })
801
+ }),
802
+ popover.anchor({
803
+ placement: 'bottom-start',
804
+ ...options,
805
+ }),
806
+ on('click', () => {
807
+ if (context.isOpen) {
808
+ void context.closeAll()
809
+ } else {
1007
810
  void context.open({ strategy: 'list' })
1008
- }
1009
- }),
1010
- ]
811
+ }
812
+ }),
813
+ on('keydown', (event) => {
814
+ switch (event.key) {
815
+ case 'ArrowDown':
816
+ event.preventDefault()
817
+ event.stopPropagation()
818
+ void context.open({ strategy: 'first' })
819
+ break
820
+ case 'ArrowUp':
821
+ event.preventDefault()
822
+ event.stopPropagation()
823
+ void context.open({ strategy: 'last' })
824
+ break
825
+ case 'Enter':
826
+ case ' ':
827
+ event.preventDefault()
828
+ event.stopPropagation()
829
+ void context.open({ strategy: 'list' })
830
+ }
831
+ }),
832
+ ]
833
+ }
1011
834
  })
1012
835
 
1013
836
  const contextTriggerMixin: MixinFactory<
@@ -1036,46 +859,52 @@ const contextTriggerMixin: MixinFactory<
1036
859
  }
1037
860
  }
1038
861
 
1039
- return (options = {}) => [
1040
- attrs({
1041
- id: handle.id,
1042
- 'aria-controls': context.listId,
1043
- 'aria-expanded': context.isOpen ? 'true' : 'false',
1044
- 'aria-haspopup': 'menu',
1045
- }),
1046
- ref((node: HTMLElement, signal) => {
1047
- context.registerTrigger(node, handle.id)
1048
- signal.addEventListener('abort', () => {
1049
- context.unregisterTrigger(node)
1050
- })
1051
- }),
1052
- on('contextmenu', (event) => {
1053
- event.preventDefault()
1054
- event.stopPropagation()
1055
- setAnchorPoint({ x: event.clientX, y: event.clientY }, options)
1056
- void context.open({ strategy: 'list' })
1057
- }),
1058
- on('keydown', (event) => {
1059
- if (event.key !== 'ContextMenu' && !(event.shiftKey && event.key === 'F10')) {
1060
- return
1061
- }
862
+ return (options = {}, props = options as ElementProps) => {
863
+ if (props === options) {
864
+ options = {}
865
+ }
1062
866
 
1063
- event.preventDefault()
1064
- event.stopPropagation()
1065
-
1066
- let rect = event.currentTarget.getBoundingClientRect()
1067
- setAnchorPoint(
1068
- {
1069
- x: rect.left,
1070
- y: rect.top,
1071
- width: rect.width,
1072
- height: rect.height,
1073
- },
1074
- options,
1075
- )
1076
- void context.open({ strategy: 'list' })
1077
- }),
1078
- ]
867
+ return [
868
+ attrs({
869
+ id: handle.id,
870
+ 'aria-controls': context.listId,
871
+ 'aria-expanded': context.isOpen ? 'true' : 'false',
872
+ 'aria-haspopup': 'menu',
873
+ }),
874
+ ref((node: HTMLElement, signal) => {
875
+ context.registerTrigger(node, handle.id)
876
+ signal.addEventListener('abort', () => {
877
+ context.unregisterTrigger(node)
878
+ })
879
+ }),
880
+ on('contextmenu', (event) => {
881
+ event.preventDefault()
882
+ event.stopPropagation()
883
+ setAnchorPoint({ x: event.clientX, y: event.clientY }, options)
884
+ void context.open({ strategy: 'list' })
885
+ }),
886
+ on('keydown', (event) => {
887
+ if (event.key !== 'ContextMenu' && !(event.shiftKey && event.key === 'F10')) {
888
+ return
889
+ }
890
+
891
+ event.preventDefault()
892
+ event.stopPropagation()
893
+
894
+ let rect = event.currentTarget.getBoundingClientRect()
895
+ setAnchorPoint(
896
+ {
897
+ x: rect.left,
898
+ y: rect.top,
899
+ width: rect.width,
900
+ height: rect.height,
901
+ },
902
+ options,
903
+ )
904
+ void context.open({ strategy: 'list' })
905
+ }),
906
+ ]
907
+ }
1079
908
  })
1080
909
 
1081
910
  const popoverMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
@@ -1456,13 +1285,6 @@ const submenuTriggerMixin: MixinFactory<
1456
1285
  }
1457
1286
  })
1458
1287
 
1459
- export interface MenuListProps extends Props<'div'> {}
1460
-
1461
- type MenuListChildProps = Omit<
1462
- JSX.LibraryManagedAttributes<typeof MenuList, MenuListProps>,
1463
- 'children'
1464
- >
1465
-
1466
1288
  export const Context = MenuProvider
1467
1289
  export const contextTrigger = contextTriggerMixin
1468
1290
  export const item = itemMixin
@@ -1487,139 +1309,3 @@ export function onMenuSelect<target extends HTMLElement>(
1487
1309
  ): ReturnType<typeof on<target, typeof MENU_SELECT_EVENT>> {
1488
1310
  return on(MENU_SELECT_EVENT, handler, captureBoolean)
1489
1311
  }
1490
-
1491
- export interface MenuProps extends Omit<Props<'button'>, 'children'> {
1492
- children?: RemixNode
1493
- label: RemixNode
1494
- menuLabel?: string
1495
- }
1496
-
1497
- export interface MenuItemProps
1498
- extends Omit<Props<'div'>, 'children' | 'name' | 'type' | 'value'>,
1499
- MenuItemOptions {
1500
- children?: RemixNode
1501
- }
1502
-
1503
- export interface SubmenuProps
1504
- extends Omit<Props<'div'>, 'children' | 'name' | 'type' | 'value'>,
1505
- Omit<SubmenuTriggerOptions, 'label'> {
1506
- children?: RemixNode
1507
- label: RemixNode
1508
- listProps?: MenuListChildProps
1509
- menuLabel?: string
1510
- }
1511
-
1512
- export function Menu(handle: Handle<MenuProps>): () => RemixNode {
1513
- let buttonRef: HTMLButtonElement | undefined
1514
-
1515
- return () => {
1516
- let { children, label, menuLabel, mix, type, ...buttonProps } = handle.props
1517
-
1518
- return (
1519
- <menu.Context label={menuLabel}>
1520
- <button
1521
- {...buttonProps}
1522
- type={type ?? 'button'}
1523
- mix={[
1524
- button.baseStyle,
1525
- button.ghostStyle,
1526
- buttonStyle,
1527
- menu.trigger(),
1528
- ref((node: HTMLButtonElement, signal) => {
1529
- buttonRef = node
1530
- signal.addEventListener('abort', () => {
1531
- if (buttonRef === node) {
1532
- buttonRef = undefined
1533
- }
1534
- })
1535
- }),
1536
- mix,
1537
- ]}
1538
- >
1539
- <span mix={button.labelStyle}>{label}</span>
1540
- <Glyph mix={button.iconStyle} name="chevronDown" />
1541
- </button>
1542
- <MenuList
1543
- mix={onMenuSelect((event) => {
1544
- if (!buttonRef) {
1545
- return
1546
- }
1547
-
1548
- event.stopPropagation()
1549
- buttonRef.dispatchEvent(new MenuSelectEvent(event.item))
1550
- })}
1551
- >
1552
- {children}
1553
- </MenuList>
1554
- </menu.Context>
1555
- )
1556
- }
1557
- }
1558
-
1559
- export function MenuList(handle: Handle<MenuListProps>): () => RemixNode {
1560
- return () => {
1561
- let { children, mix, ...divProps } = handle.props
1562
-
1563
- return (
1564
- <div mix={[popover.surfaceStyle, popoverStyle, menu.popover()]}>
1565
- <div {...divProps} mix={[listStyle, menu.list(), mix]}>
1566
- {children}
1567
- </div>
1568
- </div>
1569
- )
1570
- }
1571
- }
1572
-
1573
- export function MenuItem(handle: Handle<MenuItemProps>): () => RemixNode {
1574
- return () => {
1575
- let { checked, children, disabled, label, mix, name, searchValue, type, value, ...divProps } =
1576
- handle.props
1577
-
1578
- return (
1579
- <div
1580
- {...divProps}
1581
- mix={[
1582
- itemStyle,
1583
- menu.item({ checked, disabled, label, name, searchValue, type, value }),
1584
- mix,
1585
- ]}
1586
- >
1587
- <span mix={itemSlotStyle}>
1588
- <Glyph mix={itemGlyphStyle} name="check" />
1589
- </span>
1590
- <span mix={itemLabelStyle}>{children ?? label}</span>
1591
- </div>
1592
- )
1593
- }
1594
- }
1595
-
1596
- export function Submenu(handle: Handle<SubmenuProps>): () => RemixNode {
1597
- return () => {
1598
- let { children, disabled, label, listProps, menuLabel, mix, searchValue, value, ...divProps } =
1599
- handle.props
1600
-
1601
- return (
1602
- <menu.Context label={menuLabel}>
1603
- <div
1604
- {...divProps}
1605
- mix={[
1606
- itemStyle,
1607
- menu.submenuTrigger({
1608
- disabled,
1609
- searchValue,
1610
- value,
1611
- }),
1612
- mix,
1613
- ]}
1614
- >
1615
- <span mix={itemSlotStyle}>
1616
- <Glyph mix={itemGlyphStyle} name="check" />
1617
- </span>
1618
- <span mix={itemLabelStyle}>{label}</span>
1619
- <Glyph mix={triggerGlyphStyle} name="chevronRight" />
1620
- </div>
1621
- <MenuList {...listProps}>{children}</MenuList>
1622
- </menu.Context>
1623
- )
1624
- }
1625
- }
@@ -1,16 +1,15 @@
1
- # Popover
1
+ # popover
2
2
 
3
3
  `popover` is a low-level primitive for anchored, dismissible floating panels.
4
4
 
5
- Use it for custom surfaces like filters, inspectors, and view options. Higher-level widgets like `menu`, `select`, and `combobox` should build on top of it instead of exposing raw `popover.*` mixins directly.
5
+ Use it for custom surfaces like filters, inspectors, and view options. Higher-level widgets like menu, select, and combobox should build on top of it instead of exposing raw `popover.*` mixins directly.
6
6
 
7
- ## Usage
7
+ ## Primitive Usage
8
8
 
9
9
  ```tsx
10
- import { css, on, type Handle } from 'remix/ui'
11
- import { Button } from 'remix/ui/button'
12
- import { Glyph } from 'remix/ui/glyph'
13
- import { popover } from 'remix/ui/popover'
10
+ import { on, type Handle } from 'remix/ui'
11
+ import * as popover from 'remix/ui/popover'
12
+ import { panelStyle } from './popover.styles'
14
13
 
15
14
  export function ViewOptions(handle: Handle) {
16
15
  let open = false
@@ -27,21 +26,20 @@ export function ViewOptions(handle: Handle) {
27
26
 
28
27
  return () => (
29
28
  <popover.Context>
30
- <Button
31
- endIcon={<Glyph name="chevronDown" />}
29
+ <button
32
30
  mix={[
33
31
  popover.anchor({ placement: 'bottom-end' }),
34
32
  popover.focusOnHide(),
35
33
  on('click', openPopover),
36
34
  ]}
37
- tone="secondary"
35
+ type="button"
38
36
  >
39
37
  View options
40
- </Button>
38
+ </button>
41
39
 
42
40
  <div
43
41
  mix={[
44
- popover.surfaceStyle,
42
+ panelStyle,
45
43
  popover.surface({
46
44
  open,
47
45
  onHide() {
@@ -50,23 +48,17 @@ export function ViewOptions(handle: Handle) {
50
48
  }),
51
49
  ]}
52
50
  >
53
- <div mix={popover.contentStyle}>
54
- <Button mix={[popover.focusOnShow(), on('click', closePopover)]} tone="ghost">
55
- Close
56
- </Button>
57
- <div mix={panelBody}>Panel content</div>
58
- </div>
51
+ <button mix={[popover.focusOnShow(), on('click', closePopover)]} type="button">
52
+ Close
53
+ </button>
54
+ <div>Panel content</div>
59
55
  </div>
60
56
  </popover.Context>
61
57
  )
62
58
  }
63
-
64
- let panelBody = css({
65
- padding: '12px',
66
- })
67
59
  ```
68
60
 
69
- ## `popover.*`
61
+ ## `remix/ui/popover`
70
62
 
71
63
  ### `popover.Context`
72
64
 
@@ -76,10 +68,6 @@ Provides shared coordination for one popover instance. Render the anchor, any fo
76
68
 
77
69
  Registers the host element as the anchor for the current popover surface.
78
70
 
79
- - Accepts standard `AnchorOptions`.
80
- - The stored anchor controls where the surface is positioned when it opens.
81
- - Apply it to the button or other element the surface should attach to.
82
-
83
71
  ### `popover.surface({ open, onHide, ... })`
84
72
 
85
73
  Turns the host into the controlled popover surface.
@@ -99,13 +87,12 @@ Registers the element that should receive focus when the popover opens.
99
87
 
100
88
  Registers the element that should receive focus again when the popover closes.
101
89
 
102
- ### `popover.surfaceStyle`
103
-
104
- Default floating-surface presentation for popovers.
90
+ ### Primitive Types
105
91
 
106
- ### `popover.contentStyle`
107
-
108
- Default inner scroll container for popover content.
92
+ - `PopoverContext`: context object exposed by `popover.Context`.
93
+ - `PopoverProps`: provider props for one popover instance.
94
+ - `PopoverSurfaceOptions`: options accepted by `popover.surface(...)`.
95
+ - `PopoverHideRequest`: hide request passed to `onHide`.
109
96
 
110
97
  ## Behavior Notes
111
98
 
@@ -114,9 +101,3 @@ Default inner scroll container for popover content.
114
101
  - `popover.focusOnShow()` wins on open when present.
115
102
  - `popover.focusOnHide()` is used on close by default when focus restoration is enabled.
116
103
  - `closeOnAnchorClick: false` keeps anchor clicks inside the current session, which is useful for input-driven popovers like comboboxes.
117
-
118
- ## When To Use Something Else
119
-
120
- - Use `menu` for command surfaces.
121
- - Use `listbox` or `select` for committed value picking.
122
- - Use `combobox` for input-first popup selection.