refactor(webui): unify floating controls

This commit is contained in:
Xubin Ren
2026-08-04 18:05:34 +08:00
parent faff0ac2fa
commit 7819cef7bd
16 changed files with 777 additions and 257 deletions
+32 -13
View File
@@ -2,6 +2,12 @@ import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { Check, ChevronRight, Circle } from "lucide-react";
import {
floatingItemClassName,
floatingItemFocusClassName,
floatingSurfaceClassName,
floatingSurfaceMotionClassName,
} from "@/components/ui/floating-surface";
import { cn } from "@/lib/utils";
const DropdownMenu = DropdownMenuPrimitive.Root;
@@ -11,11 +17,8 @@ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const menuContentClassName =
"z-50 max-h-[min(var(--radix-dropdown-menu-content-available-height),28rem)] min-w-[10rem] overflow-x-hidden overflow-y-auto overscroll-contain rounded-[18px] border border-border/65 bg-popover/96 p-1.5 text-popover-foreground shadow-[0_18px_55px_rgba(15,23,42,0.18)] backdrop-blur-xl scrollbar-thin scrollbar-track-transparent dark:border-white/10 dark:shadow-[0_22px_55px_rgba(0,0,0,0.45)]";
const menuItemClassName =
"relative flex min-h-8 cursor-default select-none items-center gap-2 rounded-[12px] px-2.5 py-2 text-[13px] outline-none transition-colors focus:bg-foreground/[0.055] focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-white/[0.08]";
`${floatingItemClassName} ${floatingItemFocusClassName} cursor-default data-[disabled]:pointer-events-none data-[disabled]:opacity-50`;
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
@@ -46,7 +49,8 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
menuContentClassName,
floatingSurfaceClassName,
"min-w-[10rem]",
className,
)}
{...props}
@@ -68,8 +72,9 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
menuContentClassName,
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
floatingSurfaceClassName,
floatingSurfaceMotionClassName,
"min-w-[10rem]",
className,
)}
{...props}
@@ -82,13 +87,15 @@ const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
tone?: "default" | "destructive";
}
>(({ className, inset, ...props }, ref) => (
>(({ className, inset, tone = "default", ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
menuItemClassName,
inset && "pl-8",
tone === "destructive" && "text-destructive focus:text-destructive",
className,
)}
{...props}
@@ -123,20 +130,32 @@ DropdownMenuCheckboxItem.displayName =
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & {
indicator?: "dot" | "check";
indicatorPosition?: "start" | "end";
}
>(({ className, children, indicator = "dot", indicatorPosition = "start", ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
menuItemClassName,
"pl-8 pr-2.5",
indicatorPosition === "start" ? "pl-8 pr-2.5" : "pl-2.5 pr-8",
className,
)}
{...props}
>
<span className="absolute left-2.5 flex h-3.5 w-3.5 items-center justify-center">
<span
className={cn(
"absolute flex h-3.5 w-3.5 items-center justify-center",
indicatorPosition === "start" ? "left-2.5" : "right-2.5",
)}
>
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
{indicator === "check" ? (
<Check className="h-3.5 w-3.5" />
) : (
<Circle className="h-2 w-2 fill-current" />
)}
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}