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' ;
33import { twMerge } from 'tailwind-merge' ;
4- import genericForwardRef from '../../helpers/generic-forward-ref' ;
54import { mergeDeep } from '../../helpers/merge-deep' ;
65import { getTheme } from '../../theme-store' ;
76import type { DeepPartial } from '../../types' ;
@@ -16,6 +15,7 @@ import { Spinner } from '../Spinner';
1615import { ButtonBase , type ButtonBaseProps } from './ButtonBase' ;
1716import type { PositionInButtonGroup } from './ButtonGroup' ;
1817import { ButtonGroup } from './ButtonGroup' ;
18+ import type { PolymorphicComponentPropWithRef , PolymorphicRef } from '../../helpers/generic-as-prop' ;
1919
2020export interface FlowbiteButtonTheme {
2121 base : string ;
@@ -67,105 +67,110 @@ export interface ButtonSizes extends Pick<FlowbiteSizes, 'xs' | 'sm' | 'lg' | 'x
6767 [ key : string ] : string ;
6868}
6969
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 ,
9072 {
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 ) ;
113120
114- const theirProps = props as ButtonBaseProps < T > ;
121+ const theirProps = props as ButtonBaseProps < T > ;
115122
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 }
135127 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 ,
144138 ) }
139+ { ...theirProps }
145140 >
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 ] ,
151151 ) }
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+ ) ;
164172
165173ButtonComponentFn . displayName = 'Button' ;
166-
167- const ButtonComponent = genericForwardRef ( ButtonComponentFn ) ;
168-
169- export const Button = Object . assign ( ButtonComponent , {
174+ export const Button = Object . assign ( ButtonComponentFn , {
170175 Group : ButtonGroup ,
171176} ) ;
0 commit comments