@commercetools-uikit/collapsible-motion
Version:
A component which allows building collapsible elements with an arbitrary height.
158 lines (152 loc) • 7.99 kB
JavaScript
import _Object$keys from '@babel/runtime-corejs3/core-js-stable/object/keys';
import _Object$getOwnPropertySymbols from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols';
import _filterInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/filter';
import _Object$getOwnPropertyDescriptor from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor';
import _forEachInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/for-each';
import _Object$getOwnPropertyDescriptors from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors';
import _Object$defineProperties from '@babel/runtime-corejs3/core-js-stable/object/define-properties';
import _Object$defineProperty from '@babel/runtime-corejs3/core-js-stable/object/define-property';
import _objectDestructuringEmpty from '@babel/runtime-corejs3/helpers/esm/objectDestructuringEmpty';
import _extends from '@babel/runtime-corejs3/helpers/esm/extends';
import _objectWithoutProperties from '@babel/runtime-corejs3/helpers/esm/objectWithoutProperties';
import _defineProperty from '@babel/runtime-corejs3/helpers/esm/defineProperty';
import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray';
import { useRef, useEffect, useCallback } from 'react';
import { warning } from '@commercetools-uikit/utils';
import { ClassNames, keyframes } from '@emotion/react';
import isNil from 'lodash/isNil';
import { useToggleState, usePrevious } from '@commercetools-uikit/hooks';
import { jsx } from '@emotion/react/jsx-runtime';
const _excluded = ["minHeight"];
function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
const getMinHeight = minHeight => minHeight !== 0 ? `${minHeight}px` : minHeight;
const getVisibility = height => height === 0 ? 'hidden' : 'visible';
const createOpeningAnimation = function (height) {
let minHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
return keyframes`
0% { height: ${getMinHeight(minHeight)}; overflow: hidden; visibility: ${getVisibility(minHeight)}; }
99% { height: ${height}px; overflow: hidden; }
100% { height: auto; overflow: visible; }
`;
};
const createClosingAnimation = function (height) {
let minHeight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
return keyframes`
from { height: ${height}px; }
to { height: ${getMinHeight(minHeight)}; overflow: hidden; visibility: ${getVisibility(minHeight)}; }
`;
};
const useToggleAnimation = function (isOpen, toggle) {
let minHeight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
const nodeRef = useRef(null);
const animationRef = useRef(null);
const prevIsOpen = usePrevious(isOpen);
useEffect(() => {
process.env.NODE_ENV !== "production" ? warning(nodeRef.current, 'You need to call `registerContentNode` in order to use this component') : void 0;
},
// to match the componentDidMount behaviour
[nodeRef]);
const handleToggle = useCallback(() => {
process.env.NODE_ENV !== "production" ? warning(nodeRef.current, 'You need to call `registerContentNode` in order to use this component') : void 0;
// set panel height to the height of the content,
// so we can animate between the height and 0
toggle && toggle();
}, [nodeRef, toggle]);
const containerStyles = isOpen ? {
height: 'auto'
} : {
height: getMinHeight(minHeight),
overflow: 'hidden',
visibility: getVisibility(minHeight)
};
// if state has changed
if (typeof prevIsOpen !== 'undefined' && prevIsOpen !== isOpen && nodeRef.current) {
animationRef.current = isOpen ? createOpeningAnimation(nodeRef.current.clientHeight, minHeight) : createClosingAnimation(nodeRef.current.clientHeight, minHeight);
}
return [animationRef.current, containerStyles, handleToggle, nodeRef];
};
const ControlledCollapsibleMotion = props => {
const _useToggleAnimation = useToggleAnimation(!props.isClosed, props.onToggle, props.minHeight),
_useToggleAnimation2 = _slicedToArray(_useToggleAnimation, 4),
animation = _useToggleAnimation2[0],
containerStyles = _useToggleAnimation2[1],
animationToggle = _useToggleAnimation2[2],
registerContentNode = _useToggleAnimation2[3];
return jsx(ClassNames, {
children: _ref => {
let css = _ref.css;
let animationStyle = {};
if (animation) {
// By calling `css`, emotion injects the required CSS into the document head.
// eslint-disable-next-line no-unused-expressions
css`
animation: ${animation} 200ms
forwards;
`;
animationStyle = {
animation: `${animation.name} 200ms forwards`
};
}
return props.children({
isOpen: !props.isClosed,
containerStyles: _objectSpread(_objectSpread({}, containerStyles), animationStyle),
toggle: animationToggle,
registerContentNode: registerContentNode
});
}
});
};
ControlledCollapsibleMotion.displayName = 'ControlledCollapsibleMotion';
const UncontrolledCollapsibleMotion = _ref2 => {
let _ref2$minHeight = _ref2.minHeight,
minHeight = _ref2$minHeight === void 0 ? 0 : _ref2$minHeight,
props = _objectWithoutProperties(_ref2, _excluded);
const _useToggleState = useToggleState(!props.isDefaultClosed),
_useToggleState2 = _slicedToArray(_useToggleState, 2),
isOpen = _useToggleState2[0],
toggle = _useToggleState2[1];
const _useToggleAnimation3 = useToggleAnimation(isOpen, toggle, minHeight),
_useToggleAnimation4 = _slicedToArray(_useToggleAnimation3, 4),
animation = _useToggleAnimation4[0],
containerStyles = _useToggleAnimation4[1],
animationToggle = _useToggleAnimation4[2],
registerContentNode = _useToggleAnimation4[3];
return jsx(ClassNames, {
children: _ref3 => {
let css = _ref3.css;
let animationStyle = {};
if (animation) {
// By calling `css`, emotion injects the required CSS into the document head.
// eslint-disable-next-line no-unused-expressions
css`
animation: ${animation} 200ms
forwards;
`;
animationStyle = {
animation: `${animation.name} 200ms forwards`
};
}
return props.children({
isOpen,
containerStyles: _objectSpread(_objectSpread({}, containerStyles), animationStyle),
toggle: animationToggle,
registerContentNode: registerContentNode
});
}
});
};
UncontrolledCollapsibleMotion.displayName = 'UncontrolledCollapsibleMotion';
const CollapsibleMotion = _ref4 => {
let props = _extends({}, (_objectDestructuringEmpty(_ref4), _ref4));
const isControlledComponent = !isNil(props.isClosed);
if (isControlledComponent) {
return jsx(ControlledCollapsibleMotion, _objectSpread({}, props));
}
return jsx(UncontrolledCollapsibleMotion, _objectSpread({}, props));
};
CollapsibleMotion.displayName = 'CollapsibleMotion';
var CollapsibleMotion$1 = CollapsibleMotion;
// NOTE: This string will be replaced on build time with the package version.
var version = "20.6.5";
export { CollapsibleMotion$1 as default, version };