sidebar group

This commit is contained in:
Nate Kelley 2025-02-27 22:22:23 -07:00
parent 60b6bc9e5b
commit 5169b4eb92
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 121 additions and 2 deletions

View File

@ -0,0 +1,49 @@
import type { Meta, StoryObj } from '@storybook/react';
import { SidebarGroup } from './SidebarCollapsible';
//import { Home, Settings, User } from 'lucide-react';
import { HouseModern, MapSettings, User } from '../icons/NucleoIconOutlined';
import { BusterRoutes } from '@/routes';
const meta: Meta<typeof SidebarGroup> = {
title: 'UI/Sidebar/SidebarGroup',
component: SidebarGroup,
parameters: {
layout: 'centered'
},
decorators: [
(Story) => (
<div className="bg-background min-w-[270px]">
<Story />
</div>
)
]
};
export default meta;
type Story = StoryObj<typeof SidebarGroup>;
export const Default: Story = {
args: {
label: 'Settings',
items: [
{
id: '1',
label: 'Profile',
icon: <User />,
route: BusterRoutes.SETTINGS
},
{
id: '2',
label: 'Account',
icon: <MapSettings />,
route: BusterRoutes.APP_CHAT
},
{
id: '3',
label: 'Dashboard',
icon: <HouseModern />,
route: BusterRoutes.APP_METRIC
}
]
}
};

View File

@ -6,7 +6,52 @@ import {
} from '../collapsible/CollapsibleBase';
import { type ISidebarGroup } from './interfaces';
import { SidebarItem } from './SidebarItem';
import { CaretDown } from '../icons/NucleoIconFilled';
import { cn } from '@/lib/classMerge';
interface SidebarTriggerProps {
label: string;
isOpen: boolean;
}
const SidebarTrigger: React.FC<SidebarTriggerProps> = React.memo(({ label, isOpen }) => {
return (
<div
className={cn(
'flex items-center gap-1 rounded px-1.5 py-1 text-sm transition-colors',
'hover:text-text-default text-text-secondary',
'group cursor-pointer'
)}>
<span className="">{label}</span>
<div
className={cn(
'text-icon-color text-2xs -rotate-90 transition-transform duration-200',
isOpen && 'rotate-0',
'group-hover:text-text-default'
)}>
<CaretDown />
</div>
</div>
);
});
export const SidebarGroup: React.FC<ISidebarGroup> = React.memo(({ label, items }) => {
return <></>;
const [isOpen, setIsOpen] = React.useState(false);
return (
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="space-y-0.5">
<CollapsibleTrigger asChild className="w-full">
<button className="w-full text-left">
<SidebarTrigger label={label} isOpen={isOpen} />
</button>
</CollapsibleTrigger>
<CollapsibleContent className="data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up pl-0">
<div className="space-y-0.5">
{items.map((item) => (
<SidebarItem key={item.id} {...item} />
))}
</div>
</CollapsibleContent>
</Collapsible>
);
});

View File

@ -5,7 +5,7 @@ import { type ISidebarItem } from './interfaces';
import { cva, VariantProps } from 'class-variance-authority';
const itemVariants = cva(
'flex items-center gap-2.5 rounded px-1.5 py-1.5 text-sm transition-colors',
'flex items-center gap-2 rounded px-1.5 py-1.5 text-sm transition-colors',
{
variants: {
variant: {

View File

@ -1,6 +1,9 @@
@theme {
/* animations */
--animate-indeterminate-progress-bar: indeterminate-progress-bar 1s infinite linear;
--animate-collapsible-down: collapsible-down 0.12s ease-out;
--animate-collapsible-up: collapsible-up 0.12s ease-out;
@keyframes indeterminate-progress-bar {
0% {
transform: translateX(0) scaleX(0);
@ -12,4 +15,26 @@
transform: translateX(100%) scaleX(0.5);
}
}
@keyframes collapsible-down {
from {
height: 0;
opacity: 0;
}
to {
height: var(--radix-collapsible-content-height);
opacity: 1;
}
}
@keyframes collapsible-up {
from {
height: var(--radix-collapsible-content-height);
opacity: 1;
}
to {
height: 0;
opacity: 0;
}
}
}