{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "accordion", "type": "registry:ui", "title": "Accordion", "description": "An accordion component that can be used to display collapsible content.", "files": [ { "path": "registry/tra-kit/components/accordion.tsx", "content": "import { CaretDownIcon } from '@phosphor-icons/react';\r\nimport { cva } from 'class-variance-authority';\r\nimport React, { ReactNode, useEffect, useState } from 'react';\r\nimport { cn } from '@/lib/utils';\r\n\r\nconst accordionItemVariants = cva('border-border border-b', {\r\n variants: {\r\n variant: {\r\n underlined: '',\r\n outlined: '',\r\n solid: 'data-[disabled=\"true\"]:bg-disabled-light-dark',\r\n splitted: 'bg-neutral data-[disabled=\"true\"]:bg-disabled-light-dark rounded-md',\r\n },\r\n },\r\n});\r\n\r\nconst accordionVariants = cva('w-fit rounded-md ', {\r\n variants: {\r\n variant: {\r\n underlined: '',\r\n outlined: 'border-border border',\r\n solid: 'bg-neutral',\r\n splitted: 'flex flex-col gap-1',\r\n },\r\n },\r\n});\r\n\r\nconst accordionTriggerVariants = cva(\r\n `data-[disabled=\"true\"]:text-disabled flex cursor-pointer select-none items-center justify-between rounded-md \r\n p-4 text-lg font-medium transition-all \r\n duration-300 data-[disabled=\"true\"]:cursor-default`,\r\n {\r\n variants: {\r\n variant: {\r\n underlined: '',\r\n outlined: '',\r\n solid: '',\r\n splitted: '',\r\n },\r\n },\r\n },\r\n);\r\n\r\nconst accoridonContentVariants = cva(`overflow-hidden px-4 py-0.5 transition-all duration-200\r\n data-[open=\"false\"]:max-h-0 data-[open=\"true\"]:max-h-full\r\n data-[open=\"false\"]:opacity-0 data-[open=\"true\"]:opacity-100`);\r\n\r\ninterface IAccordion {\r\n children: ReactNode;\r\n className?: string;\r\n multipleExpand?: boolean;\r\n variant?: 'solid' | 'outlined' | 'splitted' | 'underlined';\r\n}\r\n\r\ninterface IAccordionItem {\r\n title: string;\r\n subTitle?: string;\r\n children: ReactNode;\r\n className?: string;\r\n contentClassName?: string;\r\n triggerClassName?: string;\r\n isOpen?: boolean;\r\n onClick?: () => void | undefined;\r\n variant?: 'solid' | 'outlined' | 'splitted' | 'underlined';\r\n disabled?: boolean;\r\n defaultOpen?: boolean;\r\n startContent?: ReactNode;\r\n icon?: ReactNode;\r\n titleClassName?: string;\r\n}\r\n\r\ninterface IAccordionTrigger {\r\n onClick?: () => void | undefined;\r\n isOpen: boolean;\r\n title: string;\r\n subTitle?: string;\r\n className?: string;\r\n disabled?: boolean;\r\n startContent?: ReactNode;\r\n icon?: ReactNode;\r\n iconContainerClassName?: string;\r\n titleClassName?: string;\r\n subTitleClassName?: string;\r\n variant?: 'solid' | 'outlined' | 'splitted' | 'underlined';\r\n}\r\n\r\ninterface IAccordionContent {\r\n isOpen: boolean;\r\n children: ReactNode;\r\n className?: string;\r\n}\r\n\r\nconst Accordion: React.FC = ({\r\n children,\r\n className,\r\n multipleExpand = false,\r\n variant = 'underlined',\r\n}) => {\r\n const [openIndexes, setOpenIndexes] = useState([]);\r\n const [openIndex, setOpenIndex] = useState(null);\r\n\r\n // for default open\r\n useEffect(() => {\r\n const initialOpenIndexes: number[] = [];\r\n let initialOpenIndex: number | null = null;\r\n\r\n React.Children.forEach(children, (child, index) => {\r\n if (React.isValidElement(child)) {\r\n const { defaultOpen } = child.props as IAccordionItem;\r\n if (defaultOpen) {\r\n if (multipleExpand) {\r\n initialOpenIndexes.push(index);\r\n } else {\r\n initialOpenIndex = index;\r\n }\r\n }\r\n }\r\n });\r\n\r\n if (multipleExpand) {\r\n setOpenIndexes(initialOpenIndexes);\r\n } else {\r\n setOpenIndex(initialOpenIndex);\r\n }\r\n }, [children, multipleExpand]);\r\n\r\n const handleItemClick = (index: number) => {\r\n if (multipleExpand) {\r\n setOpenIndexes((prevIndexes) =>\r\n prevIndexes.includes(index)\r\n ? prevIndexes.filter((i) => i !== index)\r\n : [...prevIndexes, index],\r\n );\r\n } else {\r\n setOpenIndex((prevIndex) => (prevIndex === index ? null : index));\r\n }\r\n };\r\n\r\n const childrenArray = React.Children.toArray(children);\r\n\r\n return (\r\n
\r\n {childrenArray.map((child, index) => {\r\n if (React.isValidElement(child)) {\r\n return React.cloneElement(child as React.ReactElement, {\r\n isOpen: multipleExpand ? openIndexes.includes(index) : openIndex === index,\r\n onClick: () => handleItemClick(index),\r\n variant,\r\n });\r\n }\r\n return child;\r\n })}\r\n
\r\n );\r\n};\r\n\r\nconst AccordionTrigger: React.FC = ({\r\n title,\r\n subTitle,\r\n onClick,\r\n isOpen,\r\n className,\r\n variant,\r\n disabled,\r\n startContent,\r\n icon,\r\n iconContainerClassName,\r\n titleClassName,\r\n subTitleClassName,\r\n}) => (\r\n {} : onClick}\r\n data-disabled={disabled}\r\n className={cn(accordionTriggerVariants({ variant }), className)}\r\n >\r\n
\r\n {startContent}\r\n
\r\n

{title}

\r\n {subTitle &&

{subTitle}

}\r\n
\r\n
\r\n \r\n {icon ?? }\r\n \r\n \r\n);\r\n\r\nconst AccordionContent: React.FC = ({ isOpen, children, className }) => (\r\n \r\n {children}\r\n \r\n);\r\n\r\nconst AccordionItem: React.FC = ({\r\n title,\r\n subTitle,\r\n isOpen = false,\r\n onClick,\r\n children,\r\n className = '',\r\n triggerClassName = '',\r\n contentClassName = '',\r\n variant,\r\n disabled,\r\n startContent,\r\n titleClassName = '',\r\n icon,\r\n}) => (\r\n \r\n {} : onClick}\r\n isOpen={isOpen}\r\n variant={variant}\r\n disabled={disabled}\r\n startContent={startContent}\r\n icon={icon}\r\n />\r\n \r\n {children}\r\n \r\n \r\n);\r\n\r\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent };\r\n", "type": "registry:ui" } ] }