UNPKG

zangai-react

Version:
293 lines (292 loc) 13.2 kB
import * as tslib_1 from "tslib"; import * as React from 'react'; import { buildClassName as cx } from './core/css-builder'; var ANIMATION_STATE_CLASSES = { animating: 'rah-animating', animatingUp: 'rah-animating--up', animatingDown: 'rah-animating--down', animatingToHeightZero: 'rah-animating--to-height-zero', animatingToHeightAuto: 'rah-animating--to-height-auto', animatingToHeightSpecific: 'rah-animating--to-height-specific', static: 'rah-static', staticHeightZero: 'rah-static--height-zero', staticHeightAuto: 'rah-static--height-auto', staticHeightSpecific: 'rah-static--height-specific', }; var PROPS_TO_OMIT = [ 'animateOpacity', 'animationStateClasses', 'applyInlineTransitions', 'children', 'contentClassName', 'delay', 'duration', 'easing', 'height', 'onAnimationEnd', 'onAnimationStart', ]; function omit(obj) { var keys = []; for (var _i = 1; _i < arguments.length; _i++) { keys[_i - 1] = arguments[_i]; } if (!keys.length) { return obj; } var res = {}; var objectKeys = Object.keys(obj); for (var i = 0; i < objectKeys.length; i++) { var key = objectKeys[i]; if (keys.indexOf(key) === -1) { res[key] = obj[key]; } } return res; } // Start animation helper using nested requestAnimationFrames function startAnimationHelper(callback) { requestAnimationFrame(function () { requestAnimationFrame(function () { callback(); }); }); } function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function isPercentage(height) { // Percentage height return typeof height === 'string' && height.search('%') === height.length - 1 && isNumber(height.substr(0, height.length - 1)); } function runCallback(callback, params) { if (callback && typeof callback === 'function') { callback(params); } } var AtAnimationHeight = /** @class */ (function (_super) { tslib_1.__extends(AtAnimationHeight, _super); function AtAnimationHeight(props) { var _this = _super.call(this, props) || this; var height = 'auto'; var overflow = 'visible'; if (isNumber(props.height)) { height = props.height < 0 ? 0 : props.height; overflow = 'hidden'; } else if (isPercentage(props.height)) { height = props.height; overflow = 'hidden'; } _this.animationStateClasses = tslib_1.__assign({}, ANIMATION_STATE_CLASSES, props.animationStateClasses); var animationStateClasses = _this.getStaticStateClasses(height); _this.state = { animationStateClasses: animationStateClasses, height: height, overflow: overflow, shouldUseTransitions: false, }; return _this; } AtAnimationHeight.prototype.componentDidMount = function () { var height = this.state.height; // Hide content if height is 0 (to prevent tabbing into it) // Check for contentElement is added cause this would fail in tests (react-test-renderer) // Read more here: https://github.com/Stanko/react-animate-height/issues/17 if (this.contentElement && this.contentElement.style) { this.hideContent(height); } }; AtAnimationHeight.prototype.componentDidUpdate = function (prevProps, prevState) { var _this = this; var _a; var _b = this.props, delay = _b.delay, duration = _b.duration, height = _b.height, onAnimationEnd = _b.onAnimationEnd, onAnimationStart = _b.onAnimationStart; // Check if 'height' prop has changed if (this.contentElement && height !== prevProps.height) { // Remove display: none from the content div // if it was hidden to prevent tabbing into it this.showContent(prevState.height); // Cache content height this.contentElement.style.overflow = 'hidden'; var contentHeight = this.contentElement.offsetHeight; this.contentElement.style.overflow = ''; // set total animation time var totalDuration = duration + delay; var newHeight_1 = null; var timeoutState_1 = { height: null, overflow: 'hidden', }; var isCurrentHeightAuto = prevState.height === 'auto'; if (isNumber(height)) { // If new height is a number newHeight_1 = height < 0 ? 0 : height; timeoutState_1.height = newHeight_1; } else if (isPercentage(height)) { newHeight_1 = height; timeoutState_1.height = newHeight_1; } else { // If not, animate to content height // and then reset to auto newHeight_1 = contentHeight; // TODO solve contentHeight = 0 timeoutState_1.height = 'auto'; timeoutState_1.overflow = null; } if (isCurrentHeightAuto) { // This is the height to be animated to timeoutState_1.height = newHeight_1; // If previous height was 'auto' // set starting height explicitly to be able to use transition newHeight_1 = contentHeight; } // Animation classes var animationStateClasses = cx((_a = {}, _a[this.animationStateClasses.animating] = true, _a[this.animationStateClasses.animatingUp] = prevProps.height === 'auto' || height < prevProps.height, _a[this.animationStateClasses.animatingDown] = height === 'auto' || height > prevProps.height, _a[this.animationStateClasses.animatingToHeightZero] = timeoutState_1.height === 0, _a[this.animationStateClasses.animatingToHeightAuto] = timeoutState_1.height === 'auto', _a[this.animationStateClasses.animatingToHeightSpecific] = timeoutState_1.height > 0, _a)); // Animation classes to be put after animation is complete var timeoutAnimationStateClasses_1 = this.getStaticStateClasses(timeoutState_1.height); // Set starting height and animating classes // We are safe to call set state as it will not trigger infinite loop // because of the "height !== prevProps.height" check this.setState({ animationStateClasses: animationStateClasses, height: newHeight_1, overflow: 'hidden', // When animating from 'auto' we first need to set fixed height // that change should be animated shouldUseTransitions: !isCurrentHeightAuto, }); // Clear timeouts clearTimeout(this.timeoutID); clearTimeout(this.animationClassesTimeoutID); if (isCurrentHeightAuto) { // When animating from 'auto' we use a short timeout to start animation // after setting fixed height above timeoutState_1.shouldUseTransitions = true; startAnimationHelper(function () { _this.setState(timeoutState_1); // ANIMATION STARTS, run a callback if it exists if (onAnimationStart) runCallback(onAnimationStart, { newHeight: timeoutState_1.height }); }); // Set static classes and remove transitions when animation ends this.animationClassesTimeoutID = setTimeout(function () { _this.setState({ animationStateClasses: timeoutAnimationStateClasses_1, shouldUseTransitions: false, }); // ANIMATION ENDS // Hide content if height is 0 (to prevent tabbing into it) _this.hideContent(timeoutState_1.height); // Run a callback if it exists if (onAnimationEnd) runCallback(onAnimationEnd, { newHeight: timeoutState_1.height }); }, totalDuration); } else { // ANIMATION STARTS, run a callback if it exists if (onAnimationStart) runCallback(onAnimationStart, { newHeight: newHeight_1 }); // Set end height, classes and remove transitions when animation is complete this.timeoutID = setTimeout(function () { timeoutState_1.animationStateClasses = timeoutAnimationStateClasses_1; timeoutState_1.shouldUseTransitions = false; _this.setState(timeoutState_1); // ANIMATION ENDS // If height is auto, don't hide the content // (case when element is empty, therefore height is 0) if (height !== 'auto') { // Hide content if height is 0 (to prevent tabbing into it) _this.hideContent(newHeight_1); // TODO solve newHeight = 0 } // Run a callback if it exists if (onAnimationEnd) runCallback(onAnimationEnd, { newHeight: newHeight_1 }); }, totalDuration); } } }; AtAnimationHeight.prototype.componentWillUnmount = function () { clearTimeout(this.timeoutID); clearTimeout(this.animationClassesTimeoutID); this.timeoutID = null; this.animationClassesTimeoutID = null; this.animationStateClasses = null; }; AtAnimationHeight.prototype.showContent = function (height) { if (height === 0) { this.contentElement.style.display = ''; } }; AtAnimationHeight.prototype.hideContent = function (newHeight) { if (newHeight === 0) { this.contentElement.style.display = 'none'; } }; AtAnimationHeight.prototype.getStaticStateClasses = function (height) { var _a; return cx((_a = {}, _a[this.animationStateClasses.static] = true, _a[this.animationStateClasses.staticHeightZero] = height === 0, _a[this.animationStateClasses.staticHeightSpecific] = height > 0, _a[this.animationStateClasses.staticHeightAuto] = height === 'auto', _a)); }; AtAnimationHeight.prototype.render = function () { var _this = this; var _a; var _b = this.props, animateOpacity = _b.animateOpacity, applyInlineTransitions = _b.applyInlineTransitions, children = _b.children, className = _b.className, contentClassName = _b.contentClassName, duration = _b.duration, easing = _b.easing, delay = _b.delay, style = _b.style; var _c = this.state, height = _c.height, overflow = _c.overflow, animationStateClasses = _c.animationStateClasses, shouldUseTransitions = _c.shouldUseTransitions; var componentStyle = tslib_1.__assign({}, style, { height: height, overflow: overflow || style.overflow }); if (shouldUseTransitions && applyInlineTransitions) { componentStyle.transition = "height " + duration + "ms " + easing + " " + delay + "ms"; // Include transition passed through styles if (style.transition) { componentStyle.transition = style.transition + ", " + componentStyle.transition; } // Add webkit vendor prefix still used by opera, blackberry... componentStyle.WebkitTransition = componentStyle.transition; } var contentStyle = {}; if (animateOpacity) { contentStyle.transition = "opacity " + duration + "ms " + easing + " " + delay + "ms"; // Add webkit vendor prefix still used by opera, blackberry... contentStyle.WebkitTransition = contentStyle.transition; if (height === 0) { contentStyle.opacity = 0; } } var componentClasses = cx((_a = {}, _a[animationStateClasses] = true, _a[className] = className, _a)); return (React.createElement("div", tslib_1.__assign({}, omit.apply(void 0, [this.props].concat(PROPS_TO_OMIT)), { "aria-hidden": height === 0, className: componentClasses, style: componentStyle }), React.createElement("div", { className: contentClassName, style: contentStyle, ref: function (el) { return _this.contentElement = el; } }, children))); }; AtAnimationHeight.defaultProps = { animateOpacity: false, animationStateClasses: ANIMATION_STATE_CLASSES, applyInlineTransitions: true, duration: 250, delay: 0, easing: 'ease', style: {}, onAnimationEnd: function () { }, onAnimationStart: function () { }, }; return AtAnimationHeight; }(React.Component)); export { AtAnimationHeight }; ; export default AtAnimationHeight;