mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
105 lines
2.7 KiB
TypeScript
105 lines
2.7 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { clsx } from 'clsx';
|
|
import { ChevronDown } from 'lucide-react';
|
|
|
|
export interface AccordionItem {
|
|
id: string;
|
|
title: string;
|
|
content: React.ReactNode;
|
|
defaultOpen?: boolean;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export interface AccordionProps {
|
|
items: AccordionItem[];
|
|
allowMultiple?: boolean;
|
|
className?: string;
|
|
itemClassName?: string;
|
|
}
|
|
|
|
/**
|
|
* Accordion/Collapsible component
|
|
*/
|
|
export const Accordion = ({
|
|
items,
|
|
allowMultiple = false,
|
|
className,
|
|
itemClassName,
|
|
}: AccordionProps) => {
|
|
const [openItems, setOpenItems] = useState<Set<string>>(
|
|
new Set(items.filter((item) => item.defaultOpen).map((item) => item.id))
|
|
);
|
|
|
|
const toggleItem = (id: string) => {
|
|
setOpenItems((prev) => {
|
|
const newSet = new Set(prev);
|
|
if (newSet.has(id)) {
|
|
newSet.delete(id);
|
|
} else {
|
|
if (!allowMultiple) {
|
|
newSet.clear();
|
|
}
|
|
newSet.add(id);
|
|
}
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className={clsx('space-y-2', className)}>
|
|
{items.map((item) => {
|
|
const isOpen = openItems.has(item.id);
|
|
|
|
return (
|
|
<div
|
|
key={item.id}
|
|
className={clsx('border rounded-lg overflow-hidden', itemClassName)}
|
|
>
|
|
<button
|
|
type="button"
|
|
onClick={() => !item.disabled && toggleItem(item.id)}
|
|
disabled={item.disabled}
|
|
className={clsx(
|
|
'w-full flex items-center justify-between p-4',
|
|
'text-left font-medium',
|
|
'hover:bg-muted/50 transition-colors',
|
|
'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
{
|
|
'opacity-50 cursor-not-allowed': item.disabled,
|
|
}
|
|
)}
|
|
aria-expanded={isOpen}
|
|
aria-controls={`accordion-content-${item.id}`}
|
|
>
|
|
<span>{item.title}</span>
|
|
<ChevronDown
|
|
className={clsx(
|
|
'h-4 w-4 text-muted-foreground transition-transform flex-shrink-0',
|
|
{
|
|
'rotate-180': isOpen,
|
|
}
|
|
)}
|
|
/>
|
|
</button>
|
|
<div
|
|
id={`accordion-content-${item.id}`}
|
|
className={clsx(
|
|
'overflow-hidden transition-all duration-200',
|
|
{
|
|
'max-h-0': !isOpen,
|
|
'max-h-[2000px]': isOpen,
|
|
}
|
|
)}
|
|
>
|
|
<div className="p-4 pt-0 text-sm text-muted-foreground">
|
|
{item.content}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|