remix 3.0.0-beta.4 → 3.0.0-beta.5

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 (90) hide show
  1. package/dist/ui/accordion/primitives.d.ts +2 -0
  2. package/dist/ui/accordion/primitives.d.ts.map +1 -0
  3. package/dist/ui/accordion/primitives.js +2 -0
  4. package/dist/ui/button.d.ts +1 -0
  5. package/dist/ui/button.d.ts.map +1 -1
  6. package/dist/ui/button.js +1 -0
  7. package/dist/ui/checkbox.d.ts +3 -0
  8. package/dist/ui/checkbox.d.ts.map +1 -0
  9. package/dist/ui/checkbox.js +3 -0
  10. package/dist/ui/combobox/primitives.d.ts +2 -0
  11. package/dist/ui/combobox/primitives.d.ts.map +1 -0
  12. package/dist/ui/combobox/primitives.js +2 -0
  13. package/dist/ui/input.d.ts +3 -0
  14. package/dist/ui/input.d.ts.map +1 -0
  15. package/dist/ui/input.js +3 -0
  16. package/dist/ui/menu/primitives.d.ts +2 -0
  17. package/dist/ui/menu/primitives.d.ts.map +1 -0
  18. package/dist/ui/{glyph.js → menu/primitives.js} +1 -1
  19. package/dist/ui/radio.d.ts +3 -0
  20. package/dist/ui/radio.d.ts.map +1 -0
  21. package/dist/ui/radio.js +3 -0
  22. package/dist/ui/select/primitives.d.ts +2 -0
  23. package/dist/ui/select/primitives.d.ts.map +1 -0
  24. package/dist/ui/select/primitives.js +2 -0
  25. package/dist/ui/tabs/primitives.d.ts +2 -0
  26. package/dist/ui/tabs/primitives.d.ts.map +1 -0
  27. package/dist/ui/{theme.js → tabs/primitives.js} +1 -1
  28. package/dist/ui/tabs.d.ts +2 -0
  29. package/dist/ui/tabs.d.ts.map +1 -0
  30. package/{src/ui/glyph.ts → dist/ui/tabs.js} +1 -1
  31. package/dist/ui/toggle/primitives.d.ts +2 -0
  32. package/dist/ui/toggle/primitives.d.ts.map +1 -0
  33. package/dist/ui/toggle/primitives.js +2 -0
  34. package/dist/ui/toggle.d.ts +3 -0
  35. package/dist/ui/toggle.d.ts.map +1 -0
  36. package/dist/ui/toggle.js +3 -0
  37. package/package.json +73 -45
  38. package/src/data-table-mysql/README.md +25 -0
  39. package/src/data-table-postgres/README.md +26 -0
  40. package/src/form-data-parser/README.md +4 -4
  41. package/src/mime/README.md +8 -1
  42. package/src/node-fetch-server/README.md +28 -2
  43. package/src/route-pattern/README.md +51 -6
  44. package/src/session/README.md +1 -1
  45. package/src/ui/README.md +50 -164
  46. package/src/ui/accordion/README.md +50 -14
  47. package/src/ui/accordion/primitives/README.md +202 -0
  48. package/src/ui/accordion/primitives.ts +2 -0
  49. package/src/ui/anchor/README.md +37 -2
  50. package/src/ui/breadcrumbs/README.md +4 -4
  51. package/src/ui/button/README.md +26 -26
  52. package/src/ui/button.ts +1 -0
  53. package/src/ui/checkbox/README.md +59 -0
  54. package/src/ui/checkbox.ts +3 -0
  55. package/src/ui/combobox/README.md +58 -9
  56. package/src/ui/combobox/primitives/README.md +194 -0
  57. package/src/ui/combobox/primitives.ts +2 -0
  58. package/src/ui/input/README.md +52 -0
  59. package/src/ui/input.ts +3 -0
  60. package/src/ui/listbox/README.md +9 -41
  61. package/src/ui/menu/README.md +55 -14
  62. package/src/ui/menu/primitives/README.md +161 -0
  63. package/{dist/ui/scroll-lock.js → src/ui/menu/primitives.ts} +1 -1
  64. package/src/ui/popover/README.md +20 -39
  65. package/src/ui/radio/README.md +53 -0
  66. package/src/ui/radio.ts +3 -0
  67. package/src/ui/select/README.md +29 -19
  68. package/src/ui/select/primitives/README.md +117 -0
  69. package/src/ui/select/primitives.ts +2 -0
  70. package/src/ui/tabs/README.md +141 -0
  71. package/src/ui/tabs/primitives/README.md +141 -0
  72. package/{dist/ui/separator.js → src/ui/tabs/primitives.ts} +1 -1
  73. package/src/ui/{theme.ts → tabs.ts} +1 -1
  74. package/src/ui/toggle/README.md +56 -0
  75. package/src/ui/toggle/primitives/README.md +56 -0
  76. package/src/ui/toggle/primitives.ts +2 -0
  77. package/src/ui/toggle.ts +3 -0
  78. package/dist/ui/glyph.d.ts +0 -2
  79. package/dist/ui/glyph.d.ts.map +0 -1
  80. package/dist/ui/scroll-lock.d.ts +0 -2
  81. package/dist/ui/scroll-lock.d.ts.map +0 -1
  82. package/dist/ui/separator.d.ts +0 -2
  83. package/dist/ui/separator.d.ts.map +0 -1
  84. package/dist/ui/theme.d.ts +0 -2
  85. package/dist/ui/theme.d.ts.map +0 -1
  86. package/src/ui/glyph/README.md +0 -72
  87. package/src/ui/scroll-lock/README.md +0 -33
  88. package/src/ui/scroll-lock.ts +0 -2
  89. package/src/ui/separator.ts +0 -2
  90. package/src/ui/theme/README.md +0 -103
