mirror of https://github.com/buster-so/buster.git
update
This commit is contained in:
parent
49337658ea
commit
e41180ff20
|
@ -4,36 +4,42 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
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(
|
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',
|
'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: {
|
variants: {
|
||||||
buttonType: {
|
buttonType: buttonTypeClasses,
|
||||||
default:
|
size: sizeVariants,
|
||||||
'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]'
|
|
||||||
},
|
|
||||||
iconButton: {
|
iconButton: {
|
||||||
true: '',
|
true: '',
|
||||||
false: 'px-2.5'
|
false: 'px-2.5'
|
||||||
},
|
},
|
||||||
rounding: {
|
rounding: roundingVariants
|
||||||
default: 'rounded',
|
|
||||||
full: 'rounded-full',
|
|
||||||
large: 'rounded-lg',
|
|
||||||
small: 'rounded-sm',
|
|
||||||
none: 'rounded-none'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
compoundVariants: [
|
compoundVariants: [
|
||||||
{
|
{
|
||||||
|
@ -149,7 +155,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
|
||||||
Button.displayName = 'Button';
|
Button.displayName = 'Button';
|
||||||
|
|
||||||
const LoadingIcon: React.FC<{
|
export const LoadingIcon: React.FC<{
|
||||||
buttonType: ButtonProps['buttonType'];
|
buttonType: ButtonProps['buttonType'];
|
||||||
size: ButtonProps['size'];
|
size: ButtonProps['size'];
|
||||||
}> = ({ buttonType = 'default', size = 'default' }) => {
|
}> = ({ buttonType = 'default', size = 'default' }) => {
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { ButtonDropdown } from './ButtonDropdown';
|
||||||
|
import { PaintRoller } from '../icons/NucleoIconOutlined';
|
||||||
|
|
||||||
|
const meta: Meta<typeof ButtonDropdown> = {
|
||||||
|
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<typeof ButtonDropdown>;
|
||||||
|
|
||||||
|
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: <PaintRoller />
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,5 +1,119 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { buttonVariants, buttonIconVariants } from './Button';
|
import {
|
||||||
import { ChevronDown, ChevronUp } from '../icons/NucleoIconOutlined';
|
buttonVariants,
|
||||||
|
buttonIconVariants,
|
||||||
|
ButtonProps,
|
||||||
|
buttonTypeClasses,
|
||||||
|
LoadingIcon
|
||||||
|
} from './Button';
|
||||||
|
import { ChevronDown } from '../icons/NucleoIconOutlined';
|
||||||
import { cn } from '@/lib/classMerge';
|
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<DropdownProps, 'children'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<HTMLDivElement, ButtonDropdownProps>(
|
||||||
|
({ dropdownProps, ...buttonProps }, ref) => {
|
||||||
|
return (
|
||||||
|
<Dropdown {...dropdownProps}>
|
||||||
|
<ButtonSplit ref={ref} {...buttonProps} />
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ButtonSplit = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
Omit<ButtonDropdownProps, 'dropdownProps'>
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
buttonType = 'default',
|
||||||
|
size = 'default',
|
||||||
|
icon,
|
||||||
|
buttonText,
|
||||||
|
disabled = false,
|
||||||
|
rounding = 'default',
|
||||||
|
loading = false,
|
||||||
|
iconClassName
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ buttonType, size, rounding: 'none' }),
|
||||||
|
dropdownButtonVariants({ rounding }),
|
||||||
|
'gap-0 !bg-transparent p-0',
|
||||||
|
disabled && 'cursor-not-allowed opacity-60',
|
||||||
|
className
|
||||||
|
)}>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
primaryButtonVariants({ buttonType }),
|
||||||
|
'flex h-full items-center gap-0.5 border-none pr-2 pl-2.5'
|
||||||
|
)}>
|
||||||
|
{loading ? (
|
||||||
|
<LoadingIcon buttonType={buttonType} size={size} />
|
||||||
|
) : (
|
||||||
|
icon && (
|
||||||
|
<span
|
||||||
|
className={cn(buttonIconVariants({ buttonType, size }), 'text-sm', iconClassName)}>
|
||||||
|
{icon}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{buttonText && <span className="">{buttonText}</span>}
|
||||||
|
</div>
|
||||||
|
<div className="bg-border mr-0 h-full w-[0.5px]" />
|
||||||
|
<div className="flex h-full items-center justify-center text-sm">
|
||||||
|
<div
|
||||||
|
className={cn(splitButtonVariants({ buttonType }), 'border-none')}
|
||||||
|
aria-label="Open dropdown menu">
|
||||||
|
<ChevronDown />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ButtonSplit.displayName = 'ButtonSplit';
|
||||||
|
|
Loading…
Reference in New Issue