jobiqo-cl
Version:
[](https://circleci.com/gh/jobiqo/jobiqo-cl)
1,282 lines (1,262 loc) • 150 kB
JavaScript
'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],