@@ -2,12 +2,13 @@
2
2
 
3
3
  `Menu` renders a button-triggered menu with keyboard navigation, checked items, selection events, and nested submenus. Use it for action menus and command groups.
4
4
 
5
- ## Usage
5
+ ## Component Usage
6
6
 
7
7
  ```tsx
8
8
  import type { Handle } from 'remix/ui'
9
- import { Menu, MenuItem, Submenu, onMenuSelect } from 'remix/ui/menu'
10
- import { separatorStyle } from 'remix/ui/separator'
9
+ import { css } from 'remix/ui'
10
+ import { Menu, MenuItem, Submenu } from 'remix/ui/menu'
11
+ import { onMenuSelect } from 'remix/ui/menu/primitives'
11
12
 
12
13
  export function ViewMenu(handle: Handle) {
13
14
  let wordWrap = false
@@ -50,6 +51,12 @@ export function ViewMenu(handle: Handle) {
50
51
  </Menu>
51
52
  )
52
53
  }
54
+
55
+ let separatorStyle = css({
56
+ border: 0,
57
+ borderBlockStart: '1px solid #e5e7eb',
58
+ marginBlock: '4px',
59
+ })
53
60
  ```
54
61
 
55
62
  Use `label` or `searchValue` when the rendered item content is not the text that should be used for event labels or typeahead.
@@ -71,8 +78,8 @@ Use `menuLabel` when the menu surface needs a different accessible label from th
71
78
  Use `menu.contextTrigger()` with `menu.Context` and `MenuList` when a menu should open at the right-click location of an element.
72
79
 
73
80
  ```tsx
74
- import * as menu from 'remix/ui/menu'
75
81
  import { MenuItem, MenuList } from 'remix/ui/menu'
82
+ import * as menu from 'remix/ui/menu/primitives'
76
83
 
77
84
  export function FileContextMenu(handle: Handle) {
78
85
  return () => (
@@ -89,20 +96,54 @@ export function FileContextMenu(handle: Handle) {
89
96
  }
90
97
  ```
91
98
 
92
- Attach `onMenuSelect(...)` to `MenuList` or a shared ancestor when using lower-level context menu composition.
99
+ Attach `onMenuSelect(...)` from `remix/ui/menu/primitives` to `MenuList` or a shared ancestor when using lower-level context menu composition.
100
+
101
+ ## Primitive Usage
102
+
103
+ Use only the lower-level primitives when app code owns the trigger, surface, and item markup:
104
+
105
+ ```tsx
106
+ import * as menu from 'remix/ui/menu/primitives'
107
+ import { itemStyle, listStyle, popoverStyle, triggerStyle } from './menu.styles'
108
+
109
+ export function PrimitiveMenu() {
110
+ return (
111
+ <menu.Context label="Project actions">
112
+ <button mix={[triggerStyle, menu.trigger()]} type="button">
113
+ Actions
114
+ </button>
115
+ <div mix={[popoverStyle, menu.popover()]}>
116
+ <div mix={[listStyle, menu.list()]}>
117
+ <div mix={[itemStyle, menu.item({ name: 'rename' })]}>Rename</div>
118
+ <div mix={[itemStyle, menu.item({ disabled: true, name: 'archive' })]}>Archive</div>
119
+ </div>
120
+ </div>
121
+ </menu.Context>
122
+ )
123
+ }
124
+ ```
93
125
 
