-
-
Notifications
You must be signed in to change notification settings - Fork 437
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(inputs): adds theme support to TextInput and FileInput (#246)
* refactor(inputs): adds theme support to TextInput and FileInput components Adding theme support for TextInput and FileInput. It also rewrites the FileInput component to decouple it from FileInput. BREAKING CHANGE: Adding theme support to the component blocks the access to className property directly * fix(inputs): fix wrong default color name from `base` to `gray`
- Loading branch information
Showing
7 changed files
with
185 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,41 @@ | ||
import classNames from 'classnames'; | ||
import type { FC } from 'react'; | ||
import type { TextInputProps } from './TextInput'; | ||
import { TextInput } from './TextInput'; | ||
import type { ComponentProps, ReactNode } from 'react'; | ||
import { forwardRef } from 'react'; | ||
import { excludeClassName } from '../../helpers/exclude'; | ||
import { useTheme } from '../Flowbite/ThemeContext'; | ||
import HelperText from './HelperText'; | ||
import type { TextInputColors, TextInputSizes } from './TextInput'; | ||
|
||
export type FileInputProps = Omit<TextInputProps, 'type'>; | ||
export interface FileInputProps extends Omit<ComponentProps<'input'>, 'type' | 'ref' | 'color' | 'className'> { | ||
sizing?: keyof TextInputSizes; | ||
helperText?: ReactNode; | ||
color?: keyof TextInputColors; | ||
} | ||
|
||
export const FileInput: FC<FileInputProps> = ({ className, ...props }) => { | ||
return <TextInput className={classNames('!p-0', className)} {...props} type="file" />; | ||
}; | ||
export const FileInput = forwardRef<HTMLInputElement, FileInputProps>( | ||
({ sizing = 'md', helperText, color = 'gray', ...props }, ref) => { | ||
const theme = useTheme().theme.formControls.fileInput; | ||
const theirProps = excludeClassName(props); | ||
return ( | ||
<> | ||
<div className={theme.base}> | ||
<div className={theme.field.base}> | ||
<input | ||
className={classNames( | ||
theme.field.input.base, | ||
theme.field.input.colors[color], | ||
theme.field.input.sizes[sizing], | ||
)} | ||
{...theirProps} | ||
type="file" | ||
ref={ref} | ||
/> | ||
</div> | ||
</div> | ||
{helperText && <HelperText color={color}>{helperText}</HelperText>} | ||
</> | ||
); | ||
}, | ||
); | ||
|
||
FileInput.displayName = 'FileInput'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,60 @@ | ||
import classNames from 'classnames'; | ||
import type { ComponentProps, FC, ReactNode } from 'react'; | ||
import { forwardRef } from 'react'; | ||
import { excludeClassName } from '../../helpers/exclude'; | ||
import type { FlowbiteColors, FlowbiteSizes } from '../Flowbite/FlowbiteTheme'; | ||
import { useTheme } from '../Flowbite/ThemeContext'; | ||
import HelperText from './HelperText'; | ||
|
||
type Size = 'sm' | 'md' | 'lg'; | ||
type Color = 'base' | 'green' | 'red'; | ||
export interface TextInputColors extends Pick<FlowbiteColors, 'gray' | 'info' | 'failure' | 'warning' | 'success'> { | ||
[key: string]: string; | ||
} | ||
|
||
export type TextInputProps = Omit<ComponentProps<'input'>, 'ref'> & { | ||
sizing?: Size; | ||
export interface TextInputSizes extends Pick<FlowbiteSizes, 'sm' | 'md' | 'lg'> { | ||
[key: string]: string; | ||
} | ||
|
||
export interface TextInputProps extends Omit<ComponentProps<'input'>, 'ref' | 'color' | 'className'> { | ||
sizing?: keyof TextInputSizes; | ||
shadow?: boolean; | ||
helperText?: ReactNode; | ||
addon?: ReactNode; | ||
icon?: FC<ComponentProps<'svg'>>; | ||
color?: Color; | ||
}; | ||
|
||
const colorClasses: Record<Color, { input: string; helperText: string }> = { | ||
base: { | ||
input: | ||
'bg-gray-50 border-gray-300 text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500', | ||
helperText: 'text-gray-500 dark:text-gray-400', | ||
}, | ||
green: { | ||
input: | ||
'border-green-500 bg-green-50 text-green-900 placeholder-green-700 focus:border-green-500 focus:ring-green-500 dark:border-green-400 dark:bg-green-100 dark:focus:border-green-500 dark:focus:ring-green-500', | ||
helperText: 'text-green-600 dark:text-green-500', | ||
}, | ||
red: { | ||
input: | ||
'border-red-500 bg-red-50 text-red-900 placeholder-red-700 focus:border-red-500 focus:ring-red-500 dark:border-red-400 dark:bg-red-100 dark:focus:border-red-500 dark:focus:ring-red-500', | ||
helperText: 'text-red-600 dark:text-red-500', | ||
}, | ||
}; | ||
color?: keyof TextInputColors; | ||
} | ||
|
||
export const TextInput = forwardRef<HTMLInputElement, TextInputProps>( | ||
({ className, sizing = 'md', shadow, helperText, addon, icon: Icon, color = 'base', ...props }, ref) => ( | ||
<> | ||
<div className="flex"> | ||
{addon && ( | ||
<span className="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-200 px-3 text-sm text-gray-900 dark:border-gray-600 dark:bg-gray-600 dark:text-gray-400"> | ||
{addon} | ||
</span> | ||
)} | ||
<div className="relative w-full"> | ||
{Icon && ( | ||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> | ||
<Icon className="h-5 w-5 text-gray-500 dark:text-gray-400" /> | ||
</div> | ||
)} | ||
<input | ||
className={classNames( | ||
'block w-full border disabled:cursor-not-allowed disabled:opacity-50', | ||
colorClasses[color].input, | ||
{ | ||
'pl-10': Icon, | ||
'rounded-lg': !addon, | ||
'rounded-r-lg': addon, | ||
'shadow-sm dark:shadow-sm-light': shadow, | ||
'p-2 sm:text-xs': sizing === 'sm', | ||
'p-2.5 text-sm': sizing === 'md', | ||
'sm:text-md p-4': sizing === 'lg', | ||
}, | ||
className, | ||
({ sizing = 'md', shadow, helperText, addon, icon: Icon, color = 'gray', ...props }, ref) => { | ||
const theme = useTheme().theme.formControls.textInput; | ||
const theirProps = excludeClassName(props); | ||
return ( | ||
<> | ||
<div className={theme.base}> | ||
{addon && <span className={theme.addon}>{addon}</span>} | ||
<div className={theme.field.base}> | ||
{Icon && ( | ||
<div className={theme.field.icon.base}> | ||
<Icon className={theme.field.icon.svg} /> | ||
</div> | ||
)} | ||
{...props} | ||
ref={ref} | ||
/> | ||
<input | ||
className={classNames( | ||
theme.field.input.base, | ||
theme.field.input.colors[color], | ||
theme.field.input.withIcon[Icon ? 'on' : 'off'], | ||
theme.field.input.withAddon[addon ? 'on' : 'off'], | ||
theme.field.input.withShadow[shadow ? 'on' : 'off'], | ||
theme.field.input.sizes[sizing], | ||
)} | ||
{...theirProps} | ||
ref={ref} | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
{helperText && <p className={classNames('mt-1 text-sm', colorClasses[color].helperText)}>{helperText}</p>} | ||
</> | ||
), | ||
{helperText && <HelperText color={color}>{helperText}</HelperText>} | ||
</> | ||
); | ||
}, | ||
); | ||
|
||
TextInput.displayName = 'TextInput'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters