Aymeric Rabot f1d0d3a78c
feat(editor): redesign panel UI with shared control components (#127)
Extract reusable control components (SliderControl, MetricControl, SegmentedControl,
ToggleControl, ActionButton, PanelSection) and PanelWrapper to reduce duplication
across all property panels. Net reduction of ~500 lines.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 02:35:19 -05:00

68 lines
2.0 KiB
TypeScript

'use client'
import { cn } from '@/lib/utils'
import { ChevronDown } from 'lucide-react'
import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
interface PanelSectionProps {
title: string
children: React.ReactNode
defaultExpanded?: boolean
className?: string
}
export function PanelSection({
title,
children,
defaultExpanded = true,
className,
}: PanelSectionProps) {
const [isExpanded, setIsExpanded] = useState(defaultExpanded)
return (
<motion.div
layout
transition={{ type: "spring", bounce: 0, duration: 0.4 }}
className={cn("flex flex-col shrink-0 overflow-hidden border-b border-border/50", className)}
>
<motion.button
layout="position"
type="button"
onClick={() => setIsExpanded(!isExpanded)}
className={cn(
"group/section flex items-center justify-between h-10 px-3 transition-all duration-200 shrink-0",
isExpanded
? "bg-accent/50 text-foreground"
: "text-muted-foreground hover:bg-accent/30 hover:text-foreground"
)}
>
<span className="font-medium text-sm truncate">{title}</span>
<ChevronDown
className={cn(
"h-4 w-4 transition-transform duration-200",
isExpanded ? "rotate-180" : "rotate-0",
isExpanded ? "text-foreground" : "opacity-0 group-hover/section:opacity-100"
)}
/>
</motion.button>
<AnimatePresence initial={false}>
{isExpanded && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ type: "spring", bounce: 0, duration: 0.4 }}
className="overflow-hidden"
>
<div className="flex flex-col gap-1.5 p-3 pt-2">
{children}
</div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
)
}