'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTransitionString = exports.getNativeNode = exports.updateHeightPlaceholder = exports.removeNodeFromDOMFlow = exports.getPositionDelta = exports.getRelativeBoundingBox = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /** * React Flip Move * (c) 2016-present Joshua Comeau * * These methods read from and write to the DOM. * They almost always have side effects, and will hopefully become the * only spot in the codebase with impure functions. */ exports.applyStylesToDOMNode = applyStylesToDOMNode; exports.whichTransitionEvent = whichTransitionEvent; var _reactDom = require('react-dom'); var _helpers = require('./helpers'); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function applyStylesToDOMNode(_ref) { var domNode = _ref.domNode, styles = _ref.styles; // Can't just do an object merge because domNode.styles is no regular object. // Need to do it this way for the engine to fire its `set` listeners. Object.keys(styles).forEach(function (key) { domNode.style.setProperty((0, _helpers.hyphenate)(key), styles[key]); }); } // Modified from Modernizr function whichTransitionEvent() { var transitions = { transition: 'transitionend', '-o-transition': 'oTransitionEnd', '-moz-transition': 'transitionend', '-webkit-transition': 'webkitTransitionEnd' }; // If we're running in a browserless environment (eg. SSR), it doesn't apply. // Return a placeholder string, for consistent type return. if (typeof document === 'undefined') return ''; var el = document.createElement('fakeelement'); var match = Object.keys(transitions).find(function (t) { return el.style.getPropertyValue(t) !== undefined; }); // If no `transition` is found, we must be running in a browser so ancient, // React itself won't run. Return an empty string, for consistent type return return match ? transitions[match] : ''; } var getRelativeBoundingBox = exports.getRelativeBoundingBox = function getRelativeBoundingBox(_ref2) { var childDomNode = _ref2.childDomNode, parentDomNode = _ref2.parentDomNode, getPosition = _ref2.getPosition; var parentBox = getPosition(parentDomNode); var _getPosition = getPosition(childDomNode), top = _getPosition.top, left = _getPosition.left, right = _getPosition.right, bottom = _getPosition.bottom, width = _getPosition.width, height = _getPosition.height; return { top: top - parentBox.top, left: left - parentBox.left, right: parentBox.right - right, bottom: parentBox.bottom - bottom, width: width, height: height }; }; /** getPositionDelta * This method returns the delta between two bounding boxes, to figure out * how many pixels on each axis the element has moved. * */ var getPositionDelta = exports.getPositionDelta = function getPositionDelta(_ref3) { var childDomNode = _ref3.childDomNode, childBoundingBox = _ref3.childBoundingBox, parentBoundingBox = _ref3.parentBoundingBox, getPosition = _ref3.getPosition; // TEMP: A mystery bug is sometimes causing unnecessary boundingBoxes to var defaultBox = { top: 0, left: 0, right: 0, bottom: 0, height: 0, width: 0 }; // Our old box is its last calculated position, derived on mount or at the // start of the previous animation. var oldRelativeBox = childBoundingBox || defaultBox; var parentBox = parentBoundingBox || defaultBox; // Our new box is the new final resting place: Where we expect it to wind up // after the animation. First we get the box in absolute terms (AKA relative // to the viewport), and then we calculate its relative box (relative to the // parent container) var newAbsoluteBox = getPosition(childDomNode); var newRelativeBox = { top: newAbsoluteBox.top - parentBox.top, left: newAbsoluteBox.left - parentBox.left }; return [oldRelativeBox.left - newRelativeBox.left, oldRelativeBox.top - newRelativeBox.top]; }; /** removeNodeFromDOMFlow * This method does something very sneaky: it removes a DOM node from the * document flow, but without actually changing its on-screen position. * * It works by calculating where the node is, and then applying styles * so that it winds up being positioned absolutely, but in exactly the * same place. * * This is a vital part of the FLIP technique. */ var removeNodeFromDOMFlow = exports.removeNodeFromDOMFlow = function removeNodeFromDOMFlow(childData, verticalAlignment) { var domNode = childData.domNode, boundingBox = childData.boundingBox; if (!domNode || !boundingBox) { return; } // For this to work, we have to offset any given `margin`. var computed = window.getComputedStyle(domNode); // We need to clean up margins, by converting and removing suffix: // eg. '21px' -> 21 var marginAttrs = ['margin-top', 'margin-left', 'margin-right']; var margins = marginAttrs.reduce(function (acc, margin) { var propertyVal = computed.getPropertyValue(margin); return _extends({}, acc, _defineProperty({}, margin, Number(propertyVal.replace('px', '')))); }, {}); // If we're bottom-aligned, we need to add the height of the child to its // top offset. This is because, when the container is bottom-aligned, its // height shrinks from the top, not the bottom. We're removing this node // from the flow, so the top is going to drop by its height. var topOffset = verticalAlignment === 'bottom' ? boundingBox.top - boundingBox.height : boundingBox.top; var styles = { position: 'absolute', top: topOffset - margins['margin-top'] + 'px', left: boundingBox.left - margins['margin-left'] + 'px', right: boundingBox.right - margins['margin-right'] + 'px' }; applyStylesToDOMNode({ domNode: domNode, styles: styles }); }; /** updateHeightPlaceholder * An optional property to FlipMove is a `maintainContainerHeight` boolean. * This property creates a node that fills space, so that the parent * container doesn't collapse when its children are removed from the * document flow. */ var updateHeightPlaceholder = exports.updateHeightPlaceholder = function updateHeightPlaceholder(_ref4) { var domNode = _ref4.domNode, parentData = _ref4.parentData, getPosition = _ref4.getPosition; var parentDomNode = parentData.domNode; var parentBoundingBox = parentData.boundingBox; if (!parentDomNode || !parentBoundingBox) { return; } // We need to find the height of the container *without* the placeholder. // Since it's possible that the placeholder might already be present, // we first set its height to 0. // This allows the container to collapse down to the size of just its // content (plus container padding or borders if any). applyStylesToDOMNode({ domNode: domNode, styles: { height: '0' } }); // Find the distance by which the container would be collapsed by elements // leaving. We compare the freshly-available parent height with the original, // cached container height. var originalParentHeight = parentBoundingBox.height; var collapsedParentHeight = getPosition(parentDomNode).height; var reductionInHeight = originalParentHeight - collapsedParentHeight; // If the container has become shorter, update the padding element's // height to take up the difference. Otherwise set its height to zero, // so that it has no effect. var styles = { height: reductionInHeight > 0 ? reductionInHeight + 'px' : '0' }; applyStylesToDOMNode({ domNode: domNode, styles: styles }); }; var getNativeNode = exports.getNativeNode = function getNativeNode(element) { // When running in a windowless environment, abort! if (typeof HTMLElement === 'undefined') { return null; } // `element` may already be a native node. if (element instanceof HTMLElement) { return element; } // While ReactDOM's `findDOMNode` is discouraged, it's the only // publicly-exposed way to find the underlying DOM node for // composite components. var foundNode = (0, _reactDom.findDOMNode)(element); if (!(foundNode instanceof HTMLElement)) { // Text nodes are not supported return null; } return foundNode; }; var createTransitionString = exports.createTransitionString = function createTransitionString(index, props) { var delay = props.delay, duration = props.duration; var staggerDurationBy = props.staggerDurationBy, staggerDelayBy = props.staggerDelayBy, easing = props.easing; delay += index * staggerDelayBy; duration += index * staggerDurationBy; var cssProperties = ['transform', 'opacity']; return cssProperties.map(function (prop) { return prop + ' ' + duration + 'ms ' + easing + ' ' + delay + 'ms'; }).join(', '); };