/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ import React, { useState, useEffect, useCallback, createContext, useContext } from "react"; import { Collapsible as BaseCollapsible } from "@base-ui-components/react/collapsible"; import clsx from "clsx"; // Types type CollapsibleContextType = { isOpen: boolean; onToggle: () => void; }; type RootProps = { children: React.ReactNode; className?: string; isOpen?: boolean; onToggle?: () => void; defaultOpen?: boolean; }; type TriggerProps = { children: React.ReactNode; className?: string; buttonRef?: React.RefObject; }; type ContentProps = { children: React.ReactNode; className?: string; }; // Context const CollapsibleContext = createContext(undefined); // Hook const useCollapsible = () => { const context = useContext(CollapsibleContext); if (!context) { throw new Error("Collapsible compound components cannot be rendered outside the Collapsible component"); } return context; }; // Components function Root({ children, className, isOpen: controlledIsOpen, onToggle, defaultOpen }: RootProps) { const [localIsOpen, setLocalIsOpen] = useState(controlledIsOpen || defaultOpen || false); useEffect(() => { if (controlledIsOpen !== undefined) { setLocalIsOpen(controlledIsOpen); } }, [controlledIsOpen]); const handleToggle = useCallback(() => { if (controlledIsOpen !== undefined) { onToggle?.(); } else { setLocalIsOpen((prev) => !prev); } }, [controlledIsOpen, onToggle]); return ( {children} ); } function Trigger({ children, className, buttonRef }: TriggerProps) { const { isOpen } = useCollapsible(); return ( {children} ); } function Content({ children, className }: ContentProps) { return ( {children} ); } // Compound Component export const Collapsible = { CollapsibleRoot: Root, CollapsibleTrigger: Trigger, CollapsibleContent: Content, };