admin/components/heroui/Accordion.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

69 lines
2.1 KiB
TypeScript

'use client'
import type { ReactElement, ReactNode } from 'react'
import { Children, isValidElement } from 'react'
import { Accordion as HeroAccordion } from '@heroui/react'
interface AccordionItemProps {
'aria-label'?: string
children?: ReactNode
key?: string | number
subtitle?: ReactNode
title: ReactNode
}
interface AccordionProps {
children?: ReactNode
itemClasses?: { base?: string; content?: string; title?: string }
variant?: 'light' | 'shadow' | 'bordered' | 'splitted'
}
export function AccordionItem(_props: AccordionItemProps) {
return null
}
function itemId(item: ReactElement, index: number): string {
if (item.key == null) return String(index)
const value = String(item.key)
return value.startsWith('.$') ? value.slice(2) : value
}
export function Accordion({ children, itemClasses, variant }: AccordionProps) {
const items = Children.toArray(children).filter((child): child is ReactElement<AccordionItemProps> =>
isValidElement<AccordionItemProps>(child)
)
return (
<HeroAccordion
className={variant === 'bordered' ? 'rounded-xl border border-divider' : undefined}
variant={variant === 'bordered' ? 'surface' : 'default'}
>
{items.map((item, index) => (
<HeroAccordion.Item
key={itemId(item, index)}
className={itemClasses?.base}
id={itemId(item, index)}
>
<HeroAccordion.Heading>
<HeroAccordion.Trigger
aria-label={item.props['aria-label']}
className={itemClasses?.title}
>
<span className="flex flex-1 flex-col items-start">
{item.props.title}
{item.props.subtitle ? <small className="font-normal text-muted">{item.props.subtitle}</small> : null}
</span>
<HeroAccordion.Indicator />
</HeroAccordion.Trigger>
</HeroAccordion.Heading>
<HeroAccordion.Panel>
<HeroAccordion.Body className={itemClasses?.content}>{item.props.children}</HeroAccordion.Body>
</HeroAccordion.Panel>
</HeroAccordion.Item>
))}
</HeroAccordion>
)
}