add: accordion component
This commit is contained in:
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user