packages/bui-core/src/Collapse/Collapse.tsx (113 lines of code) (raw):
import React, { useEffect, useRef } from 'react';
import {
useForkRef,
duration,
easing,
getTransitionProps,
createTransitions,
} from '@bifrostui/utils';
import { Transition } from '../Transition';
import { CollapseProps } from './Collapse.types';
import './Collapse.less';
const defaultEasing = {
enter: easing.easeOut,
exit: easing.sharp,
};
const defaultTimeout = {
enter: duration.enteringScreen,
exit: duration.leavingScreen,
};
const Collapse = React.forwardRef<unknown, CollapseProps>((props, ref) => {
const {
appear = false,
in: inProp,
easing: easingProp = defaultEasing,
direction = 'vertical',
timeout = defaultTimeout,
delay = 0,
collapsedSize: collapsedSizeProp = 0,
style,
className,
children,
...other
} = props;
const nodeRef = useForkRef(ref);
const wrapperRef = useRef(null);
const collapseRef = useForkRef(wrapperRef, ref);
const transitions = createTransitions();
const isHorizontal = direction === 'horizontal';
const collapsedSize =
typeof collapsedSizeProp === 'number'
? `${collapsedSizeProp}px`
: collapsedSizeProp;
const size = isHorizontal ? 'width' : 'height';
const getCollapseWrapperSize = (reactNode) => {
return reactNode
? `${reactNode[isHorizontal ? 'clientWidth' : 'clientHeight']}px`
: 'fit-content';
};
useEffect(() => {
// 修复未挂载时获取不到children元素宽高,动画异常
if (
appear === false &&
inProp === true &&
wrapperRef.current?.style?.[size] === 'fit-content'
) {
wrapperRef.current.style[size] = getCollapseWrapperSize(
wrapperRef.current?.children?.[0],
);
}
}, [appear, inProp]);
if (!children) return null;
return (
<Transition
{...other}
ref={nodeRef}
in={inProp}
timeout={timeout}
delay={delay}
appear={appear}
>
{(state, childProps) => {
const transition = transitions.create(
size,
getTransitionProps(
{ timeout, style, easing: easingProp, delay },
{ mode: state },
),
);
const wrapperSize = () => {
const collapseWrapperSize =
state === 'entering' || state === 'entered'
? getCollapseWrapperSize(wrapperRef.current?.children?.[0])
: collapsedSize;
return isHorizontal
? {
width: collapseWrapperSize,
WebKitWidth: collapseWrapperSize,
}
: {
height: collapseWrapperSize,
WebKitHeight: collapseWrapperSize,
};
};
return React.createElement(
'div',
{
className: `bui-collapse ${className}`,
style: {
...style,
transition,
WebkitTransition: transition,
...wrapperSize(),
},
ref: collapseRef,
},
React.cloneElement(children, {
style: {
...children.props?.style,
},
...childProps,
}),
);
}}
</Transition>
);
});
Collapse.displayName = 'BuiCollapse';
export default Collapse;