@remix-run/ui 0.1.1 → 0.2.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 (243) hide show
  1. package/README.md +33 -10
  2. package/dist/animation/animate-layout-mixin.js +66 -66
  3. package/dist/animation/animate-layout-mixin.js.map +1 -1
  4. package/dist/animation/spring.d.ts +6 -35
  5. package/dist/animation/spring.js +9 -8
  6. package/dist/animation/spring.js.map +1 -1
  7. package/dist/components/accordion/accordion.d.ts +7 -7
  8. package/dist/components/accordion/accordion.js +0 -2
  9. package/dist/components/accordion/accordion.js.map +1 -1
  10. package/dist/components/accordion/demos/card.demo.d.ts +6 -0
  11. package/dist/components/accordion/demos/card.demo.js +58 -0
  12. package/dist/components/accordion/demos/card.demo.js.map +1 -0
  13. package/dist/components/accordion/demos/multiple.demo.d.ts +6 -0
  14. package/dist/components/accordion/demos/multiple.demo.js +19 -0
  15. package/dist/components/accordion/demos/multiple.demo.js.map +1 -0
  16. package/dist/components/accordion/demos/overview.demo.d.ts +6 -0
  17. package/dist/components/accordion/demos/overview.demo.js +19 -0
  18. package/dist/components/accordion/demos/overview.demo.js.map +1 -0
  19. package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
  20. package/dist/components/breadcrumbs/breadcrumbs.js +0 -2
  21. package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -1
  22. package/dist/components/breadcrumbs/demos/basic.demo.d.ts +5 -0
  23. package/dist/components/breadcrumbs/demos/basic.demo.js +14 -0
  24. package/dist/components/breadcrumbs/demos/basic.demo.js.map +1 -0
  25. package/dist/components/breadcrumbs/demos/separator.demo.d.ts +5 -0
  26. package/dist/components/breadcrumbs/demos/separator.demo.js +14 -0
  27. package/dist/components/breadcrumbs/demos/separator.demo.js.map +1 -0
  28. package/dist/components/button/button.d.ts +86 -4
  29. package/dist/components/button/button.js +63 -3
  30. package/dist/components/button/button.js.map +1 -1
  31. package/dist/components/button/demos/aliases.demo.d.ts +6 -0
  32. package/dist/components/button/demos/aliases.demo.js +18 -0
  33. package/dist/components/button/demos/aliases.demo.js.map +1 -0
  34. package/dist/components/button/demos/basic.demo.d.ts +6 -0
  35. package/dist/components/button/demos/basic.demo.js +19 -0
  36. package/dist/components/button/demos/basic.demo.js.map +1 -0
  37. package/dist/components/button/demos/states.demo.d.ts +6 -0
  38. package/dist/components/button/demos/states.demo.js +29 -0
  39. package/dist/components/button/demos/states.demo.js.map +1 -0
  40. package/dist/components/combobox/combobox.d.ts +11 -11
  41. package/dist/components/combobox/combobox.js +0 -2
  42. package/dist/components/combobox/combobox.js.map +1 -1
  43. package/dist/components/combobox/demos/overview.demo.d.ts +6 -0
  44. package/dist/components/combobox/demos/overview.demo.js +153 -0
  45. package/dist/components/combobox/demos/overview.demo.js.map +1 -0
  46. package/dist/components/listbox/demos/overview.demo.d.ts +6 -0
  47. package/dist/components/listbox/demos/overview.demo.js +64 -0
  48. package/dist/components/listbox/demos/overview.demo.js.map +1 -0
  49. package/dist/components/listbox/listbox.d.ts +4 -9
  50. package/dist/components/listbox/listbox.js +21 -33
  51. package/dist/components/listbox/listbox.js.map +1 -1
  52. package/dist/components/menu/demos/bubbling.demo.d.ts +5 -0
  53. package/dist/components/menu/demos/bubbling.demo.js +15 -0
  54. package/dist/components/menu/demos/bubbling.demo.js.map +1 -0
  55. package/dist/components/menu/demos/overview.demo.d.ts +6 -0
  56. package/dist/components/menu/demos/overview.demo.js +32 -0
  57. package/dist/components/menu/demos/overview.demo.js.map +1 -0
  58. package/dist/components/menu/menu.d.ts +15 -24
  59. package/dist/components/menu/menu.js +23 -38
  60. package/dist/components/menu/menu.js.map +1 -1
  61. package/dist/components/popover/demos/overview.demo.d.ts +6 -0
  62. package/dist/components/popover/demos/overview.demo.js +64 -0
  63. package/dist/components/popover/demos/overview.demo.js.map +1 -0
  64. package/dist/components/popover/popover.d.ts +5 -5
  65. package/dist/components/popover/popover.js.map +1 -1
  66. package/dist/components/select/demos/deconstructed.demo.d.ts +6 -0
  67. package/dist/components/select/demos/deconstructed.demo.js +53 -0
  68. package/dist/components/select/demos/deconstructed.demo.js.map +1 -0
  69. package/dist/components/select/demos/overview.demo.d.ts +5 -0
  70. package/dist/components/select/demos/overview.demo.js +27 -0
  71. package/dist/components/select/demos/overview.demo.js.map +1 -0
  72. package/dist/components/select/select.d.ts +11 -11
  73. package/dist/components/select/select.js +17 -26
  74. package/dist/components/select/select.js.map +1 -1
  75. package/dist/components/tabs/tabs.d.ts +6 -6
  76. package/dist/components/tabs/tabs.js +0 -2
  77. package/dist/components/tabs/tabs.js.map +1 -1
  78. package/dist/index.d.ts +2 -2
  79. package/dist/index.js.map +1 -1
  80. package/dist/interactions/keydown/keydown.d.ts +1 -1
  81. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
  82. package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
  83. package/dist/runtime/component.d.ts +18 -12
  84. package/dist/runtime/component.js +93 -80
  85. package/dist/runtime/component.js.map +1 -1
  86. package/dist/runtime/core/attributes.d.ts +14 -0
  87. package/dist/runtime/core/attributes.js +103 -0
  88. package/dist/runtime/core/attributes.js.map +1 -0
  89. package/dist/runtime/core/children.d.ts +7 -0
  90. package/dist/runtime/core/children.js +30 -0
  91. package/dist/runtime/core/children.js.map +1 -0
  92. package/dist/runtime/core/props.d.ts +9 -0
  93. package/dist/runtime/core/props.js +170 -0
  94. package/dist/runtime/core/props.js.map +1 -0
  95. package/dist/runtime/core/vnode.d.ts +3 -0
  96. package/dist/runtime/core/vnode.js +39 -0
  97. package/dist/runtime/core/vnode.js.map +1 -0
  98. package/dist/runtime/create-element.js +2 -1
  99. package/dist/runtime/create-element.js.map +1 -1
  100. package/dist/runtime/diff-dom.js +47 -14
  101. package/dist/runtime/diff-dom.js.map +1 -1
  102. package/dist/runtime/diff-props.d.ts +1 -2
  103. package/dist/runtime/diff-props.js +2 -181
  104. package/dist/runtime/diff-props.js.map +1 -1
  105. package/dist/runtime/dom.d.ts +20 -9
  106. package/dist/runtime/frame.d.ts +23 -2
  107. package/dist/runtime/frame.js +41 -47
  108. package/dist/runtime/frame.js.map +1 -1
  109. package/dist/runtime/jsx.d.ts +1 -1
  110. package/dist/runtime/jsx.js +2 -28
  111. package/dist/runtime/jsx.js.map +1 -1
  112. package/dist/runtime/mixins/link-mixin.d.ts +5 -1
  113. package/dist/runtime/mixins/link-mixin.js.map +1 -1
  114. package/dist/runtime/mixins/mixin.d.ts +2 -1
  115. package/dist/runtime/mixins/mixin.js +9 -4
  116. package/dist/runtime/mixins/mixin.js.map +1 -1
  117. package/dist/runtime/mixins/on-mixin.d.ts +15 -4
  118. package/dist/runtime/mixins/on-mixin.js +9 -2
  119. package/dist/runtime/mixins/on-mixin.js.map +1 -1
  120. package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
  121. package/dist/runtime/mixins/ref-mixin.js.map +1 -1
  122. package/dist/runtime/navigation.js +9 -1
  123. package/dist/runtime/navigation.js.map +1 -1
  124. package/dist/runtime/reconcile.d.ts +1 -1
  125. package/dist/runtime/reconcile.js +452 -197
  126. package/dist/runtime/reconcile.js.map +1 -1
  127. package/dist/runtime/run.d.ts +17 -2
  128. package/dist/runtime/run.js.map +1 -1
  129. package/dist/runtime/scheduler.d.ts +10 -10
  130. package/dist/runtime/scheduler.js +1 -1
  131. package/dist/runtime/scheduler.js.map +1 -1
  132. package/dist/runtime/stream-protocol.d.ts +8 -0
  133. package/dist/runtime/stream-protocol.js +20 -0
  134. package/dist/runtime/stream-protocol.js.map +1 -0
  135. package/dist/runtime/svg-attributes.js +1 -0
  136. package/dist/runtime/svg-attributes.js.map +1 -1
  137. package/dist/runtime/to-vnode.js +7 -11
  138. package/dist/runtime/to-vnode.js.map +1 -1
  139. package/dist/runtime/vnode.d.ts +11 -5
  140. package/dist/runtime/vnode.js +6 -3
  141. package/dist/runtime/vnode.js.map +1 -1
  142. package/dist/server/stream.d.ts +2 -0
  143. package/dist/server/stream.js +154 -114
  144. package/dist/server/stream.js.map +1 -1
  145. package/dist/style/css-mixin.d.ts +2 -1
  146. package/dist/style/css-mixin.js.map +1 -1
  147. package/dist/style/index.d.ts +1 -1
  148. package/dist/style/layers.d.ts +2 -0
  149. package/dist/style/layers.js +3 -0
  150. package/dist/style/layers.js.map +1 -0
  151. package/dist/style/stylesheet.d.ts +13 -11
  152. package/dist/style/stylesheet.js +69 -8
  153. package/dist/style/stylesheet.js.map +1 -1
  154. package/dist/theme/contract.d.ts +24 -222
  155. package/dist/theme/contract.js +2 -2
  156. package/dist/theme/contract.js.map +1 -1
  157. package/dist/theme/glyph-contract.d.ts +1 -3
  158. package/dist/theme/glyph-contract.js.map +1 -1
  159. package/dist/theme/presets/rmx-01/index.d.ts +4 -2
  160. package/dist/theme/presets/rmx-01/index.js.map +1 -1
  161. package/dist/theme/runtime.js +4 -1
  162. package/dist/theme/runtime.js.map +1 -1
  163. package/dist/utils/scroll-lock.d.ts +3 -3
  164. package/dist/utils/scroll-lock.js.map +1 -1
  165. package/package.json +3 -3
  166. package/src/animation/README.md +316 -0
  167. package/src/animation/animate-layout-mixin.ts +79 -71
  168. package/src/animation/spring.ts +21 -9
  169. package/src/components/accordion/README.md +166 -0
  170. package/src/components/accordion/accordion.tsx +14 -14
  171. package/src/components/accordion/demos/card.demo.tsx +113 -0
  172. package/src/components/accordion/demos/multiple.demo.tsx +56 -0
  173. package/src/components/accordion/demos/overview.demo.tsx +56 -0
  174. package/src/components/anchor/README.md +153 -0
  175. package/src/components/anchor/anchor.ts +1 -1
  176. package/src/components/breadcrumbs/README.md +55 -0
  177. package/src/components/breadcrumbs/breadcrumbs.tsx +1 -3
  178. package/src/components/breadcrumbs/demos/basic.demo.tsx +17 -0
  179. package/src/components/breadcrumbs/demos/separator.demo.tsx +18 -0
  180. package/src/components/button/README.md +4 -4
  181. package/src/components/button/button.tsx +118 -20
  182. package/src/components/button/demos/aliases.demo.tsx +27 -0
  183. package/src/components/button/demos/basic.demo.tsx +30 -0
  184. package/src/components/button/demos/states.demo.tsx +52 -0
  185. package/src/components/combobox/README.md +1 -1
  186. package/src/components/combobox/combobox.tsx +37 -21
  187. package/src/components/combobox/demos/overview.demo.tsx +199 -0
  188. package/src/components/glyph/README.md +72 -0
  189. package/src/components/glyph/glyph.tsx +2 -2
  190. package/src/components/listbox/README.md +115 -0
  191. package/src/components/listbox/demos/overview.demo.tsx +101 -0
  192. package/src/components/listbox/listbox.ts +106 -110
  193. package/src/components/menu/README.md +96 -0
  194. package/src/components/menu/demos/bubbling.demo.tsx +34 -0
  195. package/src/components/menu/demos/overview.demo.tsx +76 -0
  196. package/src/components/menu/menu.tsx +61 -55
  197. package/src/components/popover/README.md +3 -3
  198. package/src/components/popover/demos/overview.demo.tsx +143 -0
  199. package/src/components/popover/popover.ts +90 -79
  200. package/src/components/select/README.md +107 -0
  201. package/src/components/select/demos/deconstructed.demo.tsx +95 -0
  202. package/src/components/select/demos/overview.demo.tsx +51 -0
  203. package/src/components/select/select.tsx +53 -36
  204. package/src/components/tabs/tabs.tsx +10 -5
  205. package/src/index.ts +2 -1
  206. package/src/runtime/component.ts +122 -88
  207. package/src/runtime/core/attributes.ts +118 -0
  208. package/src/runtime/core/children.ts +42 -0
  209. package/src/runtime/core/props.ts +187 -0
  210. package/src/runtime/core/vnode.ts +48 -0
  211. package/src/runtime/create-element.ts +2 -1
  212. package/src/runtime/diff-dom.ts +59 -12
  213. package/src/runtime/diff-props.ts +2 -187
  214. package/src/runtime/dom.ts +20 -9
  215. package/src/runtime/frame.ts +71 -55
  216. package/src/runtime/jsx.ts +3 -30
  217. package/src/runtime/mixins/link-mixin.ts +88 -86
  218. package/src/runtime/mixins/mixin.ts +18 -5
  219. package/src/runtime/mixins/on-mixin.ts +20 -12
  220. package/src/runtime/mixins/ref-mixin.ts +4 -5
  221. package/src/runtime/navigation.ts +10 -1
  222. package/src/runtime/reconcile.ts +563 -223
  223. package/src/runtime/run.ts +17 -1
  224. package/src/runtime/scheduler.ts +20 -20
  225. package/src/runtime/stream-protocol.ts +27 -0
  226. package/src/runtime/svg-attributes.ts +1 -0
  227. package/src/runtime/to-vnode.ts +7 -10
  228. package/src/runtime/vnode.ts +16 -9
  229. package/src/server/README.md +90 -0
  230. package/src/server/stream.ts +176 -124
  231. package/src/style/css-mixin.ts +6 -1
  232. package/src/style/index.ts +1 -2
  233. package/src/style/layers.ts +2 -0
  234. package/src/style/stylesheet.ts +88 -11
  235. package/src/test/README.md +107 -0
  236. package/src/test/utils.ts +3 -1
  237. package/src/theme/README.md +103 -0
  238. package/src/theme/contract.ts +37 -5
  239. package/src/theme/glyph-contract.ts +3 -1
  240. package/src/theme/presets/rmx-01/index.ts +4 -2
  241. package/src/theme/runtime.ts +4 -1
  242. package/src/utils/scroll-lock/README.md +33 -0
  243. package/src/utils/scroll-lock.ts +7 -3
