11import {
2- cloneElement ,
32 forwardRef ,
4- isValidElement ,
53 type FC ,
64 type ForwardedRef ,
75 type ReactElement ,
6+ isValidElement ,
87} from 'react' ;
98import { Box , type BoxProps } from './box.js' ;
109import type { ButtonState , ButtonVariant } from './button.css.js' ;
@@ -13,7 +12,7 @@ import {
1312 buttonClassName ,
1413 buttonStateClassNames ,
1514 buttonVariantClassNames ,
16- iconClass ,
15+ iconClassName ,
1716 inlineBleedClass ,
1817 visiblyHiddenClass ,
1918} from './button.css.js' ;
@@ -22,7 +21,7 @@ import { useStringLikeDetector } from './hooks/use-string-like.js';
2221import { Flex , type FlexProps } from './layout.js' ;
2322import { Spinner } from './loaders.js' ;
2423import type { Falsy , Merge , ReactHTMLElementsHacked } from './types.js' ;
25- import { ExactText } from './typography.js' ;
24+ import { ExactText , type FontSize } from './typography.js' ;
2625
2726export { ButtonState , ButtonVariant } ;
2827
@@ -73,11 +72,16 @@ export type ButtonIconProps<
7372const IconBox : FC < {
7473 icon : ReactElement | FC ;
7574 busy ?: boolean | Falsy ;
76- } > = ( { icon, busy } ) => (
77- < Box component = "span" className = { [ iconClass , busy && visiblyHiddenClass ] } >
75+ capSize ?: FontSize ;
76+ } > = ( { icon, busy, ...props } ) => (
77+ < Box
78+ { ...props }
79+ component = "span"
80+ className = { [ iconClassName , busy && visiblyHiddenClass ] }
81+ >
7882 { isValidElement < ReactElementDefaultPropsType > ( icon )
79- ? cloneElement ( icon , { className : iconClass } )
80- : icon ( { className : iconClass } ) }
83+ ? icon
84+ : icon ( { } /* { className: iconClassName } */ ) }
8185 </ Box >
8286) ;
8387
@@ -104,14 +108,14 @@ function getSizeProps(size: ButtonSize | Falsy) {
104108 switch ( size ) {
105109 case 'small' :
106110 return {
107- space : '1 ' ,
111+ space : '2 ' ,
108112 fontSize : '0' ,
109113 paddingBlock : '4' ,
110114 paddingInline : '5' ,
111115 } satisfies BoxProps ;
112116 case 'large' :
113117 return {
114- space : '2 ' ,
118+ space : '3 ' ,
115119 fontSize : '3' ,
116120 paddingBlock : '6' ,
117121 paddingInline : '7' ,
@@ -144,7 +148,7 @@ export const Button = forwardRef(
144148 flexGrow,
145149 state,
146150 size = 'medium' ,
147- variant = 'default' ,
151+ variant,
148152 ...props
149153 } : ButtonProps < T > ,
150154 ref : ForwardedRef < HTMLElement > ,
@@ -163,6 +167,15 @@ export const Button = forwardRef(
163167 'aria-live' : busy ? 'polite' : undefined ,
164168 } as const ;
165169
170+ const autoButtonTypeVariant =
171+ 'type' in props && props . type === 'submit' && ! variant
172+ ? 'primary'
173+ : variant ;
174+
175+ const resolvedVariant = autoButtonTypeVariant || 'default' ;
176+
177+ const hasStringChildren = isStringLike ( children ) ;
178+
166179 return (
167180 < UnstyledButton
168181 ref = { ref }
@@ -178,34 +191,37 @@ export const Button = forwardRef(
178191 inline && inlineBleedClass ,
179192
180193 // order is important here, as we want the state to override the variant
181- state && variant && buttonStateClassNames [ variant ] [ state ] ,
194+ state &&
195+ resolvedVariant &&
196+ buttonStateClassNames [ resolvedVariant ] [ state ] ,
182197
183- variant && buttonVariantClassNames [ variant ] ,
198+ resolvedVariant && buttonVariantClassNames [ resolvedVariant ] ,
184199 ] }
185200 >
186- { iconStart && < IconBox icon = { iconStart } busy = { busy } /> }
187-
188- { ! busy &&
189- ( isStringLike ( children ) ? (
190- < ExactText
191- component = "span"
192- textAlign = { textAlign }
193- capSize = { fontSize }
194- { ...busyAttributes }
195- >
196- { children }
197- </ ExactText >
198- ) : (
199- children && (
200- < Box flexGrow component = "span" { ...busyAttributes } >
201- { children }
202- </ Box >
203- )
204- ) ) }
205-
206- { busy && < Spinner /> }
207-
208- { iconEnd && < IconBox icon = { iconEnd } busy = { busy } /> }
201+ { iconStart && (
202+ < IconBox capSize = { fontSize } busy = { busy } icon = { iconStart } />
203+ ) }
204+
205+ { ! busy && hasStringChildren && (
206+ < ExactText
207+ component = "span"
208+ textAlign = { textAlign }
209+ capSize = { fontSize }
210+ { ...busyAttributes }
211+ >
212+ { children }
213+ </ ExactText >
214+ ) }
215+
216+ { ! busy && children && ! hasStringChildren && (
217+ < Box flexGrow component = "span" { ...busyAttributes } >
218+ { children }
219+ </ Box >
220+ ) }
221+
222+ { busy && < Spinner capSize = { fontSize } /> }
223+
224+ { iconEnd && < IconBox capSize = { fontSize } busy = { busy } icon = { iconEnd } /> }
209225 </ UnstyledButton >
210226 ) ;
211227 } ,
0 commit comments