{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shuffle-pinned-item",
  "type": "registry:component",
  "title": "Shuffle Pinned List",
  "description": "A dynamic list that promotes pinned items to a shuffleable hero header with spring animations and interactive pin toggles.",
  "dependencies": [
    "lucide-react",
    "motion",
    "react-icons"
  ],
  "files": [
    {
      "path": "components/watermelon/shuffle-pinned-item.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport {\n  useState,\n  useCallback,\n  useMemo,\n  useRef,\n  useEffect,\n  type FC,\n} from 'react';\nimport {\n  motion,\n  AnimatePresence,\n  MotionConfig,\n  type Transition,\n} from 'motion/react';\nimport { Pin, ChevronsUpDown } from 'lucide-react';\nimport { BsChatFill } from 'react-icons/bs';\n\nexport interface ListItem {\n  id: string;\n  text: string;\n  isPinned: boolean;\n}\n\ninterface ShufflePinnedListProps {\n  items?: ListItem[];\n  onPinChange?: (updatedItems: ListItem[]) => void;\n  onShuffle?: (currentHeroItem?: ListItem) => void;\n}\n\nconst DEFAULT_ITEMS: ListItem[] = [\n  { id: '1', text: 'Daily Fitness Tracker', isPinned: false },\n  { id: '2', text: 'Voice Command Tips', isPinned: false },\n  { id: '3', text: 'iOS Shortcuts Guide', isPinned: false },\n  { id: '4', text: 'Focus Mode Ideas', isPinned: false },\n  { id: '5', text: '50 Productivity Hacks', isPinned: false },\n  { id: '6', text: 'Lunch Recipe Ideas', isPinned: false },\n  { id: '7', text: 'Snack Ideas For Kids', isPinned: false },\n];\n\nconst springConfig: Transition = {\n  type: 'spring',\n  stiffness: 400,\n  damping: 40,\n};\n\nexport const ShufflePinnedList: FC<ShufflePinnedListProps> = ({\n  items: propItems,\n  onPinChange,\n  onShuffle,\n}) => {\n  const [items, setItems] = useState<ListItem[]>(propItems ?? DEFAULT_ITEMS);\n  const [activePinnedIndex, setActivePinnedIndex] = useState<number>(0);\n  const [showFade, setShowFade] = useState(false);\n\n  const scrollRef = useRef<HTMLDivElement>(null);\n\n  const pinnedItems = useMemo(\n    () => items.filter((item) => item.isPinned),\n    [items],\n  );\n\n  const togglePin = useCallback(\n    (id: string) => {\n      setItems((prev) => {\n        const updated = prev.map((item) =>\n          item.id === id ? { ...item, isPinned: !item.isPinned } : item,\n        );\n\n        const toggledItem = prev.find((i) => i.id === id);\n\n        if (!toggledItem?.isPinned) {\n          const newPinnedItems = updated.filter((i) => i.isPinned);\n          const newIndex = newPinnedItems.findIndex((i) => i.id === id);\n          setActivePinnedIndex(newIndex);\n        }\n\n        if (toggledItem?.isPinned) {\n          const remainingPinned = updated.filter((i) => i.isPinned);\n          setActivePinnedIndex((current) =>\n            Math.min(current, Math.max(remainingPinned.length - 1, 0)),\n          );\n        }\n\n        onPinChange?.(updated);\n        return updated;\n      });\n    },\n    [onPinChange],\n  );\n\n  const shufflePinned = useCallback(() => {\n    if (pinnedItems.length <= 1) return;\n    setActivePinnedIndex((i) => (i + 1) % pinnedItems.length);\n    onShuffle?.(pinnedItems[(activePinnedIndex + 1) % pinnedItems.length]);\n  }, [pinnedItems, activePinnedIndex, onShuffle]);\n\n  const currentHeroItem = pinnedItems[activePinnedIndex];\n\n  useEffect(() => {\n    const el = scrollRef.current;\n    if (!el) return;\n\n    const checkScroll = () => {\n      const isAtBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;\n      setShowFade(!isAtBottom);\n    };\n\n    checkScroll();\n    el.addEventListener('scroll', checkScroll);\n    window.addEventListener('resize', checkScroll);\n\n    return () => {\n      el.removeEventListener('scroll', checkScroll);\n      window.removeEventListener('resize', checkScroll);\n    };\n  }, [items]);\n\n  return (\n    <div className=\"relative h-[500px] w-xs overflow-hidden rounded-4xl border border-[#E5E5E9] bg-[#fefefe] px-4 py-4 sm:w-sm dark:border-neutral-800 dark:bg-neutral-900\">\n      <MotionConfig transition={springConfig}>\n        <motion.div\n          ref={scrollRef}\n          layout\n          className=\"no-scrollbar relative h-full overflow-y-scroll scroll-smooth\"\n        >\n          <div className=\"space-y-2\">\n            <motion.div\n              layout\n              className=\"overflow-hidden\"\n              transition={{ type: 'spring', stiffness: 300, damping: 30 }}\n            >\n              <AnimatePresence mode=\"popLayout\">\n                {pinnedItems.length > 0 && (\n                  <motion.div\n                    key=\"open\"\n                    layout\n                    initial={{ opacity: 0, scale: 0.8, y: -20 }}\n                    animate={{ opacity: 1, scale: 1, y: 0 }}\n                    exit={{ opacity: 0, scale: 0.8, y: -20 }}\n                    className=\"flex cursor-pointer items-center justify-between rounded-full bg-[#F6F5FA] p-3 py-2 dark:bg-neutral-800\"\n                    onClick={shufflePinned}\n                  >\n                    <div className=\"flex min-w-0 flex-1 items-center gap-4\">\n                      <div className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-white dark:bg-neutral-700\">\n                        <Pin\n                          className=\"h-5 w-5 text-[#D9D9DF] dark:text-neutral-400\"\n                          fill=\"currentColor\"\n                        />\n                      </div>\n                      <AnimatePresence mode=\"popLayout\">\n                        <motion.span\n                          key={currentHeroItem?.id}\n                          initial={{\n                            opacity: 0,\n                            scale: 0.7,\n                            filter: 'blur(8px)',\n                          }}\n                          animate={{\n                            opacity: 1,\n                            scale: 1,\n                            filter: 'blur(0px)',\n                          }}\n                          exit={{ opacity: 0, scale: 0.7, filter: 'blur(8px)' }}\n                          transition={{\n                            duration: 0.6,\n                            type: 'spring',\n                            bounce: 0,\n                          }}\n                          className=\"truncate text-lg font-bold text-[#29292D] dark:text-neutral-100\"\n                        >\n                          {currentHeroItem?.text}\n                        </motion.span>\n                      </AnimatePresence>\n                    </div>\n\n                    {pinnedItems.length > 1 && (\n                      <motion.button\n                        whileTap={{ scale: 0.95 }}\n                        className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-[#fefefe] text-gray-400 dark:bg-neutral-700 dark:text-neutral-400\"\n                      >\n                        <ChevronsUpDown className=\"h-6 w-6\" />\n                      </motion.button>\n                    )}\n                  </motion.div>\n                )}\n              </AnimatePresence>\n            </motion.div>\n\n            <motion.h1\n              layout\n              className=\"px-2 text-base font-semibold text-[#ADACB4] dark:text-neutral-500\"\n            >\n              Today\n            </motion.h1>\n\n            <div className=\"w-full space-y-1\">\n              <AnimatePresence mode=\"popLayout\" initial={false}>\n                {items.map((item) => {\n                  const isHighlighted = currentHeroItem?.id === item.id;\n                  return (\n                    <motion.div\n                      layout\n                      key={item.id}\n                      initial={{ opacity: 0, scale: 0.9 }}\n                      animate={{\n                        opacity: 1,\n                        scale: isHighlighted ? [1, 1.03, 1] : 1,\n                      }}\n                      exit={{ opacity: 0, scale: 0.9 }}\n                      transition={{\n                        type: 'spring',\n                        stiffness: 400,\n                        damping: 30,\n                        scale: { duration: 0.3 },\n                      }}\n                      className=\"group relative flex cursor-default items-center justify-between overflow-hidden rounded-full p-2 px-2 transition-colors hover:bg-[#F6F5FA] dark:hover:bg-neutral-800\"\n                    >\n                      {isHighlighted && (\n                        <motion.div\n                          initial={{ opacity: 0 }}\n                          animate={{ opacity: [0, 1, 0] }}\n                          transition={{ duration: 1, times: [0, 0.2, 1] }}\n                          className=\"pointer-events-none absolute inset-0 bg-[#F6F5FA] dark:bg-neutral-800\"\n                        />\n                      )}\n\n                      <div className=\"relative z-10 flex min-w-0 flex-1 items-center gap-4\">\n                        <div className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-full border border-[#E5E5E9] bg-[#FEFEFE] pl-0.5 dark:border-neutral-700 dark:bg-neutral-800\">\n                          <BsChatFill\n                            className=\"h-5 w-5 text-[#D5D4E0] dark:text-neutral-500\"\n                            fill=\"currentColor\"\n                          />\n                        </div>\n                        <span className=\"truncate font-bold text-[#262626] dark:text-neutral-100\">\n                          {item.text}\n                        </span>\n                      </div>\n\n                      <button\n                        title=\"pin\"\n                        type=\"button\"\n                        onClick={(e) => {\n                          e.stopPropagation();\n                          togglePin(item.id);\n                        }}\n                        className={`relative z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full transition-all ${\n                          item.isPinned\n                            ? 'text-[#6B6A72] opacity-100 dark:text-neutral-400'\n                            : 'text-[#ADACB8] opacity-0 group-hover:opacity-80 hover:text-[#6A6970] dark:text-neutral-500 dark:hover:text-neutral-300'\n                        }`}\n                      >\n                        <Pin className=\"h-5 w-5\" fill=\"currentColor\" />\n                      </button>\n                    </motion.div>\n                  );\n                })}\n              </AnimatePresence>\n            </div>\n          </div>\n        </motion.div>\n      </MotionConfig>\n\n      <AnimatePresence>\n        {showFade && (\n          <motion.div\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            className=\"pointer-events-none absolute right-0 bottom-0 left-0 z-20 h-20 rounded-b-[40px] bg-gradient-to-t from-[#fefefe] via-[#fefefe]/90 to-transparent backdrop-blur-[2px] dark:from-neutral-900 dark:via-neutral-900/90\"\n          />\n        )}\n      </AnimatePresence>\n    </div>\n  );\n};\n"
    }
  ]
}
