feat(ledger): add bookkeeping workbench and transaction views
- Add transactions_dirty table and zero-tolerance cleansing action - Implement bookkeeping workbench (/bookkeeping) with searchable Combobox channel selector and card suffix / account identifier - Implement transaction ledger (/transactions) with date groupings, multi-currency metrics, and filters - Update AGENTS.md guidelines for Base UI Select and Combobox bindings
This commit is contained in:
@@ -7,9 +7,7 @@ import {
|
||||
BreadcrumbSeparator,
|
||||
} from "@/components/ui/breadcrumb"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import {
|
||||
SidebarTrigger,
|
||||
} from "@/components/ui/sidebar"
|
||||
import { SidebarTrigger } from "@/components/ui/sidebar"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
|
||||
export function AppHeader({ title }: { title: string }) {
|
||||
@@ -17,7 +15,7 @@ export function AppHeader({ title }: { title: string }) {
|
||||
|
||||
return (
|
||||
<header className="flex h-16 shrink-0 items-center justify-between gap-2 border-b px-4 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
||||
<div className="flex min-w-0 h-5 items-center gap-2">
|
||||
<div className="flex h-5 min-w-0 items-center gap-2">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Separator orientation="vertical" className="mr-2" />
|
||||
<Breadcrumb>
|
||||
|
||||
+26
-14
@@ -1,16 +1,18 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import * as React from "react";
|
||||
import Link from "next/link";
|
||||
import * as React from "react"
|
||||
import Link from "next/link"
|
||||
import {
|
||||
CreditCardIcon,
|
||||
LayoutDashboardIcon,
|
||||
PenToolIcon,
|
||||
ReceiptTextIcon,
|
||||
WalletCardsIcon,
|
||||
} from "lucide-react";
|
||||
} from "lucide-react"
|
||||
|
||||
import { FluxentLogo } from "@/components/fluxent-logo";
|
||||
import { NavMain, type NavMainItem } from "@/components/nav-main";
|
||||
import { NavUser, type NavUserData } from "@/components/nav-user";
|
||||
import { FluxentLogo } from "@/components/fluxent-logo"
|
||||
import { NavMain, type NavMainItem } from "@/components/nav-main"
|
||||
import { NavUser, type NavUserData } from "@/components/nav-user"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
@@ -20,7 +22,7 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
const navMainItems: NavMainItem[] = [
|
||||
{
|
||||
@@ -28,6 +30,16 @@ const navMainItems: NavMainItem[] = [
|
||||
url: "/",
|
||||
icon: LayoutDashboardIcon,
|
||||
},
|
||||
{
|
||||
title: "流水记账",
|
||||
url: "/bookkeeping",
|
||||
icon: PenToolIcon,
|
||||
},
|
||||
{
|
||||
title: "交易记录",
|
||||
url: "/transactions",
|
||||
icon: ReceiptTextIcon,
|
||||
},
|
||||
{
|
||||
title: "资金账户",
|
||||
url: "/accounts",
|
||||
@@ -38,21 +50,21 @@ const navMainItems: NavMainItem[] = [
|
||||
url: "/channels",
|
||||
icon: CreditCardIcon,
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
export function AppSidebar({
|
||||
user,
|
||||
...props
|
||||
}: {
|
||||
user: NavUserData;
|
||||
user: NavUserData
|
||||
} & React.ComponentProps<typeof Sidebar>) {
|
||||
const { isMobile, setOpenMobile } = useSidebar();
|
||||
const { isMobile, setOpenMobile } = useSidebar()
|
||||
|
||||
const handleLogoNavigation = () => {
|
||||
if (isMobile) {
|
||||
setOpenMobile(false);
|
||||
setOpenMobile(false)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Sidebar variant="inset" {...props}>
|
||||
@@ -89,5 +101,5 @@ export function AppSidebar({
|
||||
<NavUser user={user} />
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export function FluxentLogo({
|
||||
className,
|
||||
size = 36,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
className?: string
|
||||
size?: number
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
@@ -44,5 +44,5 @@ export function FluxentLogo({
|
||||
<circle cx="15" cy="20" r="1.5" fill="currentColor" fillOpacity="0.8" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
+18
-17
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { type LucideIcon } from "lucide-react";
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { type LucideIcon } from "lucide-react"
|
||||
|
||||
import {
|
||||
SidebarGroup,
|
||||
@@ -11,24 +11,24 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export interface NavMainItem {
|
||||
title: string;
|
||||
url: string;
|
||||
icon: LucideIcon;
|
||||
badge?: string;
|
||||
title: string
|
||||
url: string
|
||||
icon: LucideIcon
|
||||
badge?: string
|
||||
}
|
||||
|
||||
export function NavMain({ items }: { items: NavMainItem[] }) {
|
||||
const pathname = usePathname();
|
||||
const { isMobile, setOpenMobile } = useSidebar();
|
||||
const pathname = usePathname()
|
||||
const { isMobile, setOpenMobile } = useSidebar()
|
||||
|
||||
const handleNavigation = () => {
|
||||
if (isMobile) {
|
||||
setOpenMobile(false);
|
||||
setOpenMobile(false)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarGroup>
|
||||
@@ -37,14 +37,15 @@ export function NavMain({ items }: { items: NavMainItem[] }) {
|
||||
</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton
|
||||
isActive={
|
||||
item.url === "/"
|
||||
? pathname === "/"
|
||||
: pathname === item.url || pathname.startsWith(`${item.url}/`)
|
||||
: pathname === item.url ||
|
||||
pathname.startsWith(`${item.url}/`)
|
||||
}
|
||||
tooltip={item.title}
|
||||
render={<Link href={item.url} onClick={handleNavigation} />}
|
||||
@@ -58,9 +59,9 @@ export function NavMain({ items }: { items: NavMainItem[] }) {
|
||||
)}
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { type LucideIcon } from "lucide-react";
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { type LucideIcon } from "lucide-react"
|
||||
|
||||
import {
|
||||
SidebarGroup,
|
||||
@@ -11,13 +11,13 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export interface NavSecondaryItem {
|
||||
title: string;
|
||||
url: string;
|
||||
icon: LucideIcon;
|
||||
badge?: React.ReactNode;
|
||||
title: string
|
||||
url: string
|
||||
icon: LucideIcon
|
||||
badge?: React.ReactNode
|
||||
}
|
||||
|
||||
export function NavSecondary({
|
||||
@@ -25,23 +25,23 @@ export function NavSecondary({
|
||||
className,
|
||||
...props
|
||||
}: {
|
||||
items: NavSecondaryItem[];
|
||||
items: NavSecondaryItem[]
|
||||
} & React.ComponentProps<typeof SidebarGroup>) {
|
||||
const pathname = usePathname();
|
||||
const { isMobile, setOpenMobile } = useSidebar();
|
||||
const pathname = usePathname()
|
||||
const { isMobile, setOpenMobile } = useSidebar()
|
||||
|
||||
const handleNavigation = () => {
|
||||
if (isMobile) {
|
||||
setOpenMobile(false);
|
||||
setOpenMobile(false)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarGroup className={className} {...props}>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton
|
||||
@@ -56,10 +56,10 @@ export function NavSecondary({
|
||||
<span>{item.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
+16
-22
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import {
|
||||
ChevronsUpDownIcon,
|
||||
@@ -6,14 +6,10 @@ import {
|
||||
Settings2Icon,
|
||||
SparklesIcon,
|
||||
UserCogIcon,
|
||||
} from "lucide-react";
|
||||
import { signOut } from "next-auth/react";
|
||||
} from "lucide-react"
|
||||
import { signOut } from "next-auth/react"
|
||||
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/components/ui/avatar";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -22,29 +18,27 @@ import {
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import {
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
} from "@/components/ui/sidebar"
|
||||
|
||||
export interface NavUserData {
|
||||
name: string;
|
||||
email: string;
|
||||
avatar?: string | null;
|
||||
name: string
|
||||
email: string
|
||||
avatar?: string | null
|
||||
}
|
||||
|
||||
export function NavUser({ user }: { user: NavUserData }) {
|
||||
const { isMobile } = useSidebar();
|
||||
const initials = (user.name || user.email || "U")
|
||||
.slice(0, 2)
|
||||
.toUpperCase();
|
||||
const { isMobile } = useSidebar()
|
||||
const initials = (user.name || user.email || "U").slice(0, 2).toUpperCase()
|
||||
|
||||
const handleSignOut = () => {
|
||||
signOut({ callbackUrl: "/login" });
|
||||
};
|
||||
signOut({ callbackUrl: "/login" })
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
@@ -62,7 +56,7 @@ export function NavUser({ user }: { user: NavUserData }) {
|
||||
{user.avatar ? (
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
) : null}
|
||||
<AvatarFallback className="rounded-lg font-medium text-xs">
|
||||
<AvatarFallback className="rounded-lg text-xs font-medium">
|
||||
{initials}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
@@ -89,7 +83,7 @@ export function NavUser({ user }: { user: NavUserData }) {
|
||||
{user.avatar ? (
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
) : null}
|
||||
<AvatarFallback className="rounded-lg font-medium text-xs">
|
||||
<AvatarFallback className="rounded-lg text-xs font-medium">
|
||||
{initials}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
@@ -140,5 +134,5 @@ export function NavUser({ user }: { user: NavUserData }) {
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
+11
-12
@@ -1,17 +1,17 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import * as React from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { MoonIcon, SunIcon } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import * as React from "react"
|
||||
import { useTheme } from "next-themes"
|
||||
import { MoonIcon, SunIcon } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const { resolvedTheme, setTheme } = useTheme()
|
||||
const mounted = React.useSyncExternalStore(
|
||||
() => () => {},
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
)
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
@@ -23,10 +23,10 @@ export function ThemeToggle() {
|
||||
>
|
||||
<span className="size-4" />
|
||||
</Button>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
const isDark = resolvedTheme === "dark";
|
||||
const isDark = resolvedTheme === "dark"
|
||||
|
||||
return (
|
||||
<Button
|
||||
@@ -35,7 +35,7 @@ export function ThemeToggle() {
|
||||
onClick={() => setTheme(isDark ? "light" : "dark")}
|
||||
aria-label="Toggle color theme"
|
||||
title={isDark ? "切换为浅色模式" : "切换为深色模式"}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
{isDark ? (
|
||||
<SunIcon data-icon="inline-start" />
|
||||
@@ -43,6 +43,5 @@ export function ThemeToggle() {
|
||||
<MoonIcon data-icon="inline-start" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -84,9 +84,7 @@ function BreadcrumbSeparator({
|
||||
className={cn("[&>svg]:size-3.5", className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? (
|
||||
<ChevronRightIcon />
|
||||
)}
|
||||
{children ?? <ChevronRightIcon />}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -106,8 +104,7 @@ function BreadcrumbEllipsis({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontalIcon
|
||||
/>
|
||||
<MoreHorizontalIcon />
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
"use client"
|
||||
|
||||
import * as React from "react";
|
||||
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as React from "react"
|
||||
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
|
||||
import { CheckIcon } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Checkbox({
|
||||
className,
|
||||
@@ -13,7 +13,7 @@ function Checkbox({
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"peer size-4 shrink-0 rounded-[4px] border border-input bg-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-primary data-[checked]:border-primary data-[checked]:text-primary-foreground",
|
||||
"peer size-4 shrink-0 rounded-[4px] border border-input bg-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:border-primary data-[checked]:bg-primary data-[checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -22,7 +22,7 @@ function Checkbox({
|
||||
<CheckIcon data-icon="inline-start" className="size-3.5 stroke-[2.5]" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Checkbox };
|
||||
export { Checkbox }
|
||||
|
||||
@@ -110,7 +110,10 @@ function ComboboxContent({
|
||||
<ComboboxPrimitive.Popup
|
||||
data-slot="combobox-content"
|
||||
data-chips={!!anchor}
|
||||
className={cn("group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
||||
className={cn(
|
||||
"group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</ComboboxPrimitive.Positioner>
|
||||
|
||||
@@ -70,8 +70,7 @@ function DialogContent({
|
||||
/>
|
||||
}
|
||||
>
|
||||
<XIcon
|
||||
/>
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
|
||||
@@ -40,7 +40,10 @@ function DropdownMenuContent({
|
||||
>
|
||||
<MenuPrimitive.Popup
|
||||
data-slot="dropdown-menu-content"
|
||||
className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
||||
className={cn(
|
||||
"z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</MenuPrimitive.Positioner>
|
||||
@@ -134,7 +137,10 @@ function DropdownMenuSubContent({
|
||||
return (
|
||||
<DropdownMenuContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
||||
className={cn(
|
||||
"w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
className
|
||||
)}
|
||||
align={align}
|
||||
alignOffset={alignOffset}
|
||||
side={side}
|
||||
@@ -169,8 +175,7 @@ function DropdownMenuCheckboxItem({
|
||||
data-slot="dropdown-menu-checkbox-item-indicator"
|
||||
>
|
||||
<MenuPrimitive.CheckboxItemIndicator>
|
||||
<CheckIcon
|
||||
/>
|
||||
<CheckIcon />
|
||||
</MenuPrimitive.CheckboxItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
@@ -210,8 +215,7 @@ function DropdownMenuRadioItem({
|
||||
data-slot="dropdown-menu-radio-item-indicator"
|
||||
>
|
||||
<MenuPrimitive.RadioItemIndicator>
|
||||
<CheckIcon
|
||||
/>
|
||||
<CheckIcon />
|
||||
</MenuPrimitive.RadioItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
|
||||
@@ -82,7 +82,10 @@ function SelectContent({
|
||||
<SelectPrimitive.Popup
|
||||
data-slot="select-content"
|
||||
data-align-trigger={alignItemWithTrigger}
|
||||
className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
||||
className={cn(
|
||||
"relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
@@ -161,8 +164,7 @@ function SelectScrollUpButton({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon
|
||||
/>
|
||||
<ChevronUpIcon />
|
||||
</SelectPrimitive.ScrollUpArrow>
|
||||
)
|
||||
}
|
||||
@@ -180,8 +182,7 @@ function SelectScrollDownButton({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon
|
||||
/>
|
||||
<ChevronDownIcon />
|
||||
</SelectPrimitive.ScrollDownArrow>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -70,8 +70,7 @@ function SheetContent({
|
||||
/>
|
||||
}
|
||||
>
|
||||
<XIcon
|
||||
/>
|
||||
<XIcon />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user