@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 { css, createElement } from '@remix-run/ui'
4
2
  import type { Handle, Props, RemixNode } from '@remix-run/ui'
5
3
 
@@ -74,7 +72,7 @@ const textCss = css({
74
72
  whiteSpace: 'nowrap',
75
73
  })
76
74
 
77
- export function Breadcrumbs(handle: Handle<BreadcrumbsProps>) {
75
+ export function Breadcrumbs(handle: Handle<BreadcrumbsProps>): () => RemixNode {
78
76
  return () => {
79
77
  let { 'aria-label': ariaLabel, items, separator, mix, ...navProps } = handle.props
80
78
  let currentIndex = items.findIndex((item) => item.current)
@@ -0,0 +1,17 @@
1
+ import { Breadcrumbs } from '@remix-run/ui/breadcrumbs'
2
+
3
+ /**
4
+ * @name Breadcrumbs Basic
5
+ * @description A basic breadcrumb trail linking back through the page hierarchy.
6
+ */
7
+ export default function Example() {
8
+ return () => (
9
+ <Breadcrumbs
10
+ items={[
11
+ { href: '/', label: 'Home' },
12
+ { href: '/components', label: 'Components' },
13
+ { label: 'Breadcrumbs' },
14
+ ]}
15
+ />
16
+ )
17
+ }
@@ -0,0 +1,18 @@
1
+ import { Breadcrumbs } from '@remix-run/ui/breadcrumbs'
2
+
3
+ /**
4
+ * @name Breadcrumbs with Separator
5
+ * @description Pass a custom separator string to override the default chevron icon.
6
+ */
7
+ export default function Example() {
8
+ return () => (
9
+ <Breadcrumbs
10
+ items={[
11
+ { href: '/', label: 'Workspace' },
12
+ { href: '/projects', label: 'Projects' },
13
+ { label: 'RMX_01' },
14
+ ]}
15
+ separator="/"
16
+ />
17
+ )
18
+ }
@@ -1,13 +1,13 @@
1
1
  # button
2
2
 
3
- `button` is the shared button styling contract for `@remix-run/ui`. Use `Button` for ordinary action buttons, or compose flat `button.*Style` exports directly when a higher-level control needs button structure without a wrapper.
3
+ `button` is the shared button styling contract for `remix/ui`. Use `Button` for ordinary action buttons, or compose flat `button.*Style` exports directly when a higher-level control needs button structure without a wrapper.
4
4
 
5
5
  ## Usage
6
6
 
7
7
  ```tsx
8
- import { Button } from '@remix-run/ui/button'
9
- import * as button from '@remix-run/ui/button'
10
- import { Glyph } from '@remix-run/ui/glyph'
8
+ import { Button } from 'remix/ui/button'
9
+ import * as button from 'remix/ui/button'
10
+ import { Glyph } from 'remix/ui/glyph'
11
11
 
12
12
  function Actions() {
13
13
  return (
@@ -1,7 +1,13 @@
1
- // @jsxRuntime classic
2
- // @jsx createElement
3
1
  import { attrs, createElement, createMixin, css } from '@remix-run/ui'
4
- import type { CSSMixinDescriptor, ElementProps, Handle, Props, RemixNode } from '@remix-run/ui'
2
+ import type {
3
+ CSSMixinDescriptor,
4
+ ElementProps,
5
+ Handle,
6
+ MixinDescriptor,
7
+ MixinFactory,
8
+ Props,
9
+ RemixNode,
10
+ } from '@remix-run/ui'
5
11
 
6
12
  import { theme } from '../../theme/theme.ts'
7
13
 
@@ -76,30 +82,83 @@ const buttonIconCss: CSSMixinDescriptor = css({
76
82
  },
77
83
  })
78
84
 
79
- const buttonIconAttrsCss = attrs({ 'aria-hidden': true })
85
+ const buttonIconAttrsCss: MixinDescriptor<Element, [Partial<ElementProps>], ElementProps> = attrs({
86
+ 'aria-hidden': true,
87
+ })
80
88
 
81
- const buttonDefaultsMixin = createMixin<Element, [], ElementProps>(
82
- (handle, hostType) => (props) => {
83
- if (hostType !== 'button' || props.type !== undefined) {
84
- return handle.element
85
- }
89
+ const buttonDefaultsMixin: MixinFactory<Element, [], ElementProps> = createMixin<
90
+ Element,
91
+ [],
92
+ ElementProps
93
+ >((handle, hostType) => (props) => {
94
+ if (hostType !== 'button' || props.type !== undefined) {
95
+ return handle.element
96
+ }
86
97
 
87
- return createElement(handle.element as unknown as string, {
88
- ...props,
89
- type: 'button',
90
- })
91
- },
92
- )
98
+ return createElement(handle.element as unknown as string, {
99
+ ...props,
100
+ type: 'button',
101
+ })
102
+ })
93
103
 
94
- const buttonDefaultsCss = buttonDefaultsMixin()
104
+ const buttonDefaultsCss: MixinDescriptor<Element, [], ElementProps> = buttonDefaultsMixin()
95
105
 
106
+ /**
107
+ * Base button styling with the default `type="button"` behavior for `<button>`
108
+ * hosts. Compose with a tone style (e.g. {@link primaryStyle}) when applying
109
+ * button styling without using the {@link Button} component.
110
+ *
111
+ * @category mixin
112
+ */
96
113
  export const baseStyle = [buttonDefaultsCss, buttonBaseStyleCss] as const
114
+
115
+ /**
116
+ * Icon slot sizing and `aria-hidden` defaults for decorative icons rendered
117
+ * inside a button.
118
+ *
119
+ * @category mixin
120
+ */
97
121
  export const iconStyle = [buttonIconAttrsCss, buttonIconCss] as const
122
+
123
+ /**
124
+ * Inline label slot with the standard button label spacing.
125
+ *
126
+ * @category mixin
127
+ */
98
128
  export const labelStyle = buttonLabelCss
99
- export const primaryStyle = createButtonCss(theme.colors.action.primary)
100
- export const secondaryStyle = createButtonCss(theme.colors.action.secondary)
129
+
130
+ /**
131
+ * Primary visual treatment for buttons. Combine with {@link baseStyle} when
132
+ * styling a non-`Button` host element.
133
+ *
134
+ * @category mixin
135
+ */
136
+ export const primaryStyle: CSSMixinDescriptor = createButtonCss(theme.colors.action.primary)
137
+
138
+ /**
139
+ * Secondary visual treatment for buttons. Combine with {@link baseStyle} when
140
+ * styling a non-`Button` host element.
141
+ *
142
+ * @category mixin
143
+ */
144
+ export const secondaryStyle: CSSMixinDescriptor = createButtonCss(theme.colors.action.secondary)
145
+
146
+ /**
147
+ * Ghost visual treatment for buttons — transparent background with a hover
148
+ * surface. Combine with {@link baseStyle} when styling a non-`Button` host
149
+ * element.
150
+ *
151
+ * @category mixin
152
+ */
101
153
  export const ghostStyle = ghostButtonToneCss
102
- export const dangerStyle = createButtonCss(theme.colors.action.danger)
154
+
155
+ /**
156
+ * Danger visual treatment for destructive actions. Combine with
157
+ * {@link baseStyle} when styling a non-`Button` host element.
158
+ *
159
+ * @category mixin
160
+ */
161
+ export const dangerStyle: CSSMixinDescriptor = createButtonCss(theme.colors.action.danger)
103
162
 
104
163
  const toneStyleByTone = {
105
164
  primary: primaryStyle,
@@ -108,16 +167,55 @@ const toneStyleByTone = {
108
167
  danger: dangerStyle,
109
168
  } as const
110
169
 
170
+ /**
171
+ * Visual treatment supported by {@link Button} — `'primary'`, `'secondary'`,
172
+ * `'ghost'`, or `'danger'`.
173
+ */
111
174
  export type ButtonTone = keyof typeof toneStyleByTone
112
175
 
176
+ /**
177
+ * Props accepted by the {@link Button} component.
178
+ *
179
+ * Extends the native `<button>` element props with optional icon slots and a
180
+ * tone variant
181
+ */
113
182
  export type ButtonProps = Omit<Props<'button'>, 'children'> & {
183
+ /**
184
+ * Content rendered inside the button's label slot.
185
+ */
114
186
  readonly children?: RemixNode
187
+ /**
188
+ * Decorative icon rendered after the label, inside the icon slot.
189
+ */
115
190
  readonly endIcon?: RemixNode
191
+ /**
192
+ * Decorative icon rendered before the label, inside the icon slot.
193
+ */
116
194
  readonly startIcon?: RemixNode
195
+ /**
196
+ * Visual treatment to apply to the button (default `'secondary'`).
197
+ */
117
198
  readonly tone?: ButtonTone
118
199
  }
119
200
 
120
- export function Button(handle: Handle<ButtonProps>) {
201
+ /**
202
+ * Renders a `<button>` with `baseStyle` and the resolved tone style, along with
203
+ * optional start and end icons.
204
+ *
205
+ * @param handle Component handle providing the runtime API and the resolved {@link ButtonProps}.
206
+ * @returns A render function for the button element.
207
+ *
208
+ * @example
209
+ * ```tsx
210
+ * import { Button } from '@remix-run/ui/button'
211
+ * import { Glyph } from '@remix-run/ui/glyph'
212
+ *
213
+ * <Button startIcon={<Glyph name="add" />} tone="primary">
214
+ * Create project
215
+ * </Button>
216
+ * ```
217
+ */
218
+ export function Button(handle: Handle<ButtonProps>): () => RemixNode {
121
219
  return () => {
122
220
  let { children, endIcon, mix, startIcon, tone = 'secondary', ...buttonProps } = handle.props
123
221
 
@@ -0,0 +1,27 @@
1
+ import { Button } from '@remix-run/ui/button'
2
+ import { css } from '@remix-run/ui'
3
+ import { theme } from '@remix-run/ui/theme'
4
+
5
+ /**
6
+ * @name Button Component
7
+ * @description The Button component wraps the low-level style primitives and accepts a tone prop for quick theming.
8
+ * @order 2
9
+ */
10
+ export default function Example() {
11
+ return () => (
12
+ <div mix={buttonRowCss}>
13
+ <Button tone="primary" type="submit">
14
+ Save
15
+ </Button>
16
+ <Button tone="secondary">Secondary</Button>
17
+ <Button tone="ghost">Ghost</Button>
18
+ <Button tone="danger">Delete</Button>
19
+ </div>
20
+ )
21
+ }
22
+
23
+ const buttonRowCss = css({
24
+ display: 'flex',
25
+ alignItems: 'center',
26
+ gap: theme.space.sm,
27
+ })
@@ -0,0 +1,30 @@
1
+ import { css } from '@remix-run/ui'
2
+ import * as button from '@remix-run/ui/button'
3
+ import { Glyph } from '@remix-run/ui/glyph'
4
+ import { theme } from '@remix-run/ui/theme'
5
+
6
+ /**
7
+ * @name Basic Button
8
+ * @description The default button contract supports both ordinary actions and link-shaped navigation.
9
+ * @order 1
10
+ */
11
+ export default function Example() {
12
+ return () => (
13
+ <div mix={buttonRowCss}>
14
+ <button type="submit" mix={[button.baseStyle, button.primaryStyle]}>
15
+ <Glyph mix={button.iconStyle} name="add" />
16
+ <span mix={button.labelStyle}>Publish</span>
17
+ </button>
18
+ <a href="/api/remix/ui/button/overview/" mix={[button.baseStyle, button.secondaryStyle]}>
19
+ <span mix={button.labelStyle}>View button docs</span>
20
+ <Glyph mix={button.iconStyle} name="chevronRight" />
21
+ </a>
22
+ </div>
23
+ )
24
+ }
25
+
26
+ const buttonRowCss = css({
27
+ display: 'flex',
28
+ alignItems: 'center',
29
+ gap: theme.space.sm,
30
+ })
@@ -0,0 +1,52 @@
1
+ import * as button from '@remix-run/ui/button'
2
+ import { css } from '@remix-run/ui'
3
+ import { Glyph } from '@remix-run/ui/glyph'
4
+ import { theme } from '@remix-run/ui/theme'
5
+
6
+ /**
7
+ * @name Button States
8
+ * @description Buttons support normal, disabled, and busy (loading) states using aria attributes.
9
+ * @order 3
10
+ */
11
+ export default function Example() {
12
+ return () => (
13
+ <div mix={buttonRowCss}>
14
+ <button mix={[button.baseStyle, button.primaryStyle]}>
15
+ <Glyph mix={button.iconStyle} name="add" />
16
+ <span mix={button.labelStyle}>New issue</span>
17
+ </button>
18
+
19
+ <button mix={[button.baseStyle, button.ghostStyle]}>
20
+ <span mix={button.labelStyle}>Open</span>
21
+ <Glyph mix={button.iconStyle} name="chevronRight" />
22
+ </button>
23
+
24
+ <button disabled mix={[button.baseStyle, button.secondaryStyle]}>
25
+ <span mix={button.labelStyle}>Disabled</span>
26
+ </button>
27
+
28
+ <button aria-busy="true" mix={[button.baseStyle, button.secondaryStyle]}>
29
+ <Glyph mix={[button.iconStyle, spinnerGlyphCss, spinCss]} name="spinner" />
30
+ <span mix={button.labelStyle}>Saving</span>
31
+ </button>
32
+ </div>
33
+ )
34
+ }
35
+
36
+ const buttonRowCss = css({
37
+ display: 'flex',
38
+ alignItems: 'center',
39
+ gap: theme.space.sm,
40
+ })
41
+
42
+ const spinnerGlyphCss = css({
43
+ opacity: 0.72,
44
+ })
45
+
46
+ const spinCss = css({
47
+ '@keyframes demo-button-spin': {
48
+ from: { transform: 'rotate(0deg)' },
49
+ to: { transform: 'rotate(360deg)' },
50
+ },
51
+ animation: 'demo-button-spin 1s linear infinite',
52
+ })
@@ -8,7 +8,7 @@ Use it when the user should type draft text, filter a popup list, and still comm
8
8
 
9
9
  ```tsx
10
10
  import { css, type Handle } from 'remix/ui'
11
- import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui'
11
+ import { Combobox, ComboboxOption, onComboboxChange } from 'remix/ui/combobox'
12
12
 
13
13
  let airports = [
14
14
  {
@@ -1,5 +1,3 @@
1
- // @jsxRuntime classic
2
- // @jsx createElement
3
1
  import {
4
2
  attrs,
5
3
  css,
@@ -8,9 +6,11 @@ import {
8
6
  on,
9
7
  ref,
10
8
  type CSSMixinDescriptor,
9
+ type Dispatched,
11
10
  type ElementProps,
12
11
  type Handle,
13
12
  type MixinHandle,
13
+ type MixinFactory,
14
14
  type Props,
15
15
  type RemixNode,
16
16
  } from '@remix-run/ui'
@@ -26,8 +26,8 @@ import { wait } from '../../utils/wait.ts'
26
26
  const COMBOBOX_CHANGE_EVENT = 'rmx:combobox-change' as const
27
27
  const INPUT_COMMIT_DELAY_MS = 50
28
28
 
29
- type ComboboxChangeHandler = (
30
- event: ComboboxChangeEvent,
29
+ type ComboboxChangeHandler<target extends HTMLElement> = (
30
+ event: Dispatched<ComboboxChangeEvent, target>,
31
31
  signal: AbortSignal,
32
32
  ) => void | Promise<void>
33
33
 
@@ -214,7 +214,9 @@ function getOptionSearchValue(option: {
214
214
  return option.searchValue ?? option.textValue ?? option.label
215
215
  }
216
216
 
217
- function ComboboxProvider(handle: Handle<ComboboxContextProps, ComboboxContextValue>) {
217
+ function ComboboxProvider(
218
+ handle: Handle<ComboboxContextProps, ComboboxContextValue>,
219
+ ): () => RemixNode {
218
220
  let inputRef: HTMLInputElement | undefined
219
221
  let listboxRef: listbox.ListboxRef | undefined
220
222
  let surfaceRef: HTMLElement | undefined
@@ -808,7 +810,11 @@ function getComboboxContext(handle: Handle | MixinHandle) {
808
810
  return handle.context.get(ComboboxProvider)
809
811
  }
810
812
 
811
- const inputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle) => {
813
+ const inputMixin: MixinFactory<HTMLInputElement, [], ElementProps> = createMixin<
814
+ HTMLInputElement,
815
+ [],
816
+ ElementProps
817
+ >((handle) => {
812
818
  let context = getComboboxContext(handle)
813
819
 
814
820
  return (props) => [
@@ -883,7 +889,11 @@ const inputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle) => {
883
889
  ]
884
890
  })
885
891
 
886
- const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
892
+ const popoverMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
893
+ HTMLElement,
894
+ [],
895
+ ElementProps
896
+ >((handle) => {
887
897
  let context = getComboboxContext(handle)
888
898
 
889
899
  return () => [
@@ -909,12 +919,20 @@ const popoverMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
909
919
  ]
910
920
  })
911
921
 
912
- const listMixin = createMixin<HTMLElement, [], ElementProps>((handle) => {
922
+ const listMixin: MixinFactory<HTMLElement, [], ElementProps> = createMixin<
923
+ HTMLElement,
924
+ [],
925
+ ElementProps
926
+ >((handle) => {
913
927
  let context = getComboboxContext(handle)
914
928
  return () => [attrs({ id: context.listId }), listbox.list()]
915
929
  })
916
930
 
917
- const hiddenInputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle) => {
931
+ const hiddenInputMixin: MixinFactory<HTMLInputElement, [], ElementProps> = createMixin<
932
+ HTMLInputElement,
933
+ [],
934
+ ElementProps
935
+ >((handle) => {
918
936
  let context = getComboboxContext(handle)
919
937
  return () =>
920
938
  attrs({
@@ -925,8 +943,8 @@ const hiddenInputMixin = createMixin<HTMLInputElement, [], ElementProps>((handle
925
943
  })
926
944
  })
927
945
 
928
- const optionMixin = createMixin<HTMLElement, [options: ComboboxOptionOptions], ElementProps>(
929
- (handle) => {
946
+ const optionMixin: MixinFactory<HTMLElement, [options: ComboboxOptionOptions], ElementProps> =
947
+ createMixin<HTMLElement, [options: ComboboxOptionOptions], ElementProps>((handle) => {
930
948
  let context = getComboboxContext(handle)
931
949
 
932
950
  return (options) => {
@@ -947,8 +965,7 @@ const optionMixin = createMixin<HTMLElement, [options: ComboboxOptionOptions], E
947
965
  }),
948
966
  ]
949
967
  }
950
- },
951
- )
968
+ })
952
969
 
953
970
  export const Context = ComboboxProvider
954
971
  export const hiddenInput = hiddenInputMixin
@@ -966,15 +983,14 @@ const combobox = {
966
983
  popover: popoverMixin,
967
984
  } as const
968
985
 
969
- export function onComboboxChange(handler: ComboboxChangeHandler, captureBoolean?: boolean) {
970
- return on<HTMLElement, typeof COMBOBOX_CHANGE_EVENT>(
971
- COMBOBOX_CHANGE_EVENT,
972
- handler,
973
- captureBoolean,
974
- )
986
+ export function onComboboxChange<target extends HTMLElement>(
987
+ handler: ComboboxChangeHandler<target>,
988
+ captureBoolean?: boolean,
989
+ ): ReturnType<typeof on<target, typeof COMBOBOX_CHANGE_EVENT>> {
990
+ return on(COMBOBOX_CHANGE_EVENT, handler, captureBoolean)
975
991
  }
976
992
 
977
- export function Combobox(handle: Handle<ComboboxProps>) {
993
+ export function Combobox(handle: Handle<ComboboxProps>): () => RemixNode {
978
994
  return () => {
979
995
  let { children, defaultValue, disabled, inputId, name, placeholder, ...divProps } = handle.props
980
996
 
@@ -997,7 +1013,7 @@ export function Combobox(handle: Handle<ComboboxProps>) {
997
1013
  }
998
1014
  }
999
1015
 
1000
- export function ComboboxOption(handle: Handle<ComboboxOptionProps>) {
1016
+ export function ComboboxOption(handle: Handle<ComboboxOptionProps>): () => RemixNode {
1001
1017
  return () => {
1002
1018
  let { children, disabled, label, mix, searchValue, value, ...divProps } = handle.props
1003
1019