{
  "name": "morphing-popover-textarea",
  "type": "registry:ui",
  "componentName": "morphing-popover",
  "description": "Morphing popover integrated with a textarea input.",
  "files": [
    {
      "path": "morphing-popover-textarea.tsx",
      "content": "'use client';\nimport {\n  MorphingPopover,\n  MorphingPopoverTrigger,\n  MorphingPopoverContent,\n} from '@/components/core/morphing-popover';\nimport { motion } from 'motion/react';\nimport { useId, useState } from 'react';\nimport { ArrowLeftIcon } from 'lucide-react';\n\nexport function MorphingPopoverTextarea() {\n  const uniqueId = useId();\n  const [note, setNote] = useState('');\n  const [isOpen, setIsOpen] = useState(false);\n\n  const closeMenu = () => {\n    setNote('');\n    setIsOpen(false);\n  };\n\n  return (\n    <MorphingPopover\n      transition={{\n        type: 'spring',\n        bounce: 0.05,\n        duration: 0.3,\n      }}\n      open={isOpen}\n      onOpenChange={setIsOpen}\n    >\n      <MorphingPopoverTrigger className='flex h-9 items-center rounded-lg border border-zinc-950/10 bg-white px-3 text-zinc-950 dark:border-zinc-50/10 dark:bg-zinc-700 dark:text-zinc-50'>\n        <motion.span layoutId={`popover-label-${uniqueId}`} className='text-sm'>\n          Add Note\n        </motion.span>\n      </MorphingPopoverTrigger>\n      <MorphingPopoverContent className='rounded-xl border border-zinc-950/10 bg-white p-0 shadow-[0_9px_9px_0px_rgba(0,0,0,0.01),_0_2px_5px_0px_rgba(0,0,0,0.06)] dark:bg-zinc-700'>\n        <div className='h-[200px] w-[364px]'>\n          <form\n            className='flex h-full flex-col'\n            onSubmit={(e) => {\n              e.preventDefault();\n            }}\n          >\n            <motion.span\n              layoutId={`popover-label-${uniqueId}`}\n              aria-hidden='true'\n              style={{\n                opacity: note ? 0 : 1,\n              }}\n              className='absolute top-3 left-4 text-sm text-zinc-500 select-none dark:text-zinc-400'\n            >\n              Add Note\n            </motion.span>\n            <textarea\n              className='h-full w-full resize-none rounded-md bg-transparent px-4 py-3 text-sm outline-hidden'\n              autoFocus\n              onChange={(e) => setNote(e.target.value)}\n            />\n            <div key='close' className='flex justify-between py-3 pr-4 pl-2'>\n              <button\n                type='button'\n                className='flex items-center rounded-lg bg-white px-2 py-1 text-sm text-zinc-950 hover:bg-zinc-100 dark:bg-zinc-700 dark:text-zinc-50 dark:hover:bg-zinc-600'\n                onClick={closeMenu}\n                aria-label='Close popover'\n              >\n                <ArrowLeftIcon\n                  size={16}\n                  className='text-zinc-900 dark:text-zinc-100'\n                />\n              </button>\n              <button\n                className='relative ml-1 flex h-8 shrink-0 scale-100 appearance-none items-center justify-center rounded-lg border border-zinc-950/10 bg-transparent px-2 text-sm text-zinc-500 transition-colors select-none hover:bg-zinc-100 hover:text-zinc-800 focus-visible:ring-2 active:scale-[0.98] dark:border-zinc-50/10 dark:text-zinc-50 dark:hover:bg-zinc-800'\n                type='submit'\n                aria-label='Submit note'\n                onClick={() => {\n                  closeMenu();\n                }}\n              >\n                Submit\n              </button>\n            </div>\n          </form>\n        </div>\n      </MorphingPopoverContent>\n    </MorphingPopover>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "components/core/morphing-popover.tsx",
      "content": "'use client';\n\nimport {\n  useState,\n  useId,\n  useRef,\n  useEffect,\n  useCallback,\n  createContext,\n  useContext,\n  isValidElement,\n} from 'react';\nimport {\n  AnimatePresence,\n  MotionConfig,\n  motion,\n  Transition,\n  Variants,\n} from 'motion/react';\nimport useClickOutside from '@/hooks/useClickOutside';\nimport { cn } from '@/lib/utils';\n\nconst TRANSITION: Transition = {\n  type: 'spring',\n  bounce: 0.1,\n  duration: 0.4,\n};\n\ntype MorphingPopoverContextValue = {\n  isOpen: boolean;\n  open: () => void;\n  close: () => void;\n  uniqueId: string;\n  variants?: Variants;\n};\n\nconst MorphingPopoverContext =\n  createContext<MorphingPopoverContextValue | null>(null);\n\nfunction usePopoverLogic({\n  defaultOpen = false,\n  open: controlledOpen,\n  onOpenChange,\n}: {\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n} = {}) {\n  const uniqueId = useId();\n  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n\n  const isOpen = controlledOpen ?? uncontrolledOpen;\n\n  const open = useCallback(() => {\n    if (controlledOpen === undefined) {\n      setUncontrolledOpen(true);\n    }\n    onOpenChange?.(true);\n  }, [controlledOpen, onOpenChange]);\n\n  const close = useCallback(() => {\n    if (controlledOpen === undefined) {\n      setUncontrolledOpen(false);\n    }\n    onOpenChange?.(false);\n  }, [controlledOpen, onOpenChange]);\n\n  return { isOpen, open, close, uniqueId };\n}\n\nexport type MorphingPopoverProps = {\n  children: React.ReactNode;\n  transition?: Transition;\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  variants?: Variants;\n  className?: string;\n} & React.ComponentProps<'div'>;\n\nfunction MorphingPopover({\n  children,\n  transition = TRANSITION,\n  defaultOpen,\n  open,\n  onOpenChange,\n  variants,\n  className,\n  ...props\n}: MorphingPopoverProps) {\n  const popoverLogic = usePopoverLogic({ defaultOpen, open, onOpenChange });\n\n  return (\n    <MorphingPopoverContext.Provider value={{ ...popoverLogic, variants }}>\n      <MotionConfig transition={transition}>\n        <div\n          className={cn('relative flex items-center justify-center', className)}\n          key={popoverLogic.uniqueId}\n          {...props}\n        >\n          {children}\n        </div>\n      </MotionConfig>\n    </MorphingPopoverContext.Provider>\n  );\n}\n\nexport type MorphingPopoverTriggerProps = {\n  asChild?: boolean;\n  children: React.ReactNode;\n  className?: string;\n} & React.ComponentProps<typeof motion.button>;\n\nfunction MorphingPopoverTrigger({\n  children,\n  className,\n  asChild = false,\n  ...props\n}: MorphingPopoverTriggerProps) {\n  const context = useContext(MorphingPopoverContext);\n  if (!context) {\n    throw new Error(\n      'MorphingPopoverTrigger must be used within MorphingPopover'\n    );\n  }\n\n  if (asChild && isValidElement(children)) {\n    const MotionComponent = motion.create(\n      children.type as React.ForwardRefExoticComponent<any>\n    );\n    const childProps = children.props as Record<string, unknown>;\n\n    return (\n      <MotionComponent\n        {...childProps}\n        onClick={context.open}\n        layoutId={`popover-trigger-${context.uniqueId}`}\n        className={childProps.className}\n        key={context.uniqueId}\n        aria-expanded={context.isOpen}\n        aria-controls={`popover-content-${context.uniqueId}`}\n      />\n    );\n  }\n\n  return (\n    <motion.div\n      key={context.uniqueId}\n      layoutId={`popover-trigger-${context.uniqueId}`}\n      onClick={context.open}\n    >\n      <motion.button\n        {...props}\n        layoutId={`popover-label-${context.uniqueId}`}\n        key={context.uniqueId}\n        className={className}\n        aria-expanded={context.isOpen}\n        aria-controls={`popover-content-${context.uniqueId}`}\n      >\n        {children}\n      </motion.button>\n    </motion.div>\n  );\n}\n\nexport type MorphingPopoverContentProps = {\n  children: React.ReactNode;\n  className?: string;\n} & React.ComponentProps<typeof motion.div>;\n\nfunction MorphingPopoverContent({\n  children,\n  className,\n  ...props\n}: MorphingPopoverContentProps) {\n  const context = useContext(MorphingPopoverContext);\n  if (!context)\n    throw new Error(\n      'MorphingPopoverContent must be used within MorphingPopover'\n    );\n\n  const { close, isOpen, uniqueId, variants } = context;\n  const ref = useRef<HTMLDivElement>(null);\n  useClickOutside(ref, close);\n\n  useEffect(() => {\n    if (!isOpen) return;\n\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === 'Escape') close();\n    };\n\n    document.addEventListener('keydown', handleKeyDown);\n    return () => document.removeEventListener('keydown', handleKeyDown);\n  }, [close, isOpen]);\n\n  return (\n    <AnimatePresence>\n      {isOpen && (\n        <>\n          <motion.div\n            {...props}\n            ref={ref}\n            layoutId={`popover-trigger-${uniqueId}`}\n            key={uniqueId}\n            id={`popover-content-${uniqueId}`}\n            role='dialog'\n            aria-modal='true'\n            className={cn(\n              'absolute overflow-hidden rounded-md border border-zinc-950/10 bg-white p-2 text-zinc-950 shadow-md dark:border-zinc-50/10 dark:bg-zinc-700 dark:text-zinc-50',\n              className\n            )}\n            initial='initial'\n            animate='animate'\n            exit='exit'\n            variants={variants}\n          >\n            {children}\n          </motion.div>\n        </>\n      )}\n    </AnimatePresence>\n  );\n}\n\nexport { MorphingPopover, MorphingPopoverTrigger, MorphingPopoverContent };\n",
      "type": "registry:ui"
    },
    {
      "path": "components/core/hooks/useClickOutside.tsx",
      "content": "import { RefObject, useEffect } from 'react';\n\nfunction useClickOutside<T extends HTMLElement>(\n  ref: RefObject<T | null>,\n  handler: (event: MouseEvent | TouchEvent) => void\n): void {\n  useEffect(() => {\n    const handleClickOutside = (event: MouseEvent | TouchEvent) => {\n      if (!ref || !ref.current || ref.current.contains(event.target as Node)) {\n        return;\n      }\n\n      handler(event);\n    };\n\n    document.addEventListener('mousedown', handleClickOutside);\n    document.addEventListener('touchstart', handleClickOutside);\n\n    return () => {\n      document.removeEventListener('mousedown', handleClickOutside);\n      document.removeEventListener('touchstart', handleClickOutside);\n    };\n  }, [ref, handler]);\n}\n\nexport default useClickOutside;\n",
      "type": "registry:hook"
    }
  ]
}