UNPKG

jobiqo-cl

Version:

[![CircleCI](https://circleci.com/gh/jobiqo/jobiqo-cl.svg?style=svg&circle-token=5a24efa5b8bbc4879276123e77d0d3f35ca7144c)](https://circleci.com/gh/jobiqo/jobiqo-cl)

1,282 lines (1,262 loc) 150 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var tslib_es6 = require('../../tslib/tslib.es6.js'); var React = require('react'); var React__default = _interopDefault(React); var heyListen_es = require('../../hey-listen/dist/hey-listen.es.js'); var framesync_es = require('../../framesync/dist/framesync.es.js'); var styleValueTypes_es = require('../../style-value-types/dist/style-value-types.es.js'); var easing_es = require('../../@popmotion/easing/dist/easing.es.js'); var popcorn_es = require('../../@popmotion/popcorn/dist/popcorn.es.js'); var stylefire_es = require('../../stylefire/dist/stylefire.es.js'); var popmotion_es = require('../../popmotion/dist/popmotion.es.js'); /** * Uses the ref that is passed in, or creates a new one * @param external - External ref * @internal */ function useExternalRef(external) { // We're conditionally calling `useRef` here which is sort of naughty as hooks // shouldn't be called conditionally. However, Framer Motion will break if this // condition changes anyway. It might be possible to use an invariant here to // make it explicit, but I expect changing `ref` is not normal behaviour. var ref = !external || typeof external === "function" ? React.useRef(null) : external; React.useEffect(function () { if (external && typeof external === "function") { external(ref.current); return function () { return external(null); }; } }, []); return ref; } var isFloat = function (value) { return !isNaN(parseFloat(value)); }; /** * `MotionValue` is used to track the state and velocity of motion values. * * @public */ var MotionValue = /** @class */ (function () { /** * @param init - The initiating value * @param config - Optional configuration options * * - `transformer`: A function to transform incoming values with. * * @internal */ function MotionValue(init, _a) { var _this = this; var _b = _a === void 0 ? {} : _a, transformer = _b.transformer, parent = _b.parent; /** * Duration, in milliseconds, since last updating frame. * * @internal */ this.timeDelta = 0; /** * Timestamp of the last time this `MotionValue` was updated. * * @internal */ this.lastUpdated = 0; /** * Tracks whether this value can output a velocity. Currently this is only true * if the value is numerical, but we might be able to widen the scope here and support * other value types. * * @internal */ this.canTrackVelocity = false; this.updateAndNotify = function (v, render) { if (render === void 0) { render = true; } _this.prev = _this.current; _this.current = _this.transformer ? _this.transformer(v) : v; if (_this.updateSubscribers && _this.prev !== _this.current) { _this.updateSubscribers.forEach(_this.notifySubscriber); } if (_this.children) { _this.children.forEach(_this.setChild); } if (render && _this.renderSubscribers) { _this.renderSubscribers.forEach(_this.notifySubscriber); } // Update timestamp var _a = framesync_es.getFrameData(), delta = _a.delta, timestamp = _a.timestamp; if (_this.lastUpdated !== timestamp) { _this.timeDelta = delta; _this.lastUpdated = timestamp; framesync_es.default.postRender(_this.scheduleVelocityCheck); } }; /** * Notify a subscriber with the latest value. * * This is an instanced and bound function to prevent generating a new * function once per frame. * * @param subscriber - The subscriber to notify. * * @internal */ this.notifySubscriber = function (subscriber) { subscriber(_this.current); }; /** * Schedule a velocity check for the next frame. * * This is an instanced and bound function to prevent generating a new * function once per frame. * * @internal */ this.scheduleVelocityCheck = function () { return framesync_es.default.postRender(_this.velocityCheck); }; /** * Updates `prev` with `current` if the value hasn't been updated this frame. * This ensures velocity calculations return `0`. * * This is an instanced and bound function to prevent generating a new * function once per frame. * * @internal */ this.velocityCheck = function (_a) { var timestamp = _a.timestamp; if (timestamp !== _this.lastUpdated) { _this.prev = _this.current; } }; /** * Updates child `MotionValue`. * * @param child - Child `MotionValue`. * * @internal */ this.setChild = function (child) { return child.set(_this.current); }; this.parent = parent; this.transformer = transformer; this.set(init, false); this.canTrackVelocity = isFloat(this.current); } /** * Creates a new `MotionValue` that's subscribed to the output of this one. * * @param config - Optional configuration options * * - `transformer`: A function to transform incoming values with. * * @internal */ MotionValue.prototype.addChild = function (config) { if (config === void 0) { config = {}; } var child = new MotionValue(this.current, tslib_es6.__assign({ parent: this }, config)); if (!this.children) this.children = new Set(); this.children.add(child); return child; }; /** * Stops a `MotionValue` from being subscribed to this one. * * @param child - The subscribed `MotionValue` * * @internal */ MotionValue.prototype.removeChild = function (child) { if (!this.children) { return; } this.children.delete(child); }; /** * Subscribes a subscriber function to a subscription list. * * @param subscriptions - A `Set` of subscribers. * @param subscription - A subscriber function. */ MotionValue.prototype.subscribeTo = function (subscriptions, subscription) { var _this = this; var updateSubscriber = function () { return subscription(_this.current); }; subscriptions.add(updateSubscriber); return function () { return subscriptions.delete(updateSubscriber); }; }; /** * Adds a function that will be notified when the `MotionValue` is updated. * * It returns a function that, when called, will cancel the subscription. * * When calling `onChange` inside a React component, it should be wrapped with the * `useEffect` hook. As it returns an unsubscribe function, this should be returned * from the `useEffect` function to ensure you don't add duplicate subscribers.. * * @library * * ```jsx * function MyComponent() { * const x = useMotionValue(0) * const y = useMotionValue(0) * const opacity = useMotionValue(1) * * useEffect(() => { * function updateOpacity() { * const maxXY = Math.max(x.get(), y.get()) * const newOpacity = transform(maxXY, [0, 100], [1, 0]) * opacity.set(newOpacity) * } * * const unsubscribeX = x.onChange(updateOpacity) * const unsubscribeY = y.onChange(updateOpacity) * * return () => { * unsubscribeX() * unsubscribeY() * } * }, []) * * return <Frame x={x} /> * } * ``` * * @motion * * ```jsx * export const MyComponent = () => { * const x = useMotionValue(0) * const y = useMotionValue(0) * const opacity = useMotionValue(1) * * useEffect(() => { * function updateOpacity() { * const maxXY = Math.max(x.get(), y.get()) * const newOpacity = transform(maxXY, [0, 100], [1, 0]) * opacity.set(newOpacity) * } * * const unsubscribeX = x.onChange(updateOpacity) * const unsubscribeY = y.onChange(updateOpacity) * * return () => { * unsubscribeX() * unsubscribeY() * } * }, []) * * return <motion.div style={{ x }} /> * } * ``` * * @internalremarks * * We could look into a `useOnChange` hook if the above lifecycle management proves confusing. * * ```jsx * useOnChange(x, () => {}) * ``` * * @param subscriber - A function that receives the latest value. * @returns A function that, when called, will cancel this subscription. * * @public */ MotionValue.prototype.onChange = function (subscription) { if (!this.updateSubscribers) this.updateSubscribers = new Set(); return this.subscribeTo(this.updateSubscribers, subscription); }; /** * Adds a function that will be notified when the `MotionValue` requests a render. * * @param subscriber - A function that's provided the latest value. * @returns A function that, when called, will cancel this subscription. * * @internal */ MotionValue.prototype.onRenderRequest = function (subscription) { if (!this.renderSubscribers) this.renderSubscribers = new Set(); // Render immediately this.notifySubscriber(subscription); return this.subscribeTo(this.renderSubscribers, subscription); }; /** * Attaches a passive effect to the `MotionValue`. * * @internal */ MotionValue.prototype.attach = function (passiveEffect) { this.passiveEffect = passiveEffect; }; /** * Sets the state of the `MotionValue`. * * @remarks * * ```jsx * const x = useMotionValue(0) * x.set(10) * ``` * * @param latest - Latest value to set. * @param render - Whether to notify render subscribers. Defaults to `true` * * @public */ MotionValue.prototype.set = function (v, render) { if (render === void 0) { render = true; } if (!render || !this.passiveEffect) { this.updateAndNotify(v, render); } else { this.passiveEffect(v, this.updateAndNotify); } }; /** * Returns the latest state of `MotionValue` * * @returns - The latest state of `MotionValue` * * @public */ MotionValue.prototype.get = function () { return this.current; }; /** * Returns the latest velocity of `MotionValue` * * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical. * * @public */ MotionValue.prototype.getVelocity = function () { // This could be isFloat(this.prev) && isFloat(this.current), but that would be wasteful return this.canTrackVelocity ? // These casts could be avoided if parseFloat would be typed better popcorn_es.velocityPerSecond(parseFloat(this.current) - parseFloat(this.prev), this.timeDelta) : 0; }; /** * Registers a new animation to control this `MotionValue`. Only one * animation can drive a `MotionValue` at one time. * * ```jsx * value.start() * ``` * * @param animation - A function that starts the provided animation * * @internal */ MotionValue.prototype.start = function (animation) { var _this = this; this.stop(); return new Promise(function (resolve) { _this.stopAnimation = animation(resolve); }).then(function () { return _this.clearAnimation(); }); }; /** * Stop the currently active animation. * * @public */ MotionValue.prototype.stop = function () { if (this.stopAnimation) this.stopAnimation(); this.clearAnimation(); }; /** * Returns `true` if this value is currently animating. * * @public */ MotionValue.prototype.isAnimating = function () { return !!this.stopAnimation; }; MotionValue.prototype.clearAnimation = function () { this.stopAnimation = null; }; /** * Destroy and clean up subscribers to this `MotionValue`. * * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually * created a `MotionValue` via the `motionValue` function. * * @public */ MotionValue.prototype.destroy = function () { this.updateSubscribers && this.updateSubscribers.clear(); this.renderSubscribers && this.renderSubscribers.clear(); this.parent && this.parent.removeChild(this); this.stop(); }; return MotionValue; }()); /** * @internal */ function motionValue(init, opts) { return new MotionValue(init, opts); } /** * Creates a constant value over the lifecycle of a component. * * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer * a guarantee that it won't re-run for performance reasons later on. By using `useConstant` * you can ensure that initialisers don't execute twice or more. */ function useConstant(init) { var ref = React.useRef(null); if (ref.current === null) { ref.current = init(); } return ref.current; } var isMotionValue = function (value) { return value instanceof MotionValue; }; // Creating a styler factory for the `onUpdate` prop allows all values // to fire and the `onUpdate` prop will only fire once per frame var updateStyler = stylefire_es.createStylerFactory({ onRead: function () { return null; }, onRender: function (state, _a) { var onUpdate = _a.onUpdate; return onUpdate(state); }, }); var MotionValuesMap = /** @class */ (function () { function MotionValuesMap() { this.hasMounted = false; this.values = new Map(); this.unsubscribers = new Map(); } MotionValuesMap.prototype.has = function (key) { return this.values.has(key); }; MotionValuesMap.prototype.set = function (key, value) { this.values.set(key, value); if (this.hasMounted) { this.bindValueToOutput(key, value); } }; MotionValuesMap.prototype.get = function (key, defaultValue) { var value = this.values.get(key); if (value === undefined && defaultValue !== undefined) { value = new MotionValue(defaultValue); this.set(key, value); } return value; }; MotionValuesMap.prototype.forEach = function (callback) { return this.values.forEach(callback); }; MotionValuesMap.prototype.bindValueToOutput = function (key, value) { var _this = this; var onRender = function (v) { return _this.output && _this.output(key, v); }; var unsubscribeOnRender = value.onRenderRequest(onRender); var onChange = function (v) { _this.onUpdate && _this.onUpdate.set(key, v); }; var unsubscribeOnChange = value.onChange(onChange); if (this.unsubscribers.has(key)) { this.unsubscribers.get(key)(); } this.unsubscribers.set(key, function () { unsubscribeOnRender(); unsubscribeOnChange(); }); }; MotionValuesMap.prototype.setOnUpdate = function (onUpdate) { this.onUpdate = undefined; if (onUpdate) { this.onUpdate = updateStyler({ onUpdate: onUpdate }); } }; MotionValuesMap.prototype.setTransformTemplate = function (transformTemplate) { if (this.transformTemplate !== transformTemplate) { this.transformTemplate = transformTemplate; this.updateTransformTemplate(); } }; MotionValuesMap.prototype.getTransformTemplate = function () { return this.transformTemplate; }; MotionValuesMap.prototype.updateTransformTemplate = function () { if (this.output) { this.output("transform", this.transformTemplate); } }; MotionValuesMap.prototype.mount = function (output) { var _this = this; this.hasMounted = true; if (output) this.output = output; this.values.forEach(function (value, key) { return _this.bindValueToOutput(key, value); }); this.updateTransformTemplate(); }; MotionValuesMap.prototype.unmount = function () { var _this = this; this.values.forEach(function (_value, key) { var unsubscribe = _this.unsubscribers.get(key); unsubscribe && unsubscribe(); }); }; return MotionValuesMap; }()); var specialMotionValueProps = new Set(["dragOriginX", "dragOriginY"]); var useMotionValues = function (props) { var motionValues = useConstant(function () { var map = new MotionValuesMap(); /** * Loop through every prop and add any detected `MotionValue`s. This is SVG-specific * code that should be extracted, perhaps considered hollistically with `useMotionStyles`. * * <motion.circle cx={motionValue(0)} /> */ for (var key in props) { if (isMotionValue(props[key]) && !specialMotionValueProps.has(key)) { map.set(key, props[key]); } } return map; }); motionValues.setOnUpdate(props.onUpdate); motionValues.setTransformTemplate(props.transformTemplate); return motionValues; }; /** * `useEffect` gets resolved bottom-up. We defer some optional functionality to child * components, so to ensure everything runs correctly we export the ref-binding logic * to a new component rather than in `useMotionValues`. */ var MountMotionValuesComponent = function (_a, ref) { var values = _a.values, isStatic = _a.isStatic; React.useEffect(function () { heyListen_es.invariant(ref.current instanceof Element, "No `ref` found. Ensure components created with `motion.custom` forward refs using `React.forwardRef`"); var domStyler = stylefire_es.default(ref.current, { preparseOutput: false, enableHardwareAcceleration: !isStatic, }); values.mount(function (key, value) { return domStyler.set(key, value); }); return function () { return values.unmount(); }; }, []); return null; }; var MountMotionValues = React.memo(React.forwardRef(MountMotionValuesComponent)); var createValueResolver = function (resolver) { return function (values) { var resolvedValues = {}; values.forEach(function (value, key) { return (resolvedValues[key] = resolver(value)); }); return resolvedValues; }; }; var resolveCurrent = createValueResolver(function (value) { return value.get(); }); var transformOriginProps = new Set(["originX", "originY", "originZ"]); var isTransformOriginProp = function (key) { return transformOriginProps.has(key); }; var buildStyleAttr = function (values, styleProp, isStatic) { var motionValueStyles = resolveCurrent(values); var transformTemplate = values.getTransformTemplate(); if (transformTemplate) { // If `transform` has been manually set as a string, pass that through the template // otherwise pass it forward to Stylefire's style property builder motionValueStyles.transform = styleProp.transform ? transformTemplate({}, styleProp.transform) : transformTemplate; } return tslib_es6.__assign({}, styleProp, stylefire_es.buildStyleProperty(motionValueStyles, !isStatic)); }; var useMotionStyles = function (values, styleProp, transformValues) { if (styleProp === void 0) { styleProp = {}; } var style = React.useRef({}).current; var prevMotionStyles = React.useRef({}).current; var currentStyleKeys = new Set(Object.keys(style)); for (var key in styleProp) { currentStyleKeys.delete(key); var thisStyle = styleProp[key]; if (isMotionValue(thisStyle)) { // If this is a motion value, add it to our MotionValuesMap values.set(key, thisStyle); } else if (stylefire_es.isTransformProp(key) || isTransformOriginProp(key)) { // Or if it's a transform prop, create a motion value (or update an existing one) // to ensure Stylefire can reconcile all the transform values together. if (!values.has(key)) { // If it doesn't exist as a motion value, create it values.set(key, motionValue(thisStyle)); } else { // Otherwise only update it if it's changed from a previous render if (thisStyle !== prevMotionStyles[key]) { var value = values.get(key); value.set(thisStyle); } } prevMotionStyles[key] = thisStyle; } else { style[key] = thisStyle; } } currentStyleKeys.forEach(function (key) { return delete style[key]; }); return transformValues ? transformValues(style) : style; }; var isKeyframesTarget = function (v) { return Array.isArray(v); }; var resolveFinalValueInKeyframes = function (v) { // TODO maybe throw if v.length - 1 is placeholder token? return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v; }; var auto = { test: function (v) { return v === "auto"; }, parse: function (v) { return v; }, }; var dimensionTypes = [styleValueTypes_es.number, styleValueTypes_es.px, styleValueTypes_es.percent, styleValueTypes_es.degrees, styleValueTypes_es.vw, styleValueTypes_es.vh, auto]; var valueTypes = dimensionTypes.concat([styleValueTypes_es.color, styleValueTypes_es.complex]); var testValueType = function (v) { return function (type) { return type.test(v); }; }; var getDimensionValueType = function (v) { return dimensionTypes.find(testValueType(v)); }; var getValueType = function (v) { return valueTypes.find(testValueType(v)); }; var underDampedSpring = function () { return ({ type: "spring", stiffness: 500, damping: 25, restDelta: 0.5, restSpeed: 10, }); }; var overDampedSpring = function (to) { return ({ type: "spring", stiffness: 700, damping: to === 0 ? 100 : 35, }); }; var linearTween = function () { return ({ ease: "linear", duration: 0.3, }); }; var keyframes = function (values) { return ({ type: "keyframes", duration: 0.8, values: values, }); }; var defaultTransitions = { x: underDampedSpring, y: underDampedSpring, z: underDampedSpring, rotate: underDampedSpring, rotateX: underDampedSpring, rotateY: underDampedSpring, rotateZ: underDampedSpring, scaleX: overDampedSpring, scaleY: overDampedSpring, scale: overDampedSpring, opacity: linearTween, backgroundColor: linearTween, color: linearTween, default: overDampedSpring, }; var getDefaultTransition = function (valueKey, to) { var transitionFactory; if (isKeyframesTarget(to)) { transitionFactory = keyframes; } else { transitionFactory = defaultTransitions[valueKey] || defaultTransitions.default; } return tslib_es6.__assign({ to: to }, transitionFactory(to)); }; /** * A Popmotion action that accepts a single `to` prop. When it starts, it immediately * updates with `to` and then completes. By using this we can compose instant transitions * in with the same logic that applies `delay` or returns a `Promise` etc. */ var just = function (_a) { var to = _a.to; return popmotion_es.action(function (_a) { var update = _a.update, complete = _a.complete; update(to); complete(); }); }; var easingDefinitionToFunction = function (definition) { if (Array.isArray(definition)) { // If cubic bezier definition, create bezier curve heyListen_es.invariant(definition.length === 4, "Cubic bezier arrays must contain four numerical values."); var x1 = definition[0], y1 = definition[1], x2 = definition[2], y2 = definition[3]; return easing_es.cubicBezier(x1, y1, x2, y2); } else if (typeof definition === "string") { // Else lookup from table heyListen_es.invariant(easing_es[definition] !== undefined, "Invalid easing type '" + definition + "'"); return easing_es[definition]; } return definition; }; var isEasingArray = function (ease) { return Array.isArray(ease) && typeof ease[0] !== "number"; }; var isDurationAnimation = function (v) { return v.hasOwnProperty("duration") || v.hasOwnProperty("repeatDelay"); }; /** * Check if a value is animatable. Examples: * * ✅: 100, "100px", "#fff" * ❌: "block", "url(2.jpg)" * @param value * * @internal */ var isAnimatable = function (key, value) { // If the list of keys tat might be non-animatable grows, replace with Set if (key === "zIndex") return false; // If it's a number or a keyframes array, we can animate it. We might at some point // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this, // but for now lets leave it like this for performance reasons if (typeof value === "number" || Array.isArray(value)) return true; if (typeof value === "string" && // It's animatable if we have a string styleValueTypes_es.complex.test(value) && // And it contains numbers and/or colors !value.startsWith("url(") // Unless it starts with "url(" ) { return true; } return false; }; /** * Converts seconds to milliseconds * * @param seconds - Time in seconds. * @return milliseconds - Converted time in milliseconds. */ var secondsToMilliseconds = function (seconds) { return seconds * 1000; }; var transitions = { tween: popmotion_es.tween, spring: popmotion_es.spring, keyframes: popmotion_es.keyframes, inertia: popmotion_es.inertia, just: just }; var transitionOptionParser = { tween: function (opts) { if (opts.ease) { var ease = isEasingArray(opts.ease) ? opts.ease[0] : opts.ease; opts.ease = easingDefinitionToFunction(ease); } return opts; }, keyframes: function (_a) { var from = _a.from, to = _a.to, velocity = _a.velocity, opts = tslib_es6.__rest(_a, ["from", "to", "velocity"]); if (opts.values && opts.values[0] === null) { var values = opts.values.slice(); values[0] = from; opts.values = values; } if (opts.ease) { opts.easings = isEasingArray(opts.ease) ? opts.ease.map(easingDefinitionToFunction) : easingDefinitionToFunction(opts.ease); } opts.ease = easing_es.linear; return opts; }, }; var isTransitionDefined = function (_a) { var when = _a.when, delay = _a.delay, delayChildren = _a.delayChildren, staggerChildren = _a.staggerChildren, staggerDirection = _a.staggerDirection, transition = tslib_es6.__rest(_a, ["when", "delay", "delayChildren", "staggerChildren", "staggerDirection"]); return Object.keys(transition).length; }; var getTransitionDefinition = function (key, to, transitionDefinition) { var delay = transitionDefinition ? transitionDefinition.delay : 0; // If no object, return default transition // A better way to handle this would be to deconstruct out all the shared Orchestration props // and see if there's any props remaining if (transitionDefinition === undefined || !isTransitionDefined(transitionDefinition)) { return tslib_es6.__assign({ delay: delay }, getDefaultTransition(key, to)); } var valueTransitionDefinition = transitionDefinition[key] || transitionDefinition.default || transitionDefinition; if (valueTransitionDefinition.type === false) { return { type: "just", delay: delay, to: isKeyframesTarget(to) ? to[to.length - 1] : to, }; } else if (isKeyframesTarget(to)) { return tslib_es6.__assign({ values: to, duration: 0.8, delay: delay, ease: "linear" }, valueTransitionDefinition, { // This animation must be keyframes if we're animating through an array type: "keyframes" }); } else { return tslib_es6.__assign({ type: "tween", to: to, delay: delay }, valueTransitionDefinition); } }; var preprocessOptions = function (type, opts) { return transitionOptionParser[type] ? transitionOptionParser[type](opts) : opts; }; var getAnimation = function (key, value, target, transition) { var origin = value.get(); var isOriginAnimatable = isAnimatable(key, origin); var isTargetAnimatable = isAnimatable(key, target); // TODO we could probably improve this check to ensure both values are of the same type - // for instance 100 to #fff. This might live better in Popmotion. heyListen_es.warning(isOriginAnimatable === isTargetAnimatable, "You are trying to animate " + key + " from \"" + origin + "\" to " + target + ". \"" + origin + "\" is not an animatable value - to enable this animation set " + origin + " to a value animatable to " + target + " via the `style` property."); // Parse the `transition` prop and return options for the Popmotion animation var _a = getTransitionDefinition(key, target, transition), _b = _a.type, type = _b === void 0 ? "tween" : _b, transitionDefinition = tslib_es6.__rest(_a, ["type"]); // If this is an animatable pair of values, return an animation, otherwise use `just` var actionFactory = isOriginAnimatable && isTargetAnimatable ? transitions[type] : just; var opts = preprocessOptions(type, tslib_es6.__assign({ from: origin, velocity: value.getVelocity() }, transitionDefinition)); // Convert duration from Framer Motion's seconds into Popmotion's milliseconds if (isDurationAnimation(opts)) { if (opts.duration) { opts.duration = secondsToMilliseconds(opts.duration); } if (opts.repeatDelay) { opts.repeatDelay = secondsToMilliseconds(opts.repeatDelay); } } return actionFactory(opts); }; /** * Start animation on a value. This function completely encapsulates Popmotion-specific logic. * * @internal */ function startAnimation(key, value, target, _a) { var _b = _a.delay, delay$1 = _b === void 0 ? 0 : _b, transition = tslib_es6.__rest(_a, ["delay"]); return value.start(function (complete) { var activeAnimation; var animate = function () { var animation = getAnimation(key, value, target, transition); // Bind animation opts to animation activeAnimation = animation.start({ update: function (v) { return value.set(v); }, complete: complete, }); }; // If we're delaying this animation, only resolve it **after** the delay to // ensure the value's resolve velocity is up-to-date. if (delay$1) { activeAnimation = popmotion_es.delay(secondsToMilliseconds(delay$1)).start({ complete: animate, }); } else { animate(); } return function () { if (activeAnimation) activeAnimation.stop(); }; }); } /** * Get the current value of every `MotionValue` * @param values - */ var getCurrent = function (values) { var current = {}; values.forEach(function (value, key) { return (current[key] = value.get()); }); return current; }; /** * Get the current velocity of every `MotionValue` * @param values - */ var getVelocity = function (values) { var velocity = {}; values.forEach(function (value, key) { return (velocity[key] = value.getVelocity()); }); return velocity; }; /** * Check if value is a function that returns a `Target`. A generic typeof === 'function' * check, just helps with typing. * @param p - */ var isTargetResolver = function (p) { return typeof p === "function"; }; /** * Check if value is a list of variant labels * @param v - */ var isVariantLabels = function (v) { return Array.isArray(v); }; /** * Check if value is a numerical string, ie "100" or "100px" */ var isNumericalString = function (v) { return /^\d*\.?\d+$/.test(v); }; /** * Control animations for a single component * @internal */ var ValueAnimationControls = /** @class */ (function () { function ValueAnimationControls(_a) { var _this = this; var values = _a.values, readValueFromSource = _a.readValueFromSource, makeTargetAnimatable = _a.makeTargetAnimatable; /** * The component's variants, as provided by `variants` */ this.variants = {}; /** * A set of values that we animate back to when a value is cleared of all overrides. */ this.baseTarget = {}; /** * A series of target overrides that we can animate to/from when overrides are set/cleared. */ this.overrides = []; /** * A series of target overrides as they were originally resolved. */ this.resolvedOverrides = []; /** * A Set of currently active override indexes */ this.activeOverrides = new Set(); /** * A Set of value keys that are currently animating. */ this.isAnimating = new Set(); /** * Check if the associated `MotionValueMap` has a key with the provided string. * Pre-bound to the class so we can provide directly to the `filter` in `checkForNewValues`. */ this.hasValue = function (key) { return !_this.values.has(key); }; this.values = values; this.readValueFromSource = readValueFromSource; this.makeTargetAnimatable = makeTargetAnimatable; this.values.forEach(function (value, key) { return (_this.baseTarget[key] = value.get()); }); } /** * Set the reference to the component's props. * @param props - */ ValueAnimationControls.prototype.setProps = function (props) { this.props = props; }; /** * Set the reference to the component's variants * @param variants - */ ValueAnimationControls.prototype.setVariants = function (variants) { if (variants) this.variants = variants; }; /** * Set the component's default transition * @param transition - */ ValueAnimationControls.prototype.setDefaultTransition = function (transition) { if (transition) this.defaultTransition = transition; }; /** * Set motion values without animation. * * @param target - * @param isActive - */ ValueAnimationControls.prototype.setValues = function (target, _a) { var _this = this; var _b = _a === void 0 ? {} : _a, _c = _b.isActive, isActive = _c === void 0 ? new Set() : _c, priority = _b.priority; target = this.transformValues(target); return Object.keys(target).forEach(function (key) { if (isActive.has(key)) return; isActive.add(key); var targetValue = resolveFinalValueInKeyframes(target[key]); if (_this.values.has(key)) { var value = _this.values.get(key); value && value.set(targetValue); } else { _this.values.set(key, motionValue(targetValue)); } if (!priority) _this.baseTarget[key] = targetValue; }); }; /** * Allows `transformValues` to be set by a component that allows us to * transform the values in a given `Target`. This allows Framer Library * to extend Framer Motion to animate `Color` variables etc. Currently we have * to manually support these extended types here in Framer Motion. * * @param values - */ ValueAnimationControls.prototype.transformValues = function (values) { var transformValues = this.props.transformValues; return transformValues ? transformValues(values) : values; }; /** * Check a `Target` for new values we haven't animated yet, and add them * to the `MotionValueMap`. * * Currently there's functionality here that is DOM-specific, we should allow * this functionality to be injected by the factory that creates DOM-specific * components. * * @param target - */ ValueAnimationControls.prototype.checkForNewValues = function (target) { var newValueKeys = Object.keys(target).filter(this.hasValue); var numNewValues = newValueKeys.length; if (!numNewValues) return; for (var i = 0; i < numNewValues; i++) { var key = newValueKeys[i]; var targetValue = target[key]; var value = null; // If this is a keyframes value, we can attempt to use the first value in the // array as that's going to be the first value of the animation anyway if (Array.isArray(targetValue)) { value = targetValue[0]; } // If it isn't a keyframes or the first keyframes value was set as `null`, read the // value from the DOM. It might be worth investigating whether to check props (for SVG) // or props.style (for HTML) if the value exists there before attempting to read. if (value === null) { value = this.readValueFromSource(key); heyListen_es.invariant(value !== null, "No initial value for \"" + key + "\" can be inferred. Ensure an initial value for \"" + key + "\" is defined on the component."); } if (typeof value === "string" && isNumericalString(value)) { // If this is a number read as a string, ie "0" or "200", convert it to a number value = parseFloat(value); } else if (!getValueType(value) && styleValueTypes_es.complex.test(targetValue)) { // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target value = styleValueTypes_es.complex.getAnimatableNone(targetValue); } this.values.set(key, motionValue(value)); this.baseTarget[key] = value; } }; /** * Resolve a variant from its label or resolver into an actual `Target` we can animate to. * @param variant - */ ValueAnimationControls.prototype.resolveVariant = function (variant) { if (!variant) { return { target: undefined, transition: undefined, transitionEnd: undefined, }; } if (isTargetResolver(variant)) { // resolve current and velocity variant = variant(this.props.custom, getCurrent(this.values), getVelocity(this.values)); } var _a = variant.transition, transition = _a === void 0 ? this.defaultTransition : _a, transitionEnd = variant.transitionEnd, target = tslib_es6.__rest(variant, ["transition", "transitionEnd"]); return { transition: transition, transitionEnd: transitionEnd, target: target }; }; /** * Get the highest active override priority index */ ValueAnimationControls.prototype.getHighestPriority = function () { if (!this.activeOverrides.size) return 0; return Math.max.apply(Math, Array.from(this.activeOverrides)); }; /** * Set an override. We add this layer of indirection so if, for instance, a tap gesture * starts and overrides a hover gesture, when we clear the tap gesture and fallback to the * hover gesture, if that hover gesture has changed in the meantime we can go to that rather * than the one that was resolved when the hover gesture animation started. * * @param definition - * @param overrideIndex - */ ValueAnimationControls.prototype.setOverride = function (definition, overrideIndex) { this.overrides[overrideIndex] = definition; if (this.children) { this.children.forEach(function (child) { return child.setOverride(definition, overrideIndex); }); } }; /** * Start an override animation. * @param overrideIndex - */ ValueAnimationControls.prototype.startOverride = function (overrideIndex) { var override = this.overrides[overrideIndex]; if (override) { return this.start(override, { priority: overrideIndex }); } }; /** * Clear an override. We check every value we animated to in this override to see if * its present on any lower-priority overrides. If not, we animate it back to its base target. * @param overrideIndex - */ ValueAnimationControls.prototype.clearOverride = function (overrideIndex) { var _this = this; if (this.children) { this.children.forEach(function (child) { return child.clearOverride(overrideIndex); }); } var override = this.overrides[overrideIndex]; if (!override) return; this.activeOverrides.delete(overrideIndex); var highest = this.getHighestPriority(); this.resetIsAnimating(); if (highest) { var highestOverride = this.overrides[highest]; highestOverride && this.startOverride(highest); } // Figure out which remaining values were affected by the override and animate those var overrideTarget = this.resolvedOverrides[overrideIndex]; if (!overrideTarget) return; var remainingValues = {}; for (var key in this.baseTarget) { if (overrideTarget[key] !== undefined) { remainingValues[key] = this.baseTarget[key]; } } this.onStart(); this.animate(remainingValues).then(function () { return _this.onComplete(); }); }; /** * Apply a target/variant without any animation */ ValueAnimationControls.prototype.apply = function (definition) { if (Array.isArray(definition)) { return this.applyVariantLabels(definition); } else if (typeof definition === "string") { return this.applyVariantLabels([definition]); } else { this.setValues(definition); } }; /** * Apply variant labels without animation */ ValueAnimationControls.prototype.applyVariantLabels = function (variantLabelList) { var _this = this; var isActive = new Set(); var reversedList = variantLabelList.slice().reverse(); reversedList.forEach(function (key) { var _a = _this.resolveVariant(_this.variants[key]), target = _a.target, transitionEnd = _a.transitionEnd; if (transitionEnd) { _this.setValues(transitionEnd, { isActive: isActive }); } if (target) { _this.setValues(target, { isActive: isActive }); } if (_this.children && _this.children.size) { _this.children.forEach(function (child) { return child.applyVariantLabels(variantLabelList); }); } }); }; ValueAnimationControls.prototype.start = function (definition, opts) { var _this = this; if (opts === void 0) { opts = {}; } if (opts.priority) { this.activeOverrides.add(opts.priority); } this.resetIsAnimating(opts.priority); var animation; if (isVariantLabels(definition)) { animation = this.animateVariantLabels(definition, opts); } else if (typeof definition === "string") { animation = this.animateVariant(definition, opts); } else { animation = this.animate(definition, opts); } this.onStart(); return animation.then(function () { return _this.onComplete(); }); }; ValueAnimationControls.prototype.animate = function (animationDefinition, _a) { var _this = this; var _b = _a === void 0 ? {} : _a, _c = _b.delay, delay = _c === void 0 ? 0 : _c, _d = _b.priority, priority = _d === void 0 ? 0 : _d, transitionOverride = _b.transitionOverride; var _e = this.resolveVariant(animationDefinition), target = _e.target, transition = _e.transition, transitionEnd = _e.transitionEnd; if (transitionOverride) { transition = transitionOverride; } if (!target) return Promise.resolve(); target = this.transformValues(target); if (transitionEnd) { transitionEnd = this.transformValues(transitionEnd); } this.checkForNewValues(target); if (this.makeTargetAnimatable) { var animatable = this.makeTargetAnimatable(target, transitionEnd); target = animatable.target; transitionEnd = animatable.transitionEnd; } if (priority) { this.resolvedOverrides[priority] = target; } this.checkForNewValues(target); var animations = Object.keys(target).reduce(function (acc, key) { var value = _this.values.get(key); if (!value || !target || target[key] === undefined) return acc; var valueTarget = target[key]; if (!priority) { _this.baseTarget[key] = resolveFinalValueInKeyframes(valueTarget); } if (_this.isAnimating.has(key)) return acc; acc.push(startAnimation(key, value, valueTarget, tslib_es6.__assign({ delay: delay }, transition))); _this.isAnimating.add(key); return acc; }, []); return Promise.all(animations).then(function () { if (!transitionEnd) return; _this.setValues(transitionEnd, { priority: priority }); }); }; ValueAnimationControls.prototype.animateVariantLabels = function (variantLabels, opts) { var _this = this; var animations = variantLabels.slice().reverse() .map(function (label) { return _this.animateVariant(label, opts); }); return Promise.all(animations); }; ValueAnimationControls.prototype.animateVariant = function (variantLabel, opts) { var _this = this; var when = false; var delayChildren = 0; var staggerChildren = 0; var staggerDirection = 1; var priority = (opts && opts.priority) || 0; var variant = this.variants[variantLabel]; var getAnimations = variant ? function () { return _this.animate(variant, opts); } : function () { return Promise.resolve(); }; var getChildrenAnimations = this.children ? function () { return _this.animateChildren(variantLabel, delayChildren, staggerChildren, staggerDirection, priority); } : function () { return Promise.resolve(); }; if (variant && this.children) { var transition = this.resolveVariant(variant).transition; if (transition) { when = transition.when || when; delayChildren = transition.delayChildren || delayChildren; staggerChildren = transition.staggerChildren || staggerChildren; staggerDirection = transition.staggerDirection || staggerDirection; } } if (when) { var _a = when === "beforeChildren" ? [getAnimations, getChildrenAnimations] : [getChildrenAnimations, getAnimations], first = _a[0],