style: rollback tailwindcss to v3 for temp fix

This commit is contained in:
2025-03-04 03:16:06 +08:00
parent e2fdeaabb2
commit 6e4c136614
14 changed files with 630 additions and 288 deletions

View File

@@ -0,0 +1,174 @@
import {
AudioWaveform,
BookOpen,
Bot,
ChartPie,
Command,
Frame,
GalleryVerticalEnd,
Map as LucideMap,
Settings2,
SquareTerminal,
} from 'lucide-solid';
import type { ComponentProps } from 'solid-js';
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarRail,
} from '~/components/ui/sidebar';
import { NavMain } from './nav-main';
import { NavProjects } from './nav-projects';
import { NavUser } from './nav-user';
import { TeamSwitcher } from './team-switcher';
// This is sample data.
const data = {
user: {
name: 'shadcn',
email: 'm@example.com',
avatar: '/avatars/shadcn.jpg',
},
teams: [
{
name: 'Acme Inc',
logo: GalleryVerticalEnd,
plan: 'Enterprise',
},
{
name: 'Acme Corp.',
logo: AudioWaveform,
plan: 'Startup',
},
{
name: 'Evil Corp.',
logo: Command,
plan: 'Free',
},
],
navMain: [
{
title: 'Playground',
url: '#',
icon: SquareTerminal,
isActive: true,
items: [
{
title: 'History',
url: '#',
},
{
title: 'Starred',
url: '#',
},
{
title: 'Settings',
url: '#',
},
],
},
{
title: 'Models',
url: '#',
icon: Bot,
items: [
{
title: 'Genesis',
url: '#',
},
{
title: 'Explorer',
url: '#',
},
{
title: 'Quantum',
url: '#',
},
],
},
{
title: 'Documentation',
url: '#',
icon: BookOpen,
items: [
{
title: 'Introduction',
url: '#',
},
{
title: 'Get Started',
url: '#',
},
{
title: 'Tutorials',
url: '#',
},
{
title: 'Changelog',
url: '#',
},
],
},
{
title: 'Settings',
url: '#',
icon: Settings2,
items: [
{
title: 'General',
url: '#',
},
{
title: 'Team',
url: '#',
},
{
title: 'Billing',
url: '#',
},
{
title: 'Limits',
url: '#',
},
],
},
],
projects: [
{
name: 'Design Engineering',
url: '#',
icon: Frame,
},
{
name: 'Sales & Marketing',
url: '#',
icon: ChartPie,
},
{
name: 'Travel',
url: '#',
icon: LucideMap,
},
],
};
type AppSidebarRootProps = Omit<ComponentProps<typeof Sidebar>, 'collapsible'>;
export const AppSidebar = (props: AppSidebarRootProps) => {
return (
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<TeamSwitcher teams={data.teams} />
</SidebarHeader>
<SidebarContent>
<NavMain items={data.navMain} />
<NavProjects projects={data.projects} />
</SidebarContent>
<SidebarFooter>
<NavUser user={data.user} />
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
};

View File

@@ -0,0 +1,71 @@
import { ChevronRight, type LucideIcon } from 'lucide-solid';
import { For } from 'solid-js';
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '~/components/ui/collapsible';
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from '~/components/ui/sidebar';
export function NavMain({
items,
}: {
items: {
title: string;
url: string;
icon?: LucideIcon;
isActive?: boolean;
items?: {
title: string;
url: string;
}[];
}[];
}) {
return (
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
<For each={items}>
{(item) => (
<Collapsible
as={SidebarMenuItem}
defaultOpen={item.isActive}
class="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger as={SidebarMenuButton} tooltip={item.title}>
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRight class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
<For each={item.items || []}>
{(subItem) => (
<SidebarMenuSubItem>
<SidebarMenuSubButton as={'a'} href={subItem.url}>
<span>{subItem.title}</span>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
)}
</For>
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
)}
</For>
</SidebarMenu>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,79 @@
import {
Folder,
Forward,
type LucideIcon,
MoreHorizontal,
Trash2,
} from 'lucide-solid';
import { For } from 'solid-js';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu';
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
} from '~/components/ui/sidebar';
export function NavProjects({
projects,
}: {
projects: {
name: string;
url: string;
icon: LucideIcon;
}[];
}) {
return (
<SidebarGroup class="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarMenu>
<For each={projects}>
{(item) => (
<SidebarMenuItem>
<SidebarMenuButton as="a" href={item.url}>
<item.icon />
<span>{item.name}</span>
</SidebarMenuButton>
<DropdownMenu>
<DropdownMenuTrigger as={SidebarMenuAction} showOnHover>
<MoreHorizontal />
<span class="sr-only">More</span>
</DropdownMenuTrigger>
<DropdownMenuContent class="w-48 rounded-lg">
<DropdownMenuItem>
<Folder class="text-muted-foreground" />
<span>View Project</span>
</DropdownMenuItem>
<DropdownMenuItem>
<Forward class="text-muted-foreground" />
<span>Share Project</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Trash2 class="text-muted-foreground" />
<span>Delete Project</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
)}
</For>
<SidebarMenuItem>
<SidebarMenuButton class="text-sidebar-foreground/70">
<MoreHorizontal class="text-sidebar-foreground/70" />
<span>More</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
);
}

View File

@@ -0,0 +1,106 @@
import {
BadgeCheck,
Bell,
ChevronsUpDown,
CreditCard,
LogOut,
Sparkles,
} from 'lucide-solid';
import { Avatar, AvatarFallback, AvatarImage } from '~/components/ui/avatar';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu';
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from '~/components/ui/sidebar';
export function NavUser({
user,
}: {
user: {
name: string;
email: string;
avatar: string;
};
}) {
const { isMobile } = useSidebar();
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
as={SidebarMenuButton}
size="lg"
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar class="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback class="rounded-lg">CN</AvatarFallback>
</Avatar>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{user.name}</span>
<span class="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown class="ml-auto size-4" />
</DropdownMenuTrigger>
<DropdownMenuContent
class="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
align="end"
sideOffset={4}
>
<DropdownMenuLabel class="p-0 font-normal">
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar class="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback class="rounded-lg">CN</AvatarFallback>
</Avatar>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{user.name}</span>
<span class="truncate text-xs">{user.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}

View File

@@ -0,0 +1,79 @@
import { ChevronsUpDown, type LucideIcon, Plus } from 'lucide-solid';
import { For, createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from '~/components/ui/dropdown-menu';
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from '~/components/ui/sidebar';
export function TeamSwitcher(props: {
teams: {
name: string;
logo: LucideIcon;
plan: string;
}[];
}) {
const [activeTeam, setActiveTeam] = createSignal(props.teams[0]);
const logo = activeTeam().logo;
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
as={SidebarMenuButton}
size="lg"
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<div class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
<Dynamic component={logo} class="size-4" />
</div>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{activeTeam().name}</span>
<span class="truncate text-xs">{activeTeam().plan}</span>
</div>
<ChevronsUpDown class="ml-auto" />
</DropdownMenuTrigger>
<DropdownMenuContent class="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg">
<DropdownMenuLabel class="text-muted-foreground text-xs">
Teams
</DropdownMenuLabel>
<For each={props.teams}>
{(team, index) => (
<DropdownMenuItem
onClick={() => setActiveTeam(team)}
class="gap-2 p-2"
>
<div class="flex size-6 items-center justify-center rounded-sm border">
<Dynamic component={team.logo} class="size-4 shrink-0" />
</div>
{team.name}
<DropdownMenuShortcut>{index() + 1}</DropdownMenuShortcut>
</DropdownMenuItem>
)}
</For>
<DropdownMenuSeparator />
<DropdownMenuItem class="gap-2 p-2">
<div class="flex size-6 items-center justify-center rounded-md border bg-background">
<Plus class="size-4" />
</div>
<div class="font-medium text-muted-foreground">Add team</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}