mirror of https://github.com/buster-so/buster.git
update dropdown
This commit is contained in:
parent
780f9a489b
commit
bdfc75c859
File diff suppressed because it is too large
Load Diff
|
@ -27,12 +27,13 @@
|
|||
"@million/lint": "^1.0.14",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-switch": "^1.1.3",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@supabase/auth-helpers-nextjs": "^0.10.0",
|
||||
"@supabase/auth-helpers-react": "^0.5.0",
|
||||
"@supabase/ssr": "^0.5.2",
|
||||
"@supabase/supabase-js": "^2.48.1",
|
||||
"@supabase/supabase-js": "^2.49.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tanstack/react-query": "^5.66.9",
|
||||
"@tanstack/react-query-devtools": "^5.66.9",
|
||||
|
@ -70,8 +71,7 @@
|
|||
"framer-motion": "^12.4.7",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jspdf": "^2.5.2",
|
||||
"jspdf-autotable": "^3.8.4",
|
||||
"jspdf": "^3.0.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"material-symbols": "^0.28.2",
|
||||
"monaco-sql-languages": "^0.13.1",
|
||||
|
@ -85,7 +85,7 @@
|
|||
"patternomaly": "^1.3.2",
|
||||
"pluralize": "^8.0.0",
|
||||
"posthog-js": "*",
|
||||
"prettier": "^3.5.1",
|
||||
"prettier": "^3.5.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"react": "^18",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
|
@ -96,27 +96,27 @@
|
|||
"react-markdown": "^9.0.3",
|
||||
"react-material-symbols": "^4.4.0",
|
||||
"react-monaco-editor": "^0.58.0",
|
||||
"react-scan": "^0.1.3",
|
||||
"react-scan": "^0.2.3",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.25",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sonner": "^2.0.1",
|
||||
"split-pane-react": "^0.1.3",
|
||||
"tailwind-merge": "^3.0.1",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"utility-types": "^3.11.0",
|
||||
"uuid": "^11.1.0",
|
||||
"virtua": "^0.40.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^3.2.4",
|
||||
"@storybook/addon-essentials": "^8.5.8",
|
||||
"@storybook/addon-interactions": "^8.5.8",
|
||||
"@storybook/addon-onboarding": "^8.5.8",
|
||||
"@storybook/blocks": "^8.5.8",
|
||||
"@storybook/nextjs": "^8.5.8",
|
||||
"@storybook/react": "^8.5.8",
|
||||
"@storybook/test": "^8.5.8",
|
||||
"@storybook/addon-essentials": "^8.6.0",
|
||||
"@storybook/addon-interactions": "^8.6.0",
|
||||
"@storybook/addon-onboarding": "^8.6.0",
|
||||
"@storybook/blocks": "^8.6.0",
|
||||
"@storybook/nextjs": "^8.6.0",
|
||||
"@storybook/react": "^8.6.0",
|
||||
"@storybook/test": "^8.6.0",
|
||||
"@tailwindcss/postcss": "^4.0.9",
|
||||
"@tanstack/eslint-plugin-query": "^5.66.1",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
|
@ -147,11 +147,11 @@
|
|||
"jest-environment-jsdom": "^29.7.0",
|
||||
"monaco-editor-webpack-plugin": "^7.1.0",
|
||||
"postcss": "8.5.3",
|
||||
"sass": "^1.83.4",
|
||||
"storybook": "^8.5.8",
|
||||
"tailwindcss": "^4.0.8",
|
||||
"sass": "^1.85.1",
|
||||
"storybook": "^8.6.0",
|
||||
"tailwindcss": "^4.0.9",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"overrides": {}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import { Button } from '../buttons/Button';
|
||||
import { PaintRoller } from '../icons';
|
||||
import { PaintRoller, Star, Storage } from '../icons';
|
||||
|
||||
const meta: Meta<typeof Dropdown> = {
|
||||
title: 'Base/Dropdown',
|
||||
component: Dropdown,
|
||||
|
@ -210,7 +211,7 @@ export const WithLoadingItems: Story = {
|
|||
// Example with selection
|
||||
export const WithSelection: Story = {
|
||||
args: {
|
||||
selectType: 'single',
|
||||
selectType: true,
|
||||
items: [
|
||||
{
|
||||
id: '1',
|
||||
|
@ -233,3 +234,35 @@ export const WithSelection: Story = {
|
|||
children: <Button>Selection Menu</Button>
|
||||
}
|
||||
};
|
||||
|
||||
// Example with secondary labels
|
||||
export const WithSecondaryLabel: Story = {
|
||||
args: {
|
||||
menuLabel: 'Items with Secondary Labels',
|
||||
items: [
|
||||
{
|
||||
id: '1',
|
||||
label: 'Profile Settings',
|
||||
secondaryLabel: 'User preferences',
|
||||
onClick: () => console.log('Profile clicked'),
|
||||
icon: <PaintRoller />
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
label: 'Storage',
|
||||
secondaryLabel: '45GB used',
|
||||
onClick: () => console.log('Storage clicked'),
|
||||
icon: <Storage />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
id: '3',
|
||||
label: 'Subscription',
|
||||
secondaryLabel: 'Pro Plan',
|
||||
onClick: () => console.log('Subscription clicked'),
|
||||
icon: <Star />
|
||||
}
|
||||
],
|
||||
children: <Button>Menu with Secondary Labels</Button>
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,8 +13,7 @@ import {
|
|||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem
|
||||
DropdownMenuCheckboxItem
|
||||
} from './DropdownBase';
|
||||
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
@ -22,6 +21,7 @@ import { cn } from '@/lib/classMerge';
|
|||
|
||||
export interface DropdownItem {
|
||||
label: React.ReactNode;
|
||||
secondaryLabel?: string;
|
||||
id: string;
|
||||
showIndex?: boolean;
|
||||
shortcut?: string;
|
||||
|
@ -41,7 +41,7 @@ export type DropdownItems = (DropdownItem | DropdownDivider | React.ReactNode)[]
|
|||
|
||||
export interface DropdownProps extends DropdownMenuProps {
|
||||
items?: DropdownItems;
|
||||
selectType?: 'default' | 'single' | 'multiple';
|
||||
selectType?: boolean;
|
||||
menuLabel?: string | React.ReactNode;
|
||||
minWidth?: number;
|
||||
maxWidth?: number;
|
||||
|
@ -61,7 +61,7 @@ const dropdownItemKey = (item: DropdownItems[number], index: number) => {
|
|||
export const Dropdown: React.FC<DropdownProps> = React.memo(
|
||||
({
|
||||
items = [],
|
||||
selectType = 'default',
|
||||
selectType = false,
|
||||
menuLabel,
|
||||
minWidth = 240,
|
||||
maxWidth,
|
||||
|
@ -110,7 +110,7 @@ const DropdownItemSelector: React.FC<{
|
|||
index: number;
|
||||
onSelect: DropdownProps['onSelect'];
|
||||
closeOnSelect: boolean;
|
||||
selectType: NonNullable<DropdownProps['selectType']>;
|
||||
selectType: DropdownProps['selectType'];
|
||||
}> = React.memo(({ item, index, onSelect, closeOnSelect, selectType }) => {
|
||||
if ((item as DropdownDivider).type === 'divider') {
|
||||
return <DropdownMenuSeparator />;
|
||||
|
@ -133,7 +133,14 @@ const DropdownItemSelector: React.FC<{
|
|||
|
||||
DropdownItemSelector.displayName = 'DropdownItemSelector';
|
||||
|
||||
const DropdownItem = ({
|
||||
const DropdownItem: React.FC<
|
||||
DropdownItem & {
|
||||
onSelect: DropdownProps['onSelect'];
|
||||
closeOnSelect: boolean;
|
||||
index: number;
|
||||
selectType: DropdownProps['selectType'];
|
||||
}
|
||||
> = ({
|
||||
label,
|
||||
id,
|
||||
showIndex,
|
||||
|
@ -147,12 +154,8 @@ const DropdownItem = ({
|
|||
items,
|
||||
closeOnSelect,
|
||||
onSelect,
|
||||
selectType
|
||||
}: DropdownItem & {
|
||||
onSelect: DropdownProps['onSelect'];
|
||||
closeOnSelect: boolean;
|
||||
index: number;
|
||||
selectType: NonNullable<DropdownProps['selectType']>;
|
||||
selectType = false,
|
||||
secondaryLabel
|
||||
}) => {
|
||||
const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (onClick) onClick();
|
||||
|
@ -163,24 +166,51 @@ const DropdownItem = ({
|
|||
|
||||
const Wrapper = useMemo(() => {
|
||||
if (isSubItem) return DropdownSubMenuWrapper;
|
||||
if (selectType === 'multiple' || selectType === 'single') return DropdownMenuCheckboxItem;
|
||||
if (selectType) return DropdownMenuCheckboxItem;
|
||||
return DropdownMenuItem;
|
||||
}, [isSubItem, selectType]);
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
items={items}
|
||||
disabled={disabled}
|
||||
checked={selected}
|
||||
onClick={onClickItem}
|
||||
closeOnSelect={closeOnSelect}
|
||||
selectType={selectType}>
|
||||
const content = (
|
||||
<>
|
||||
{showIndex && <span className="text-gray-light">{index}</span>}
|
||||
{icon && !loading && <span className="text-icon-color">{icon}</span>}
|
||||
{loading && <CircleSpinnerLoader size={9} />}
|
||||
<div className="flex flex-col gap-y-1">
|
||||
{label}
|
||||
{secondaryLabel && <span className="text-gray-light text-xxs">{secondaryLabel}</span>}
|
||||
</div>
|
||||
{shortcut && <DropdownMenuShortcut>{shortcut}</DropdownMenuShortcut>}
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
if (isSubItem) {
|
||||
return (
|
||||
<DropdownSubMenuWrapper
|
||||
items={items}
|
||||
closeOnSelect={closeOnSelect}
|
||||
onSelect={onSelect}
|
||||
selectType={selectType}>
|
||||
{content}
|
||||
</DropdownSubMenuWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
if (selectType) {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={selected}
|
||||
disabled={disabled}
|
||||
onClick={onClickItem}
|
||||
closeOnSelect={closeOnSelect}>
|
||||
{content}
|
||||
</DropdownMenuCheckboxItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuItem disabled={disabled} onClick={onClickItem} closeOnSelect={closeOnSelect}>
|
||||
{content}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -196,7 +226,7 @@ const DropdownSubMenuWrapper = React.memo(
|
|||
children: React.ReactNode;
|
||||
closeOnSelect: boolean;
|
||||
onSelect?: DropdownProps['onSelect'];
|
||||
selectType: NonNullable<DropdownProps['selectType']>;
|
||||
selectType: DropdownProps['selectType'];
|
||||
}) => {
|
||||
return (
|
||||
<DropdownMenuSub>
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||
//import { Check, ChevronRight, Circle } from 'lucide-react';
|
||||
import { Check, ChevronRight } from '../icons/NucleoIconOutlined';
|
||||
import { ShapeCircle } from '../icons/NucleoIconOutlined';
|
||||
|
||||
import { cn } from '@/lib/classMerge';
|
||||
|
||||
|
@ -18,8 +16,6 @@ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
|
@ -106,7 +102,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem> & {
|
||||
closeOnSelect?: boolean;
|
||||
selectType?: string;
|
||||
selectType?: boolean;
|
||||
}
|
||||
>(({ className, children, onClick, checked, closeOnSelect = true, selectType, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
|
@ -136,29 +132,6 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||
));
|
||||
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'focus:bg-item-hover focus:text-foreground relative flex cursor-pointer items-center rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<div className="flex h-4 w-4 items-center justify-center">
|
||||
<ShapeCircle />
|
||||
</div>
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
));
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
|
@ -167,7 +140,7 @@ const DropdownMenuLabel = React.forwardRef<
|
|||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
||||
className={cn('px-2 py-1.5 text-sm', inset && 'pl-8', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
@ -179,7 +152,7 @@ const DropdownMenuSeparator = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('bg-muted -mx-1 my-1 h-px', className)}
|
||||
className={cn('bg-border -mx-1 my-1 h-[0.5px]', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
@ -198,7 +171,6 @@ export {
|
|||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
|
@ -206,6 +178,5 @@ export {
|
|||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup
|
||||
DropdownMenuSubTrigger
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ const AppToaster = ({ ...props }: ToasterProps) => {
|
|||
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
||||
description: 'group-[.toast]:text-muted-foreground',
|
||||
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
||||
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground'
|
||||
cancelButton: 'group-[.toast]:bg-border group-[.toast]:text-foreground'
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
|
|
Loading…
Reference in New Issue