{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-base-accordion",
  "title": "Base Accordion",
  "description": "An easily stylable accordion component.",
  "dependencies": [
    "motion",
    "@base-ui-components/react"
  ],
  "registryDependencies": [
    "@odysseyui/lib-get-strict-context",
    "@odysseyui/hooks-use-controlled-state"
  ],
  "files": [
    {
      "path": "registry/primitives/base/accordion/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { Accordion as AccordionPrimitive } from '@base-ui-components/react/accordion';\nimport { AnimatePresence, motion, type HTMLMotionProps } from 'motion/react';\n\nimport { getStrictContext } from '@/lib/get-strict-context';\nimport { useControlledState } from '@/hooks/use-controlled-state';\n\ntype AccordionContextType = {\n  value: string | string[] | undefined;\n  setValue: (value: string | string[] | undefined) => void;\n};\n\ntype AccordionItemContextType = {\n  isOpen: boolean;\n  setIsOpen: (open: boolean) => void;\n};\n\nconst [AccordionProvider, useAccordion] =\n  getStrictContext<AccordionContextType>('AccordionContext');\n\nconst [AccordionItemProvider, useAccordionItem] =\n  getStrictContext<AccordionItemContextType>('AccordionItemContext');\n\ntype AccordionProps = React.ComponentProps<typeof AccordionPrimitive.Root>;\n\nfunction Accordion(props: AccordionProps) {\n  const [value, setValue] = useControlledState<string | string[] | undefined>({\n    value: props?.value,\n    defaultValue: props?.defaultValue,\n    onChange: props?.onValueChange as (\n      value: string | string[] | undefined,\n    ) => void,\n  });\n\n  return (\n    <AccordionProvider value={{ value, setValue }}>\n      <AccordionPrimitive.Root\n        data-slot=\"accordion\"\n        {...props}\n        onValueChange={setValue}\n      />\n    </AccordionProvider>\n  );\n}\n\ntype AccordionItemProps = React.ComponentProps<typeof AccordionPrimitive.Item>;\n\nfunction AccordionItem(props: AccordionItemProps) {\n  const { value } = useAccordion();\n  const [isOpen, setIsOpen] = React.useState(\n    value?.includes(props?.value) ?? false,\n  );\n\n  React.useEffect(() => {\n    setIsOpen(value?.includes(props?.value) ?? false);\n  }, [value, props?.value]);\n\n  return (\n    <AccordionItemProvider value={{ isOpen, setIsOpen }}>\n      <AccordionPrimitive.Item data-slot=\"accordion-item\" {...props} />\n    </AccordionItemProvider>\n  );\n}\n\ntype AccordionHeaderProps = React.ComponentProps<\n  typeof AccordionPrimitive.Header\n>;\n\nfunction AccordionHeader(props: AccordionHeaderProps) {\n  return <AccordionPrimitive.Header data-slot=\"accordion-header\" {...props} />;\n}\n\ntype AccordionTriggerProps = React.ComponentProps<\n  typeof AccordionPrimitive.Trigger\n>;\n\nfunction AccordionTrigger(props: AccordionTriggerProps) {\n  return (\n    <AccordionPrimitive.Trigger data-slot=\"accordion-trigger\" {...props} />\n  );\n}\n\ntype AccordionPanelProps = Omit<\n  React.ComponentProps<typeof AccordionPrimitive.Panel>,\n  'keepMounted' | 'render'\n> &\n  HTMLMotionProps<'div'> & {\n    keepRendered?: boolean;\n  };\n\nfunction AccordionPanel({\n  transition = { duration: 0.35, ease: 'easeInOut' },\n  hiddenUntilFound,\n  keepRendered = false,\n  ...props\n}: AccordionPanelProps) {\n  const { isOpen } = useAccordionItem();\n\n  return (\n    <AnimatePresence>\n      {keepRendered ? (\n        <AccordionPrimitive.Panel\n          hidden={false}\n          hiddenUntilFound={hiddenUntilFound}\n          keepMounted\n          render={\n            <motion.div\n              key=\"accordion-panel\"\n              data-slot=\"accordion-panel\"\n              initial={{ height: 0, opacity: 0, '--mask-stop': '0%', y: 20 }}\n              animate={\n                isOpen\n                  ? { height: 'auto', opacity: 1, '--mask-stop': '100%', y: 0 }\n                  : { height: 0, opacity: 0, '--mask-stop': '0%', y: 20 }\n              }\n              transition={transition}\n              style={{\n                maskImage:\n                  'linear-gradient(black var(--mask-stop), transparent var(--mask-stop))',\n                WebkitMaskImage:\n                  'linear-gradient(black var(--mask-stop), transparent var(--mask-stop))',\n                overflow: 'hidden',\n              }}\n              {...props}\n            />\n          }\n        />\n      ) : (\n        isOpen && (\n          <AccordionPrimitive.Panel\n            hidden={false}\n            hiddenUntilFound={hiddenUntilFound}\n            keepMounted\n            render={\n              <motion.div\n                key=\"accordion-panel\"\n                data-slot=\"accordion-panel\"\n                initial={{ height: 0, opacity: 0, '--mask-stop': '0%', y: 20 }}\n                animate={{\n                  height: 'auto',\n                  opacity: 1,\n                  '--mask-stop': '100%',\n                  y: 0,\n                }}\n                exit={{ height: 0, opacity: 0, '--mask-stop': '0%', y: 20 }}\n                transition={transition}\n                style={{\n                  maskImage:\n                    'linear-gradient(black var(--mask-stop), transparent var(--mask-stop))',\n                  WebkitMaskImage:\n                    'linear-gradient(black var(--mask-stop), transparent var(--mask-stop))',\n                  overflow: 'hidden',\n                }}\n                {...props}\n              />\n            }\n          />\n        )\n      )}\n    </AnimatePresence>\n  );\n}\n\nexport {\n  Accordion,\n  AccordionItem,\n  AccordionHeader,\n  AccordionTrigger,\n  AccordionPanel,\n  useAccordionItem,\n  type AccordionProps,\n  type AccordionItemProps,\n  type AccordionHeaderProps,\n  type AccordionTriggerProps,\n  type AccordionPanelProps,\n  type AccordionItemContextType,\n};\n",
      "type": "registry:ui",
      "target": "components/odysseyui/primitives/base/accordion.tsx"
    }
  ],
  "type": "registry:ui"
}