'use client' import * as React from 'react' import { motion, useMotionValue, useSpring, useTransform } from 'motion/react' import { GripVerticalIcon, X } from 'lucide-react' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' interface SlidingPanelContextType { panelWidth: number setPanelWidth: (width: number) => void motionPanelWidth: ReturnType> isPanelOpen: boolean closePanel: () => void } const SlidingPanelContext = React.createContext(undefined) const useSlidingPanel = () => { const context = React.useContext(SlidingPanelContext) if (!context) { throw new Error('useSlidingPanel must be used within a SlidingPanel') } return context } export interface SlidingPanelProps { children: React.ReactNode isOpen: boolean onClose: () => void defaultWidth?: number minWidth?: number maxWidth?: number className?: string } export function SlidingPanel({ children, isOpen, onClose, defaultWidth = 50, minWidth = 30, maxWidth = 70, className, }: SlidingPanelProps) { const [isDragging, setIsDragging] = React.useState(false) const motionValue = useMotionValue(defaultWidth) const motionPanelWidth = useSpring(motionValue, { bounce: 0, duration: isDragging ? 0 : 300, }) const [panelWidth, setPanelWidth] = React.useState(defaultWidth) // Calculate resizer position - must be called unconditionally const resizerLeft = useTransform(motionPanelWidth, (value) => `${value}%`) const handleDrag = (domRect: DOMRect, clientX: number) => { if (!isDragging) return const x = clientX - domRect.left const percentage = Math.min( Math.max((x / domRect.width) * 100, minWidth), maxWidth ) motionValue.set(percentage) setPanelWidth(percentage) } const handleMouseDrag = (event: React.MouseEvent) => { if (!event.currentTarget) return const containerRect = event.currentTarget.getBoundingClientRect() handleDrag(containerRect, event.clientX) } const handleTouchDrag = (event: React.TouchEvent) => { if (!event.currentTarget) return const containerRect = event.currentTarget.getBoundingClientRect() const touch = event.touches[0] if (touch) { handleDrag(containerRect, touch.clientX) } } return (
setIsDragging(false)} onMouseLeave={() => setIsDragging(false)} onTouchMove={handleTouchDrag} onTouchEnd={() => setIsDragging(false)} > {children} {/* Resizer Handle */} {isOpen && ( setIsDragging(true)} onTouchStart={() => setIsDragging(true)} > )}
) } export interface SlidingPanelContentProps { children: React.ReactNode className?: string } export function SlidingPanelMain({ children, className }: SlidingPanelContentProps) { const { motionPanelWidth, isPanelOpen } = useSlidingPanel() const width = useTransform( motionPanelWidth, (value: number) => isPanelOpen ? `${value}%` : '100%' ) return ( {children} ) } export interface SlidingPanelSideProps { children: React.ReactNode className?: string title?: string } export function SlidingPanelSide({ children, className, title }: SlidingPanelSideProps) { const { motionPanelWidth, isPanelOpen, closePanel } = useSlidingPanel() const width = useTransform( motionPanelWidth, (value: number) => `${100 - value}%` ) if (!isPanelOpen) return null return ( {/* Header */}

{title || 'My Awesome List'}

{/* Content */}
{children}
) }