@@ -7,36 +7,86 @@ import { SingleColors } from "../../theme/config";
77import { setColor } from "../../utils/setcolor" ;
88import { Spinner } from "../spinner" ;
99import { SVGIcon , SVGIconProps } from "../svgicon" ;
10+ import { PolymorphicComponentPropWithRef , PolymorphicRef } from "./typeUtils" ;
1011
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+ }
4090
4191const changeSize = ( size : string ) => {
4292 switch ( size ) {
@@ -161,7 +211,8 @@ const changeStatus = (status?: ButtonProps["status"]) => {
161211 }
162212} ;
163213
164- const StyledButton = styled . button < ButtonProps < React . ElementType > > `
214+ /* stylelint-disable no-descending-specificity */
215+ const StyledButton = styled . button < ButtonProps > `
165216 background: ${ theme . color . interactive . primary } ;
166217 white-space: nowrap;
167218 font-family: ${ theme . fonts . body } ;
@@ -175,8 +226,7 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
175226 text-decoration: none;
176227 display: ${ ( { display, linkTo } ) =>
177228 ! 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" ) } ;
180230 align-items: center;
181231 text-transform: ${ theme . typography . titleCase } ;
182232
@@ -263,15 +313,15 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
263313 ${ changeSize ( size ) }
264314 ` } ;
265315
266- ${ ( { color } ) =>
316+ ${ ( { color, severity } ) =>
267317 color &&
268318 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 } ;
275325 border : 1px solid ${ setColor ( color ) } ;
276326 & : hover ,
277327 & : active ,
@@ -281,68 +331,25 @@ const StyledButton = styled.button<ButtonProps<React.ElementType>>`
281331 }
282332 .spinner {
283333 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 }
288337 transparent transparent transparent;
289338 }
290339 }
291340 & : 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 ) ) } ;
300347 }
301348 & : 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 ) ) } ;
306352 }
307353 ` }
308354 ${ space }
309355` ;
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