You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

249 lines
9.0 KiB

'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(', ');
};