lucid-ui
Version:
A UI component library from Xandr.
150 lines • 4.97 kB
JavaScript
import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { Motion, spring } from 'react-motion';
import { QUICK_SLIDE_MOTION } from '../../constants/motion-spring';
import { lucidClassNames } from '../../util/style-helpers';
const cx = lucidClassNames.bind('&-Collapsible');
const { any, bool, node, number, string, func } = PropTypes;
/** TODO: Remove this constant when the component is converted to a functional component */
const nonPassthroughs = [
'children',
'className',
'isExpanded',
'isAnimated',
'isMountControlled',
'mountControlThreshold',
'onRest',
'rootType',
];
class Collapsible extends React.Component {
constructor() {
super(...arguments);
this.rootRef = React.createRef();
this.isAnimated = false;
this.delayTimer = null;
this._isMounted = false;
this.state = {
maxHeight: 0,
};
}
UNSAFE_componentWillMount() {
this._isMounted = false;
this.isAnimated = false;
this.delayTimer = null;
}
componentDidMount() {
this._isMounted = true;
_.delay(() => {
// const maxHeight = _.get(this, 'rootRef.current.scrollHeight');
if (this._isMounted) {
this.setState({
maxHeight: _.get(this, 'rootRef.current.scrollHeight'),
});
}
this.isAnimated = this.props.isAnimated;
}, 32);
}
componentDidUpdate() {
this.isAnimated = false;
this.delayTimer = _.delay(() => {
if (this.props.isExpanded) {
const maxHeight = _.get(this, 'rootRef.current.scrollHeight');
if (maxHeight !== this.state.maxHeight) {
if (this._isMounted) {
this.setState({
maxHeight,
});
}
}
}
this.isAnimated = this.props.isAnimated;
}, 32);
}
componentWillUnmount() {
this.delayTimer && clearTimeout(this.delayTimer);
}
render() {
const { children, className, isExpanded, isMountControlled, mountControlThreshold, rootType, onRest, style, ...passThroughs } = this.props;
const { maxHeight } = this.state;
return (React.createElement(Motion, { style: this.isAnimated
? {
height: isExpanded
? spring(maxHeight, QUICK_SLIDE_MOTION)
: spring(0, QUICK_SLIDE_MOTION),
}
: { height: isExpanded ? maxHeight : 0 }, onRest: onRest }, (tween) => React.createElement(rootType, {
..._.omit(passThroughs, nonPassthroughs),
ref: this.rootRef,
className: cx('&', className),
style: {
height: tween.height !== maxHeight
? tween.height < 0
? 0
: tween.height
: null,
overflow: 'hidden',
padding: 0,
...style,
},
}, [
React.createElement("div", { key: 'content', className: cx('&-content'), style: { margin: 0 } }, isMountControlled && !isExpanded
? _.isNull(maxHeight) ||
Math.abs(tween.height) > mountControlThreshold
? children
: null
: children),
])));
}
}
Collapsible.displayName = 'Collapsible';
Collapsible.peek = {
description: `This is a simple container that can render content as expanded or collapsed.`,
categories: ['utility'],
};
// static _isPrivate = true;
Collapsible.propTypes = {
/**
Expandable content.
*/
children: node,
/**
Appended to the component-specific class names set on the root element.
*/
className: string,
/**
Indicates that the component is in the "expanded" state when true and in
the "unexpanded" state when false.
*/
isExpanded: bool,
/**
Show an animated transition for alternating values of \`isExpanded\`.
*/
isAnimated: bool,
/**
If true, do not render children when fully collapsed.
*/
isMountControlled: bool,
/**
If \`isMountControlled\` is true, this value sets is the minimum height
the container needs to reach to not render any children.
*/
mountControlThreshold: number,
/**
Optional. The callback that fires when the animation comes to a rest.
*/
onRest: func,
/**
Pass in a custom root element type.
*/
rootType: any,
};
Collapsible.defaultProps = {
isExpanded: true,
isAnimated: true,
isMountControlled: true,
mountControlThreshold: 4,
rootType: 'div',
};
export default Collapsible;
//# sourceMappingURL=Collapsible.js.map