94
- ## `menu.*`
126
+ ## `remix/ui/menu`
95
127
 
96
128
  - `Menu`: composed trigger, popover, and list component for the common menu case.
97
- - `MenuItem`: menu item wrapper. Supports regular, checkbox, and radio item roles through `type`, `checked`, `name`, `value`, `label`, `disabled`, and `searchValue`.
98
- - `Submenu`: nested menu wrapper with its own trigger and child menu surface.
99
- - `MenuList`: lower-level list wrapper for custom composition.
129
+ - `MenuItem`: menu item component. Supports regular, checkbox, and radio item roles through `type`, `checked`, `name`, `value`, `label`, `disabled`, and `searchValue`.
130
+ - `Submenu`: nested menu component with its own trigger and child menu surface.
131
+ - `MenuList`: lower-level styled list component for custom composition inside `menu.Context`.
132
+ - `buttonStyle`, `popoverStyle`, `listStyle`, `itemStyle`, `itemSlotStyle`, `itemLabelStyle`, `itemIndicatorStyle`, and `triggerIndicatorStyle`: flat style mixins used by the component markup.
133
+ - `MenuProps`, `MenuItemProps`, `MenuListProps`, and `SubmenuProps`: public TypeScript props for the composed APIs.
134
+
135
+ ## `remix/ui/menu/primitives`
136
+
137
+ - `Context`: lower-level provider for custom menu composition.
138
+ - `trigger()`: wires a button-style trigger to open the root menu.
139
+ - `contextTrigger()`: opens the root menu from a `contextmenu` event at pointer coordinates, or from keyboard context-menu shortcuts.
140
+ - `popover()`: wires the menu popover surface.
141
+ - `list()`: wires the menu list root, focus handling, keyboard navigation, and typeahead.
142
+ - `item(...)`: registers one menu item. Supports regular, checkbox, and radio roles through `type`, `checked`, `name`, `value`, `label`, `disabled`, and `searchValue`.
143
+ - `submenuTrigger(...)`: registers a menu item that opens a child menu.
100
144
  - `onMenuSelect(...)`: event mixin for the bubbling `MenuSelectEvent`.
101
- - `MenuSelectEvent`: bubbling event class whose `item` describes the selected item.
102
- - `MenuSelectItem`: selected item shape with `checked`, `id`, `label`, `name`, `type`, and `value`.
103
- - `menu.Context`, `menu.trigger()`, `menu.contextTrigger()`, `menu.popover()`, `menu.list()`, `menu.item(...)`, and `menu.submenuTrigger(...)`: lower-level composition primitives.
104
- - `buttonStyle`, `popoverStyle`, `listStyle`, `itemStyle`, `itemSlotStyle`, `itemLabelStyle`, `itemGlyphStyle`, and `triggerGlyphStyle`: flat style mixins used by the wrappers.
105
- - `MenuProps`, `MenuItemProps`, `MenuListProps`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuContextTriggerOptions`, `MenuItemOptions`, and `SubmenuProps`: public TypeScript props and option types.
145
+ - `MenuSelectEvent`: bubbling event whose `item` describes the selected item.
146
+ - `MenuSelectItem`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuContextTriggerOptions`, `MenuItemOptions`, and `SubmenuTriggerOptions`: public TypeScript event, prop, and option types for the primitives.
106
147
 
107
148
  ## Behavior Notes
108
149
 
