mirror of https://github.com/buster-so/buster.git
dropdown selection done
This commit is contained in:
parent
bbbbcaa1b8
commit
a4fd95b7fd
|
@ -86,7 +86,7 @@ export const DisabledChecked: Story = {
|
||||||
// Example with event handler
|
// Example with event handler
|
||||||
export const WithOnChange: Story = {
|
export const WithOnChange: Story = {
|
||||||
args: {
|
args: {
|
||||||
onCheckedChange: (checked: boolean) => console.log('Checked:', checked)
|
onCheckedChange: (checked: boolean) => alert(`Checked: ${checked}`)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,20 +35,20 @@ export const Basic: Story = {
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Profile',
|
label: 'Profile',
|
||||||
onClick: () => console.log('Profile clicked'),
|
onClick: () => alert('Profile clicked'),
|
||||||
loading: false,
|
loading: false,
|
||||||
icon: <PaintRoller />
|
icon: <PaintRoller />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
onClick: () => console.log('Settings clicked'),
|
onClick: () => alert('Settings clicked'),
|
||||||
shortcut: '⌘S'
|
shortcut: '⌘S'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Logout',
|
label: 'Logout',
|
||||||
onClick: () => console.log('Logout clicked'),
|
onClick: () => alert('Logout clicked'),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: '3-1',
|
id: '3-1',
|
||||||
|
@ -75,21 +75,21 @@ export const WithIconsAndShortcuts: Story = {
|
||||||
label: 'Profile',
|
label: 'Profile',
|
||||||
icon: '👤',
|
icon: '👤',
|
||||||
shortcut: '⌘P',
|
shortcut: '⌘P',
|
||||||
onClick: () => console.log('Profile clicked')
|
onClick: () => alert('Profile clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
icon: '⚙️',
|
icon: '⚙️',
|
||||||
shortcut: '⌘S',
|
shortcut: '⌘S',
|
||||||
onClick: () => console.log('Settings clicked')
|
onClick: () => alert('Settings clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Logout',
|
label: 'Logout',
|
||||||
icon: '🚪',
|
icon: '🚪',
|
||||||
shortcut: '⌘L',
|
shortcut: '⌘L',
|
||||||
onClick: () => console.log('Logout clicked')
|
onClick: () => alert('Logout clicked')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
children: <Button>Menu with Icons</Button>
|
children: <Button>Menu with Icons</Button>
|
||||||
|
@ -108,12 +108,12 @@ export const WithNestedItems: Story = {
|
||||||
{
|
{
|
||||||
id: '1-1',
|
id: '1-1',
|
||||||
label: 'Option 1',
|
label: 'Option 1',
|
||||||
onClick: () => console.log('Option 1 clicked')
|
onClick: () => alert('Option 1 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1-2',
|
id: '1-2',
|
||||||
label: 'Option 2',
|
label: 'Option 2',
|
||||||
onClick: () => console.log('Option 2 clicked')
|
onClick: () => alert('Option 2 clicked')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -124,12 +124,12 @@ export const WithNestedItems: Story = {
|
||||||
{
|
{
|
||||||
id: '2-1',
|
id: '2-1',
|
||||||
label: 'Sub Option 1',
|
label: 'Sub Option 1',
|
||||||
onClick: () => console.log('Sub Option 1 clicked')
|
onClick: () => alert('Sub Option 1 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2-2',
|
id: '2-2',
|
||||||
label: 'Sub Option 2',
|
label: 'Sub Option 2',
|
||||||
onClick: () => console.log('Sub Option 2 clicked')
|
onClick: () => alert('Sub Option 2 clicked')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -145,18 +145,18 @@ export const WithDisabledItems: Story = {
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Available Option',
|
label: 'Available Option',
|
||||||
onClick: () => console.log('Available clicked')
|
onClick: () => alert('Available clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Disabled Option',
|
label: 'Disabled Option',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
onClick: () => console.log('Should not be called')
|
onClick: () => alert('Should not be called')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Another Available',
|
label: 'Another Available',
|
||||||
onClick: () => console.log('Another clicked')
|
onClick: () => alert('Another clicked')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
children: <Button>Menu with Disabled Items</Button>
|
children: <Button>Menu with Disabled Items</Button>
|
||||||
|
@ -173,12 +173,12 @@ export const CustomWidth: Story = {
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'This is a very long menu item that might need wrapping',
|
label: 'This is a very long menu item that might need wrapping',
|
||||||
onClick: () => console.log('Long item clicked')
|
onClick: () => alert('Long item clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Short item',
|
label: 'Short item',
|
||||||
onClick: () => console.log('Short item clicked')
|
onClick: () => alert('Short item clicked')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
children: <Button>Wide Menu</Button>
|
children: <Button>Wide Menu</Button>
|
||||||
|
@ -192,29 +192,29 @@ export const WithLoadingItems: Story = {
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Normal Item',
|
label: 'Normal Item',
|
||||||
onClick: () => console.log('Normal clicked')
|
onClick: () => alert('Normal clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Loading Item',
|
label: 'Loading Item',
|
||||||
loading: true,
|
loading: true,
|
||||||
onClick: () => console.log('Loading clicked')
|
onClick: () => alert('Loading clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Another Normal',
|
label: 'Another Normal',
|
||||||
onClick: () => console.log('Another clicked')
|
onClick: () => alert('Another clicked')
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
label: 'Option 4',
|
label: 'Option 4',
|
||||||
onClick: () => console.log('Option 4 clicked')
|
onClick: () => alert('Option 4 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
label: 'Option 5',
|
label: 'Option 5',
|
||||||
onClick: () => console.log('Option 5 clicked')
|
onClick: () => alert('Option 5 clicked')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
children: <Button>Menu with Loading</Button>
|
children: <Button>Menu with Loading</Button>
|
||||||
|
@ -230,17 +230,17 @@ export const WithSelectionSingle: Story = {
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Option 1',
|
label: 'Option 1',
|
||||||
selected: false,
|
selected: false,
|
||||||
onClick: () => console.log('Option 1 clicked')
|
onClick: () => alert('Option 1 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Option 2',
|
label: 'Option 2',
|
||||||
onClick: () => console.log('Option 2 clicked')
|
onClick: () => alert('Option 2 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Option 3 - Selected',
|
label: 'Option 3 - Selected',
|
||||||
onClick: () => console.log('Option 3 clicked'),
|
onClick: () => alert('Option 3 clicked'),
|
||||||
selected: true
|
selected: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -257,32 +257,32 @@ export const WithSelectionMultiple: Story = {
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Option 1',
|
label: 'Option 1',
|
||||||
selected: selectedIds.has('1'),
|
selected: selectedIds.has('1'),
|
||||||
onClick: () => console.log('Option 1 clicked')
|
onClick: () => alert('Option 1 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Option 2',
|
label: 'Option 2',
|
||||||
selected: selectedIds.has('2'),
|
selected: selectedIds.has('2'),
|
||||||
onClick: () => console.log('Option 2 clicked')
|
onClick: () => alert('Option 2 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Option 3',
|
label: 'Option 3',
|
||||||
selected: selectedIds.has('3'),
|
selected: selectedIds.has('3'),
|
||||||
onClick: () => console.log('Option 3 clicked')
|
onClick: () => alert('Option 3 clicked')
|
||||||
},
|
},
|
||||||
{ type: 'divider' as const },
|
{ type: 'divider' as const },
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
label: 'Option 4',
|
label: 'Option 4',
|
||||||
selected: selectedIds.has('4'),
|
selected: selectedIds.has('4'),
|
||||||
onClick: () => console.log('Option 4 clicked')
|
onClick: () => alert('Option 4 clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
label: 'Option 5',
|
label: 'Option 5',
|
||||||
selected: selectedIds.has('5'),
|
selected: selectedIds.has('5'),
|
||||||
onClick: () => console.log('Option 5 clicked')
|
onClick: () => alert('Option 5 clicked')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -319,14 +319,14 @@ export const WithSecondaryLabel: Story = {
|
||||||
id: '1',
|
id: '1',
|
||||||
label: 'Profile Settings',
|
label: 'Profile Settings',
|
||||||
secondaryLabel: 'User preferences',
|
secondaryLabel: 'User preferences',
|
||||||
onClick: () => console.log('Profile clicked'),
|
onClick: () => alert('Profile clicked'),
|
||||||
icon: <PaintRoller />
|
icon: <PaintRoller />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
label: 'Storage',
|
label: 'Storage',
|
||||||
secondaryLabel: '45GB used',
|
secondaryLabel: '45GB used',
|
||||||
onClick: () => console.log('Storage clicked'),
|
onClick: () => alert('Storage clicked'),
|
||||||
icon: <Storage />
|
icon: <Storage />
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
|
@ -334,7 +334,7 @@ export const WithSecondaryLabel: Story = {
|
||||||
id: '3',
|
id: '3',
|
||||||
label: 'Subscription',
|
label: 'Subscription',
|
||||||
secondaryLabel: 'Pro Plan',
|
secondaryLabel: 'Pro Plan',
|
||||||
onClick: () => console.log('Subscription clicked'),
|
onClick: () => alert('Subscription clicked'),
|
||||||
icon: <Star />
|
icon: <Star />
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -354,7 +354,7 @@ export const WithSearchHeader: Story = {
|
||||||
label: 'Profile Settings',
|
label: 'Profile Settings',
|
||||||
searchLabel: 'profile settings user preferences account',
|
searchLabel: 'profile settings user preferences account',
|
||||||
secondaryLabel: 'User preferences',
|
secondaryLabel: 'User preferences',
|
||||||
onClick: () => console.log('Profile clicked'),
|
onClick: () => alert('Profile clicked'),
|
||||||
icon: <PaintRoller />
|
icon: <PaintRoller />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -362,7 +362,7 @@ export const WithSearchHeader: Story = {
|
||||||
label: 'Storage Options',
|
label: 'Storage Options',
|
||||||
searchLabel: 'storage disk space memory',
|
searchLabel: 'storage disk space memory',
|
||||||
secondaryLabel: 'Manage storage space',
|
secondaryLabel: 'Manage storage space',
|
||||||
onClick: () => console.log('Storage clicked'),
|
onClick: () => alert('Storage clicked'),
|
||||||
icon: <Storage />
|
icon: <Storage />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -370,7 +370,7 @@ export const WithSearchHeader: Story = {
|
||||||
label: 'Favorites',
|
label: 'Favorites',
|
||||||
searchLabel: 'favorites starred items bookmarks',
|
searchLabel: 'favorites starred items bookmarks',
|
||||||
secondaryLabel: 'View starred items',
|
secondaryLabel: 'View starred items',
|
||||||
onClick: () => console.log('Favorites clicked'),
|
onClick: () => alert('Favorites clicked'),
|
||||||
icon: <Star />
|
icon: <Star />
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
|
@ -378,12 +378,12 @@ export const WithSearchHeader: Story = {
|
||||||
id: '4',
|
id: '4',
|
||||||
label: 'Logout',
|
label: 'Logout',
|
||||||
|
|
||||||
onClick: () => console.log('Logout clicked')
|
onClick: () => alert('Logout clicked')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
label: 'Invite User',
|
label: 'Invite User',
|
||||||
onClick: () => console.log('Invite User clicked')
|
onClick: () => alert('Invite User clicked')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
children: <Button>Searchable Menu</Button>
|
children: <Button>Searchable Menu</Button>
|
||||||
|
@ -405,7 +405,7 @@ export const WithLongText: Story = {
|
||||||
label,
|
label,
|
||||||
secondaryLabel,
|
secondaryLabel,
|
||||||
searchLabel: label + ' ' + secondaryLabel,
|
searchLabel: label + ' ' + secondaryLabel,
|
||||||
onClick: () => console.log('Long text clicked'),
|
onClick: () => alert('Long text clicked'),
|
||||||
truncate: true
|
truncate: true
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -413,3 +413,105 @@ export const WithLongText: Story = {
|
||||||
children: <Button>Long Text Menu</Button>
|
children: <Button>Long Text Menu</Button>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Example with links
|
||||||
|
export const WithLinks: Story = {
|
||||||
|
args: {
|
||||||
|
menuHeader: 'Navigation Links',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Documentation',
|
||||||
|
link: '/docs',
|
||||||
|
icon: <Storage />,
|
||||||
|
onClick: () => alert('Documentation clicked')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
label: 'GitHub Repository',
|
||||||
|
link: 'https://github.com/example/repo',
|
||||||
|
icon: <Star />,
|
||||||
|
secondaryLabel: 'External Link'
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
label: 'Settings Page',
|
||||||
|
link: '/settings',
|
||||||
|
icon: <PaintRoller />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
label: 'Help Center',
|
||||||
|
link: '/help',
|
||||||
|
secondaryLabel: 'Get Support'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
children: <Button>Menu with Links</Button>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interactive example with links and multiple selection
|
||||||
|
export const WithLinksAndMultipleSelection: Story = {
|
||||||
|
render: () => {
|
||||||
|
const [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set(['2']));
|
||||||
|
|
||||||
|
const items: DropdownItems = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
label: 'Documentation Home',
|
||||||
|
link: '/docs',
|
||||||
|
selected: selectedIds.has('1'),
|
||||||
|
icon: <Storage />,
|
||||||
|
secondaryLabel: 'Main documentation'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
label: 'API Reference',
|
||||||
|
link: '/docs/api',
|
||||||
|
selected: selectedIds.has('2'),
|
||||||
|
icon: <Star />,
|
||||||
|
secondaryLabel: 'API documentation'
|
||||||
|
},
|
||||||
|
{ type: 'divider' as const },
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
label: 'Tutorials',
|
||||||
|
link: '/docs/tutorials',
|
||||||
|
selected: selectedIds.has('3'),
|
||||||
|
icon: <PaintRoller />,
|
||||||
|
secondaryLabel: 'Learn step by step'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
label: 'Examples',
|
||||||
|
link: '/docs/examples',
|
||||||
|
selected: selectedIds.has('4'),
|
||||||
|
secondaryLabel: 'Code examples'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleSelect = (itemId: string) => {
|
||||||
|
setSelectedIds((prev) => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has(itemId)) {
|
||||||
|
newSet.delete(itemId);
|
||||||
|
} else {
|
||||||
|
newSet.add(itemId);
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
open
|
||||||
|
selectType="multiple"
|
||||||
|
items={items}
|
||||||
|
menuHeader={{ placeholder: 'Search documentation...' }}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
children={<Button>Documentation Sections</Button>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { DropdownMenuLabel, DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
|
import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuGroup, //Do I need this?
|
DropdownMenuGroup, //Do I need this?
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
DropdownMenuPortal,
|
DropdownMenuPortal,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuShortcut,
|
DropdownMenuShortcut,
|
||||||
|
@ -13,7 +14,8 @@ import {
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
DropdownMenuCheckboxItemSingle,
|
DropdownMenuCheckboxItemSingle,
|
||||||
DropdownMenuCheckboxItemMultiple
|
DropdownMenuCheckboxItemMultiple,
|
||||||
|
DropdownMenuLink
|
||||||
} from './DropdownBase';
|
} from './DropdownBase';
|
||||||
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
import { CircleSpinnerLoader } from '../loaders/CircleSpinnerLoader';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
@ -35,6 +37,8 @@ export interface DropdownItem {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
items?: DropdownItems;
|
items?: DropdownItems;
|
||||||
|
link?: string;
|
||||||
|
linkIcon?: 'arrow-right' | 'arrow-external' | 'caret-right';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropdownDivider {
|
export interface DropdownDivider {
|
||||||
|
@ -233,7 +237,9 @@ const DropdownItem: React.FC<
|
||||||
onSelect,
|
onSelect,
|
||||||
selectType,
|
selectType,
|
||||||
secondaryLabel,
|
secondaryLabel,
|
||||||
truncate
|
truncate,
|
||||||
|
link,
|
||||||
|
linkIcon
|
||||||
}) => {
|
}) => {
|
||||||
const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (onClick) onClick();
|
if (onClick) onClick();
|
||||||
|
@ -252,6 +258,13 @@ const DropdownItem: React.FC<
|
||||||
{secondaryLabel && <span className="text-gray-light text-xxs">{secondaryLabel}</span>}
|
{secondaryLabel && <span className="text-gray-light text-xxs">{secondaryLabel}</span>}
|
||||||
</div>
|
</div>
|
||||||
{shortcut && <DropdownMenuShortcut>{shortcut}</DropdownMenuShortcut>}
|
{shortcut && <DropdownMenuShortcut>{shortcut}</DropdownMenuShortcut>}
|
||||||
|
{link && (
|
||||||
|
<DropdownMenuLink
|
||||||
|
className="-mr-1 ml-auto opacity-0 group-hover:opacity-50 hover:opacity-100"
|
||||||
|
link={link}
|
||||||
|
linkIcon={linkIcon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,19 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||||
import { Check3 as Check, ChevronRight } from '../icons/NucleoIconOutlined';
|
import {
|
||||||
|
ArrowRight,
|
||||||
|
ArrowUpRight,
|
||||||
|
CaretRight,
|
||||||
|
Check3 as Check,
|
||||||
|
ChevronRight
|
||||||
|
} from '../icons/NucleoIconOutlined';
|
||||||
|
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { Checkbox } from '../checkbox/Checkbox';
|
import { Checkbox } from '../checkbox/Checkbox';
|
||||||
|
import { Button } from '../buttons/Button';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||||
|
|
||||||
|
@ -83,6 +92,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||||
'focus:bg-item-hover focus:text-foreground relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
'focus:bg-item-hover focus:text-foreground relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||||
inset && 'pl-8',
|
inset && 'pl-8',
|
||||||
truncate && 'overflow-hidden',
|
truncate && 'overflow-hidden',
|
||||||
|
'group',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
@ -100,7 +110,8 @@ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
||||||
const itemClass = cn(
|
const itemClass = cn(
|
||||||
'focus:bg-item-hover focus:text-foreground',
|
'focus:bg-item-hover focus:text-foreground',
|
||||||
'relative flex cursor-pointer items-center rounded-sm py-1.5 text-sm transition-colors outline-none select-none',
|
'relative flex cursor-pointer items-center rounded-sm py-1.5 text-sm transition-colors outline-none select-none',
|
||||||
'data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
|
'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
'gap-1.5'
|
||||||
);
|
);
|
||||||
|
|
||||||
const DropdownMenuCheckboxItemSingle = React.forwardRef<
|
const DropdownMenuCheckboxItemSingle = React.forwardRef<
|
||||||
|
@ -180,7 +191,7 @@ const DropdownMenuLabel = React.forwardRef<
|
||||||
>(({ className, inset, ...props }, ref) => (
|
>(({ className, inset, ...props }, ref) => (
|
||||||
<DropdownMenuPrimitive.Label
|
<DropdownMenuPrimitive.Label
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('px-2 py-1.5 text-sm', inset && 'pl-8', className)}
|
className={cn('text-gray-dark px-2 py-1.5 text-sm', inset && 'pl-8', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
@ -209,6 +220,33 @@ const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTML
|
||||||
};
|
};
|
||||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
||||||
|
|
||||||
|
const DropdownMenuLink: React.FC<{
|
||||||
|
className?: string;
|
||||||
|
link: string;
|
||||||
|
linkIcon?: 'arrow-right' | 'arrow-external' | 'caret-right';
|
||||||
|
}> = ({ className, link, linkIcon = 'arrow-right', ...props }) => {
|
||||||
|
const icon = React.useMemo(() => {
|
||||||
|
if (linkIcon === 'arrow-right') return <ArrowRight />;
|
||||||
|
if (linkIcon === 'arrow-external') return <ArrowUpRight />;
|
||||||
|
if (linkIcon === 'caret-right') return <CaretRight />;
|
||||||
|
}, [linkIcon]);
|
||||||
|
|
||||||
|
const isExternal = link?.startsWith('http');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link className={className} href={link} target={isExternal ? '_blank' : '_self'}>
|
||||||
|
<Button
|
||||||
|
prefix={icon}
|
||||||
|
variant="ghost"
|
||||||
|
size="small"
|
||||||
|
rounding={'default'}
|
||||||
|
className={cn('text-gray-dark hover:bg-gray-dark/8')}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
DropdownMenuLink.displayName = 'DropdownMenuLink';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
|
@ -223,5 +261,6 @@ export {
|
||||||
DropdownMenuSub,
|
DropdownMenuSub,
|
||||||
DropdownMenuSubContent,
|
DropdownMenuSubContent,
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuCheckboxItemMultiple
|
DropdownMenuCheckboxItemMultiple,
|
||||||
|
DropdownMenuLink
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,7 +50,7 @@ export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
items: items,
|
items: items,
|
||||||
open: true,
|
open: true,
|
||||||
onSelect: (item) => console.log('Selected:', item.value)
|
onSelect: (item) => alert(`Selected: ${item.value}`)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ export const WithDisabledItems: Story = {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
open: true,
|
open: true,
|
||||||
onSelect: (item) => console.log('Selected:', item)
|
onSelect: (item) => alert(`Selected: ${item.value}`)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,6 +76,6 @@ export const CustomStyling: Story = {
|
||||||
items: items,
|
items: items,
|
||||||
open: true,
|
open: true,
|
||||||
className: 'bg-slate-100 rounded-lg shadow-lg',
|
className: 'bg-slate-100 rounded-lg shadow-lg',
|
||||||
onSelect: (item) => console.log('Selected:', item)
|
onSelect: (item) => alert(`Selected: ${item.value}`)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue