add: accordion component

This commit is contained in:
Mahyar Khanbolooki
2025-08-11 21:03:22 +03:30
parent f962000b1f
commit 18f56afd82
2 changed files with 96 additions and 2 deletions
+94
View File
@@ -0,0 +1,94 @@
'use client';
import useToggle from '@/hooks/helpers/useToggle';
import clsx from 'clsx'
import { motion } from 'framer-motion';
import { Icon } from 'iconsax-react'
import { ChevronDown } from 'lucide-react';
import React, { useEffect } from 'react'
interface DropdownProps extends React.HTMLAttributes<HTMLDivElement> {
initial?: boolean,
children?: React.ReactNode
title?: string
icon?: Icon
group?: string
};
function Accordion({ initial, group, title, icon: Icon, children, className, ...rest }: DropdownProps) {
const { state, toggle, set } = useToggle(initial);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (event.target) {
const targetGroup = (event.target as HTMLElement).getAttribute('data-group');
if (targetGroup && targetGroup === group) {
set?.(false)
}
}
};
if (group && state) {
document.addEventListener('click', handleClickOutside);
}
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [group, set, state]);
return (
<div
className={clsx(
'rounded-normal py-3',
className
)}
{...rest}
>
<button
data-group={group}
onClick={toggle}
data-active={state}
className={clsx(
'hover:underline rounded-normal flex items-center justify-between w-full',
'active:brightness-97 hover:brightness-99',
'data-[active=true]:rounded-b-none data-[active=true]:border-b-0 **:pointer-events-none'
)}
>
<div className='flex items-center gap-2'>
{Icon &&
<Icon size={16} className='stroke-foreground mb-0.5' />
}
<span className='text-sm2'>{title ?? ''}</span>
</div>
<ChevronDown
size={16}
data-active={state}
className={clsx(
'cursor-pointer stroke-foreground data-[active=false]:rotate-x-0',
'data-[active=true]:rotate-z-180 transition-transform duration-200'
)}
/>
</button>
<motion.div
initial={{ height: state ? 'auto' : 0 }}
animate={{ height: state ? 'auto' : 0 }}
transition={{ duration: 0.1 }}
data-active={state}
className={clsx(
'mt-0 relative overflow-hidden',
'rounded-b-normal z-50',
'w-full',
!state && 'pointer-events-none'
)}
>
<div className='py-2'>
{children}
</div>
</motion.div>
</div>
)
}
export default Accordion
+2 -2
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
const useToggle = () => {
const [state, setState] = useState<boolean>(false);
const useToggle = (initial?: boolean) => {
const [state, setState] = useState<boolean>(initial ?? false);
const toggle = (e?: React.MouseEvent | null) => {
if (e) {