@@ -0,0 +1,161 @@
1
+ # menu
2
+
3
+ `Menu` renders a button-triggered menu with keyboard navigation, checked items, selection events, and nested submenus. Use it for action menus and command groups.
4
+
5
+ ## Component Usage
6
+
7
+ ```tsx
8
+ import type { Handle } from 'remix/ui'
9
+ import { css } from 'remix/ui'
10
+ import { Menu, MenuItem, Submenu } from 'remix/ui/menu'
11
+ import { onMenuSelect } from 'remix/ui/menu/primitives'
12
+
13
+ export function ViewMenu(handle: Handle) {
14
+ let wordWrap = false
15
+ let density = 'comfortable'
16
+
17
+ return () => (
18
+ <Menu
19
+ label="View"
20
+ mix={onMenuSelect((event) => {
21
+ if (event.item.name === 'wordWrap') {
22
+ wordWrap = event.item.checked ?? false
23
+ } else if (event.item.name === 'density' && event.item.value) {
24
+ density = event.item.value
25
+ }
26
+
27
+ void handle.update()
28
+ })}
29
+ >
30
+ <MenuItem checked={wordWrap} name="wordWrap" type="checkbox">
31
+ Word wrap
32
+ </MenuItem>
33
+ <MenuItem disabled name="minimap">
34
+ Minimap
35
+ </MenuItem>
36
+ <hr mix={separatorStyle} />
37
+ <MenuItem checked={density === 'compact'} name="density" type="radio" value="compact">
38
+ Compact
39
+ </MenuItem>
40
+ <MenuItem checked={density === 'comfortable'} name="density" type="radio" value="comfortable">
41
+ Comfortable
42
+ </MenuItem>
43
+ <Submenu label="Zoom">
44
+ <MenuItem name="zoomIn" value="zoom-in">
45
+ Zoom in
46
+ </MenuItem>
47
+ <MenuItem name="zoomOut" value="zoom-out">
48
+ Zoom out
49
+ </MenuItem>
50
+ </Submenu>
51
+ </Menu>
52
+ )
53
+ }
54
+
55
+ let separatorStyle = css({
56
+ border: 0,
57
+ borderBlockStart: '1px solid #e5e7eb',
58
+ marginBlock: '4px',
59
+ })
60
+ ```
61
+
62
+ Use `label` or `searchValue` when the rendered item content is not the text that should be used for event labels or typeahead.
63
+
64
+ ```tsx
65
+ <MenuItem label="Open command palette" name="commandPalette" searchValue="palette">
66
+ Command palette
67
+ </MenuItem>
68
+ ```
69
+
70
+ Use `menuLabel` when the menu surface needs a different accessible label from the visible trigger.
71
+
72
+ ```tsx
73
+ <Menu label="..." menuLabel="Project actions">
74
+ <MenuItem name="rename">Rename project</MenuItem>
75
+ </Menu>
76
+ ```
77
+
78
+ Use `menu.contextTrigger()` with `menu.Context` and `MenuList` when a menu should open at the right-click location of an element.
79
+
80
+ ```tsx
81
+ import { MenuItem, MenuList } from 'remix/ui/menu'
82
+ import * as menu from 'remix/ui/menu/primitives'
83
+
84
+ export function FileContextMenu(handle: Handle) {
85
+ return () => (
86
+ <menu.Context label="File actions">
87
+ <div mix={menu.contextTrigger()} tabIndex={0}>
88
+ File.txt
89
+ </div>
90
+ <MenuList>
91
+ <MenuItem name="rename">Rename</MenuItem>
92
+ <MenuItem name="delete">Delete</MenuItem>
93
+ </MenuList>
94
+ </menu.Context>
95
+ )
96
+ }
97
+ ```
98
+
99
+ Attach `onMenuSelect(...)` from `remix/ui/menu/primitives` to `MenuList` or a shared ancestor when using lower-level context menu composition.
100
+
101
+ ## Primitive Usage
102
+
103
+ Use only the lower-level primitives when app code owns the trigger, surface, and item markup:
104
+
105
+ ```tsx
106
+ import * as menu from 'remix/ui/menu/primitives'
107
+ import { itemStyle, listStyle, popoverStyle, triggerStyle } from './menu.styles'
108
+
109
+ export function PrimitiveMenu() {
110
+ return (
111
+ <menu.Context label="Project actions">
112
+ <button mix={[triggerStyle, menu.trigger()]} type="button">
113
+ Actions
114
+ </button>
115
+ <div mix={[popoverStyle, menu.popover()]}>
116
+ <div mix={[listStyle, menu.list()]}>
117
+ <div mix={[itemStyle, menu.item({ name: 'rename' })]}>Rename</div>
118
+ <div mix={[itemStyle, menu.item({ disabled: true, name: 'archive' })]}>Archive</div>
119
+ </div>
120
+ </div>
121
+ </menu.Context>
122
+ )
123
+ }
124
+ ```
125
+
126
+ ## `remix/ui/menu`
127
+
128
+ - `Menu`: composed trigger, popover, and list component for the common menu case.
129
+ - `MenuItem`: menu item component. Supports regular, checkbox, and radio item roles through `type`, `checked`, `name`, `value`, `label`, `disabled`, and `searchValue`.
130
+ - `Submenu`: nested menu component with its own trigger and child menu surface.
131
+ - `MenuList`: lower-level styled list component for custom composition inside `menu.Context`.
132
+ - `buttonStyle`, `popoverStyle`, `listStyle`, `itemStyle`, `itemSlotStyle`, `itemLabelStyle`, `itemIndicatorStyle`, and `triggerIndicatorStyle`: flat style mixins used by the component markup.
133
+ - `MenuProps`, `MenuItemProps`, `MenuListProps`, and `SubmenuProps`: public TypeScript props for the composed APIs.
134
+
135
+ ## `remix/ui/menu/primitives`
136
+
137
+ - `Context`: lower-level provider for custom menu composition.
138
+ - `trigger()`: wires a button-style trigger to open the root menu.
139
+ - `contextTrigger()`: opens the root menu from a `contextmenu` event at pointer coordinates, or from keyboard context-menu shortcuts.
140
+ - `popover()`: wires the menu popover surface.
141
+ - `list()`: wires the menu list root, focus handling, keyboard navigation, and typeahead.
142
+ - `item(...)`: registers one menu item. Supports regular, checkbox, and radio roles through `type`, `checked`, `name`, `value`, `label`, `disabled`, and `searchValue`.
143
+ - `submenuTrigger(...)`: registers a menu item that opens a child menu.
144
+ - `onMenuSelect(...)`: event mixin for the bubbling `MenuSelectEvent`.
145
+ - `MenuSelectEvent`: bubbling event whose `item` describes the selected item.
146
+ - `MenuSelectItem`, `MenuProviderProps`, `MenuTriggerOptions`, `MenuContextTriggerOptions`, `MenuItemOptions`, and `SubmenuTriggerOptions`: public TypeScript event, prop, and option types for the primitives.
147
+
148
+ ## Behavior Notes
149
+
150
+ - Click opens the root menu and focuses the list; clicking the trigger again closes it and restores focus.
151
+ - `menu.contextTrigger()` opens the root menu from a `contextmenu` event at the pointer coordinates and supports keyboard opening with the Context Menu key or Shift+F10.
152
+ - `ArrowDown` opens from the trigger at the first enabled item. `ArrowUp` opens at the last enabled item. Enter and Space open the menu with focus on the list.
153
+ - Keyboard navigation skips disabled items and does not wrap past the first or last enabled item.
154
+ - `Home` and `End` move to the first and last enabled item. Enter and Space activate the highlighted item.
155
+ - Printable keys use typeahead. Typeahead matches `searchValue` when provided and otherwise uses the item label.
156
+ - Submenus open with `ArrowRight`, close with `ArrowLeft`, and are anchored to the submenu trigger with `right-start` placement.
157
+ - Pointer movement highlights enabled items. Submenus open after a short focus/pointer delay and use hover aim so they stay open while moving toward the child surface.
158
+ - Selecting a regular item flashes that item. Selecting a checkbox or radio item flashes the committed checked state.
159
+ - Selection dispatches one bubbled `MenuSelectEvent`, closes the full menu tree, and restores focus to the root trigger.
160
+ - The composed `Menu` re-dispatches selection from its trigger so handlers on the `Menu` button and shared ancestors see the event once.
161
+ - `menuLabel`, `Submenu.menuLabel`, and `Submenu.listProps` let composed menus label and customize menu surfaces separately from trigger content.
@@ -1,2 +1,2 @@
1
1
  // IMPORTANT: This file is auto-generated, please do not edit manually.
