1
- import type { ComponentPropsWithoutRef , ElementType , ForwardedRef } from 'react' ;
2
- import { type ReactNode } from 'react' ;
1
+ import type { ElementType } from 'react' ;
2
+ import { forwardRef , type ReactNode } from 'react' ;
3
3
import { twMerge } from 'tailwind-merge' ;
4
- import genericForwardRef from '../../helpers/generic-forward-ref' ;
5
4
import { mergeDeep } from '../../helpers/merge-deep' ;
6
5
import { getTheme } from '../../theme-store' ;
7
6
import type { DeepPartial } from '../../types' ;
@@ -16,6 +15,7 @@ import { Spinner } from '../Spinner';
16
15
import { ButtonBase , type ButtonBaseProps } from './ButtonBase' ;
17
16
import type { PositionInButtonGroup } from './ButtonGroup' ;
18
17
import { ButtonGroup } from './ButtonGroup' ;
18
+ import type { PolymorphicComponentPropWithRef , PolymorphicRef } from '../../helpers/generic-as-prop' ;
19
19
20
20
export interface FlowbiteButtonTheme {
21
21
base : string ;
@@ -67,105 +67,110 @@ export interface ButtonSizes extends Pick<FlowbiteSizes, 'xs' | 'sm' | 'lg' | 'x
67
67
[ key : string ] : string ;
68
68
}
69
69
70
- export type ButtonProps < T extends ElementType = 'button' > = {
71
- as ?: T | null ;
72
- href ?: string ;
73
- color ?: keyof FlowbiteColors ;
74
- fullSized ?: boolean ;
75
- gradientDuoTone ?: keyof ButtonGradientDuoToneColors ;
76
- gradientMonochrome ?: keyof ButtonGradientColors ;
77
- target ?: string ;
78
- isProcessing ?: boolean ;
79
- processingLabel ?: string ;
80
- processingSpinner ?: ReactNode ;
81
- label ?: ReactNode ;
82
- outline ?: boolean ;
83
- pill ?: boolean ;
84
- positionInGroup ?: keyof PositionInButtonGroup ;
85
- size ?: keyof ButtonSizes ;
86
- theme ?: DeepPartial < FlowbiteButtonTheme > ;
87
- } & ComponentPropsWithoutRef < T > ;
88
-
89
- const ButtonComponentFn = < T extends ElementType = 'button' > (
70
+ export type ButtonProps < T extends ElementType = 'button' > = PolymorphicComponentPropWithRef <
71
+ T ,
90
72
{
91
- children,
92
- className,
93
- color = 'info' ,
94
- disabled,
95
- fullSized,
96
- isProcessing = false ,
97
- processingLabel = 'Loading...' ,
98
- processingSpinner,
99
- gradientDuoTone,
100
- gradientMonochrome,
101
- label,
102
- outline = false ,
103
- pill = false ,
104
- positionInGroup = 'none' ,
105
- size = 'md' ,
106
- theme : customTheme = { } ,
107
- ...props
108
- } : ButtonProps < T > ,
109
- ref : ForwardedRef < T > ,
110
- ) => {
111
- const { buttonGroup : groupTheme , button : buttonTheme } = getTheme ( ) ;
112
- const theme = mergeDeep ( buttonTheme , customTheme ) ;
73
+ href ?: string ;
74
+ color ?: keyof FlowbiteColors ;
75
+ fullSized ?: boolean ;
76
+ gradientDuoTone ?: keyof ButtonGradientDuoToneColors ;
77
+ gradientMonochrome ?: keyof ButtonGradientColors ;
78
+ target ?: string ;
79
+ isProcessing ?: boolean ;
80
+ processingLabel ?: string ;
81
+ processingSpinner ?: ReactNode ;
82
+ label ?: ReactNode ;
83
+ outline ?: boolean ;
84
+ pill ?: boolean ;
85
+ positionInGroup ?: keyof PositionInButtonGroup ;
86
+ size ?: keyof ButtonSizes ;
87
+ theme ?: DeepPartial < FlowbiteButtonTheme > ;
88
+ }
89
+ > ;
90
+
91
+ type ButtonComponentType = ( < C extends React . ElementType = 'button' > (
92
+ props : ButtonProps < C > ,
93
+ ) => React . ReactNode | null ) & { displayName ?: string } ;
94
+
95
+ const ButtonComponentFn : ButtonComponentType = forwardRef (
96
+ < T extends ElementType = 'button' > (
97
+ {
98
+ children,
99
+ className,
100
+ color = 'info' ,
101
+ disabled,
102
+ fullSized,
103
+ isProcessing = false ,
104
+ processingLabel = 'Loading...' ,
105
+ processingSpinner,
106
+ gradientDuoTone,
107
+ gradientMonochrome,
108
+ label,
109
+ outline = false ,
110
+ pill = false ,
111
+ positionInGroup = 'none' ,
112
+ size = 'md' ,
113
+ theme : customTheme = { } ,
114
+ ...props
115
+ } : ButtonProps < T > ,
116
+ ref : PolymorphicRef < T > ,
117
+ ) => {
118
+ const { buttonGroup : groupTheme , button : buttonTheme } = getTheme ( ) ;
119
+ const theme = mergeDeep ( buttonTheme , customTheme ) ;
113
120
114
- const theirProps = props as ButtonBaseProps < T > ;
121
+ const theirProps = props as ButtonBaseProps < T > ;
115
122
116
- return (
117
- < ButtonBase
118
- ref = { ref }
119
- disabled = { disabled }
120
- className = { twMerge (
121
- theme . base ,
122
- disabled && theme . disabled ,
123
- ! gradientDuoTone && ! gradientMonochrome && theme . color [ color ] ,
124
- gradientDuoTone && ! gradientMonochrome && theme . gradientDuoTone [ gradientDuoTone ] ,
125
- ! gradientDuoTone && gradientMonochrome && theme . gradient [ gradientMonochrome ] ,
126
- outline && ( theme . outline . color [ color ] ?? theme . outline . color . default ) ,
127
- theme . pill [ pill ? 'on' : 'off' ] ,
128
- fullSized && theme . fullSized ,
129
- groupTheme . position [ positionInGroup ] ,
130
- className ,
131
- ) }
132
- { ...theirProps }
133
- >
134
- < span
123
+ return (
124
+ < ButtonBase
125
+ ref = { ref }
126
+ disabled = { disabled }
135
127
className = { twMerge (
136
- theme . inner . base ,
137
- theme . outline [ outline ? 'on' : 'off' ] ,
138
- theme . outline . pill [ outline && pill ? 'on' : 'off' ] ,
139
- theme . size [ size ] ,
140
- outline && ! theme . outline . color [ color ] && theme . inner . outline ,
141
- isProcessing && theme . isProcessing ,
142
- isProcessing && theme . inner . isProcessingPadding [ size ] ,
143
- theme . inner . position [ positionInGroup ] ,
128
+ theme . base ,
129
+ disabled && theme . disabled ,
130
+ ! gradientDuoTone && ! gradientMonochrome && theme . color [ color ] ,
131
+ gradientDuoTone && ! gradientMonochrome && theme . gradientDuoTone [ gradientDuoTone ] ,
132
+ ! gradientDuoTone && gradientMonochrome && theme . gradient [ gradientMonochrome ] ,
133
+ outline && ( theme . outline . color [ color ] ?? theme . outline . color . default ) ,
134
+ theme . pill [ pill ? 'on' : 'off' ] ,
135
+ fullSized && theme . fullSized ,
136
+ groupTheme . position [ positionInGroup ] ,
137
+ className ,
144
138
) }
139
+ { ...theirProps }
145
140
>
146
- < >
147
- { isProcessing && (
148
- < span className = { twMerge ( theme . spinnerSlot , theme . spinnerLeftPosition [ size ] ) } >
149
- { processingSpinner || < Spinner size = { size } /> }
150
- </ span >
141
+ < span
142
+ className = { twMerge (
143
+ theme . inner . base ,
144
+ theme . outline [ outline ? 'on' : 'off' ] ,
145
+ theme . outline . pill [ outline && pill ? 'on' : 'off' ] ,
146
+ theme . size [ size ] ,
147
+ outline && ! theme . outline . color [ color ] && theme . inner . outline ,
148
+ isProcessing && theme . isProcessing ,
149
+ isProcessing && theme . inner . isProcessingPadding [ size ] ,
150
+ theme . inner . position [ positionInGroup ] ,
151
151
) }
152
- { typeof children !== 'undefined' ? (
153
- children
154
- ) : (
155
- < span data-testid = "flowbite-button-label" className = { twMerge ( theme . label ) } >
156
- { isProcessing ? processingLabel : label }
157
- </ span >
158
- ) }
159
- </ >
160
- </ span >
161
- </ ButtonBase >
162
- ) ;
163
- } ;
152
+ >
153
+ < >
154
+ { isProcessing && (
155
+ < span className = { twMerge ( theme . spinnerSlot , theme . spinnerLeftPosition [ size ] ) } >
156
+ { processingSpinner || < Spinner size = { size } /> }
157
+ </ span >
158
+ ) }
159
+ { typeof children !== 'undefined' ? (
160
+ children
161
+ ) : (
162
+ < span data-testid = "flowbite-button-label" className = { twMerge ( theme . label ) } >
163
+ { isProcessing ? processingLabel : label }
164
+ </ span >
165
+ ) }
166
+ </ >
167
+ </ span >
168
+ </ ButtonBase >
169
+ ) ;
170
+ } ,
171
+ ) ;
164
172
165
173
ButtonComponentFn . displayName = 'Button' ;
166
-
167
- const ButtonComponent = genericForwardRef ( ButtonComponentFn ) ;
168
-
169
- export const Button = Object . assign ( ButtonComponent , {
174
+ export const Button = Object . assign ( ButtonComponentFn , {
170
175
Group : ButtonGroup ,
171
176
} ) ;
0 commit comments