返回 JoyAI-Echo
DurationPicker.tsx
根目录 / echo_longvideo / Director_Agent / webui / src / components / thread / DurationPicker.tsx
1 import { ChevronDown, Clock } from "lucide-react";
2 import { useTranslation } from "react-i18next";
3
4 import {
5 DropdownMenu,
6 DropdownMenuContent,
7 DropdownMenuItem,
8 DropdownMenuTrigger,
9 } from "@/components/ui/dropdown-menu";
10 import { cn } from "@/lib/utils";
11
12 /** Short-video duration buckets. */
13 export const DURATION_OPTIONS = [5, 6, 7, 8, 9, 10];
14 export const DEFAULT_DURATION = 5;
15
16 /** Director automatic-generation duration buckets. */
17 export const LONG_VIDEO_DURATION_OPTIONS = [10, 20, 30, 60, 90, 120, 150, 180];
18 export const LONG_VIDEO_DEFAULT_DURATION = 30;
19
20 interface DurationPickerProps {
21 value: number;
22 onChange: (sec: number) => void;
23 disabled?: boolean;
24 size?: "hero" | "thread";
25 options?: readonly number[];
26 }
27
28 export function DurationPicker({
29 value,
30 onChange,
31 disabled,
32 size = "thread",
33 options = DURATION_OPTIONS,
34 }: DurationPickerProps) {
35 const { t } = useTranslation();
36 const isHero = size === "hero";
37
38 return (
39 <DropdownMenu>
40 <DropdownMenuTrigger asChild disabled={disabled}>
41 <span
42 aria-label={t("thread.composer.durationPickerAria")}
43 className={cn(
44 "cursor-pointer",
45 "group inline-flex min-w-0 items-center gap-1.5 rounded-full border px-2.5 py-1",
46 "border-foreground/10 bg-foreground/[0.035] font-medium text-foreground/80",
47 "whitespace-nowrap transition-colors hover:border-foreground/15 hover:bg-foreground/[0.04]",
48 "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
49 "disabled:cursor-not-allowed disabled:opacity-60",
50 "data-[state=open]:border-foreground/15 data-[state=open]:bg-foreground/[0.04]",
51 isHero ? "text-[11px]" : "text-[12px]",
52 )}
53 >
54 <Clock className={cn(isHero ? "h-3 w-3" : "h-3 w-3")} aria-hidden />
55 <span>{t("thread.composer.durationLabel", { sec: value })}</span>
56 <ChevronDown
57 className={cn(
58 "h-2.5 w-2.5 opacity-60 transition-transform duration-200",
59 "group-data-[state=open]:rotate-180",
60 )}
61 aria-hidden
62 />
63 </span>
64 </DropdownMenuTrigger>
65 <DropdownMenuContent
66 side="top"
67 align="start"
68 sideOffset={6}
69 className="max-h-[320px] min-w-[var(--radix-dropdown-menu-trigger-width)] overflow-y-auto rounded-xl p-1"
70 >
71 {options.map((sec) => (
72 <DropdownMenuItem
73 key={sec}
74 onSelect={() => onChange(sec)}
75 className={cn(
76 "cursor-pointer",
77 "justify-center rounded-lg px-3 py-1.5 font-mono text-[12px]",
78 sec === value &&
79 "bg-foreground/[0.06] font-medium text-foreground/85",
80 )}
81 >
82 {sec}s
83 </DropdownMenuItem>
84 ))}
85 </DropdownMenuContent>
86 </DropdownMenu>
87 );
88 }
89
89 lines Plain Text