2
- export * from '@remix-run/ui/scroll-lock';
2
+ export * from '@remix-run/ui/menu/primitives'
@@ -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.
@@ -0,0 +1,53 @@
1
+ # radio
2
+
3
+ `radio` is a style mixin for native radio inputs. It only owns radio visuals and the default `type="radio"` behavior for native `<input>` hosts.
4
+
5
+ ## Primitive Usage
6
+
7
+ ```tsx
8
+ import radio from 'remix/ui/radio'
9
+
10
+ function ShippingSpeed() {
11
+ return () => (
12
+ <fieldset>
13
+ <label>
14
+ <input defaultChecked mix={radio()} name="shipping-speed" value="standard" />
15
+ Standard
16
+ </label>
17
+ <label>
18
+ <input mix={radio({ size: 'lg' })} name="shipping-speed" value="express" />
19
+ Express
20
+ </label>
21
+ </fieldset>
22
+ )
23
+ }
24
+ ```
25
+
26
+ Compose app-owned styles around the primitive when a radio option needs local layout:
27
+
28
+ ```tsx
29
+ import radio from 'remix/ui/radio'
30
+ import { optionStyle } from './shipping.styles'
31
+
32
+ function ShippingOption() {
33
+ return () => (
34
+ <label mix={optionStyle}>
35
+ <input mix={radio()} name="shipping-speed" value="overnight" />
36
+ Overnight
37
+ </label>
38
+ )
39
+ }
40
+ ```
41
+
42
+ ## `remix/ui/radio`
43
+
44
+ - `radio(options)`: style mixin for native radio inputs or radio-like hosts.
45
+ - `RadioOptions`: accepts `size`.
46
+ - `RadioSize`: `'md'` or `'lg'`. Defaults to `'md'`.
47
+
48
+ ## Behavior Notes
49
+
50
+ - `radio()` returns a mixin descriptor, so it composes with other mixins in the host element's `mix` prop.
51
+ - Native `<input>` hosts receive `type="radio"` unless an explicit `type` is provided.
52
+ - Radio state is native browser behavior. The mixin also styles `aria-checked="true"` and `data-state="checked"` for custom hosts.
53
+ - Disabled hosts use the shared disabled treatment through `disabled` or `aria-disabled="true"`.
@@ -0,0 +1,3 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/radio'
3
+ export { default } from '@remix-run/ui/radio'
@@ -2,10 +2,11 @@
2
2
 
