'use client' import * as React from 'react' import { Accordion as BaseAccordion } from '@base-ui/react/accordion' import { cva } from 'class-variance-authority' import { cn } from '../../internal/utils' type AccordionVariant = 'default' | 'alt' | 'flush' type AccordionIcon = 'chevron' | 'plus' | false type AccordionIconVariant = 'icon' | 'icon-box' type AccordionIconPosition = 'end' | 'start' interface AccordionContextValue { variant: AccordionVariant icon: AccordionIcon iconVariant: AccordionIconVariant iconPosition: AccordionIconPosition } const AccordionContext = React.createContext({ variant: 'default', icon: 'chevron', iconVariant: 'icon', iconPosition: 'end', }) const AccordionItemContext = React.createContext<{ variant: AccordionVariant } | null>(null) const itemVariants = cva( cn( 'group/accordion-item', 'has-focus-visible:ring-ring has-focus-visible:ring-3', 'data-disabled:opacity-disabled data-disabled:pointer-events-none', ), { variants: { variant: { default: cn( 'bg-background-subtle border-border-muted', 'px-5 pb-5 rounded-xl border backdrop-blur-lg', 'hover:bg-background-muted data-open:bg-background-muted', 'transition-colors duration-300', 'hover:border-transparent data-open:border-transparent', 'motion-reduce:transition-none', ), alt: cn( 'bg-background', 'px-5 pb-5 rounded-xl border border-transparent', 'hover:border-border data-open:border-border', 'transition-colors duration-300', 'motion-reduce:transition-none', ), flush: 'pb-3.5', }, }, }, ) const triggerVariants = cva( cn( 'text-foreground-intense flex flex-1 cursor-pointer items-start justify-between gap-3.5', 'text-lg font-medium outline-none', '[&_svg]:pointer-events-none [&_svg]:shrink-0', "[&_svg:not([class*='size-'])]:size-5.5", "[&_svg:not([class*='stroke-'])]:stroke-[1.85]", ), { variants: { variant: { default: '-mx-5 -mb-5 p-5', alt: '-mx-5 -mb-5 p-5', flush: '-mb-3.5 py-3.5', }, }, }, ) const iconBoxVariants = cva( cn( 'inline-flex items-center justify-center', 'size-[1.778em]', 'rounded-[calc(tan(atan2(var(--radius-sm),2rem))*100%)]', ), { variants: { variant: { default: 'bg-background border-border border', alt: 'bg-background-muted', flush: 'bg-background border-border border', }, }, }, ) interface AccordionProps extends React.ComponentProps { /** * Surface style applied to every item. * @default 'default' */ variant?: AccordionVariant /** * The open/close indicator, or `false` to hide it. * @default 'chevron' */ icon?: AccordionIcon /** * Plain glyph, or wrapped in a tinted tile. * @default 'icon' */ iconVariant?: AccordionIconVariant /** * Place the icon after or before the trigger label. * @default 'end' */ iconPosition?: AccordionIconPosition } function Accordion({ variant = 'default', icon = 'chevron', iconVariant = 'icon', iconPosition = 'end', className, ...props }: AccordionProps) { const ctx = React.useMemo( () => ({ variant, icon, iconVariant, iconPosition }), [variant, icon, iconVariant, iconPosition], ) return ( ) } interface AccordionItemProps extends Omit, 'disabled'> { /** Override the root's `variant` for this item. */ variant?: AccordionVariant /** * Lock this item shut and skip it during keyboard navigation. * @default false */ disabled?: boolean | undefined } function AccordionItem({ className, variant, ...props }: AccordionItemProps) { const root = React.useContext(AccordionContext) const resolvedVariant = variant ?? root.variant const itemCtx = React.useMemo(() => ({ variant: resolvedVariant }), [resolvedVariant]) return ( ) } interface AccordionTriggerProps extends React.ComponentProps { /** Override the root's icon for this trigger. */ icon?: AccordionIcon /** Override the root's icon style. */ iconVariant?: AccordionIconVariant /** Override the root's icon position. */ iconPosition?: AccordionIconPosition } function AccordionTrigger({ className, children, icon, iconVariant, iconPosition, ...props }: AccordionTriggerProps) { const root = React.useContext(AccordionContext) const item = React.useContext(AccordionItemContext) const variant = item?.variant ?? root.variant const resolvedIcon = icon ?? root.icon const resolvedIconVariant = iconVariant ?? root.iconVariant const resolvedIconPosition = iconPosition ?? root.iconPosition const svgSize = resolvedIconVariant === 'icon-box' ? resolvedIcon === 'chevron' ? 'size-[56%]' : 'size-[50%]' : resolvedIcon === 'plus' ? 'size-[1em]' : 'size-[1.12em]' let iconNode: React.ReactNode = null if (resolvedIcon !== false) { const svg = resolvedIcon === 'plus' ? : const inner = resolvedIconVariant === 'icon-box' ? ( {svg} ) : ( svg ) iconNode = {inner} } return ( {resolvedIconPosition === 'start' && iconNode} {children} {resolvedIconPosition === 'end' && iconNode} ) } type AccordionContentProps = React.ComponentProps function AccordionContent({ className, children, ...props }: AccordionContentProps) { const root = React.useContext(AccordionContext) const item = React.useContext(AccordionItemContext) const variant = item?.variant ?? root.variant return (
{children}
) } function ChevronIcon({ className }: { className?: string }) { return ( ) } function PlusIcon({ className }: { className?: string }) { return ( ) } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } export type { AccordionProps, AccordionItemProps, AccordionTriggerProps, AccordionContentProps }