Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 75 additions & 12 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ComponentProps, FC } from 'react';
import classNames from 'classnames';
import { Spinner } from './Spinner';

type Color = 'blue' | 'alternative' | 'dark' | 'light' | 'green' | 'red' | 'yellow' | 'purple';
type GradientMonochrome = 'blue' | 'green' | 'cyan' | 'teal' | 'lime' | 'red' | 'pink' | 'purple';
Expand All @@ -11,28 +12,36 @@ type GradientDuoTone =
| 'pinkToOrange'
| 'tealToLime'
| 'redToYellow';

type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
type IconPosition = 'start' | 'end';
export type ButtonProps = ComponentProps<'button'> & {
pill?: boolean;
outline?: boolean;
loader?: boolean;
iconButton?: boolean;
label?: string;
color?: Color;
size?: Size;
icon?: FC<ComponentProps<'svg'>>;
iconPosition?: IconPosition;
gradientMonochrome?: GradientMonochrome;
gradientDuoTone?: GradientDuoTone;
};

const colorClasses: Record<Color, string> = {
blue: 'text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800',
blue: 'text-white bg-blue-700 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 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-2',
dark: 'text-white bg-gray-800 hover:bg-gray-900 focus:ring-4 focus:ring-gray-300 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-800 dark:border-gray-700',
'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-gray-800 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 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 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-800',
'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-800 dark:disabled:hover:bg-gray-600',
green:
'text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 dark:bg-green-600 dark:hover:bg-green-700 dark:focus:ring-green-800',
red: 'text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900',
yellow: 'text-white bg-yellow-400 hover:bg-yellow-500 focus:ring-4 focus:ring-yellow-300 dark:focus:ring-yellow-900',
'text-white bg-green-700 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 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 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 hover:bg-purple-800 focus:ring-4 focus:ring-purple-300 dark:bg-purple-600 dark:hover:bg-purple-700 dark:focus:ring-purple-900',
'text-white bg-purple-700 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',
};

const gradientMonochromeClasses: Record<GradientMonochrome, string> = {
Expand Down Expand Up @@ -65,35 +74,89 @@ const gradientDuoToneClasses: Record<GradientDuoTone, string> = {
'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',
};

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',
};

const iconSizeClasses: Record<Size, string> = {
xs: '!px-1',
sm: '!px-1.5',
md: '!px-2',
lg: '!p-2.5',
xl: '!p-3',
};

const previousSize: Record<Size, Size> = {
xs: 'xs',
sm: 'xs',
md: 'sm',
lg: 'md',
xl: 'lg',
};