3
3
  `Select` is a button-triggered popup value picker backed by `listbox` and `popover`. Use it when the user should choose one stable string value from a finite set.
4
4
 
5
- ## Usage
5
+ ## Component Usage
6
6
 
7
7
  ```tsx
8
- import { Option, Select, onSelectChange } from 'remix/ui/select'
8
+ import { Option, Select } from 'remix/ui/select'
9
+ import { onSelectChange } from 'remix/ui/select/primitives'
9
10
 
10
11
  export function FrameworkSelect() {
11
12
  return (
@@ -44,34 +45,34 @@ Use `textValue` when closed-trigger typeahead should match a different string fr
44
45
  </Select>
45
46
  ```
46
47
 
48
+ ## Primitive Usage
49
+
47
50
  Use the lower-level primitives when the trigger or popup structure needs to be owned by another component. Keep the same provider, trigger, popover, list, option, and hidden-input relationship.
48
51
 
49
52
  ```tsx
50
53
  import type { Handle } from 'remix/ui'
51
- import * as button from 'remix/ui/button'
52
- import * as listbox from 'remix/ui/listbox'
53
54
  import * as popover from 'remix/ui/popover'
54
- import * as select from 'remix/ui/select'
55
+ import { triggerStyle } from 'remix/ui/select'
56
+ import * as select from 'remix/ui/select/primitives'
57
+ import { listStyle, optionStyle, surfaceStyle } from './select.styles'
55
58
 
56
59
  function SelectValue(handle: Handle) {
57
60
  let context = handle.context.get(select.Context)
58
61
 
59
- return () => <span mix={button.labelStyle}>{context.displayedLabel}</span>
62
+ return () => <span>{context.displayedLabel}</span>
60
63
  }
61
64
 
62
65
  function IssueTypeSelect() {
63
66
  return () => (
64
- <select.Context defaultLabel="Select a type" name="issueType">
65
- <button type="button" mix={[button.baseStyle, select.triggerStyle, select.trigger()]}>
67
+ <select.Context defaultLabel="Select a type" labelSwapDelayMs={100} name="issueType">
68
+ <button type="button" mix={[triggerStyle, select.trigger()]}>
66
69
  <SelectValue />
67
70
  </button>
68
71
  <popover.Context>
69
- <div mix={[popover.surfaceStyle, select.popover()]}>
70
- <div mix={[popover.contentStyle, listbox.listStyle, select.list()]}>
71
- <div mix={[listbox.optionStyle, select.option({ label: 'Bug', value: 'bug' })]}>
72
- Bug
73
- </div>
74
- <div mix={[listbox.optionStyle, select.option({ label: 'Feature', value: 'feature' })]}>
72
+ <div mix={[surfaceStyle, select.popover()]}>
73
+ <div mix={[listStyle, select.list()]}>
74
+ <div mix={[optionStyle, select.option({ label: 'Bug', value: 'bug' })]}>Bug</div>
75
+ <div mix={[optionStyle, select.option({ label: 'Feature', value: 'feature' })]}>
75
76
  Feature
76
77
  </div>
77
78
  </div>
@@ -83,15 +84,24 @@ function IssueTypeSelect() {
83
84
  }
84
85
  ```
