@base-ui-components/react
Version:
Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.
132 lines (131 loc) • 3.94 kB
JavaScript
'use client';
import * as React from 'react';
import { useIsoLayoutEffect } from '@base-ui-components/utils/useIsoLayoutEffect';
import { warn } from '@base-ui-components/utils/warn';
import { useRenderElement } from "../../utils/useRenderElement.js";
import { useCollapsibleRootContext } from "../root/CollapsibleRootContext.js";
import { collapsibleStateAttributesMapping } from "../root/stateAttributesMapping.js";
import { useCollapsiblePanel } from "./useCollapsiblePanel.js";
import { CollapsiblePanelCssVars } from "./CollapsiblePanelCssVars.js";
import { useOpenChangeComplete } from "../../utils/useOpenChangeComplete.js";
/**
* A panel with the collapsible contents.
* Renders a `<div>` element.
*
* Documentation: [Base UI Collapsible](https://base-ui.com/react/components/collapsible)
*/
export const CollapsiblePanel = /*#__PURE__*/React.forwardRef(function CollapsiblePanel(componentProps, forwardedRef) {
const {
className,
hiddenUntilFound: hiddenUntilFoundProp,
keepMounted: keepMountedProp,
render,
id: idProp,
...elementProps
} = componentProps;
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line react-hooks/rules-of-hooks
useIsoLayoutEffect(() => {
if (hiddenUntilFoundProp && keepMountedProp === false) {
warn('The `keepMounted={false}` prop on a Collapsible will be ignored when using `hiddenUntilFound` since it requires the Panel to remain mounted even when closed.');
}
}, [hiddenUntilFoundProp, keepMountedProp]);
}
const {
abortControllerRef,
animationTypeRef,
height,
mounted,
onOpenChange,
open,
panelId,
panelRef,
runOnceAnimationsFinish,
setDimensions,
setHiddenUntilFound,
setKeepMounted,
setMounted,
setPanelIdState,
setOpen,
setVisible,
state,
transitionDimensionRef,
visible,
width,
transitionStatus
} = useCollapsibleRootContext();
const hiddenUntilFound = hiddenUntilFoundProp ?? false;
const keepMounted = keepMountedProp ?? false;
useIsoLayoutEffect(() => {
if (idProp) {
setPanelIdState(idProp);
return () => {
setPanelIdState(undefined);
};
}
return undefined;
}, [idProp, setPanelIdState]);
useIsoLayoutEffect(() => {
setHiddenUntilFound(hiddenUntilFound);
}, [setHiddenUntilFound, hiddenUntilFound]);
useIsoLayoutEffect(() => {
setKeepMounted(keepMounted);
}, [setKeepMounted, keepMounted]);
const {
props
} = useCollapsiblePanel({
abortControllerRef,
animationTypeRef,
externalRef: forwardedRef,
height,
hiddenUntilFound,
id: panelId,
keepMounted,
mounted,
onOpenChange,
open,
panelRef,
runOnceAnimationsFinish,
setDimensions,
setMounted,
setOpen,
setVisible,
transitionDimensionRef,
visible,
width
});
useOpenChangeComplete({
open: open && transitionStatus === 'idle',
ref: panelRef,
onComplete() {
if (!open) {
return;
}
setDimensions({
height: undefined,
width: undefined
});
}
});
const panelState = React.useMemo(() => ({
...state,
transitionStatus
}), [state, transitionStatus]);
const element = useRenderElement('div', componentProps, {
state: panelState,
ref: [forwardedRef, panelRef],
props: [props, {
style: {
[CollapsiblePanelCssVars.collapsiblePanelHeight]: height === undefined ? 'auto' : `${height}px`,
[CollapsiblePanelCssVars.collapsiblePanelWidth]: width === undefined ? 'auto' : `${width}px`
}
}, elementProps],
stateAttributesMapping: collapsibleStateAttributesMapping
});
const shouldRender = keepMounted || hiddenUntilFound || !keepMounted && mounted;
if (!shouldRender) {
return null;
}
return element;
});
if (process.env.NODE_ENV !== "production") CollapsiblePanel.displayName = "CollapsiblePanel";