Skip to content

Commit

Permalink
feature(component): Rewrite Buttons to use themes, resolves themesb…
Browse files Browse the repository at this point in the history
  • Loading branch information
tulup-conner committed May 28, 2022
1 parent 41e7e1b commit 367e73e
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 148 deletions.
18 changes: 9 additions & 9 deletions src/docs/pages/ButtonGroupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ const ButtonGroupPage: FC = () => {
<Button color="blue">Messages</Button>
</Button.Group>
<Button.Group>
<Button gradientMonochrome="blue">Profile</Button>
<Button gradientMonochrome="blue">Settings</Button>
<Button gradientMonochrome="blue">Messages</Button>
<Button gradientMonochrome="info">Profile</Button>
<Button gradientMonochrome="info">Settings</Button>
<Button gradientMonochrome="info">Messages</Button>
</Button.Group>
<Button.Group>
<Button gradientDuoTone="greenToBlue">Profile</Button>
Expand All @@ -64,9 +64,9 @@ const ButtonGroupPage: FC = () => {
<Button color="alternative">Messages</Button>
</Button.Group>
<Button.Group outline>
<Button gradientMonochrome="blue">Profile</Button>
<Button gradientMonochrome="blue">Settings</Button>
<Button gradientMonochrome="blue">Messages</Button>
<Button gradientMonochrome="info">Profile</Button>
<Button gradientMonochrome="info">Settings</Button>
<Button gradientMonochrome="info">Messages</Button>
</Button.Group>
<Button.Group outline>
<Button gradientDuoTone="cyanToBlue">Profile</Button>
Expand All @@ -92,13 +92,13 @@ const ButtonGroupPage: FC = () => {
</Button>
</Button.Group>
<Button.Group outline>
<Button gradientMonochrome="blue">
<Button gradientMonochrome="info">
<HiUserCircle className="mr-3 h-4 w-4" /> Profile
</Button>
<Button gradientMonochrome="blue">
<Button gradientMonochrome="info">
<HiAdjustments className="mr-3 h-4 w-4" /> Settings
</Button>
<Button gradientMonochrome="blue">
<Button gradientMonochrome="info">
<HiCloudDownload className="mr-3 h-4 w-4" /> Messages
</Button>
</Button.Group>
Expand Down
22 changes: 15 additions & 7 deletions src/docs/pages/ButtonsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ const ButtonsPage: FC = () => {
title: 'Gradient Monochrome',
code: (
<div className="flex flex-wrap gap-2">
<Button gradientMonochrome="blue">Blue</Button>
<Button gradientMonochrome="green">Green</Button>
<Button gradientMonochrome="info">Info</Button>
<Button gradientMonochrome="success">Success</Button>
<Button gradientMonochrome="cyan">Cyan</Button>
<Button gradientMonochrome="teal">Teal</Button>
<Button gradientMonochrome="lime">Lime</Button>
<Button gradientMonochrome="red">Red</Button>
<Button gradientMonochrome="failure">Failure</Button>
<Button gradientMonochrome="pink">Pink</Button>
<Button gradientMonochrome="purple">Purple</Button>
</div>
Expand Down Expand Up @@ -150,10 +150,18 @@ const ButtonsPage: FC = () => {
title: 'Icon buttons',
code: (
<div className="flex flex-wrap items-center gap-2">
<Button icon={HiOutlineArrowRight} />
<Button icon={HiOutlineArrowRight} pill />
<Button icon={HiOutlineArrowRight} outline />
<Button icon={HiOutlineArrowRight} pill outline />
<Button>
<HiOutlineArrowRight className="h-6 w-6" />
</Button>
<Button pill>
<HiOutlineArrowRight className="h-6 w-6" />
</Button>
<Button outline>
<HiOutlineArrowRight className="h-6 w-6" />
</Button>
<Button outline pill>
<HiOutlineArrowRight className="h-6 w-6" />
</Button>
</div>
),
codeClassName: 'dark:!bg-gray-900',
Expand Down
205 changes: 73 additions & 132 deletions src/lib/components/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,156 +1,97 @@
import { ComponentProps, FC, ReactNode } from 'react';
import classNames from 'classnames';
import ButtonGroup from './ButtonGroup';
import ButtonGroup, { PositionInButtonGroup } from './ButtonGroup';
import {
FlowbiteColors,
FlowbiteGradientColors,
FlowbiteGradientDuoToneColors,
FlowbiteSizes,
} from '../Flowbite/FlowbiteTheme';
import { useTheme } from '../Flowbite/ThemeContext';
import { excludeClassName } from '../../helpers/exclude';

export type Color = 'blue' | 'alternative' | 'dark' | 'light' | 'green' | 'red' | 'yellow' | 'purple';
type GradientMonochrome = 'blue' | 'green' | 'cyan' | 'teal' | 'lime' | 'red' | 'pink' | 'purple';
type GradientDuoTone =
| 'purpleToBlue'
| 'cyanToBlue'
| 'greenToBlue'
| 'purpleToPink'
| 'pinkToOrange'
| 'tealToLime'
| 'redToYellow';
type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
type PositionInGroup = 'start' | 'middle' | 'end';

export type ButtonComponentProps = Omit<ComponentProps<'button'>, 'color'> & {
pill?: boolean;
outline?: boolean;
export interface ButtonComponentProps extends Omit<ComponentProps<'button'>, 'color'> {
color?: keyof ButtonColors;
gradientDuoTone?: keyof ButtonGradientDuoToneColors;
gradientMonochrome?: keyof ButtonGradientColors;
label?: ReactNode;
color?: Color;
size?: Size;
icon?: FC<ComponentProps<'svg'>>;
gradientMonochrome?: GradientMonochrome;
gradientDuoTone?: GradientDuoTone;
positionInGroup?: PositionInGroup;
};

const colorClasses: Record<Color, string> = {
blue: 'text-white bg-blue-700 border border-transparent hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 disabled:hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 dark:disabled:hover:bg-blue-600',
alternative:
'text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 hover:text-blue-700 disabled:hover:bg-white focus:ring-blue-700 focus:text-blue-700 dark:bg-transparent dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-2 dark:disabled:hover:bg-gray-800',
dark: 'text-white bg-gray-800 border border-transparent hover:bg-gray-900 focus:ring-4 focus:ring-gray-300 disabled:hover:bg-gray-800 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:border-gray-700 dark:disabled:hover:bg-gray-800',
light:
'text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-blue-300 disabled:hover:bg-white dark:bg-gray-600 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-700 dark:focus:ring-gray-700',
green:
'text-white bg-green-700 border border-transparent hover:bg-green-800 focus:ring-4 focus:ring-green-300 disabled:hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800 dark:disabled:hover:bg-green-600',
red: 'text-white bg-red-700 border border-transparent hover:bg-red-800 focus:ring-4 focus:ring-red-300 disabled:hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900 dark:disabled:hover:bg-red-600',
yellow:
'text-white bg-yellow-400 border border-transparent hover:bg-yellow-500 focus:ring-4 focus:ring-yellow-300 disabled:hover:bg-yellow-400 dark:focus:ring-yellow-900 dark:disabled:hover:bg-yellow-400',
purple:
'text-white bg-purple-700 border border-transparent hover:bg-purple-800 focus:ring-4 focus:ring-purple-300 disabled:hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-700 dark:focus:ring-purple-900 dark:disabled:hover:bg-purple-600',
};
outline?: boolean;
pill?: boolean;
positionInGroup?: keyof PositionInButtonGroup;
size?: keyof ButtonSizes;
}

const gradientMonochromeClasses: Record<GradientMonochrome, string> = {
blue: 'text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800 ',
green:
'text-white bg-gradient-to-r from-green-400 via-green-500 to-green-600 hover:bg-gradient-to-br focus:ring-4 focus:ring-green-300 dark:focus:ring-green-800',
cyan: 'text-white bg-gradient-to-r from-cyan-400 via-cyan-500 to-cyan-600 hover:bg-gradient-to-br focus:ring-4 focus:ring-cyan-300 dark:focus:ring-cyan-800',
teal: 'text-white bg-gradient-to-r from-teal-400 via-teal-500 to-teal-600 hover:bg-gradient-to-br focus:ring-4 focus:ring-teal-300 dark:focus:ring-teal-800',
lime: 'text-gray-900 bg-gradient-to-r from-lime-200 via-lime-400 to-lime-500 hover:bg-gradient-to-br focus:ring-4 focus:ring-lime-300 dark:focus:ring-lime-800',
red: 'text-white bg-gradient-to-r from-red-400 via-red-500 to-red-600 hover:bg-gradient-to-br focus:ring-4 focus:ring-red-300 dark:focus:ring-red-800',
pink: 'text-white bg-gradient-to-r from-pink-400 via-pink-500 to-pink-600 hover:bg-gradient-to-br focus:ring-4 focus:ring-pink-300 dark:focus:ring-pink-800',
purple:
'text-white bg-gradient-to-r from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:ring-purple-300 dark:focus:ring-purple-800',
};
export interface ButtonColors
extends Pick<FlowbiteColors, 'dark' | 'failure' | 'gray' | 'info' | 'light' | 'purple' | 'success' | 'warning'> {
[key: string]: string;
}

const gradientDuoToneClasses: Record<GradientDuoTone, string> = {
purpleToBlue:
'text-white bg-gradient-to-br from-purple-600 to-blue-500 hover:bg-gradient-to-bl focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800',
cyanToBlue:
'text-white bg-gradient-to-r from-cyan-500 to-blue-500 hover:bg-gradient-to-bl focus:ring-4 focus:ring-cyan-300 dark:focus:ring-cyan-800',
greenToBlue:
'text-white bg-gradient-to-br from-green-400 to-blue-600 hover:bg-gradient-to-bl focus:ring-4 focus:ring-green-200 dark:focus:ring-green-800',
purpleToPink:
'text-white bg-gradient-to-r from-purple-500 to-pink-500 hover:bg-gradient-to-l focus:ring-4 focus:ring-purple-200 dark:focus:ring-purple-800',
pinkToOrange:
'text-white bg-gradient-to-br from-pink-500 to-orange-400 hover:bg-gradient-to-bl focus:ring-4 focus:ring-pink-200 dark:focus:ring-pink-800',
tealToLime:
'text-gray-900 bg-gradient-to-r from-teal-200 to-lime-200 hover:bg-gradient-to-l hover:from-teal-200 hover:to-lime-200 hover:!text-gray-900 focus:ring-4 focus:ring-lime-200 dark:focus:ring-teal-700',
redToYellow:
'text-gray-900 bg-gradient-to-r from-red-200 via-red-300 to-yellow-200 hover:bg-gradient-to-bl focus:ring-4 focus:ring-red-100 dark:focus:ring-red-400',
};
export interface ButtonGradientColors extends FlowbiteGradientColors {
[key: string]: string;
}

const sizeClasses: Record<Size, string> = {
xs: 'text-xs px-2 py-1',
sm: 'text-sm px-3 py-1.5',
md: 'text-sm px-4 py-2',
lg: 'text-base px-5 py-2.5',
xl: 'text-base px-6 py-3',
};
export interface ButtonGradientDuoToneColors extends FlowbiteGradientDuoToneColors {
[key: string]: string;
}

const iconSizeClasses: Record<Size, string> = {
xs: '!px-1',
sm: '!px-1.5',
md: '!px-2',
lg: '!p-2.5',
xl: '!p-3',
};
export interface ButtonSizes extends FlowbiteSizes {
[key: string]: string;
}

const ButtonComponent: FC<ButtonComponentProps> = ({
children,
className,
color = 'info',
disabled = false,
gradientDuoTone,
gradientMonochrome,
label,
pill,
outline,
disabled = false,
pill,
positionInGroup = 'none',
size = 'md',
icon: Icon,
color = 'blue',
gradientMonochrome,
gradientDuoTone,
positionInGroup,
...props
}) => (
<button
data-testid="button-element"
disabled={disabled}
className={classNames(
'group flex h-min w-fit items-center justify-center p-0.5 text-center font-medium focus:z-10',
pill ? 'rounded-full' : 'rounded-lg',
!gradientMonochrome && !gradientDuoTone && colorClasses[color],
!gradientDuoTone && gradientMonochrome && gradientMonochromeClasses[gradientMonochrome],
gradientDuoTone && gradientDuoToneClasses[gradientDuoTone],
{
'border border-gray-900 dark:border-white': color === 'alternative' && outline,
'cursor-not-allowed opacity-50': disabled,
'focus:!ring-2': !!positionInGroup,
'rounded-r-none': positionInGroup === 'start',
'!rounded-none border-l-0 pl-0': positionInGroup === 'middle',
'rounded-l-none border-l-0 pl-0': positionInGroup === 'end',
},
className,
)}
type="button"
{...props}
>
<span
className={classNames('flex items-center', sizeClasses[size], outline && pill ? 'rounded-full' : 'rounded-md', {
'bg-white text-gray-900 transition-all duration-75 ease-in group-hover:bg-opacity-0 group-hover:text-inherit dark:bg-gray-900 dark:text-white':
outline,
'rounded-r-none': positionInGroup === 'start',
'!rounded-none': positionInGroup === 'middle',
'rounded-l-none': positionInGroup === 'end',
[iconSizeClasses[size]]: !!Icon,
})}
}): JSX.Element => {
const theirProps = excludeClassName(props);

const theme = useTheme().theme.button;

return (
<button
className={classNames(
color === 'gray' && outline && 'border border-gray-900 dark:border-white',
disabled && theme.disabled,
!gradientDuoTone && !gradientMonochrome && theme.color[color],
gradientDuoTone && !gradientMonochrome && theme.gradientDuoTone[gradientDuoTone],
!gradientDuoTone && gradientMonochrome && theme.gradient[gradientMonochrome],
theme.base,
theme.pill[pill ? 'on' : 'off'],
theme.position[positionInGroup],
)}
disabled={disabled}
type="button"
{...theirProps}
>
{Icon ? (
<Icon className="h-5 w-5" />
) : (
<span
className={classNames(
theme.inner.base,
theme.inner.position[positionInGroup],
theme.outline[outline ? 'on' : 'off'],
theme.outline.pill[outline && pill ? 'on' : 'off'],
theme.size[size],
)}
>
<>
{children}
{label !== undefined && (
<span className="ml-2 inline-flex h-4 w-4 items-center justify-center rounded-full bg-blue-200 text-xs font-semibold text-blue-800">
{typeof children !== 'undefined' && children}
{typeof label !== 'undefined' && (
<span className={theme.label} data-testid="flowbite-button-label">
{label}
</span>
)}
</>
)}
</span>
</button>
);
</span>
</button>
);
};

ButtonComponent.displayName = 'Button';
export const Button = Object.assign(ButtonComponent, {
Expand Down

0 comments on commit 367e73e

Please sign in to comment.