@@ -7,36 +7,86 @@ import { SingleColors } from "../../theme/config";
7
7
import { setColor } from "../../utils/setcolor" ;
8
8
import { Spinner } from "../spinner" ;
9
9
import { SVGIcon , SVGIconProps } from "../svgicon" ;
10
+ import { PolymorphicComponentPropWithRef , PolymorphicRef } from "./typeUtils" ;
10
11
11
- export type ButtonProps < T extends React . ElementType = "button" > =
12
- React . ComponentProps < T > & {
13
- children ?: React . ReactNode | string ;
14
- severity ?: "high" | "low" | "medium" ;
15
- status ?:
16
- | "accent"
17
- | "danger"
18
- | "discovery"
19
- | "notification"
20
- | "success"
21
- | "warning" ;
22
- size ?: "md" | "sm" ;
23
- icon ?: SVGIconProps [ "name" ] ;
24
- iconRight ?: boolean ;
25
- initialState ?: string ;
26
- /**
27
- * @deprecated The property should not be used
28
- */
29
- color ?: SingleColors ;
30
- form ?: string ;
31
- select ?: boolean ;
32
- className ?: string ;
33
- onClick ?: ( event : React . MouseEvent < HTMLButtonElement > ) => void ;
34
- as ?: T ;
35
- linkTo ?: string ;
36
- display ?: "flex" | "inline-flex" ;
37
- outline ?: boolean ;
38
- secondary ?: boolean ;
39
- } ;
12
+ type ButtonPropsInner < C extends React . ElementType > =
13
+ PolymorphicComponentPropWithRef <
14
+ C ,
15
+ {
16
+ severity ?: "high" | "low" | "medium" ;
17
+ status ?:
18
+ | "accent"
19
+ | "danger"
20
+ | "discovery"
21
+ | "notification"
22
+ | "success"
23
+ | "warning" ;
24
+ size ?: "md" | "sm" ;
25
+ icon ?: SVGIconProps [ "name" ] ;
26
+ iconRight ?: boolean ;
27
+ initialState ?: string ;
28
+ /**
29
+ * @deprecated The property should not be used
30
+ */
31
+ color ?: SingleColors ;
32
+ form ?: string ;
33
+ select ?: boolean ;
34
+ className ?: string ;
35
+ onClick ?: ( event : React . MouseEvent < HTMLButtonElement > ) => void ;
36
+ linkTo ?: string ;
37
+ display ?: "flex" | "inline-flex" ;
38
+ outline ?: boolean ;
39
+ secondary ?: boolean ;
40
+ }
41
+ > &
42
+ SpaceProps ;
43
+
44
+ export type ButtonProps = ButtonPropsInner < "a" > | ButtonPropsInner < "button" > ;
45
+
46
+ type ButtonComponent = < C extends React . ElementType = "button" > (
47
+ props : ButtonPropsInner < C >
48
+ ) => React . ReactElement | null ;
49
+
50
+ export const Button : ButtonComponent = React . forwardRef (
51
+ < C extends React . ElementType = "button" > (
52
+ { children, icon, initialState, linkTo, as, ...props } : ButtonPropsInner < C > ,
53
+ ref ?: PolymorphicRef < C >
54
+ ) => {
55
+ const component = as || linkTo ? "a" : "button" ;
56
+
57
+ return (
58
+ < StyledButton
59
+ ref = { ref }
60
+ as = { component }
61
+ href = { linkTo }
62
+ linkTo = { linkTo }
63
+ { ...props }
64
+ >
65
+ < ButtonIcon { ...{ initialState, icon } } />
66
+ { children && < span > { children } </ span > }
67
+ </ StyledButton >
68
+ ) ;
69
+ }
70
+ ) ;
71
+
72
+ type ButtonIconProps = Pick < ButtonProps , "icon" | "initialState" > ;
73
+
74
+ function ButtonIcon ( { initialState, icon } : ButtonIconProps ) {
75
+ switch ( initialState ) {
76
+ case "success" :
77
+ return < SVGIcon name = "success" /> ;
78
+ case "error" :
79
+ return < SVGIcon name = "danger" /> ;
80
+ case "loading" :
81
+ return (
82
+ < div className = "spinner" >
83
+ < Spinner size = "sm" />
84
+ </ div >
85
+ ) ;
86
+ default :
87
+ return icon ? < SVGIcon name = { icon } /> : null ;
88
+ }
89
+ }
40
90
41
91
const changeSize = ( size : string ) => {
42
92
switch ( size ) {
@@ -161,7 +211,8 @@ const changeStatus = (status?: ButtonProps["status"]) => {
161
211
}
162
212
} ;
163
213
164
- const StyledButton = styled . button < ButtonProps < React . ElementType > > `
214
+ /* stylelint-disable no-descending-specificity */
215
+ const StyledButton = styled . button < ButtonProps > `
165
216
background: ${ theme . color . interactive . primary } ;
166
217
white-space: nowrap;
167
218
font-family: ${ theme . fonts . body } ;
@@ -175,8 +226,7 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
175
226
text-decoration: none;
176
227
display: ${ ( { display, linkTo } ) =>
177
228
! display && linkTo ? "inline-flex" : display || "flex" } ;
178
- flex-direction: ${ ( props : ButtonProps ) =>
179
- props . iconRight ? "row-reverse" : "row" } ;
229
+ flex-direction: ${ ( props ) => ( props . iconRight ? "row-reverse" : "row" ) } ;
180
230
align-items: center;
181
231
text-transform: ${ theme . typography . titleCase } ;
182
232
@@ -263,15 +313,15 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
263
313
${ changeSize ( size ) }
264
314
` } ;
265
315
266
- ${ ( { color } ) =>
316
+ ${ ( { color, severity } ) =>
267
317
color &&
268
318
css `
269
- background-color : ${ ( props : ButtonProps ) =>
270
- props . severity === "medium" ? "transparent" : setColor ( color ) } ;
271
- color : ${ ( props : ButtonProps ) =>
272
- props . severity === "medium"
273
- ? setColor ( color )
274
- : theme . color . text . text04 } ;
319
+ background-color : ${ severity === "medium"
320
+ ? "transparent"
321
+ : setColor ( color ) } ;
322
+ color : ${ severity === "medium"
323
+ ? setColor ( color )
324
+ : theme . color . text . text04 } ;
275
325
border : 1px solid ${ setColor ( color ) } ;
276
326
& : hover ,
277
327
& : active ,
@@ -281,68 +331,25 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
281
331
}
282
332
.spinner {
283
333
div {
284
- border-color : ${ ( props : ButtonProps ) =>
285
- props . severity === "medium"
286
- ? setColor ( color )
287
- : theme . color . text . text04 }
334
+ border-color : ${ severity === "medium"
335
+ ? setColor ( color )
336
+ : theme . color . text . text04 }
288
337
transparent transparent transparent;
289
338
}
290
339
}
291
340
& : hover {
292
- color : ${ ( props : ButtonProps ) =>
293
- props . severity === "medium"
294
- ? setColor ( color )
295
- : theme . color . text . text04 } ;
296
- background-color : ${ ( props : ButtonProps ) =>
297
- props . severity === "medium"
298
- ? lighten ( 0.35 , setColor ( color ) )
299
- : darken ( 0.1 , setColor ( color ) ) } ;
341
+ color : ${ severity === "medium"
342
+ ? setColor ( color )
343
+ : theme . color . text . text04 } ;
344
+ background-color : ${ severity === "medium"
345
+ ? lighten ( 0.35 , setColor ( color ) )
346
+ : darken ( 0.1 , setColor ( color ) ) } ;
300
347
}
301
348
& : active {
302
- background : ${ ( props : ButtonProps ) =>
303
- props . severity === "medium"
304
- ? lighten ( 0.25 , setColor ( color ) )
305
- : darken ( 0.2 , setColor ( color ) ) } ;
349
+ background : ${ severity === "medium"
350
+ ? lighten ( 0.25 , setColor ( color ) )
351
+ : darken ( 0.2 , setColor ( color ) ) } ;
306
352
}
307
353
` }
308
354
${ space }
309
355
` ;
310
-
311
- export function Button < T extends React . ElementType = "button" > ( {
312
- children,
313
- icon,
314
- initialState,
315
- linkTo,
316
- ...props
317
- } : ButtonProps < T > & SpaceProps ) {
318
- return (
319
- < StyledButton
320
- as = { linkTo ? "a" : "button" }
321
- href = { linkTo }
322
- linkTo = { linkTo }
323
- { ...props }
324
- >
325
- < ButtonIcon { ...{ initialState, icon } } />
326
- { children && < span > { children } </ span > }
327
- </ StyledButton >
328
- ) ;
329
- }
330
-
331
- type ButtonIconProps = Pick < ButtonProps , "icon" | "initialState" > ;
332
-
333
- function ButtonIcon ( { initialState, icon } : ButtonIconProps ) {
334
- switch ( initialState ) {
335
- case "success" :
336
- return < SVGIcon name = "success" /> ;
337
- case "error" :
338
- return < SVGIcon name = "danger" /> ;
339
- case "loading" :
340
- return (
341
- < div className = "spinner" >
342
- < Spinner size = "sm" />
343
- </ div >
344
- ) ;
345
- default :
346
- return icon ? < SVGIcon name = { icon } /> : null ;
347
- }
348
- }
0 commit comments