diff --git a/web/src/components/ui/buttons/Button.tsx b/web/src/components/ui/buttons/Button.tsx index 81b007607..0ce0db4f9 100644 --- a/web/src/components/ui/buttons/Button.tsx +++ b/web/src/components/ui/buttons/Button.tsx @@ -4,36 +4,42 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/classMerge'; import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader'; +export const buttonTypeClasses = { + default: + 'bg-white border hover:bg-item-hover disabled:bg-disabled disabled:text-gray-light active:bg-item-active data-[selected=true]:bg-item-select', + black: 'bg-black text-white hover:bg-foreground-hover disabled:bg-foreground-hover', + primary: + 'bg-primary text-white hover:bg-primary-light active:bg-primary-dark data-[selected=true]:bg-primary-dark', + ghost: + 'bg-transparent text-gray-dark shadow-none hover:bg-item-hover hover:text-foreground disabled:bg-transparent disabled:text-gray-light active:bg-item-active data-[selected=true]:bg-item-select', + link: 'bg-transparent text-gray-dark shadow-none hover:text-foreground disabled:bg-transparent disabled:text-gray-light' +}; + +const roundingVariants = { + default: 'rounded', + full: 'rounded-full', + large: 'rounded-lg', + small: 'rounded-sm', + none: 'rounded-none' +}; + +const sizeVariants = { + default: 'h-6', + tall: 'h-7', + small: 'h-[18px]' +}; + export const buttonVariants = cva( 'inline-flex items-center overflow-hidden text-base justify-center gap-[5px] shadow-btn rounded transition-all duration-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none cursor-pointer disabled:cursor-not-allowed data-[loading=true]:cursor-progress', { variants: { - buttonType: { - default: - 'bg-white border hover:bg-item-hover disabled:bg-disabled disabled:text-gray-light active:bg-item-active data-[selected=true]:bg-item-select', - black: 'bg-black text-white hover:bg-foreground-hover disabled:bg-foreground-hover', - primary: - 'bg-primary text-white hover:bg-primary-light active:bg-primary-dark data-[selected=true]:bg-primary-dark', - ghost: - 'bg-transparent text-gray-dark shadow-none hover:bg-item-hover hover:text-foreground disabled:bg-transparent disabled:text-gray-light active:bg-item-active data-[selected=true]:bg-item-select', - link: 'bg-transparent text-gray-dark shadow-none hover:text-foreground disabled:bg-transparent disabled:text-gray-light' - }, - size: { - default: 'h-6', - tall: 'h-7', - small: 'h-[18px]' - }, + buttonType: buttonTypeClasses, + size: sizeVariants, iconButton: { true: '', false: 'px-2.5' }, - rounding: { - default: 'rounded', - full: 'rounded-full', - large: 'rounded-lg', - small: 'rounded-sm', - none: 'rounded-none' - } + rounding: roundingVariants }, compoundVariants: [ { @@ -149,7 +155,7 @@ export const Button = React.forwardRef( Button.displayName = 'Button'; -const LoadingIcon: React.FC<{ +export const LoadingIcon: React.FC<{ buttonType: ButtonProps['buttonType']; size: ButtonProps['size']; }> = ({ buttonType = 'default', size = 'default' }) => { diff --git a/web/src/components/ui/buttons/ButtonDropdown.stories.tsx b/web/src/components/ui/buttons/ButtonDropdown.stories.tsx new file mode 100644 index 000000000..0fc5e98e0 --- /dev/null +++ b/web/src/components/ui/buttons/ButtonDropdown.stories.tsx @@ -0,0 +1,100 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ButtonDropdown } from './ButtonDropdown'; +import { PaintRoller } from '../icons/NucleoIconOutlined'; + +const meta: Meta = { + title: 'Base/ButtonDropdown', + component: ButtonDropdown, + parameters: { + layout: 'centered' + }, + argTypes: { + buttonType: { + control: 'select', + options: ['default', 'black', 'primary', 'ghost', 'link'] + }, + size: { + control: 'select', + options: ['default', 'tall', 'small'] + }, + disabled: { + control: 'boolean', + defaultValue: false + }, + loading: { + control: 'boolean', + defaultValue: false + }, + rounding: { + control: 'select', + options: ['default', 'full', 'large', 'small', 'none'] + } + }, + tags: ['autodocs'] +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + buttonText: 'Button Text', + buttonType: 'default' + } +}; + +export const Primary: Story = { + args: { + buttonText: 'Primary Button', + buttonType: 'primary' + } +}; + +export const Black: Story = { + args: { + buttonText: 'Black Button', + buttonType: 'black' + } +}; + +export const Ghost: Story = { + args: { + buttonText: 'Ghost Button', + buttonType: 'ghost' + } +}; + +export const Link: Story = { + args: { + buttonText: 'Link Button', + buttonType: 'link' + } +}; + +export const Tall: Story = { + args: { + buttonText: 'Tall Button', + size: 'tall' + } +}; + +export const Small: Story = { + args: { + buttonText: 'Small Button', + size: 'small' + } +}; + +export const Disabled: Story = { + args: { + buttonText: 'Disabled Button', + disabled: true + } +}; + +export const WithIcon: Story = { + args: { + buttonText: 'With Icon', + icon: + } +}; diff --git a/web/src/components/ui/buttons/ButtonDropdown.tsx b/web/src/components/ui/buttons/ButtonDropdown.tsx index c96adb559..38af8496e 100644 --- a/web/src/components/ui/buttons/ButtonDropdown.tsx +++ b/web/src/components/ui/buttons/ButtonDropdown.tsx @@ -1,5 +1,119 @@ import React from 'react'; -import { buttonVariants, buttonIconVariants } from './Button'; -import { ChevronDown, ChevronUp } from '../icons/NucleoIconOutlined'; +import { + buttonVariants, + buttonIconVariants, + ButtonProps, + buttonTypeClasses, + LoadingIcon +} from './Button'; +import { ChevronDown } from '../icons/NucleoIconOutlined'; import { cn } from '@/lib/classMerge'; -import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; +import { cva } from 'class-variance-authority'; +import { type DropdownProps, Dropdown } from '../dropdown/Dropdown'; + +interface ButtonDropdownProps { + icon?: React.ReactNode; + buttonText?: string; + disabled?: boolean; + rounding?: ButtonProps['rounding']; + size?: ButtonProps['size']; + buttonType?: ButtonProps['buttonType']; + loading?: boolean; + className?: string; + iconClassName?: string; + dropdownProps: Omit; +} + +const dropdownButtonVariants = cva('flex items-center pr-0', { + variants: { + rounding: { + default: 'rounded', + full: 'rounded-full', + large: 'rounded-lg', + small: 'rounded-sm', + none: 'rounded-none' + } + } +}); + +const primaryButtonVariants = cva('', { + variants: { + buttonType: buttonTypeClasses + } +}); + +const splitButtonVariants = cva('flex w-full items-center justify-center h-full px-[5px]', { + variants: { + buttonType: buttonTypeClasses + } +}); + +export const ButtonDropdown = React.forwardRef( + ({ dropdownProps, ...buttonProps }, ref) => { + return ( + + + + ); + } +); + +export const ButtonSplit = React.forwardRef< + HTMLDivElement, + Omit +>( + ( + { + className, + buttonType = 'default', + size = 'default', + icon, + buttonText, + disabled = false, + rounding = 'default', + loading = false, + iconClassName + }, + ref + ) => { + return ( +
+
+ {loading ? ( + + ) : ( + icon && ( + + {icon} + + ) + )} + {buttonText && {buttonText}} +
+
+
+
+ +
+
+
+ ); + } +); + +ButtonSplit.displayName = 'ButtonSplit';