85
86
 
86
- ## `select.*`
87
+ ## `remix/ui/select`
87
88
 
88
89
  - `Select`: composed trigger, popover, listbox, option list, and optional hidden input for form participation. Accepts `defaultLabel`, `defaultValue`, `disabled`, `name`, and button props.
89
- - `Option`: option wrapper that renders the standard check glyph and label slot. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
90
+ - `Option`: option component that renders the standard check indicator and label slot. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
91
+ - `triggerStyle`: standard select trigger style for custom trigger composition.
92
+ - `SelectProps` and `SelectOptionProps`: public TypeScript props for the composed APIs.
93
+
94
+ ## `remix/ui/select/primitives`
95
+
96
+ - `Context`: lower-level provider for custom composition. Accepts `defaultLabel`, `defaultValue`, `disabled`, `name`, and primitive-only `labelSwapDelayMs`; the label-swap delay defaults to `75` milliseconds.
97
+ - `trigger()`: wires the trigger button, open behavior, closed-trigger typeahead, and trigger ARIA attributes.
98
+ - `popover()`: wires the popover surface and keeps its min-width synced to the trigger before open.
99
+ - `list()`: wires the listbox root used inside the popover.
100
+ - `option(...)`: registers one selectable option. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
101
+ - `hiddenInput()`: mirrors the selected value into a hidden input for form participation.
90
102
  - `onSelectChange(...)`: event mixin for the bubbling `SelectChangeEvent`.
91
- - `Context`, `trigger()`, `popover()`, `list()`, `option(...)`, and `hiddenInput()`: lower-level composition primitives.
92
- - `triggerStyle`: standard select trigger style.
93
103
  - `SelectChangeEvent`: event with `value`, `label`, and `optionId`.
94
- - `SelectProps`, `SelectContextProps`, and `SelectOptionProps`: public TypeScript props for the composed and lower-level APIs.
104
+ - `SelectContextProps`, `SelectProps`, and `SelectOptionProps`: primitive prop types for custom composition.
95
105
 
96
106
  ## Behavior Notes
97
107
 
