@atlaskit/motion
Version:
A set of utilities to apply motion in your application.
114 lines (110 loc) • 5.66 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useResizingHeight = exports.ResizingHeight = void 0;
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _accessibility = require("../utils/accessibility");
var _curves = require("../utils/curves");
var _durations = require("../utils/durations");
var _timerHooks = require("../utils/timer-hooks");
var _useElementRef3 = require("../utils/use-element-ref");
var _useLayoutEffect = require("../utils/use-layout-effect");
var _useSnapshotBeforeUpdate = require("../utils/use-snapshot-before-update");
var _excluded = ["children"];
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
/**
* `useResizingHeight` animates height changes over state changes. If the height hasn't changed nothing will happen.
*
* __WARNING__: Potentially janky. This hook animates height which is
* [notoriously unperformant](https://firefox-source-docs.mozilla.org/performance/bestpractices.html#Get_familiar_with_the_pipeline_that_gets_pixels_to_the_screen).
* Test your app over low powered devices, you may want to avoid this if you can see it impacting FPS.
*
* See [examples](https://atlaskit.atlassian.com/packages/design-system/motion/docs/resizing-motions).
*/
var useResizingHeight = exports.useResizingHeight = function useResizingHeight() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$duration = _ref.duration,
calcDuration = _ref$duration === void 0 ? function () {
return _durations.durations.medium;
} : _ref$duration,
_ref$timingFunction = _ref.timingFunction,
calcTimingFunction = _ref$timingFunction === void 0 ? function () {
return _curves.easeInOut;
} : _ref$timingFunction;
var prevDimensions = (0, _react.useRef)();
var _useElementRef = (0, _useElementRef3.useElementRef)(),
_useElementRef2 = (0, _slicedToArray2.default)(_useElementRef, 2),
element = _useElementRef2[0],
setElementRef = _useElementRef2[1];
// We cleanup on the next effect to prevent the previous timeout being called during
// the next motion - as now the timeout has essentially been extended!
var setTimeout = (0, _timerHooks.useSetTimeout)({
cleanup: 'next-effect'
});
var requestAnimationFrame = (0, _timerHooks.useRequestAnimationFrame)();
(0, _useSnapshotBeforeUpdate.useSnapshotBeforeUpdate)(function () {
if ((0, _accessibility.isReducedMotion)() || !element) {
return;
}
prevDimensions.current = element.getBoundingClientRect();
});
(0, _useLayoutEffect.useLayoutEffect)(function () {
if ((0, _accessibility.isReducedMotion)() || !element || !prevDimensions.current) {
return;
}
// We might already be animating.
// Because of that we need to expand to the destination height first.
element.setAttribute('style', '');
var nextDimensions = element.getBoundingClientRect();
if (nextDimensions.height === prevDimensions.current.height) {
return;
}
var duration = calcDuration(prevDimensions.current.height, nextDimensions.height);
var newStyles = {
height: "".concat(prevDimensions.current.height, "px"),
willChange: 'height',
transitionProperty: 'height',
transitionDuration: "".concat(duration, "ms"),
boxSizing: 'border-box',
transitionTimingFunction: calcTimingFunction(prevDimensions.current.height, nextDimensions.height, duration)
};
Object.assign(element.style, newStyles);
// We split this over two animation frames so the DOM has enough time to flush the changes.
// We are deliberately not skipping this frame if another render happens - if we do the motion doesn't finish properly.
requestAnimationFrame(function () {
requestAnimationFrame(function () {
if (!element) {
return;
}
element.style.height = "".concat(nextDimensions.height, "px");
setTimeout(function () {
if (!element) {
return;
}
element.setAttribute('style', '');
}, duration);
});
});
});
return {
ref: setElementRef
};
};
/**
* __ResizingHeight__
*
* Component which consumes the useResizingHook() under-the-hood. Its props are the same as the hooks opts.
*
* See [examples](https://atlaskit.atlassian.com/packages/design-system/motion/docs/resizing-motions).
*/
var ResizingHeight = exports.ResizingHeight = function ResizingHeight(_ref2) {
var children = _ref2.children,
props = (0, _objectWithoutProperties2.default)(_ref2, _excluded);
var resizing = useResizingHeight(props);
return /*#__PURE__*/_react.default.createElement(_react.Fragment, null, children(resizing));
};