@@ -1,5 +1,3 @@
1
- // @jsxRuntime classic
2
- // @jsx createElement
3
1
  import {
4
2
  attrs,
5
3
  createElement,
@@ -8,8 +6,10 @@ import {
8
6
  on,
9
7
  ref,
10
8
  type CSSMixinDescriptor,
9
+ type Dispatched,
11
10
  type ElementProps,
12
11
  type Handle,
12
+ type MixinFactory,
13
13
  type Props,
14
14
  type RemixNode,
15
15
  } from '@remix-run/ui'
@@ -32,7 +32,10 @@ const MENU_FLASH_DURATION_MS = 60
32
32
  const SUBMENU_OPEN_DELAY_MS = 300
33
33
  const SUBMENU_ANCHOR_RELATIVE_TO = '[role="menu"] > [role^="menuitem"]'
34
34
 
35
- type MenuSelectHandler = (event: MenuSelectEvent, signal: AbortSignal) => void | Promise<void>
35
+ type MenuSelectHandler<target extends HTMLElement> = (
36
+ event: Dispatched<MenuSelectEvent, target>,
37
+ signal: AbortSignal,
38
+ ) => void | Promise<void>
36
39
 
37
40
  const menuButtonCss: CSSMixinDescriptor = css({
38
41
  display: 'grid',
@@ -219,19 +222,8 @@ declare global {
219
222
  type MenuItemType = 'item' | 'checkbox' | 'radio'
220
223
  type CloseAnimation = 'fade' | 'none'
221
224
  type OpenStrategy = 'first' | 'last' | 'list' | 'none'
222
-
223
- enum NavigationStrategy {
224
- Next = 0,
225
- Previous = 1,
226
- First = 2,
227
- Last = 3,
228
- }
229
-
230
- enum State {
231
- Idle = 'idle',
232
- Dismissing = 'dismissing',
233
- Selecting = 'selecting',
234
- }
225
+ type NavigationStrategy = 'next' | 'previous' | 'first' | 'last'
226
+ type State = 'idle' | 'dismissing' | 'selecting'
235
227
 
236
228
  export interface MenuSelectItem {
237
229
  checked?: boolean
@@ -425,7 +417,7 @@ function focusNode(node: HTMLElement | undefined) {
425
417
  }
426
418
  }
427
419
 
428
- function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
420
+ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>): () => RemixNode {
429
421
  let parent = handle.context.get(MenuProvider)
430
422
 
431
423
  let activeId: string | null = null
@@ -435,7 +427,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
435
427
  let items: RegisteredMenuItem[] = []
436
428
  let listRef: HTMLElement | undefined
437
429
  let open = false
438
- let state = State.Idle
430
+ let state: State = 'idle'
439
431
  let suppressNextPointerLeaveClear = false
440
432
  let surfaceRef: HTMLElement | undefined
441
433
  let triggerId: string | undefined
@@ -487,7 +479,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
487
479
  strategy = 'list',
488
480
  focus = strategy !== 'none',
489
481
  }: OpenMenuOptions = {}) {
490
- if (state !== State.Idle) {
482
+ if (state !== 'idle') {
491
483
  return
492
484
  }
493
485
 
@@ -538,9 +530,9 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
538
530
  open = false
539
531
  if (animation === 'none') {
540
532
  activeId = null
541
- state = State.Idle
542
- } else if (wasOpen && state === State.Idle) {
543
- state = State.Dismissing
533
+ state = 'idle'
534
+ } else if (wasOpen && state === 'idle') {
535
+ state = 'dismissing'
544
536
  }
545
537
  updates.push(handle.update())
546
538
  }
@@ -561,8 +553,8 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
561
553
  shouldUpdate = true
562
554
  }
563
555
 
564
- if (state !== State.Idle) {
565
- state = State.Idle
556
+ if (state !== 'idle') {
557
+ state = 'idle'
566
558
  shouldUpdate = true
567
559
  }
568
560
 
@@ -630,7 +622,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
630
622
  }
631
623
 
632
624
  function highlight(id: string | null, { focus = false }: HighlightOptions = {}) {
633
- if (state !== State.Idle) {
625
+ if (state !== 'idle') {
634
626
  return
635
627
  }
636
628
 
@@ -673,7 +665,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
673
665
  }
674
666
 
675
667
  function navigate(strategy: NavigationStrategy) {
676
- if (state !== State.Idle) {
668
+ if (state !== 'idle') {
677
669
  return
678
670
  }
679
671
 
@@ -682,22 +674,22 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
682
674
  let nextItem: RegisteredMenuItem | undefined
683
675
 
684
676
  switch (strategy) {
685
- case NavigationStrategy.Next:
677
+ case 'next':
686
678
  nextItem =
687
679
  activeIndex === -1
688
680
  ? interactableItems[0]
689
681
  : (interactableItems[activeIndex + 1] ?? interactableItems[activeIndex])
690
682
  break
691
- case NavigationStrategy.Previous:
683
+ case 'previous':
692
684
  nextItem =
693
685
  activeIndex === -1
694
686
  ? interactableItems[interactableItems.length - 1]
695
687
  : interactableItems[activeIndex - 1]
696
688
  break
697
- case NavigationStrategy.First:
689
+ case 'first':
698
690
  nextItem = interactableItems[0]
699
691
  break
700
- case NavigationStrategy.Last:
692
+ case 'last':
701
693
  nextItem = interactableItems[interactableItems.length - 1]
702
694
  break
703
695
  }
@@ -708,7 +700,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
708
700
  }
709
701
 
710
702
  function highlightSearchMatch(text: string) {
711
- if (state !== State.Idle) {
703
+ if (state !== 'idle') {
712
704
  return
713
705
  }
714
706
 
@@ -727,7 +719,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
727
719
  }
728
720
 
729
721
  async function activateItem(id: string) {
730
- if (state !== State.Idle) {
722
+ if (state !== 'idle') {
731
723
  return
732
724
  }
733
725
 
@@ -744,7 +736,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
744
736
  let committedChecked =
745
737
  item.type === 'checkbox' ? !item.checked : item.type === 'radio' ? true : undefined
746
738
 
747
- state = State.Selecting
739
+ state = 'selecting'
748
740
  activeId = item.id
749
741
  flashState = item.type === 'item' ? { id: item.id } : { checked: committedChecked, id: item.id }
750
742
  let signal = await handle.update()
@@ -776,7 +768,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
776
768
 
777
769
  await closeAll()
778
770
 
779
- state = State.Idle
771
+ state = 'idle'
780
772
  signal = await handle.update()
781
773
  if (signal.aborted) {
782
774
  return
@@ -792,7 +784,7 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
792
784
  }
793
785
 
794
786
  async function openActiveSubmenu() {
795
- if (state !== State.Idle) {
787
+ if (state !== 'idle') {
796
788
  return
797
789
  }
798
790
 
@@ -966,8 +958,8 @@ function MenuProvider(handle: Handle<MenuProviderProps, MenuContextValue>) {
966
958
  }
967
959
  }
968
960
 
969
- const triggerMixin = createMixin<HTMLElement, [options?: MenuTriggerOptions], ElementProps>(
970
- (handle) => {
961
+ const triggerMixin: MixinFactory<HTMLElement, [options?: MenuTriggerOptions], ElementProps> =
962
+ createMixin<HTMLElement, [options?: MenuTriggerOptions], ElementProps>((handle) => {
971
963
  let context = handle.context.get(MenuProvider)
972
964
 
973
965
  return (options = {}) => [
@@ -1014,10 +1006,13 @@ const triggerMixin = createMixin<HTMLElement, [options?: MenuTriggerOptions], El
1014
1006
  }
1015
1007
  }),
1016
1008
  ]
1017
- },
1018
- )
1009
+ })
1019
1010
 
1020
- const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
1011
+ const popoverMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
1012
+ HTMLElement,
1013
+ [],
1014
+ ElementProps
1015
+ >((handle) => {
1021
1016
  let context = handle.context.get(MenuProvider)
1022
1017
 
1023
1018
  return () => [
@@ -1054,7 +1049,11 @@ const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
1054
1049
  ]
1055
1050
  })
1056
1051
 
1057
- const listMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
1052
+ const listMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
1053
+ HTMLElement,
1054
+ [],
1055
+ ElementProps
1056
+ >((handle) => {
1058
1057
  let context = handle.context.get(MenuProvider)
1059
1058
 
1060
1059
  return () => [
@@ -1102,22 +1101,22 @@ const listMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
1102
1101
  case 'ArrowDown':
1103
1102
  event.preventDefault()
1104
1103
  event.stopPropagation()
1105
- context.navigate(NavigationStrategy.Next)
1104
+ context.navigate('next')
1106
1105
  return
1107
1106
  case 'ArrowUp':
1108
1107
  event.preventDefault()
1109
1108
  event.stopPropagation()
1110
- context.navigate(NavigationStrategy.Previous)
1109
+ context.navigate('previous')
1111
1110
  return
1112
1111
  case 'Home':
1113
1112
  event.preventDefault()
1114
1113
  event.stopPropagation()
1115
- context.navigate(NavigationStrategy.First)
1114
+ context.navigate('first')
1116
1115
  return
1117
1116
  case 'End':
1118
1117
  event.preventDefault()
1119
1118
  event.stopPropagation()
1120
- context.navigate(NavigationStrategy.Last)
1119
+ context.navigate('last')
1121
1120
  return
1122
1121
  case 'Enter':
1123
1122
  case ' ':
@@ -1155,7 +1154,11 @@ const listMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
1155
1154
  ]
1156
1155
  })
1157
1156
 
1158
- const itemMixin = createMixin<HTMLElement, [options: MenuItemOptions], ElementProps>((handle) => {
1157
+ const itemMixin: MixinFactory<HTMLElement, [options: MenuItemOptions], ElementProps> = createMixin<
1158
+ HTMLElement,
1159
+ [options: MenuItemOptions],
1160
+ ElementProps
1161
+ >((handle) => {
1159
1162
  let itemRef: HTMLElement | undefined
1160
1163
 
1161
1164
  handle.queueTask((node) => {
@@ -1235,11 +1238,11 @@ const itemMixin = createMixin<HTMLElement, [options: MenuItemOptions], ElementPr
1235
1238
  }
1236
1239
  })
1237
1240
 
1238
- const submenuTriggerMixin = createMixin<
1241
+ const submenuTriggerMixin: MixinFactory<
1239
1242
  HTMLElement,
1240
1243
  [options: SubmenuTriggerOptions],
1241
1244
  ElementProps
1242
- >((handle) => {
1245
+ > = createMixin<HTMLElement, [options: SubmenuTriggerOptions], ElementProps>((handle) => {
1243
1246
  let itemRef: HTMLElement | undefined
1244
1247
  let openTimeoutId = 0
1245
1248
  let aborted = false
@@ -1312,7 +1315,7 @@ const submenuTriggerMixin = createMixin<
1312
1315
  'aria-disabled': options.disabled ? 'true' : undefined,
1313
1316
  'aria-expanded': childMenu.isOpen ? 'true' : 'false',
1314
1317
  'aria-haspopup': 'menu',
1315
- 'data-submenu-state': childMenu.state === State.Idle ? undefined : childMenu.state,
1318
+ 'data-submenu-state': childMenu.state === 'idle' ? undefined : childMenu.state,
1316
1319
  'data-highlighted': parent.activeId === handle.id ? 'true' : undefined,
1317
1320
  }),
1318
1321
  ref((node: HTMLElement, signal) => {
@@ -1406,8 +1409,11 @@ const menu = {
1406
1409
  trigger,
1407
1410
  } as const
1408
1411
 
1409
- export function onMenuSelect(handler: MenuSelectHandler, captureBoolean?: boolean) {
1410
- return on<HTMLElement, typeof MENU_SELECT_EVENT>(MENU_SELECT_EVENT, handler, captureBoolean)
1412
+ export function onMenuSelect<target extends HTMLElement>(
1413
+ handler: MenuSelectHandler<target>,
1414
+ captureBoolean?: boolean,
1415
+ ): ReturnType<typeof on<target, typeof MENU_SELECT_EVENT>> {
1416
+ return on(MENU_SELECT_EVENT, handler, captureBoolean)
1411
1417
  }
1412
1418
 
1413
1419
  export interface MenuProps extends Omit<Props<'button'>, 'children'> {
@@ -1431,7 +1437,7 @@ export interface SubmenuProps
1431
1437
  menuLabel?: string
1432
1438
  }
1433
1439
 
1434
- export function Menu(handle: Handle<MenuProps>) {
1440
+ export function Menu(handle: Handle<MenuProps>): () => RemixNode {
1435
1441
  let buttonRef: HTMLButtonElement | undefined
1436
1442
 
1437
1443
  return () => {
@@ -1478,7 +1484,7 @@ export function Menu(handle: Handle<MenuProps>) {
1478
1484
  }
1479
1485
  }
1480
1486
 
1481
- export function MenuList(handle: Handle<MenuListProps>) {
1487
+ export function MenuList(handle: Handle<MenuListProps>): () => RemixNode {
1482
1488
  return () => {
1483
1489
  let { children, mix, ...divProps } = handle.props
1484
1490
 
@@ -1492,7 +1498,7 @@ export function MenuList(handle: Handle<MenuListProps>) {
1492
1498
  }
1493
1499
  }
1494
1500
 
1495
- export function MenuItem(handle: Handle<MenuItemProps>) {
1501
+ export function MenuItem(handle: Handle<MenuItemProps>): () => RemixNode {
1496
1502
  return () => {
1497
1503
  let { checked, children, disabled, label, mix, name, searchValue, type, value, ...divProps } =
1498
1504
  handle.props
@@ -1515,7 +1521,7 @@ export function MenuItem(handle: Handle<MenuItemProps>) {
1515
1521
  }
1516
1522
  }
1517
1523
 
1518
- export function Submenu(handle: Handle<SubmenuProps>) {
1524
+ export function Submenu(handle: Handle<SubmenuProps>): () => RemixNode {
1519
1525
  return () => {
1520
1526
  let { children, disabled, label, listProps, menuLabel, mix, searchValue, value, ...divProps } =
1521
1527
  handle.props
@@ -8,9 +8,9 @@ Use it for custom surfaces like filters, inspectors, and view options. Higher-le
8
8
 
9
9
  ```tsx
10
10
  import { css, on, type Handle } from 'remix/ui'
11
- import { Button } from '@remix-run/ui/button'
12
- import { Glyph } from '@remix-run/ui/glyph'
13
- import { popover } from '@remix-run/ui/popover'
11
+ import { Button } from 'remix/ui/button'
12
+ import { Glyph } from 'remix/ui/glyph'
13
+ import { popover } from 'remix/ui/popover'
14
14
 
15
15
  export function ViewOptions(handle: Handle) {
16
16
  let open = false
@@ -0,0 +1,143 @@
1
+ import { css, on, type Handle } from '@remix-run/ui'
2
+ import { Button } from '@remix-run/ui/button'
3
+ import { Glyph } from '@remix-run/ui/glyph'
4
+ import * as popover from '@remix-run/ui/popover'
5
+ import { Option, Select } from '@remix-run/ui/select'
6
+ import { theme } from '@remix-run/ui/theme'
7
+
8
+ /**
9
+ * @name Popover Overview
10
+ * @description A popover panel anchored to a trigger button, containing form controls and a close action.
11
+ */
12
+ export default function Example(handle: Handle) {
13
+ let open = false
14
+
15
+ function closePopover() {
16
+ open = false
17
+ void handle.update()
18
+ }
19
+
20
+ return () => (
21
+ <popover.Context>
22
+ <div mix={buttonRowCss}>
23
+ <Button
24
+ endIcon={<Glyph name="chevronDown" />}
25
+ mix={[
26
+ popover.anchor({ placement: 'bottom' }),
27
+ popover.focusOnHide(),
28
+ on('click', () => {
29
+ open = !open
30
+ void handle.update()
31
+ }),
32
+ ]}
33
+ tone="secondary"
34
+ >
35
+ View options
36
+ </Button>
37
+ </div>
38
+
39
+ <div
40
+ mix={[
41
+ popover.surfaceStyle,
42
+ popover.surface({
43
+ closeOnAnchorClick: false,
44
+ open,
45
+ onHide() {
46
+ closePopover()
47
+ },
48
+ }),
49
+ ]}
50
+ >
51
+ <div mix={[popover.contentStyle, panelCss]}>
52
+ <div mix={fieldCss}>
53
+ <label for={`${handle.id}-grouping`} mix={labelCss}>
54
+ Grouping
55
+ </label>
56
+ <Select
57
+ id={`${handle.id}-grouping`}
58
+ defaultLabel="No grouping"
59
+ defaultValue="none"
60
+ mix={[fieldSelectCss, popover.focusOnShow()]}
61
+ >
62
+ <Option label="No grouping" value="none" />
63
+ <Option label="Status" value="status" />
64
+ <Option label="Priority" value="priority" />
65
+ </Select>
66
+ </div>
67
+
68
+ <div mix={fieldCss}>
69
+ <label for={`${handle.id}-ordering`} mix={labelCss}>
70
+ Ordering
71
+ </label>
72
+ <Select
73
+ id={`${handle.id}-ordering`}
74
+ defaultLabel="Manual"
75
+ defaultValue="manual"
76
+ mix={fieldSelectCss}
77
+ >
78
+ <Option label="Manual" value="manual" />
79
+ <Option label="Newest first" value="newest" />
80
+ <Option label="Oldest first" value="oldest" />
81
+ </Select>
82
+ </div>
83
+
84
+ <div mix={fieldCss}>
85
+ <label for={`${handle.id}-closed-projects`} mix={labelCss}>
86
+ Show closed projects
87
+ </label>
88
+ <Select
89
+ id={`${handle.id}-closed-projects`}
90
+ defaultLabel="All"
91
+ defaultValue="all"
92
+ mix={fieldSelectCss}
93
+ >
94
+ <Option label="All" value="all" />
95
+ <Option label="Open only" value="open" />
96
+ <Option label="Closed only" value="closed" />
97
+ </Select>
98
+ </div>
99
+
100
+ <div mix={actionsCss}>
101
+ <Button mix={on('click', closePopover)} tone="ghost">
102
+ Done
103
+ </Button>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </popover.Context>
108
+ )
109
+ }
110
+
111
+ const buttonRowCss = css({
112
+ display: 'flex',
113
+ justifyContent: 'flex-start',
114
+ })
115
+
116
+ const panelCss = css({
117
+ display: 'grid',
118
+ gap: theme.space.md,
119
+ width: '22rem',
120
+ padding: theme.space.lg,
121
+ })
122
+
123
+ const fieldCss = css({
124
+ display: 'grid',
125
+ gap: theme.space.px,
126
+ })
127
+
128
+ const labelCss = css({
129
+ margin: 0,
130
+ fontSize: theme.fontSize.xs,
131
+ fontWeight: theme.fontWeight.semibold,
132
+ color: theme.colors.text.primary,
133
+ })
134
+
135
+ const fieldSelectCss = css({
136
+ width: '100%',
137
+ })
138
+
139
+ const actionsCss = css({
140
+ display: 'flex',
141
+ justifyContent: 'flex-end',
142
+ paddingTop: theme.space.xs,
143
+ })