Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
69 lines
2.1 KiB
TypeScript
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>
|
|
)
|
|
}
|