{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "floating-panel", "type": "registry:ui", "description": "Floating panel component with backdrop blur and position-aware animations", "dependencies": [ "motion" ], "files": [ { "path": "registry/default/ui/floating-panel.tsx", "content": "\"use client\"\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\"\nimport { ArrowLeftIcon } from \"lucide-react\"\nimport { AnimatePresence, motion, MotionConfig, Variants } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst TRANSITION = {\n type: \"spring\" as const,\n bounce: 0.1,\n duration: 0.4,\n}\n\ninterface FloatingPanelContextType {\n isOpen: boolean\n openFloatingPanel: (rect: DOMRect) => void\n closeFloatingPanel: () => void\n uniqueId: string\n note: string\n setNote: (note: string) => void\n triggerRect: DOMRect | null\n title: string\n setTitle: (title: string) => void\n}\n\nconst FloatingPanelContext = createContext<\n FloatingPanelContextType | undefined\n>(undefined)\n\nfunction useFloatingPanel() {\n const context = useContext(FloatingPanelContext)\n if (!context) {\n throw new Error(\n \"useFloatingPanel must be used within a FloatingPanelProvider\"\n )\n }\n return context\n}\n\nfunction useFloatingPanelLogic() {\n const uniqueId = useId()\n const [isOpen, setIsOpen] = useState(false)\n const [note, setNote] = useState(\"\")\n const [triggerRect, setTriggerRect] = useState(null)\n const [title, setTitle] = useState(\"\")\n\n const openFloatingPanel = (rect: DOMRect) => {\n setTriggerRect(rect)\n setIsOpen(true)\n }\n const closeFloatingPanel = () => {\n setIsOpen(false)\n setNote(\"\")\n }\n\n return {\n isOpen,\n openFloatingPanel,\n closeFloatingPanel,\n uniqueId,\n note,\n setNote,\n triggerRect,\n title,\n setTitle,\n }\n}\n\ninterface FloatingPanelRootProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FloatingPanelRoot({\n children,\n className,\n}: FloatingPanelRootProps) {\n const floatingPanelLogic = useFloatingPanelLogic()\n\n return (\n \n \n
{children}
\n
\n
\n )\n}\n\ninterface FloatingPanelTriggerProps {\n children: React.ReactNode\n className?: string\n title: string\n}\n\nexport function FloatingPanelTrigger({\n children,\n className,\n title,\n}: FloatingPanelTriggerProps) {\n const { openFloatingPanel, uniqueId, setTitle } = useFloatingPanel()\n const triggerRef = useRef(null)\n\n const handleClick = () => {\n if (triggerRef.current) {\n openFloatingPanel(triggerRef.current.getBoundingClientRect())\n setTitle(title)\n }\n }\n\n return (\n \n \n \n {children}\n \n \n \n )\n}\n\ninterface FloatingPanelContentProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FloatingPanelContent({\n children,\n className,\n}: FloatingPanelContentProps) {\n const { isOpen, closeFloatingPanel, uniqueId, triggerRect, title } =\n useFloatingPanel()\n const contentRef = useRef(null)\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n contentRef.current &&\n !contentRef.current.contains(event.target as Node)\n ) {\n closeFloatingPanel()\n }\n }\n document.addEventListener(\"mousedown\", handleClickOutside)\n return () => document.removeEventListener(\"mousedown\", handleClickOutside)\n }, [closeFloatingPanel])\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") closeFloatingPanel()\n }\n document.addEventListener(\"keydown\", handleKeyDown)\n return () => document.removeEventListener(\"keydown\", handleKeyDown)\n }, [closeFloatingPanel])\n\n const variants: Variants = {\n hidden: { opacity: 0, scale: 0.9, y: 10 },\n visible: { opacity: 1, scale: 1, y: 0 },\n }\n\n return (\n \n {isOpen && (\n <>\n \n \n {title}\n {children}\n \n \n )}\n \n )\n}\n\ninterface FloatingPanelTitleProps {\n children: React.ReactNode\n}\n\nfunction FloatingPanelTitle({ children }: FloatingPanelTitleProps) {\n const { uniqueId } = useFloatingPanel()\n\n return (\n \n \n {children}\n \n \n )\n}\n\ninterface FloatingPanelFormProps {\n children: React.ReactNode\n onSubmit?: (note: string) => void\n className?: string\n}\n\nexport function FloatingPanelForm({\n children,\n onSubmit,\n className,\n}: FloatingPanelFormProps) {\n const { note, closeFloatingPanel } = useFloatingPanel()\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault()\n onSubmit?.(note)\n closeFloatingPanel()\n }\n\n return (\n \n {children}\n \n )\n}\n\ninterface FloatingPanelLabelProps {\n children: React.ReactNode\n htmlFor: string\n className?: string\n}\n\nexport function FloatingPanelLabel({\n children,\n htmlFor,\n className,\n}: FloatingPanelLabelProps) {\n const { note } = useFloatingPanel()\n\n return (\n \n {children}\n \n )\n}\n\ninterface FloatingPanelTextareaProps {\n className?: string\n id?: string\n}\n\nexport function FloatingPanelTextarea({\n className,\n id,\n}: FloatingPanelTextareaProps) {\n const { note, setNote } = useFloatingPanel()\n\n return (\n setNote(e.target.value)}\n />\n )\n}\n\ninterface FloatingPanelHeaderProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FloatingPanelHeader({\n children,\n className,\n}: FloatingPanelHeaderProps) {\n return (\n \n {children}\n \n )\n}\n\ninterface FloatingPanelBodyProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FloatingPanelBody({\n children,\n className,\n}: FloatingPanelBodyProps) {\n return (\n \n {children}\n \n )\n}\n\ninterface FloatingPanelFooterProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FloatingPanelFooter({\n children,\n className,\n}: FloatingPanelFooterProps) {\n return (\n \n {children}\n \n )\n}\n\ninterface FloatingPanelCloseButtonProps {\n className?: string\n}\n\nexport function FloatingPanelCloseButton({\n className,\n}: FloatingPanelCloseButtonProps) {\n const { closeFloatingPanel } = useFloatingPanel()\n\n return (\n \n \n \n )\n}\n\ninterface FloatingPanelSubmitButtonProps {\n className?: string\n}\n\nexport function FloatingPanelSubmitButton({\n className,\n}: FloatingPanelSubmitButtonProps) {\n return (\n \n Submit Note\n \n )\n}\n\ninterface FloatingPanelButtonProps {\n children: React.ReactNode\n onClick?: () => void\n className?: string\n}\n\nexport function FloatingPanelButton({\n children,\n onClick,\n className,\n}: FloatingPanelButtonProps) {\n return (\n \n {children}\n \n )\n}\n\nexport default {\n Root: FloatingPanelRoot,\n Trigger: FloatingPanelTrigger,\n Content: FloatingPanelContent,\n Form: FloatingPanelForm,\n Label: FloatingPanelLabel,\n Textarea: FloatingPanelTextarea,\n Header: FloatingPanelHeader,\n Body: FloatingPanelBody,\n Footer: FloatingPanelFooter,\n CloseButton: FloatingPanelCloseButton,\n SubmitButton: FloatingPanelSubmitButton,\n Button: FloatingPanelButton,\n}\n// \"use client\"\n\n// import React, {\n// createContext,\n// useContext,\n// useEffect,\n// useId,\n// useRef,\n// useState,\n// } from \"react\"\n// import { AnimatePresence, MotionConfig, motion } from \"motion/react\"\n// import { ArrowLeftIcon } from \"lucide-react\"\n\n// import { cn } from \"@/lib/utils\"\n\n// const TRANSITION = {\n// type: \"spring\",\n// bounce: 0.1,\n// duration: 0.4,\n// }\n\n// interface FloatingPanelContextType {\n// isOpen: boolean\n// openFloatingPanel: (rect: DOMRect) => void\n// closeFloatingPanel: () => void\n// uniqueId: string\n// note: string\n// setNote: (note: string) => void\n// triggerRect: DOMRect | null\n// title: string\n// setTitle: (title: string) => void\n// }\n\n// const FloatingPanelContext = createContext<\n// FloatingPanelContextType | undefined\n// >(undefined)\n\n// function useFloatingPanel() {\n// const context = useContext(FloatingPanelContext)\n// if (!context) {\n// throw new Error(\n// \"useFloatingPanel must be used within a FloatingPanelProvider\"\n// )\n// }\n// return context\n// }\n\n// function useFloatingPanelLogic() {\n// const uniqueId = useId()\n// const [isOpen, setIsOpen] = useState(false)\n// const [note, setNote] = useState(\"\")\n// const [triggerRect, setTriggerRect] = useState(null)\n// const [title, setTitle] = useState(\"\")\n\n// const openFloatingPanel = (rect: DOMRect) => {\n// setTriggerRect(rect)\n// setIsOpen(true)\n// }\n// const closeFloatingPanel = () => {\n// setIsOpen(false)\n// setNote(\"\")\n// }\n\n// return {\n// isOpen,\n// openFloatingPanel,\n// closeFloatingPanel,\n// uniqueId,\n// note,\n// setNote,\n// triggerRect,\n// title,\n// setTitle,\n// }\n// }\n\n// interface FloatingPanelRootProps {\n// children: React.ReactNode\n// className?: string\n// }\n\n// export function FloatingPanelRoot({\n// children,\n// className,\n// }: FloatingPanelRootProps) {\n// const floatingPanelLogic = useFloatingPanelLogic()\n\n// return (\n// \n// \n//
{children}
\n//
\n//
\n// )\n// }\n\n// interface FloatingPanelTriggerProps {\n// children: React.ReactNode\n// className?: string\n// title: string\n// }\n\n// export function FloatingPanelTrigger({\n// children,\n// className,\n// title,\n// }: FloatingPanelTriggerProps) {\n// const { openFloatingPanel, uniqueId, setTitle } = useFloatingPanel()\n// const triggerRef = useRef(null)\n\n// const handleClick = () => {\n// if (triggerRef.current) {\n// openFloatingPanel(triggerRef.current.getBoundingClientRect())\n// setTitle(title)\n// }\n// }\n\n// return (\n// \n// \n// {children}\n// \n// \n// )\n// }\n\n// interface FloatingPanelContentProps {\n// children: React.ReactNode\n// className?: string\n// }\n\n// export function FloatingPanelContent({\n// children,\n// className,\n// }: FloatingPanelContentProps) {\n// const { isOpen, closeFloatingPanel, uniqueId, triggerRect, title } =\n// useFloatingPanel()\n// const contentRef = useRef(null)\n\n// useEffect(() => {\n// const handleClickOutside = (event: MouseEvent) => {\n// if (\n// contentRef.current &&\n// !contentRef.current.contains(event.target as Node)\n// ) {\n// closeFloatingPanel()\n// }\n// }\n// document.addEventListener(\"mousedown\", handleClickOutside)\n// return () => document.removeEventListener(\"mousedown\", handleClickOutside)\n// }, [closeFloatingPanel])\n\n// useEffect(() => {\n// const handleKeyDown = (event: KeyboardEvent) => {\n// if (event.key === \"Escape\") closeFloatingPanel()\n// }\n// document.addEventListener(\"keydown\", handleKeyDown)\n// return () => document.removeEventListener(\"keydown\", handleKeyDown)\n// }, [closeFloatingPanel])\n\n// const variants = {\n// hidden: { opacity: 0, scale: 0.9, y: 10 },\n// visible: { opacity: 1, scale: 1, y: 0 },\n// }\n\n// return (\n// \n// {isOpen && (\n// <>\n// \n// \n// {title}\n// {children}\n// \n// \n// )}\n// \n// )\n// }\n\n// interface FloatingPanelTitleProps {\n// children: React.ReactNode\n// }\n\n// function FloatingPanelTitle({ children }: FloatingPanelTitleProps) {\n// const { uniqueId } = useFloatingPanel()\n\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// interface FloatingPanelFormProps {\n// children: React.ReactNode\n// onSubmit?: (note: string) => void\n// className?: string\n// }\n\n// export function FloatingPanelForm({\n// children,\n// onSubmit,\n// className,\n// }: FloatingPanelFormProps) {\n// const { note, closeFloatingPanel } = useFloatingPanel()\n\n// const handleSubmit = (e: React.FormEvent) => {\n// e.preventDefault()\n// onSubmit?.(note)\n// closeFloatingPanel()\n// }\n\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// interface FloatingPanelLabelProps {\n// children: React.ReactNode\n// htmlFor: string\n// className?: string\n// }\n\n// export function FloatingPanelLabel({\n// children,\n// htmlFor,\n// className,\n// }: FloatingPanelLabelProps) {\n// const { note } = useFloatingPanel()\n\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// interface FloatingPanelTextareaProps {\n// className?: string\n// id?: string\n// }\n\n// export function FloatingPanelTextarea({\n// className,\n// id,\n// }: FloatingPanelTextareaProps) {\n// const { note, setNote } = useFloatingPanel()\n\n// return (\n// setNote(e.target.value)}\n// />\n// )\n// }\n\n// interface FloatingPanelHeaderProps {\n// children: React.ReactNode\n// className?: string\n// }\n\n// export function FloatingPanelHeader({\n// children,\n// className,\n// }: FloatingPanelHeaderProps) {\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// interface FloatingPanelBodyProps {\n// children: React.ReactNode\n// className?: string\n// }\n\n// export function FloatingPanelBody({\n// children,\n// className,\n// }: FloatingPanelBodyProps) {\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// interface FloatingPanelFooterProps {\n// children: React.ReactNode\n// className?: string\n// }\n\n// export function FloatingPanelFooter({\n// children,\n// className,\n// }: FloatingPanelFooterProps) {\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// interface FloatingPanelCloseButtonProps {\n// className?: string\n// }\n\n// export function FloatingPanelCloseButton({\n// className,\n// }: FloatingPanelCloseButtonProps) {\n// const { closeFloatingPanel } = useFloatingPanel()\n\n// return (\n// \n// \n// \n// )\n// }\n\n// interface FloatingPanelSubmitButtonProps {\n// className?: string\n// }\n\n// export function FloatingPanelSubmitButton({\n// className,\n// }: FloatingPanelSubmitButtonProps) {\n// return (\n// \n// Submit Note\n// \n// )\n// }\n\n// interface FloatingPanelButtonProps {\n// children: React.ReactNode\n// onClick?: () => void\n// className?: string\n// }\n\n// export function FloatingPanelButton({\n// children,\n// onClick,\n// className,\n// }: FloatingPanelButtonProps) {\n// return (\n// \n// {children}\n// \n// )\n// }\n\n// export default {\n// Root: FloatingPanelRoot,\n// Trigger: FloatingPanelTrigger,\n// Content: FloatingPanelContent,\n// Form: FloatingPanelForm,\n// Label: FloatingPanelLabel,\n// Textarea: FloatingPanelTextarea,\n// Header: FloatingPanelHeader,\n// Body: FloatingPanelBody,\n// Footer: FloatingPanelFooter,\n// CloseButton: FloatingPanelCloseButton,\n// SubmitButton: FloatingPanelSubmitButton,\n// Button: FloatingPanelButton,\n// }\n", "type": "registry:ui" } ] }