UNPKG

@atlaskit/motion

Version:

A set of utilities to apply motion in your application.

114 lines (110 loc) 5.66 kB
"use strict"; 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)); };