export const Button: FC<ButtonProps> = ({
children,
pill,
outline,
disabled = false,
loader = false,
iconButton = false,
label,
size = 'md',
icon: Icon,
iconPosition = 'start',
color = 'blue',
gradientMonochrome,
gradientDuoTone,
...props
}) => {
return (
<button
disabled={disabled}
className={classNames(
'flex items-center justify-center p-0.5 text-sm text-center font-medium group',
'flex h-min items-center justify-center p-0.5 text-center font-medium group',
pill ? 'rounded-full' : 'rounded-lg',
!gradientMonochrome && !gradientDuoTone && colorClasses[color],
!gradientDuoTone && gradientMonochrome && gradientMonochromeClasses[gradientMonochrome],
gradientDuoTone && gradientDuoToneClasses[gradientDuoTone],
{
'opacity-50 cursor-not-allowed': disabled,
},
)}
type="button"
{...props}
>
<span
className={classNames('px-5 py-2.5 rounded-md', {
className={classNames('flex items-center', sizeClasses[size], {
'text-gray-900 transition-all ease-in duration-75 bg-white dark:bg-gray-900 group-hover:bg-opacity-0 group-hover:text-inherit dark:text-white':
outline,
'rounded-md': outline && !pill,
'rounded-full': outline && pill,
[iconSizeClasses[size]]: iconButton,
'gap-2': loader,
})}
>
{children}
{iconButton ? (
Icon && <Icon className="w-5 h-5" />
) : (
<>
{loader && <Spinner size={previousSize[size]} />}
{!loader && iconPosition === 'start' && Icon && <Icon className="mr-2 w-5 h-5" />}
{children}
{iconPosition === 'end' && Icon && <Icon className="ml-2 w-5 h-5" />}
</>
)}
</span>

{label && (
<span className="inline-flex justify-center items-center mr-4 -ml-2 w-4 h-4 text-xs font-semibold text-blue-800 bg-blue-200 rounded-full">
{label}
</span>
)}
</button>
);
};
11 changes: 6 additions & 5 deletions src/components/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC } from 'react';
import classNames from 'classnames';

type Color = 'blue' | 'gray' | 'green' | 'red' | 'yellow' | 'pink' | 'purple';
type Size = 'xs' | 'sm' | 'md' | 'lg';
type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';

export type SpinnerProps = {
color?: Color;
Expand All @@ -20,10 +20,11 @@ const colorClasses: Record<Color, string> = {
};

const sizeClasses: Record<Size, string> = {
xs: 'w-4 h-4',
sm: 'w-6 h-6',
md: 'w-8 h-8',
lg: 'w-10 h-10',
xs: 'w-3 h-3',
sm: 'w-4 h-4',
md: 'w-6 h-6',
lg: 'w-8 h-8',
xl: 'w-10 h-10',
};

export const Spinner: FC<SpinnerProps> = ({ color = 'blue', size = 'md' }) => (
Expand Down
107 changes: 105 additions & 2 deletions src/pages/ButtonsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism';
import tsx from 'react-syntax-highlighter/dist/esm/languages/prism/tsx';
import { Button, Card } from '../components';
import { HiOutlineArrowRight, HiShoppingCart } from 'react-icons/hi';

SyntaxHighlighter.registerLanguage('tsx', tsx);

Expand Down Expand Up @@ -133,7 +134,7 @@ const ButtonsPage: FC = () => {
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Outline</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap gap-2">
<div className="flex flex-wrap items-center gap-2">
<Button outline gradientDuoTone="purpleToBlue">
Purple to Blue
</Button>
Expand Down Expand Up @@ -165,7 +166,109 @@ const ButtonsPage: FC = () => {
<Button outline gradientDuoTone="pinkToOrange">Pink to Orange</Button>
<Button outline gradientDuoTone="tealToLime">Teal to Lime</Button>
<Button outline gradientDuoTone="redToYellow">Red to Yellow</Button>

`.trim()}
</SyntaxHighlighter>
</Card>
</div>
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Button sizes</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap items-center gap-2">
<Button size="xs">Extra small</Button>
<Button size="sm">Small</Button>
<Button size="md">Base</Button>
<Button size="lg">Large</Button>
<Button size="xl">Extra large</Button>
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Button size="extraSmall">Extra small</Button>
<Button size="small">Small</Button>
<Button size="medium">Base</Button>
<Button size="large">Large</Button>
<Button size="extraLarge">Extra large</Button>
`.trim()}
</SyntaxHighlighter>
</Card>
</div>
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Buttons with icon</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap items-center gap-2">
<Button icon={HiShoppingCart} iconPosition="start">
Buy now
</Button>
<Button icon={HiOutlineArrowRight} iconPosition="end">
Choose plan
</Button>
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Button icon={HiShoppingCart} iconPosition="left">Buy now</Button>
<Button icon={HiOutlineArrowRight} iconPosition="right">Choose plan</Button>
`.trim()}
</SyntaxHighlighter>
</Card>
</div>
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Button with label</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap items-center gap-2">
<Button label="2">Messages</Button>
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Button label="2">Messages</Button>
`.trim()}
</SyntaxHighlighter>
</Card>
</div>
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Icon buttons</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap items-center gap-2">
<Button iconButton icon={HiOutlineArrowRight} />
<Button iconButton icon={HiOutlineArrowRight} pill />
<Button iconButton icon={HiOutlineArrowRight} outline />
<Button iconButton icon={HiOutlineArrowRight} pill outline />
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Button iconButton icon={HiOutlineArrowRight} />
<Button iconButton icon={HiOutlineArrowRight} pill />
<Button iconButton icon={HiOutlineArrowRight} outline />
<Button iconButton icon={HiOutlineArrowRight} pill outline />
`.trim()}
</SyntaxHighlighter>
</Card>
</div>
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Loader</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap items-center gap-2">
<Button loader>Loading ...</Button>
<Button loader outline>
Loading ...
</Button>
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Button loader size="sm">Loading ...</Button>
<Button loader size="sm" outline>Loading ...</Button>
`.trim()}
</SyntaxHighlighter>
</Card>
</div>
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Disabled</span>
<Card className="dark:!bg-gray-900">
<div className="flex flex-wrap items-center gap-2">
<Button disabled>Disabled button</Button>
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Button loader>Loading ...</Button>
<Button loader outline>Loading ...</Button>
`.trim()}
</SyntaxHighlighter>
</Card>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/SpinnersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ const SpinnersPage: FC = () => {
<Spinner size="sm" />
<Spinner size="md" />
<Spinner size="lg" />
<Spinner size="xl" />
</div>
<SyntaxHighlighter language="tsx" style={dracula}>
{`
<Spinner size="xs" />
<Spinner size="sm" />
<Spinner size="md" />
<Spinner size="lg" />
<Spinner size="xl" />
`.trim()}
</SyntaxHighlighter>
</Card>
Expand Down