mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 18:28:27 +00:00
feat(ui): implemented button component
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Button } from './Button';
|
||||
@@ -6,7 +8,16 @@ const meta: Meta<typeof Button> = {
|
||||
title: 'Components/Button',
|
||||
component: Button,
|
||||
tags: ['v2'],
|
||||
argTypes: {}
|
||||
argTypes: {
|
||||
isRounded: {
|
||||
defaultValue: true,
|
||||
type: 'boolean'
|
||||
},
|
||||
isFullWidth: {
|
||||
defaultValue: false,
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -18,3 +29,61 @@ export const Primary: Story = {
|
||||
children: 'Hello Infisical'
|
||||
}
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
colorSchema: 'secondary',
|
||||
variant: 'outline'
|
||||
}
|
||||
};
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
colorSchema: 'danger',
|
||||
variant: 'solid'
|
||||
}
|
||||
};
|
||||
|
||||
export const Plain: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
variant: 'plain'
|
||||
}
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
disabled: true
|
||||
}
|
||||
};
|
||||
|
||||
export const FullWidth: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
isFullWidth: true
|
||||
}
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
isLoading: true
|
||||
}
|
||||
};
|
||||
|
||||
export const LeftIcon: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
leftIcon: <FontAwesomeIcon icon={faPlus} className="pr-0.5" />
|
||||
}
|
||||
};
|
||||
|
||||
export const RightIcon: Story = {
|
||||
args: {
|
||||
children: 'Hello Infisical',
|
||||
rightIcon: <FontAwesomeIcon icon={faPlus} className="pr-0.5" />
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,32 +1,166 @@
|
||||
import { ButtonHTMLAttributes, forwardRef, ReactNode } from 'react';
|
||||
import { cva, VariantProps } from 'cva';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
isDisabled?: boolean;
|
||||
leftIcon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
// loading state
|
||||
isLoading?: boolean;
|
||||
// various button sizes
|
||||
size: 'sm' | 'md' | 'lg';
|
||||
};
|
||||
|
||||
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & Props;
|
||||
const buttonVariants = cva(
|
||||
[
|
||||
'button',
|
||||
'transition-all',
|
||||
'font-medium',
|
||||
'cursor-pointer',
|
||||
'inline-flex items-center justify-center',
|
||||
'relative'
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
colorSchema: {
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'],
|
||||
secondary: ['bg-mineshaft-200', 'text-white', 'border-mineshaft-200'],
|
||||
danger: ['bg-red', 'text-white', 'border-red']
|
||||
},
|
||||
variant: {
|
||||
solid: '',
|
||||
outline: ['bg-transparent', 'border', 'border-solid'],
|
||||
plain: ''
|
||||
},
|
||||
isDisabled: {
|
||||
true: 'bg-opacity-70',
|
||||
false: ''
|
||||
},
|
||||
isFullWidth: {
|
||||
true: 'w-full',
|
||||
false: ''
|
||||
},
|
||||
isRounded: {
|
||||
true: 'rounded-md',
|
||||
false: ''
|
||||
},
|
||||
size: {
|
||||
xs: ['text-xs', 'py-1', 'px-2'],
|
||||
sm: ['text-sm', 'py-2', 'px-4'],
|
||||
md: ['text-md', 'py-2', 'px-6'],
|
||||
lg: ['text-lg', 'py-2', 'px-8']
|
||||
}
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
colorSchema: 'primary',
|
||||
variant: 'outline',
|
||||
className: 'text-primary hover:bg-primary hover:text-black'
|
||||
},
|
||||
{
|
||||
colorSchema: 'secondary',
|
||||
variant: 'outline',
|
||||
className: 'text-mineshaft-200 hover:bg-mineshaft-400 hover:text-white'
|
||||
},
|
||||
{
|
||||
colorSchema: 'danger',
|
||||
variant: 'outline',
|
||||
className: 'text-red hover:bg-red hover:text-black'
|
||||
},
|
||||
{
|
||||
colorSchema: 'primary',
|
||||
variant: 'plain',
|
||||
className: 'text-primary'
|
||||
},
|
||||
{
|
||||
colorSchema: 'secondary',
|
||||
variant: 'plain',
|
||||
className: 'text-mineshaft-400'
|
||||
},
|
||||
{
|
||||
colorSchema: 'danger',
|
||||
variant: 'plain',
|
||||
className: 'text-red'
|
||||
},
|
||||
{
|
||||
colorSchema: ['danger', 'primary', 'secondary'],
|
||||
variant: ['plain'],
|
||||
className: 'bg-transparent py-1 px-1'
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
|
||||
VariantProps<typeof buttonVariants> &
|
||||
Props;
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ children, isDisabled = false, className, ...props }, ref): JSX.Element => {
|
||||
(
|
||||
{
|
||||
children,
|
||||
isDisabled = false,
|
||||
className = '',
|
||||
size = 'md',
|
||||
variant = 'solid',
|
||||
isFullWidth,
|
||||
isRounded = true,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
isLoading,
|
||||
colorSchema = 'primary',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
): JSX.Element => {
|
||||
const loadingToggleClass = isLoading ? 'opacity-0' : 'opacity-100';
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
aria-disabled={isDisabled}
|
||||
type="button"
|
||||
className={twMerge(
|
||||
'bg-primary hover:opacity-80 transition-all text-sm px-4 py-2 font-bold rounded',
|
||||
className
|
||||
buttonVariants({
|
||||
className,
|
||||
colorSchema,
|
||||
size,
|
||||
variant,
|
||||
isRounded,
|
||||
isDisabled,
|
||||
isFullWidth
|
||||
})
|
||||
)}
|
||||
disabled={isDisabled}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{isLoading && (
|
||||
<img
|
||||
src="/images/loading/loadingblack.gif"
|
||||
width={36}
|
||||
alt="loading animation"
|
||||
className="rounded-xl absolute"
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={twMerge(
|
||||
'transition-all shrink-0 cursor-pointer',
|
||||
loadingToggleClass,
|
||||
size === 'xs' ? 'mr-1' : 'mr-2'
|
||||
)}
|
||||
>
|
||||
{leftIcon}
|
||||
</span>
|
||||
<span className={twMerge('transition-all', loadingToggleClass)}>{children}</span>
|
||||
<span
|
||||
className={twMerge(
|
||||
'transition-all shrink-0 cursor-pointer',
|
||||
loadingToggleClass,
|
||||
size === 'xs' ? 'ml-1' : 'ml-2'
|
||||
)}
|
||||
>
|
||||
{rightIcon}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export type { ButtonProps } from './Button';
|
||||
export { Button } from './Button';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './Button';
|
||||
Reference in New Issue
Block a user