The Accordion is a classic UI element that uses the compound component pattern to create collapsible sections. It consists of multiple items, each with a header that controls visibility of its content.
// Accordion.tsx
import React, { createContext, useContext, useState, ReactNode } from 'react';
// Create context for the accordion
type AccordionContextType = {
expandedItems: Record<string, boolean>;
toggleItem: (id: string) => void;
isItemExpanded: (id: string) => boolean;
allowMultiple: boolean;
};
const AccordionContext = createContext<AccordionContextType | undefined>(undefined);
// Hook to use accordion context
const useAccordion = () => {
const context = useContext(AccordionContext);
if (!context) {
throw new Error('Accordion components must be used within an Accordion');
}
return context;
};
// Main Accordion component
const Accordion = ({ children, allowMultiple = false, className = '' }) => {
const [expandedItems, setExpandedItems] = useState({});
const toggleItem = (id) => {
setExpandedItems(prev => {
if (!allowMultiple) {
// If only one item can be expanded at a time, close others
return { [id]: !prev[id] };
}
return { ...prev, [id]: !prev[id] };
});
};
const isItemExpanded = (id) => !!expandedItems[id];
return (
<AccordionContext.Provider value={{ expandedItems, toggleItem, isItemExpanded, allowMultiple }}>
<div className={`divide-y divide-gray-200 border border-gray-200 rounded-md ${className}`}>
{children}
</div>
</AccordionContext.Provider>
);
};
// Item component and sub-components...
// Attach sub-components to Accordion
Accordion.Item = Item;
Accordion.Header = Header;
Accordion.Content = Content;
export default Accordion;