@@ -0,0 +1,117 @@
1
+ # select
2
+
3
+ `Select` is a button-triggered popup value picker backed by `listbox` and `popover`. Use it when the user should choose one stable string value from a finite set.
4
+
5
+ ## Component Usage
6
+
7
+ ```tsx
8
+ import { Option, Select } from 'remix/ui/select'
9
+ import { onSelectChange } from 'remix/ui/select/primitives'
10
+
11
+ export function FrameworkSelect() {
12
+ return (
13
+ <Select
14
+ defaultLabel="Select a framework"
15
+ defaultValue="remix"
16
+ name="framework"
17
+ mix={onSelectChange((event) => {
18
+ console.log(event.value, event.label, event.optionId)
19
+ })}
20
+ >
21
+ <Option label="Remix framework" value="remix">
22
+ Remix
23
+ </Option>
24
+ <Option disabled label="React Router framework" value="react-router">
25
+ React Router
26
+ </Option>
27
+ <Option label="React framework" value="react">
28
+ React
29
+ </Option>
30
+ </Select>
31
+ )
32
+ }
33
+ ```
34
+
35
+ Use `textValue` when closed-trigger typeahead should match a different string from the visible label.
36
+
37
+ ```tsx
38
+ <Select defaultLabel="Select an environment">
39
+ <Option label="Production environment" value="production">
40
+ Production
41
+ </Option>
42
+ <Option label="Staging environment" textValue="beta" value="staging">
43
+ Staging
44
+ </Option>
45
+ </Select>
46
+ ```
47
+
48
+ ## Primitive Usage
49
+
50
+ Use the lower-level primitives when the trigger or popup structure needs to be owned by another component. Keep the same provider, trigger, popover, list, option, and hidden-input relationship.
51
+
52
+ ```tsx
53
+ import type { Handle } from 'remix/ui'
54
+ import * as popover from 'remix/ui/popover'
55
+ import { triggerStyle } from 'remix/ui/select'
56
+ import * as select from 'remix/ui/select/primitives'
57
+ import { listStyle, optionStyle, surfaceStyle } from './select.styles'
58
+
59
+ function SelectValue(handle: Handle) {
60
+ let context = handle.context.get(select.Context)
61
+
62
+ return () => <span>{context.displayedLabel}</span>
63
+ }
64
+
65
+ function IssueTypeSelect() {
66
+ return () => (
67
+ <select.Context defaultLabel="Select a type" labelSwapDelayMs={100} name="issueType">
68
+ <button type="button" mix={[triggerStyle, select.trigger()]}>
69
+ <SelectValue />
70
+ </button>
71
+ <popover.Context>
72
+ <div mix={[surfaceStyle, select.popover()]}>
73
+ <div mix={[listStyle, select.list()]}>
74
+ <div mix={[optionStyle, select.option({ label: 'Bug', value: 'bug' })]}>Bug</div>
75
+ <div mix={[optionStyle, select.option({ label: 'Feature', value: 'feature' })]}>
76
+ Feature
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </popover.Context>
81
+ <input mix={select.hiddenInput()} />
82
+ </select.Context>
83
+ )
84
+ }
85
+ ```
86
+
87
+ ## `remix/ui/select`
88
+
89
+ - `Select`: composed trigger, popover, listbox, option list, and optional hidden input for form participation. Accepts `defaultLabel`, `defaultValue`, `disabled`, `name`, and button props.
90
+ - `Option`: option component that renders the standard check indicator and label slot. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
91
+ - `triggerStyle`: standard select trigger style for custom trigger composition.
92
+ - `SelectProps` and `SelectOptionProps`: public TypeScript props for the composed APIs.
93
+
94
+ ## `remix/ui/select/primitives`
95
+
96
+ - `Context`: lower-level provider for custom composition. Accepts `defaultLabel`, `defaultValue`, `disabled`, `name`, and primitive-only `labelSwapDelayMs`; the label-swap delay defaults to `75` milliseconds.
97
+ - `trigger()`: wires the trigger button, open behavior, closed-trigger typeahead, and trigger ARIA attributes.
98
+ - `popover()`: wires the popover surface and keeps its min-width synced to the trigger before open.
99
+ - `list()`: wires the listbox root used inside the popover.
100
+ - `option(...)`: registers one selectable option. Accepts `label`, `value`, optional `disabled`, and optional `textValue`.
101
+ - `hiddenInput()`: mirrors the selected value into a hidden input for form participation.
102
+ - `onSelectChange(...)`: event mixin for the bubbling `SelectChangeEvent`.
103
+ - `SelectChangeEvent`: event with `value`, `label`, and `optionId`.
104
+ - `SelectContextProps`, `SelectProps`, and `SelectOptionProps`: primitive prop types for custom composition.
105
+
106
+ ## Behavior Notes
107
+
108
+ - `defaultLabel` is displayed before selection settles. `defaultValue` selects the matching option without replacing the trigger label until a new selection commits.
109
+ - `Option.label` is the committed display label and event label. `children` are the rendered option contents.
110
+ - Click, `ArrowDown`, and `ArrowUp` open the popup. Focus moves into the list and Escape restores focus to the trigger through popover behavior.
111
+ - Reopening highlights the current selected value.
112
+ - The popup min-width syncs to the trigger width before opening.
113
+ - Closed-trigger typeahead selects a matching option immediately and supports option `textValue`.
114
+ - Selecting an option flashes it with `data-select-flash`, waits for the close transition and label delay, updates the displayed label, and dispatches `SelectChangeEvent`.
115
+ - Selecting the already-selected value updates state but does not dispatch a change event.
116
+ - `SelectChangeEvent` bubbles from the trigger when a trigger exists. It includes `value`, `label`, and `optionId`.
117
+ - Passing `name` renders a hidden input so the selected value participates in `FormData`; disabled selects disable the trigger and hidden input.
@@ -0,0 +1,2 @@
1
+ // IMPORTANT: This file is auto-generated, please do not edit manually.
2
+ export * from '@remix-run/ui/select/primitives'