'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var React = require('react');
var heyListen = require('hey-listen');
var styleValueTypes = require('style-value-types');
var popmotion = require('popmotion');
var sync = require('framesync');
var dom = require('@motionone/dom');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var sync__default = /*#__PURE__*/_interopDefaultLegacy(sync);
/**
* Browser-safe usage of process
*/
var defaultEnvironment = "production";
var env = typeof process === "undefined" || process.env === undefined
? defaultEnvironment
: process.env.NODE_ENV || defaultEnvironment;
var createDefinition = function (propNames) { return ({
isEnabled: function (props) { return propNames.some(function (name) { return !!props[name]; }); },
}); };
var featureDefinitions = {
measureLayout: createDefinition(["layout", "layoutId", "drag"]),
animation: createDefinition([
"animate",
"exit",
"variants",
"whileHover",
"whileTap",
"whileFocus",
"whileDrag",
"whileInView",
]),
exit: createDefinition(["exit"]),
drag: createDefinition(["drag", "dragControls"]),
focus: createDefinition(["whileFocus"]),
hover: createDefinition(["whileHover", "onHoverStart", "onHoverEnd"]),
tap: createDefinition(["whileTap", "onTap", "onTapStart", "onTapCancel"]),
pan: createDefinition([
"onPan",
"onPanStart",
"onPanSessionStart",
"onPanEnd",
]),
inView: createDefinition([
"whileInView",
"onViewportEnter",
"onViewportLeave",
]),
};
function loadFeatures(features) {
for (var key in features) {
if (features[key] === null)
continue;
if (key === "projectionNodeConstructor") {
featureDefinitions.projectionNodeConstructor = features[key];
}
else {
featureDefinitions[key].Component = features[key];
}
}
}
var LazyContext = React.createContext({ strict: false });
var featureNames = Object.keys(featureDefinitions);
var numFeatures = featureNames.length;
/**
* Load features via renderless components based on the provided MotionProps.
*/
function useFeatures(props, visualElement, preloadedFeatures) {
var features = [];
var lazyContext = React.useContext(LazyContext);
if (!visualElement)
return null;
/**
* If we're in development mode, check to make sure we're not rendering a motion component
* as a child of LazyMotion, as this will break the file-size benefits of using it.
*/
if (env !== "production" && preloadedFeatures && lazyContext.strict) {
heyListen.invariant(false, "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.");
}
for (var i = 0; i < numFeatures; i++) {
var name_1 = featureNames[i];
var _a = featureDefinitions[name_1], isEnabled = _a.isEnabled, Component = _a.Component;
/**
* It might be possible in the future to use this moment to
* dynamically request functionality. In initial tests this
* was producing a lot of duplication amongst bundles.
*/
if (isEnabled(props) && Component) {
features.push(React__namespace.createElement(Component, tslib.__assign({ key: name_1 }, props, { visualElement: visualElement })));
}
}
return features;
}
/**
* @public
*/
var MotionConfigContext = React.createContext({
transformPagePoint: function (p) { return p; },
isStatic: false,
reducedMotion: "never",
});
var MotionContext = React.createContext({});
function useVisualElementContext() {
return React.useContext(MotionContext).visualElement;
}
/**
* @public
*/
var PresenceContext = React.createContext(null);
var isBrowser = typeof document !== "undefined";
var useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect;
// Does this device prefer reduced motion? Returns `null` server-side.
var prefersReducedMotion = { current: null };
var hasDetected = false;
function initPrefersReducedMotion() {
hasDetected = true;
if (!isBrowser)
return;
if (window.matchMedia) {
var motionMediaQuery_1 = window.matchMedia("(prefers-reduced-motion)");
var setReducedMotionPreferences = function () {
return (prefersReducedMotion.current = motionMediaQuery_1.matches);
};
motionMediaQuery_1.addListener(setReducedMotionPreferences);
setReducedMotionPreferences();
}
else {
prefersReducedMotion.current = false;
}
}
/**
* A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
*
* This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing
* `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion.
*
* It will actively respond to changes and re-render your components with the latest setting.
*
* ```jsx
* export function Sidebar({ isOpen }) {
* const shouldReduceMotion = useReducedMotion()
* const closedX = shouldReduceMotion ? 0 : "-100%"
*
* return (
*
* )
* }
* ```
*
* @return boolean
*
* @public
*/
function useReducedMotion() {
/**
* Lazy initialisation of prefersReducedMotion
*/
!hasDetected && initPrefersReducedMotion();
var _a = tslib.__read(React.useState(prefersReducedMotion.current), 1), shouldReduceMotion = _a[0];
/**
* TODO See if people miss automatically updating shouldReduceMotion setting
*/
return shouldReduceMotion;
}
function useReducedMotionConfig() {
var reducedMotionPreference = useReducedMotion();
var reducedMotion = React.useContext(MotionConfigContext).reducedMotion;
if (reducedMotion === "never") {
return false;
}
else if (reducedMotion === "always") {
return true;
}
else {
return reducedMotionPreference;
}
}
function useVisualElement(Component, visualState, props, createVisualElement) {
var lazyContext = React.useContext(LazyContext);
var parent = useVisualElementContext();
var presenceContext = React.useContext(PresenceContext);
var shouldReduceMotion = useReducedMotionConfig();
var visualElementRef = React.useRef(undefined);
/**
* If we haven't preloaded a renderer, check to see if we have one lazy-loaded
*/
if (!createVisualElement)
createVisualElement = lazyContext.renderer;
if (!visualElementRef.current && createVisualElement) {
visualElementRef.current = createVisualElement(Component, {
visualState: visualState,
parent: parent,
props: props,
presenceId: presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.id,
blockInitialAnimation: (presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.initial) === false,
shouldReduceMotion: shouldReduceMotion,
});
}
var visualElement = visualElementRef.current;
useIsomorphicLayoutEffect(function () {
visualElement === null || visualElement === void 0 ? void 0 : visualElement.syncRender();
});
React.useEffect(function () {
var _a;
(_a = visualElement === null || visualElement === void 0 ? void 0 : visualElement.animationState) === null || _a === void 0 ? void 0 : _a.animateChanges();
});
useIsomorphicLayoutEffect(function () { return function () { return visualElement === null || visualElement === void 0 ? void 0 : visualElement.notifyUnmount(); }; }, []);
return visualElement;
}
function isRefObject(ref) {
return (typeof ref === "object" &&
Object.prototype.hasOwnProperty.call(ref, "current"));
}
/**
* Creates a ref function that, when called, hydrates the provided
* external ref and VisualElement.
*/
function useMotionRef(visualState, visualElement, externalRef) {
return React.useCallback(function (instance) {
var _a;
instance && ((_a = visualState.mount) === null || _a === void 0 ? void 0 : _a.call(visualState, instance));
if (visualElement) {
instance
? visualElement.mount(instance)
: visualElement.unmount();
}
if (externalRef) {
if (typeof externalRef === "function") {
externalRef(instance);
}
else if (isRefObject(externalRef)) {
externalRef.current = instance;
}
}
},
/**
* Only pass a new ref callback to React if we've received a visual element
* factory. Otherwise we'll be mounting/remounting every time externalRef
* or other dependencies change.
*/
[visualElement]);
}
/**
* Decides if the supplied variable is an array of variant labels
*/
function isVariantLabels(v) {
return Array.isArray(v);
}
/**
* Decides if the supplied variable is variant label
*/
function isVariantLabel(v) {
return typeof v === "string" || isVariantLabels(v);
}
/**
* Creates an object containing the latest state of every MotionValue on a VisualElement
*/
function getCurrent(visualElement) {
var current = {};
visualElement.forEachValue(function (value, key) { return (current[key] = value.get()); });
return current;
}
/**
* Creates an object containing the latest velocity of every MotionValue on a VisualElement
*/
function getVelocity$1(visualElement) {
var velocity = {};
visualElement.forEachValue(function (value, key) { return (velocity[key] = value.getVelocity()); });
return velocity;
}
function resolveVariantFromProps(props, definition, custom, currentValues, currentVelocity) {
var _a;
if (currentValues === void 0) { currentValues = {}; }
if (currentVelocity === void 0) { currentVelocity = {}; }
/**
* If the variant definition is a function, resolve.
*/
if (typeof definition === "function") {
definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity);
}
/**
* If the variant definition is a variant label, or
* the function returned a variant label, resolve.
*/
if (typeof definition === "string") {
definition = (_a = props.variants) === null || _a === void 0 ? void 0 : _a[definition];
}
/**
* At this point we've resolved both functions and variant labels,
* but the resolved variant label might itself have been a function.
* If so, resolve. This can only have returned a valid target object.
*/
if (typeof definition === "function") {
definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity);
}
return definition;
}
function resolveVariant(visualElement, definition, custom) {
var props = visualElement.getProps();
return resolveVariantFromProps(props, definition, custom !== null && custom !== void 0 ? custom : props.custom, getCurrent(visualElement), getVelocity$1(visualElement));
}
function checkIfControllingVariants(props) {
var _a;
return (typeof ((_a = props.animate) === null || _a === void 0 ? void 0 : _a.start) === "function" ||
isVariantLabel(props.initial) ||
isVariantLabel(props.animate) ||
isVariantLabel(props.whileHover) ||
isVariantLabel(props.whileDrag) ||
isVariantLabel(props.whileTap) ||
isVariantLabel(props.whileFocus) ||
isVariantLabel(props.exit));
}
function checkIfVariantNode(props) {
return Boolean(checkIfControllingVariants(props) || props.variants);
}
function getCurrentTreeVariants(props, context) {
if (checkIfControllingVariants(props)) {
var initial = props.initial, animate = props.animate;
return {
initial: initial === false || isVariantLabel(initial)
? initial
: undefined,
animate: isVariantLabel(animate) ? animate : undefined,
};
}
return props.inherit !== false ? context : {};
}
function useCreateMotionContext(props) {
var _a = getCurrentTreeVariants(props, React.useContext(MotionContext)), initial = _a.initial, animate = _a.animate;
return React.useMemo(function () { return ({ initial: initial, animate: animate }); }, [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]);
}
function variantLabelsAsDependency(prop) {
return Array.isArray(prop) ? prop.join(" ") : prop;
}
/**
* 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;
}
/**
* This should only ever be modified on the client otherwise it'll
* persist through server requests. If we need instanced states we
* could lazy-init via root.
*/
var globalProjectionState = {
/**
* Global flag as to whether the tree has animated since the last time
* we resized the window
*/
hasAnimatedSinceResize: true,
/**
* We set this to true once, on the first update. Any nodes added to the tree beyond that
* update will be given a `data-projection-id` attribute.
*/
hasEverUpdated: false,
};
var id$1 = 1;
function useProjectionId() {
return useConstant(function () {
if (globalProjectionState.hasEverUpdated) {
return id$1++;
}
});
}
var LayoutGroupContext = React.createContext({});
/**
* Internal, exported only for usage in Framer
*/
var SwitchLayoutGroupContext = React.createContext({});
function useProjection(projectionId, _a, visualElement, ProjectionNodeConstructor) {
var _b;
var layoutId = _a.layoutId, layout = _a.layout, drag = _a.drag, dragConstraints = _a.dragConstraints, layoutScroll = _a.layoutScroll;
var initialPromotionConfig = React.useContext(SwitchLayoutGroupContext);
if (!ProjectionNodeConstructor ||
!visualElement ||
(visualElement === null || visualElement === void 0 ? void 0 : visualElement.projection)) {
return;
}
visualElement.projection = new ProjectionNodeConstructor(projectionId, visualElement.getLatestValues(), (_b = visualElement.parent) === null || _b === void 0 ? void 0 : _b.projection);
visualElement.projection.setOptions({
layoutId: layoutId,
layout: layout,
alwaysMeasureLayout: Boolean(drag) || (dragConstraints && isRefObject(dragConstraints)),
visualElement: visualElement,
scheduleRender: function () { return visualElement.scheduleRender(); },
/**
* TODO: Update options in an effect. This could be tricky as it'll be too late
* to update by the time layout animations run.
* We also need to fix this safeToRemove by linking it up to the one returned by usePresence,
* ensuring it gets called if there's no potential layout animations.
*
*/
animationType: typeof layout === "string" ? layout : "both",
initialPromotionConfig: initialPromotionConfig,
layoutScroll: layoutScroll,
});
}
var VisualElementHandler = /** @class */ (function (_super) {
tslib.__extends(VisualElementHandler, _super);
function VisualElementHandler() {
return _super !== null && _super.apply(this, arguments) || this;
}
/**
* Update visual element props as soon as we know this update is going to be commited.
*/
VisualElementHandler.prototype.getSnapshotBeforeUpdate = function () {
this.updateProps();
return null;
};
VisualElementHandler.prototype.componentDidUpdate = function () { };
VisualElementHandler.prototype.updateProps = function () {
var _a = this.props, visualElement = _a.visualElement, props = _a.props;
if (visualElement)
visualElement.setProps(props);
};
VisualElementHandler.prototype.render = function () {
return this.props.children;
};
return VisualElementHandler;
}(React__default["default"].Component));
/**
* Create a `motion` component.
*
* This function accepts a Component argument, which can be either a string (ie "div"
* for `motion.div`), or an actual React component.
*
* Alongside this is a config option which provides a way of rendering the provided
* component "offline", or outside the React render cycle.
*/
function createMotionComponent(_a) {
var preloadedFeatures = _a.preloadedFeatures, createVisualElement = _a.createVisualElement, projectionNodeConstructor = _a.projectionNodeConstructor, useRender = _a.useRender, useVisualState = _a.useVisualState, Component = _a.Component;
preloadedFeatures && loadFeatures(preloadedFeatures);
function MotionComponent(props, externalRef) {
var layoutId = useLayoutId(props);
props = tslib.__assign(tslib.__assign({}, props), { layoutId: layoutId });
/**
* If we're rendering in a static environment, we only visually update the component
* as a result of a React-rerender rather than interactions or animations. This
* means we don't need to load additional memory structures like VisualElement,
* or any gesture/animation features.
*/
var config = React.useContext(MotionConfigContext);
var features = null;
var context = useCreateMotionContext(props);
/**
* Create a unique projection ID for this component. If a new component is added
* during a layout animation we'll use this to query the DOM and hydrate its ref early, allowing
* us to measure it as soon as any layout effect flushes pending layout animations.
*
* Performance note: It'd be better not to have to search the DOM for these elements.
* For newly-entering components it could be enough to only correct treeScale, in which
* case we could mount in a scale-correction mode. This wouldn't be enough for
* shared element transitions however. Perhaps for those we could revert to a root node
* that gets forceRendered and layout animations are triggered on its layout effect.
*/
var projectionId = config.isStatic ? undefined : useProjectionId();
/**
*
*/
var visualState = useVisualState(props, config.isStatic);
if (!config.isStatic && isBrowser) {
/**
* Create a VisualElement for this component. A VisualElement provides a common
* interface to renderer-specific APIs (ie DOM/Three.js etc) as well as
* providing a way of rendering to these APIs outside of the React render loop
* for more performant animations and interactions
*/
context.visualElement = useVisualElement(Component, visualState, tslib.__assign(tslib.__assign({}, config), props), createVisualElement);
useProjection(projectionId, props, context.visualElement, projectionNodeConstructor ||
featureDefinitions.projectionNodeConstructor);
/**
* Load Motion gesture and animation features. These are rendered as renderless
* components so each feature can optionally make use of React lifecycle methods.
*/
features = useFeatures(props, context.visualElement, preloadedFeatures);
}
/**
* The mount order and hierarchy is specific to ensure our element ref
* is hydrated by the time features fire their effects.
*/
return (React__namespace.createElement(VisualElementHandler, { visualElement: context.visualElement, props: tslib.__assign(tslib.__assign({}, config), props) },
features,
React__namespace.createElement(MotionContext.Provider, { value: context }, useRender(Component, props, projectionId, useMotionRef(visualState, context.visualElement, externalRef), visualState, config.isStatic, context.visualElement))));
}
return React.forwardRef(MotionComponent);
}
function useLayoutId(_a) {
var _b;
var layoutId = _a.layoutId;
var layoutGroupId = (_b = React.useContext(LayoutGroupContext)) === null || _b === void 0 ? void 0 : _b.id;
return layoutGroupId && layoutId !== undefined
? layoutGroupId + "-" + layoutId
: layoutId;
}
/**
* Convert any React component into a `motion` component. The provided component
* **must** use `React.forwardRef` to the underlying DOM component you want to animate.
*
* ```jsx
* const Component = React.forwardRef((props, ref) => {
* return
* })
*
* const MotionComponent = motion(Component)
* ```
*
* @public
*/
function createMotionProxy(createConfig) {
function custom(Component, customMotionComponentConfig) {
if (customMotionComponentConfig === void 0) { customMotionComponentConfig = {}; }
return createMotionComponent(createConfig(Component, customMotionComponentConfig));
}
if (typeof Proxy === "undefined") {
return custom;
}
/**
* A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc.
* Rather than generating them anew every render.
*/
var componentCache = new Map();
return new Proxy(custom, {
/**
* Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc.
* The prop name is passed through as `key` and we can use that to generate a `motion`
* DOM component with that name.
*/
get: function (_target, key) {
/**
* If this element doesn't exist in the component cache, create it and cache.
*/
if (!componentCache.has(key)) {
componentCache.set(key, custom(key));
}
return componentCache.get(key);
},
});
}
/**
* We keep these listed seperately as we use the lowercase tag names as part
* of the runtime bundle to detect SVG components
*/
var lowercaseSVGElements = [
"animate",
"circle",
"defs",
"desc",
"ellipse",
"g",
"image",
"line",
"filter",
"marker",
"mask",
"metadata",
"path",
"pattern",
"polygon",
"polyline",
"rect",
"stop",
"svg",
"switch",
"symbol",
"text",
"tspan",
"use",
"view",
];
function isSVGComponent(Component) {
if (
/**
* If it's not a string, it's a custom React component. Currently we only support
* HTML custom React components.
*/
typeof Component !== "string" ||
/**
* If it contains a dash, the element is a custom HTML webcomponent.
*/
Component.includes("-")) {
return false;
}
else if (
/**
* If it's in our list of lowercase SVG tags, it's an SVG component
*/
lowercaseSVGElements.indexOf(Component) > -1 ||
/**
* If it contains a capital letter, it's an SVG component
*/
/[A-Z]/.test(Component)) {
return true;
}
return false;
}
var scaleCorrectors = {};
function addScaleCorrector(correctors) {
Object.assign(scaleCorrectors, correctors);
}
/**
* A list of all transformable axes. We'll use this list to generated a version
* of each axes for each transform.
*/
var transformAxes = ["", "X", "Y", "Z"];
/**
* An ordered array of each transformable value. By default, transform values
* will be sorted to this order.
*/
var order = ["translate", "scale", "rotate", "skew"];
/**
* Generate a list of every possible transform key.
*/
var transformProps = ["transformPerspective", "x", "y", "z"];
order.forEach(function (operationKey) {
return transformAxes.forEach(function (axesKey) {
return transformProps.push(operationKey + axesKey);
});
});
/**
* A function to use with Array.sort to sort transform keys by their default order.
*/
function sortTransformProps(a, b) {
return transformProps.indexOf(a) - transformProps.indexOf(b);
}
/**
* A quick lookup for transform props.
*/
var transformPropSet = new Set(transformProps);
function isTransformProp(key) {
return transformPropSet.has(key);
}
/**
* A quick lookup for transform origin props
*/
var transformOriginProps = new Set(["originX", "originY", "originZ"]);
function isTransformOriginProp(key) {
return transformOriginProps.has(key);
}
function isForcedMotionValue(key, _a) {
var layout = _a.layout, layoutId = _a.layoutId;
return (isTransformProp(key) ||
isTransformOriginProp(key) ||
((layout || layoutId !== undefined) &&
(!!scaleCorrectors[key] || key === "opacity")));
}
var isMotionValue = function (value) {
return Boolean(value !== null && typeof value === "object" && value.getVelocity);
};
var translateAlias = {
x: "translateX",
y: "translateY",
z: "translateZ",
transformPerspective: "perspective",
};
/**
* Build a CSS transform style from individual x/y/scale etc properties.
*
* This outputs with a default order of transforms/scales/rotations, this can be customised by
* providing a transformTemplate function.
*/
function buildTransform(_a, _b, transformIsDefault, transformTemplate) {
var transform = _a.transform, transformKeys = _a.transformKeys;
var _c = _b.enableHardwareAcceleration, enableHardwareAcceleration = _c === void 0 ? true : _c, _d = _b.allowTransformNone, allowTransformNone = _d === void 0 ? true : _d;
// The transform string we're going to build into.
var transformString = "";
// Transform keys into their default order - this will determine the output order.
transformKeys.sort(sortTransformProps);
// Track whether the defined transform has a defined z so we don't add a
// second to enable hardware acceleration
var transformHasZ = false;
// Loop over each transform and build them into transformString
var numTransformKeys = transformKeys.length;
for (var i = 0; i < numTransformKeys; i++) {
var key = transformKeys[i];
transformString += "".concat(translateAlias[key] || key, "(").concat(transform[key], ") ");
if (key === "z")
transformHasZ = true;
}
if (!transformHasZ && enableHardwareAcceleration) {
transformString += "translateZ(0)";
}
else {
transformString = transformString.trim();
}
// If we have a custom `transform` template, pass our transform values and
// generated transformString to that before returning
if (transformTemplate) {
transformString = transformTemplate(transform, transformIsDefault ? "" : transformString);
}
else if (allowTransformNone && transformIsDefault) {
transformString = "none";
}
return transformString;
}
/**
* Build a transformOrigin style. Uses the same defaults as the browser for
* undefined origins.
*/
function buildTransformOrigin(_a) {
var _b = _a.originX, originX = _b === void 0 ? "50%" : _b, _c = _a.originY, originY = _c === void 0 ? "50%" : _c, _d = _a.originZ, originZ = _d === void 0 ? 0 : _d;
return "".concat(originX, " ").concat(originY, " ").concat(originZ);
}
/**
* Returns true if the provided key is a CSS variable
*/
function isCSSVariable$1(key) {
return key.startsWith("--");
}
/**
* Provided a value and a ValueType, returns the value as that value type.
*/
var getValueAsType = function (value, type) {
return type && typeof value === "number"
? type.transform(value)
: value;
};
var int = tslib.__assign(tslib.__assign({}, styleValueTypes.number), { transform: Math.round });
var numberValueTypes = {
// Border props
borderWidth: styleValueTypes.px,
borderTopWidth: styleValueTypes.px,
borderRightWidth: styleValueTypes.px,
borderBottomWidth: styleValueTypes.px,
borderLeftWidth: styleValueTypes.px,
borderRadius: styleValueTypes.px,
radius: styleValueTypes.px,
borderTopLeftRadius: styleValueTypes.px,
borderTopRightRadius: styleValueTypes.px,
borderBottomRightRadius: styleValueTypes.px,
borderBottomLeftRadius: styleValueTypes.px,
// Positioning props
width: styleValueTypes.px,
maxWidth: styleValueTypes.px,
height: styleValueTypes.px,
maxHeight: styleValueTypes.px,
size: styleValueTypes.px,
top: styleValueTypes.px,
right: styleValueTypes.px,
bottom: styleValueTypes.px,
left: styleValueTypes.px,
// Spacing props
padding: styleValueTypes.px,
paddingTop: styleValueTypes.px,
paddingRight: styleValueTypes.px,
paddingBottom: styleValueTypes.px,
paddingLeft: styleValueTypes.px,
margin: styleValueTypes.px,
marginTop: styleValueTypes.px,
marginRight: styleValueTypes.px,
marginBottom: styleValueTypes.px,
marginLeft: styleValueTypes.px,
// Transform props
rotate: styleValueTypes.degrees,
rotateX: styleValueTypes.degrees,
rotateY: styleValueTypes.degrees,
rotateZ: styleValueTypes.degrees,
scale: styleValueTypes.scale,
scaleX: styleValueTypes.scale,
scaleY: styleValueTypes.scale,
scaleZ: styleValueTypes.scale,
skew: styleValueTypes.degrees,
skewX: styleValueTypes.degrees,
skewY: styleValueTypes.degrees,
distance: styleValueTypes.px,
translateX: styleValueTypes.px,
translateY: styleValueTypes.px,
translateZ: styleValueTypes.px,
x: styleValueTypes.px,
y: styleValueTypes.px,
z: styleValueTypes.px,
perspective: styleValueTypes.px,
transformPerspective: styleValueTypes.px,
opacity: styleValueTypes.alpha,
originX: styleValueTypes.progressPercentage,
originY: styleValueTypes.progressPercentage,
originZ: styleValueTypes.px,
// Misc
zIndex: int,
// SVG
fillOpacity: styleValueTypes.alpha,
strokeOpacity: styleValueTypes.alpha,
numOctaves: int,
};
function buildHTMLStyles(state, latestValues, options, transformTemplate) {
var _a;
var style = state.style, vars = state.vars, transform = state.transform, transformKeys = state.transformKeys, transformOrigin = state.transformOrigin;
// Empty the transformKeys array. As we're throwing out refs to its items
// this might not be as cheap as suspected. Maybe using the array as a buffer
// with a manual incrementation would be better.
transformKeys.length = 0;
// Track whether we encounter any transform or transformOrigin values.
var hasTransform = false;
var hasTransformOrigin = false;
// Does the calculated transform essentially equal "none"?
var transformIsNone = true;
/**
* Loop over all our latest animated values and decide whether to handle them
* as a style or CSS variable.
*
* Transforms and transform origins are kept seperately for further processing.
*/
for (var key in latestValues) {
var value = latestValues[key];
/**
* If this is a CSS variable we don't do any further processing.
*/
if (isCSSVariable$1(key)) {
vars[key] = value;
continue;
}
// Convert the value to its default value type, ie 0 -> "0px"
var valueType = numberValueTypes[key];
var valueAsType = getValueAsType(value, valueType);
if (isTransformProp(key)) {
// If this is a transform, flag to enable further transform processing
hasTransform = true;
transform[key] = valueAsType;
transformKeys.push(key);
// If we already know we have a non-default transform, early return
if (!transformIsNone)
continue;
// Otherwise check to see if this is a default transform
if (value !== ((_a = valueType.default) !== null && _a !== void 0 ? _a : 0))
transformIsNone = false;
}
else if (isTransformOriginProp(key)) {
transformOrigin[key] = valueAsType;
// If this is a transform origin, flag and enable further transform-origin processing
hasTransformOrigin = true;
}
else {
style[key] = valueAsType;
}
}
if (hasTransform) {
style.transform = buildTransform(state, options, transformIsNone, transformTemplate);
}
else if (transformTemplate) {
style.transform = transformTemplate({}, "");
}
else if (!latestValues.transform && style.transform) {
style.transform = "none";
}
if (hasTransformOrigin) {
style.transformOrigin = buildTransformOrigin(transformOrigin);
}
}
var createHtmlRenderState = function () { return ({
style: {},
transform: {},
transformKeys: [],
transformOrigin: {},
vars: {},
}); };
function copyRawValuesOnly(target, source, props) {
for (var key in source) {
if (!isMotionValue(source[key]) && !isForcedMotionValue(key, props)) {
target[key] = source[key];
}
}
}
function useInitialMotionValues(_a, visualState, isStatic) {
var transformTemplate = _a.transformTemplate;
return React.useMemo(function () {
var state = createHtmlRenderState();
buildHTMLStyles(state, visualState, { enableHardwareAcceleration: !isStatic }, transformTemplate);
var vars = state.vars, style = state.style;
return tslib.__assign(tslib.__assign({}, vars), style);
}, [visualState]);
}
function useStyle(props, visualState, isStatic) {
var styleProp = props.style || {};
var style = {};
/**
* Copy non-Motion Values straight into style
*/
copyRawValuesOnly(style, styleProp, props);
Object.assign(style, useInitialMotionValues(props, visualState, isStatic));
if (props.transformValues) {
style = props.transformValues(style);
}
return style;
}
function useHTMLProps(props, visualState, isStatic) {
// The `any` isn't ideal but it is the type of createElement props argument
var htmlProps = {};
var style = useStyle(props, visualState, isStatic);
if (Boolean(props.drag) && props.dragListener !== false) {
// Disable the ghost element when a user drags
htmlProps.draggable = false;
// Disable text selection
style.userSelect =
style.WebkitUserSelect =
style.WebkitTouchCallout =
"none";
// Disable scrolling on the draggable direction
style.touchAction =
props.drag === true
? "none"
: "pan-".concat(props.drag === "x" ? "y" : "x");
}
htmlProps.style = style;
return htmlProps;
}
/**
* A list of all valid MotionProps.
*
* @privateRemarks
* This doesn't throw if a `MotionProp` name is missing - it should.
*/
var validMotionProps = new Set([
"initial",
"animate",
"exit",
"style",
"variants",
"transition",
"transformTemplate",
"transformValues",
"custom",
"inherit",
"layout",
"layoutId",
"layoutDependency",
"onLayoutAnimationStart",
"onLayoutAnimationComplete",
"onLayoutMeasure",
"onBeforeLayoutMeasure",
"onAnimationStart",
"onAnimationComplete",
"onUpdate",
"onDragStart",
"onDrag",
"onDragEnd",
"onMeasureDragConstraints",
"onDirectionLock",
"onDragTransitionEnd",
"drag",
"dragControls",
"dragListener",
"dragConstraints",
"dragDirectionLock",
"dragSnapToOrigin",
"_dragX",
"_dragY",
"dragElastic",
"dragMomentum",
"dragPropagation",
"dragTransition",
"whileDrag",
"onPan",
"onPanStart",
"onPanEnd",
"onPanSessionStart",
"onTap",
"onTapStart",
"onTapCancel",
"onHoverStart",
"onHoverEnd",
"whileFocus",
"whileTap",
"whileHover",
"whileInView",
"onViewportEnter",
"onViewportLeave",
"viewport",
"layoutScroll",
]);
/**
* Check whether a prop name is a valid `MotionProp` key.
*
* @param key - Name of the property to check
* @returns `true` is key is a valid `MotionProp`.
*
* @public
*/
function isValidMotionProp(key) {
return validMotionProps.has(key);
}
var shouldForward = function (key) { return !isValidMotionProp(key); };
function loadExternalIsValidProp(isValidProp) {
if (!isValidProp)
return;
// Explicitly filter our events
shouldForward = function (key) {
return key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key);
};
}
/**
* Emotion and Styled Components both allow users to pass through arbitrary props to their components
* to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which
* of these should be passed to the underlying DOM node.
*
* However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props
* as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props
* passed through the `custom` prop so it doesn't *need* the payload or computational overhead of
* `@emotion/is-prop-valid`, however to fix this problem we need to use it.
*
* By making it an optionalDependency we can offer this functionality only in the situations where it's
* actually required.
*/
try {
/**
* We attempt to import this package but require won't be defined in esm environments, in that case
* isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed
* in favour of explicit injection.
*/
loadExternalIsValidProp(require("@emotion/is-prop-valid").default);
}
catch (_a) {
// We don't need to actually do anything here - the fallback is the existing `isPropValid`.
}
function filterProps(props, isDom, forwardMotionProps) {
var filteredProps = {};
for (var key in props) {
if (shouldForward(key) ||
(forwardMotionProps === true && isValidMotionProp(key)) ||
(!isDom && !isValidMotionProp(key)) ||
// If trying to use native HTML drag events, forward drag listeners
(props["draggable"] && key.startsWith("onDrag"))) {
filteredProps[key] = props[key];
}
}
return filteredProps;
}
function calcOrigin$1(origin, offset, size) {
return typeof origin === "string"
? origin
: styleValueTypes.px.transform(offset + size * origin);
}
/**
* The SVG transform origin defaults are different to CSS and is less intuitive,
* so we use the measured dimensions of the SVG to reconcile these.
*/
function calcSVGTransformOrigin(dimensions, originX, originY) {
var pxOriginX = calcOrigin$1(originX, dimensions.x, dimensions.width);
var pxOriginY = calcOrigin$1(originY, dimensions.y, dimensions.height);
return "".concat(pxOriginX, " ").concat(pxOriginY);
}
var dashKeys = {
offset: "stroke-dashoffset",
array: "stroke-dasharray",
};
var camelKeys = {
offset: "strokeDashoffset",
array: "strokeDasharray",
};
/**
* Build SVG path properties. Uses the path's measured length to convert
* our custom pathLength, pathSpacing and pathOffset into stroke-dashoffset
* and stroke-dasharray attributes.
*
* This function is mutative to reduce per-frame GC.
*/
function buildSVGPath(attrs, length, spacing, offset, useDashCase) {
if (spacing === void 0) { spacing = 1; }
if (offset === void 0) { offset = 0; }
if (useDashCase === void 0) { useDashCase = true; }
// Normalise path length by setting SVG attribute pathLength to 1
attrs.pathLength = 1;
// We use dash case when setting attributes directly to the DOM node and camel case
// when defining props on a React component.
var keys = useDashCase ? dashKeys : camelKeys;
// Build the dash offset
attrs[keys.offset] = styleValueTypes.px.transform(-offset);
// Build the dash array
var pathLength = styleValueTypes.px.transform(length);
var pathSpacing = styleValueTypes.px.transform(spacing);
attrs[keys.array] = "".concat(pathLength, " ").concat(pathSpacing);
}
/**
* Build SVG visual attrbutes, like cx and style.transform
*/
function buildSVGAttrs(state, _a, options, transformTemplate) {
var attrX = _a.attrX, attrY = _a.attrY, originX = _a.originX, originY = _a.originY, pathLength = _a.pathLength, _b = _a.pathSpacing, pathSpacing = _b === void 0 ? 1 : _b, _c = _a.pathOffset, pathOffset = _c === void 0 ? 0 : _c,
// This is object creation, which we try to avoid per-frame.
latest = tslib.__rest(_a, ["attrX", "attrY", "originX", "originY", "pathLength", "pathSpacing", "pathOffset"]);
buildHTMLStyles(state, latest, options, transformTemplate);
state.attrs = state.style;
state.style = {};
var attrs = state.attrs, style = state.style, dimensions = state.dimensions;
/**
* However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
* and copy it into style.
*/
if (attrs.transform) {
if (dimensions)
style.transform = attrs.transform;
delete attrs.transform;
}
// Parse transformOrigin
if (dimensions &&
(originX !== undefined || originY !== undefined || style.transform)) {
style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
}
// Treat x/y not as shortcuts but as actual attributes
if (attrX !== undefined)
attrs.x = attrX;
if (attrY !== undefined)
attrs.y = attrY;
// Build SVG path if one has been defined
if (pathLength !== undefined) {
buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false);
}
}
var createSvgRenderState = function () { return (tslib.__assign(tslib.__assign({}, createHtmlRenderState()), { attrs: {} })); };
function useSVGProps(props, visualState) {
var visualProps = React.useMemo(function () {
var state = createSvgRenderState();
buildSVGAttrs(state, visualState, { enableHardwareAcceleration: false }, props.transformTemplate);
return tslib.__assign(tslib.__assign({}, state.attrs), { style: tslib.__assign({}, state.style) });
}, [visualState]);
if (props.style) {
var rawStyles = {};
copyRawValuesOnly(rawStyles, props.style, props);
visualProps.style = tslib.__assign(tslib.__assign({}, rawStyles), visualProps.style);
}
return visualProps;
}
function createUseRender(forwardMotionProps) {
if (forwardMotionProps === void 0) { forwardMotionProps = false; }
var useRender = function (Component, props, projectionId, ref, _a, isStatic) {
var latestValues = _a.latestValues;
var useVisualProps = isSVGComponent(Component)
? useSVGProps
: useHTMLProps;
var visualProps = useVisualProps(props, latestValues, isStatic);
var filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps);
var elementProps = tslib.__assign(tslib.__assign(tslib.__assign({}, filteredProps), visualProps), { ref: ref });
if (projectionId) {
elementProps["data-projection-id"] = projectionId;
}
return React.createElement(Component, elementProps);
};
return useRender;
}
var CAMEL_CASE_PATTERN = /([a-z])([A-Z])/g;
var REPLACE_TEMPLATE = "$1-$2";
/**
* Convert camelCase to dash-case properties.
*/
var camelToDash = function (str) {
return str.replace(CAMEL_CASE_PATTERN, REPLACE_TEMPLATE).toLowerCase();
};
function renderHTML(element, _a, styleProp, projection) {
var style = _a.style, vars = _a.vars;
Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
// Loop over any CSS variables and assign those.
for (var key in vars) {
element.style.setProperty(key, vars[key]);
}
}
/**
* A set of attribute names that are always read/written as camel case.
*/
var camelCaseAttributes = new Set([
"baseFrequency",
"diffuseConstant",
"kernelMatrix",
"kernelUnitLength",
"keySplines",
"keyTimes",
"limitingConeAngle",
"markerHeight",
"markerWidth",
"numOctaves",
"targetX",
"targetY",
"surfaceScale",
"specularConstant",
"specularExponent",
"stdDeviation",
"tableValues",
"viewBox",
"gradientTransform",
"pathLength",
]);
function renderSVG(element, renderState, _styleProp, projection) {
renderHTML(element, renderState, undefined, projection);
for (var key in renderState.attrs) {
element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
}
}
function scrapeMotionValuesFromProps$1(props) {
var style = props.style;
var newValues = {};
for (var key in style) {
if (isMotionValue(style[key]) || isForcedMotionValue(key, props)) {
newValues[key] = style[key];
}
}
return newValues;
}
function scrapeMotionValuesFromProps(props) {
var newValues = scrapeMotionValuesFromProps$1(props);
for (var key in props) {
if (isMotionValue(props[key])) {
var targetKey = key === "x" || key === "y" ? "attr" + key.toUpperCase() : key;
newValues[targetKey] = props[key];
}
}
return newValues;
}
function isAnimationControls(v) {
return typeof v === "object" && typeof v.start === "function";
}
var isKeyframesTarget = function (v) {
return Array.isArray(v);
};
var isCustomValue = function (v) {
return Boolean(v && typeof v === "object" && v.mix && v.toValue);
};
var resolveFinalValueInKeyframes = function (v) {
// TODO maybe throw if v.length - 1 is placeholder token?
return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
};
/**
* If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
*
* TODO: Remove and move to library
*/
function resolveMotionValue(value) {
var unwrappedValue = isMotionValue(value) ? value.get() : value;
return isCustomValue(unwrappedValue)
? unwrappedValue.toValue()
: unwrappedValue;
}
function makeState(_a, props, context, presenceContext) {
var scrapeMotionValuesFromProps = _a.scrapeMotionValuesFromProps, createRenderState = _a.createRenderState, onMount = _a.onMount;
var state = {
latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
renderState: createRenderState(),
};
if (onMount) {
state.mount = function (instance) { return onMount(props, instance, state); };
}
return state;
}
var makeUseVisualState = function (config) {
return function (props, isStatic) {
var context = React.useContext(MotionContext);
var presenceContext = React.useContext(PresenceContext);
return isStatic
? makeState(config, props, context, presenceContext)
: useConstant(function () {
return makeState(config, props, context, presenceContext);
});
};
};
function makeLatestValues(props, context, presenceContext, scrapeMotionValues) {
var values = {};
var blockInitialAnimation = (presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.initial) === false;
var motionValues = scrapeMotionValues(props);
for (var key in motionValues) {
values[key] = resolveMotionValue(motionValues[key]);
}
var initial = props.initial, animate = props.animate;
var isControllingVariants = checkIfControllingVariants(props);
var isVariantNode = checkIfVariantNode(props);
if (context &&
isVariantNode &&
!isControllingVariants &&
props.inherit !== false) {
initial !== null && initial !== void 0 ? initial : (initial = context.initial);
animate !== null && animate !== void 0 ? animate : (animate = context.animate);
}
var initialAnimationIsBlocked = blockInitialAnimation || initial === false;
var variantToSet = initialAnimationIsBlocked ? animate : initial;
if (variantToSet &&
typeof variantToSet !== "boolean" &&
!isAnimationControls(variantToSet)) {
var list = Array.isArray(variantToSet) ? variantToSet : [variantToSet];
list.forEach(function (definition) {
var resolved = resolveVariantFromProps(props, definition);
if (!resolved)
return;
var transitionEnd = resolved.transitionEnd; resolved.transition; var target = tslib.__rest(resolved, ["transitionEnd", "transition"]);
for (var key in target) {
var valueTarget = target[key];
if (Array.isArray(valueTarget)) {
/**
* Take final keyframe if the initial animation is blocked because
* we want to initialise at the end of that blocked animation.
*/
var index = initialAnimationIsBlocked
? valueTarget.length - 1
: 0;
valueTarget = valueTarget[index];
}
if (valueTarget !== null) {
values[key] = valueTarget;
}
}
for (var key in transitionEnd)
values[key] = transitionEnd[key];
});
}
return values;
}
var svgMotionConfig = {
useVisualState: makeUseVisualState({
scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
createRenderState: createSvgRenderState,
onMount: function (props, instance, _a) {
var renderState = _a.renderState, latestValues = _a.latestValues;
try {
renderState.dimensions =
typeof instance.getBBox ===
"function"
? instance.getBBox()
: instance.getBoundingClientRect();
}
catch (e) {
// Most likely trying to measure an unrendered element under Firefox
renderState.dimensions = {
x: 0,
y: 0,
width: 0,
height: 0,
};
}
buildSVGAttrs(renderState, latestValues, { enableHardwareAcceleration: false }, props.transformTemplate);
renderSVG(instance, renderState);
},
}),
};
var htmlMotionConfig = {
useVisualState: makeUseVisualState({
scrapeMotionValuesFromProps: scrapeMotionValuesFromProps$1,
createRenderState: createHtmlRenderState,
}),
};
function createDomMotionConfig(Component, _a, preloadedFeatures, createVisualElement, projectionNodeConstructor) {
var _b = _a.forwardMotionProps, forwardMotionProps = _b === void 0 ? false : _b;
var baseConfig = isSVGComponent(Component)
? svgMotionConfig
: htmlMotionConfig;
return tslib.__assign(tslib.__assign({}, baseConfig), { preloadedFeatures: preloadedFeatures, useRender: createUseRender(forwardMotionProps), createVisualElement: createVisualElement, projectionNodeConstructor: projectionNodeConstructor, Component: Component });
}
exports.AnimationType = void 0;
(function (AnimationType) {
AnimationType["Animate"] = "animate";
AnimationType["Hover"] = "whileHover";
AnimationType["Tap"] = "whileTap";
AnimationType["Drag"] = "whileDrag";
AnimationType["Focus"] = "whileFocus";
AnimationType["InView"] = "whileInView";
AnimationType["Exit"] = "exit";
})(exports.AnimationType || (exports.AnimationType = {}));
function addDomEvent(target, eventName, handler, options) {
if (options === void 0) { options = { passive: true }; }
target.addEventListener(eventName, handler, options);
return function () { return target.removeEventListener(eventName, handler); };
}
/**
* Attaches an event listener directly to the provided DOM element.
*
* Bypassing React's event system can be desirable, for instance when attaching non-passive
* event handlers.
*
* ```jsx
* const ref = useRef(null)
*
* useDomEvent(ref, 'wheel', onWheel, { passive: false })
*
* return
* ```
*
* @param ref - React.RefObject that's been provided to the element you want to bind the listener to.
* @param eventName - Name of the event you want listen for.
* @param handler - Function to fire when receiving the event.
* @param options - Options to pass to `Event.addEventListener`.
*
* @public
*/
function useDomEvent(ref, eventName, handler, options) {
React.useEffect(function () {
var element = ref.current;
if (handler && element) {
return addDomEvent(element, eventName, handler, options);
}
}, [ref, eventName, handler, options]);
}
/**
*
* @param props
* @param ref
* @internal
*/
function useFocusGesture(_a) {
var whileFocus = _a.whileFocus, visualElement = _a.visualElement;
var onFocus = function () {
var _a;
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.Focus, true);
};
var onBlur = function () {
var _a;
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.Focus, false);
};
useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
}
function isMouseEvent(event) {
// PointerEvent inherits from MouseEvent so we can't use a straight instanceof check.
if (typeof PointerEvent !== "undefined" && event instanceof PointerEvent) {
return !!(event.pointerType === "mouse");
}
return event instanceof MouseEvent;
}
function isTouchEvent(event) {
var hasTouches = !!event.touches;
return hasTouches;
}
/**
* Filters out events not attached to the primary pointer (currently left mouse button)
* @param eventHandler
*/
function filterPrimaryPointer(eventHandler) {
return function (event) {
var isMouseEvent = event instanceof MouseEvent;
var isPrimaryPointer = !isMouseEvent ||
(isMouseEvent && event.button === 0);
if (isPrimaryPointer) {
eventHandler(event);
}
};
}
var defaultPagePoint = { pageX: 0, pageY: 0 };
function pointFromTouch(e, pointType) {
if (pointType === void 0) { pointType = "page"; }
var primaryTouch = e.touches[0] || e.changedTouches[0];
var point = primaryTouch || defaultPagePoint;
return {
x: point[pointType + "X"],
y: point[pointType + "Y"],
};
}
function pointFromMouse(point, pointType) {
if (pointType === void 0) { pointType = "page"; }
return {
x: point[pointType + "X"],
y: point[pointType + "Y"],
};
}
function extractEventInfo(event, pointType) {
if (pointType === void 0) { pointType = "page"; }
return {
point: isTouchEvent(event)
? pointFromTouch(event, pointType)
: pointFromMouse(event, pointType),
};
}
var wrapHandler = function (handler, shouldFilterPrimaryPointer) {
if (shouldFilterPrimaryPointer === void 0) { shouldFilterPrimaryPointer = false; }
var listener = function (event) {
return handler(event, extractEventInfo(event));
};
return shouldFilterPrimaryPointer
? filterPrimaryPointer(listener)
: listener;
};
// We check for event support via functions in case they've been mocked by a testing suite.
var supportsPointerEvents = function () {
return isBrowser && window.onpointerdown === null;
};
var supportsTouchEvents = function () {
return isBrowser && window.ontouchstart === null;
};
var supportsMouseEvents = function () {
return isBrowser && window.onmousedown === null;
};
var mouseEventNames = {
pointerdown: "mousedown",
pointermove: "mousemove",
pointerup: "mouseup",
pointercancel: "mousecancel",
pointerover: "mouseover",
pointerout: "mouseout",
pointerenter: "mouseenter",
pointerleave: "mouseleave",
};
var touchEventNames = {
pointerdown: "touchstart",
pointermove: "touchmove",
pointerup: "touchend",
pointercancel: "touchcancel",
};
function getPointerEventName(name) {
if (supportsPointerEvents()) {
return name;
}
else if (supportsTouchEvents()) {
return touchEventNames[name];
}
else if (supportsMouseEvents()) {
return mouseEventNames[name];
}
return name;
}
function addPointerEvent(target, eventName, handler, options) {
return addDomEvent(target, getPointerEventName(eventName), wrapHandler(handler, eventName === "pointerdown"), options);
}
function usePointerEvent(ref, eventName, handler, options) {
return useDomEvent(ref, getPointerEventName(eventName), handler && wrapHandler(handler, eventName === "pointerdown"), options);
}
function createLock(name) {
var lock = null;
return function () {
var openLock = function () {
lock = null;
};
if (lock === null) {
lock = name;
return openLock;
}
return false;
};
}
var globalHorizontalLock = createLock("dragHorizontal");
var globalVerticalLock = createLock("dragVertical");
function getGlobalLock(drag) {
var lock = false;
if (drag === "y") {
lock = globalVerticalLock();
}
else if (drag === "x") {
lock = globalHorizontalLock();
}
else {
var openHorizontal_1 = globalHorizontalLock();
var openVertical_1 = globalVerticalLock();
if (openHorizontal_1 && openVertical_1) {
lock = function () {
openHorizontal_1();
openVertical_1();
};
}
else {
// Release the locks because we don't use them
if (openHorizontal_1)
openHorizontal_1();
if (openVertical_1)
openVertical_1();
}
}
return lock;
}
function isDragActive() {
// Check the gesture lock - if we get it, it means no drag gesture is active
// and we can safely fire the tap gesture.
var openGestureLock = getGlobalLock(true);
if (!openGestureLock)
return true;
openGestureLock();
return false;
}
function createHoverEvent(visualElement, isActive, callback) {
return function (event, info) {
var _a;
if (!isMouseEvent(event) || isDragActive())
return;
/**
* Ensure we trigger animations before firing event callback
*/
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.Hover, isActive);
callback === null || callback === void 0 ? void 0 : callback(event, info);
};
}
function useHoverGesture(_a) {
var onHoverStart = _a.onHoverStart, onHoverEnd = _a.onHoverEnd, whileHover = _a.whileHover, visualElement = _a.visualElement;
usePointerEvent(visualElement, "pointerenter", onHoverStart || whileHover
? createHoverEvent(visualElement, true, onHoverStart)
: undefined, { passive: !onHoverStart });
usePointerEvent(visualElement, "pointerleave", onHoverEnd || whileHover
? createHoverEvent(visualElement, false, onHoverEnd)
: undefined, { passive: !onHoverEnd });
}
/**
* Recursively traverse up the tree to check whether the provided child node
* is the parent or a descendant of it.
*
* @param parent - Element to find
* @param child - Element to test against parent
*/
var isNodeOrChild = function (parent, child) {
if (!child) {
return false;
}
else if (parent === child) {
return true;
}
else {
return isNodeOrChild(parent, child.parentElement);
}
};
function useUnmountEffect(callback) {
return React.useEffect(function () { return function () { return callback(); }; }, []);
}
/**
* @param handlers -
* @internal
*/
function useTapGesture(_a) {
var onTap = _a.onTap, onTapStart = _a.onTapStart, onTapCancel = _a.onTapCancel, whileTap = _a.whileTap, visualElement = _a.visualElement;
var hasPressListeners = onTap || onTapStart || onTapCancel || whileTap;
var isPressing = React.useRef(false);
var cancelPointerEndListeners = React.useRef(null);
/**
* Only set listener to passive if there are no external listeners.
*/
var eventOptions = {
passive: !(onTapStart || onTap || onTapCancel || onPointerDown),
};
function removePointerEndListener() {
var _a;
(_a = cancelPointerEndListeners.current) === null || _a === void 0 ? void 0 : _a.call(cancelPointerEndListeners);
cancelPointerEndListeners.current = null;
}
function checkPointerEnd() {
var _a;
removePointerEndListener();
isPressing.current = false;
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.Tap, false);
return !isDragActive();
}
function onPointerUp(event, info) {
if (!checkPointerEnd())
return;
/**
* We only count this as a tap gesture if the event.target is the same
* as, or a child of, this component's element
*/
!isNodeOrChild(visualElement.getInstance(), event.target)
? onTapCancel === null || onTapCancel === void 0 ? void 0 : onTapCancel(event, info)
: onTap === null || onTap === void 0 ? void 0 : onTap(event, info);
}
function onPointerCancel(event, info) {
if (!checkPointerEnd())
return;
onTapCancel === null || onTapCancel === void 0 ? void 0 : onTapCancel(event, info);
}
function onPointerDown(event, info) {
var _a;
removePointerEndListener();
if (isPressing.current)
return;
isPressing.current = true;
cancelPointerEndListeners.current = popmotion.pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
/**
* Ensure we trigger animations before firing event callback
*/
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.Tap, true);
onTapStart === null || onTapStart === void 0 ? void 0 : onTapStart(event, info);
}
usePointerEvent(visualElement, "pointerdown", hasPressListeners ? onPointerDown : undefined, eventOptions);
useUnmountEffect(removePointerEndListener);
}
var warned = new Set();
function warnOnce(condition, message, element) {
if (condition || warned.has(message))
return;
console.warn(message);
if (element)
console.warn(element);
warned.add(message);
}
/**
* Map an IntersectionHandler callback to an element. We only ever make one handler for one
* element, so even though these handlers might all be triggered by different
* observers, we can keep them in the same map.
*/
var observerCallbacks = new WeakMap();
/**
* Multiple observers can be created for multiple element/document roots. Each with
* different settings. So here we store dictionaries of observers to each root,
* using serialised settings (threshold/margin) as lookup keys.
*/
var observers = new WeakMap();
var fireObserverCallback = function (entry) {
var _a;
(_a = observerCallbacks.get(entry.target)) === null || _a === void 0 ? void 0 : _a(entry);
};
var fireAllObserverCallbacks = function (entries) {
entries.forEach(fireObserverCallback);
};
function initIntersectionObserver(_a) {
var root = _a.root, options = tslib.__rest(_a, ["root"]);
var lookupRoot = root || document;
/**
* If we don't have an observer lookup map for this root, create one.
*/
if (!observers.has(lookupRoot)) {
observers.set(lookupRoot, {});
}
var rootObservers = observers.get(lookupRoot);
var key = JSON.stringify(options);
/**
* If we don't have an observer for this combination of root and settings,
* create one.
*/
if (!rootObservers[key]) {
rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, tslib.__assign({ root: root }, options));
}
return rootObservers[key];
}
function observeIntersection(element, options, callback) {
var rootInteresectionObserver = initIntersectionObserver(options);
observerCallbacks.set(element, callback);
rootInteresectionObserver.observe(element);
return function () {
observerCallbacks.delete(element);
rootInteresectionObserver.unobserve(element);
};
}
function useViewport(_a) {
var visualElement = _a.visualElement, whileInView = _a.whileInView, onViewportEnter = _a.onViewportEnter, onViewportLeave = _a.onViewportLeave, _b = _a.viewport, viewport = _b === void 0 ? {} : _b;
var state = React.useRef({
hasEnteredView: false,
isInView: false,
});
var shouldObserve = Boolean(whileInView || onViewportEnter || onViewportLeave);
if (viewport.once && state.current.hasEnteredView)
shouldObserve = false;
var useObserver = typeof IntersectionObserver === "undefined"
? useMissingIntersectionObserver
: useIntersectionObserver;
useObserver(shouldObserve, state.current, visualElement, viewport);
}
var thresholdNames = {
some: 0,
all: 1,
};
function useIntersectionObserver(shouldObserve, state, visualElement, _a) {
var root = _a.root, rootMargin = _a.margin, _b = _a.amount, amount = _b === void 0 ? "some" : _b, once = _a.once;
React.useEffect(function () {
if (!shouldObserve)
return;
var options = {
root: root === null || root === void 0 ? void 0 : root.current,
rootMargin: rootMargin,
threshold: typeof amount === "number" ? amount : thresholdNames[amount],
};
var intersectionCallback = function (entry) {
var _a;
var isIntersecting = entry.isIntersecting;
/**
* If there's been no change in the viewport state, early return.
*/
if (state.isInView === isIntersecting)
return;
state.isInView = isIntersecting;
/**
* Handle hasEnteredView. If this is only meant to run once, and
* element isn't visible, early return. Otherwise set hasEnteredView to true.
*/
if (once && !isIntersecting && state.hasEnteredView) {
return;
}
else if (isIntersecting) {
state.hasEnteredView = true;
}
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.InView, isIntersecting);
/**
* Use the latest committed props rather than the ones in scope
* when this observer is created
*/
var props = visualElement.getProps();
var callback = isIntersecting
? props.onViewportEnter
: props.onViewportLeave;
callback === null || callback === void 0 ? void 0 : callback(entry);
};
return observeIntersection(visualElement.getInstance(), options, intersectionCallback);
}, [shouldObserve, root, rootMargin, amount]);
}
/**
* If IntersectionObserver is missing, we activate inView and fire onViewportEnter
* on mount. This way, the page will be in the state the author expects users
* to see it in for everyone.
*/
function useMissingIntersectionObserver(shouldObserve, state, visualElement, _a) {
var _b = _a.fallback, fallback = _b === void 0 ? true : _b;
React.useEffect(function () {
if (!shouldObserve || !fallback)
return;
if (env !== "production") {
warnOnce(false, "IntersectionObserver not available on this device. whileInView animations will trigger on mount.");
}
/**
* Fire this in an rAF because, at this point, the animation state
* won't have flushed for the first time and there's certain logic in
* there that behaves differently on the initial animation.
*
* This hook should be quite rarely called so setting this in an rAF
* is preferred to changing the behaviour of the animation state.
*/
requestAnimationFrame(function () {
var _a;
state.hasEnteredView = true;
var onViewportEnter = visualElement.getProps().onViewportEnter;
onViewportEnter === null || onViewportEnter === void 0 ? void 0 : onViewportEnter(null);
(_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.InView, true);
});
}, [shouldObserve]);
}
var makeRenderlessComponent = function (hook) { return function (props) {
hook(props);
return null;
}; };
var gestureAnimations = {
inView: makeRenderlessComponent(useViewport),
tap: makeRenderlessComponent(useTapGesture),
focus: makeRenderlessComponent(useFocusGesture),
hover: makeRenderlessComponent(useHoverGesture),
};
var counter = 0;
var incrementId = function () { return counter++; };
var useId = function () { return useConstant(incrementId); };
/**
* Ideally we'd use the following code to support React 18 optionally.
* But this fairly fails in Webpack (otherwise treeshaking wouldn't work at all).
* Need to come up with a different way of figuring this out.
*/
// export const useId = (React as any).useId
// ? (React as any).useId
// : () => useConstant(incrementId)
/**
* When a component is the child of `AnimatePresence`, it can use `usePresence`
* to access information about whether it's still present in the React tree.
*
* ```jsx
* import { usePresence } from "framer-motion"
*
* export const Component = () => {
* const [isPresent, safeToRemove] = usePresence()
*
* useEffect(() => {
* !isPresent && setTimeout(safeToRemove, 1000)
* }, [isPresent])
*
* return
* }
* ```
*
* If `isPresent` is `false`, it means that a component has been removed the tree, but
* `AnimatePresence` won't really remove it until `safeToRemove` has been called.
*
* @public
*/
function usePresence() {
var context = React.useContext(PresenceContext);
if (context === null)
return [true, null];
var isPresent = context.isPresent, onExitComplete = context.onExitComplete, register = context.register;
// It's safe to call the following hooks conditionally (after an early return) because the context will always
// either be null or non-null for the lifespan of the component.
// Replace with useId when released in React
var id = useId();
React.useEffect(function () { return register(id); }, []);
var safeToRemove = function () { return onExitComplete === null || onExitComplete === void 0 ? void 0 : onExitComplete(id); };
return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
}
/**
* Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
* There is no `safeToRemove` function.
*
* ```jsx
* import { useIsPresent } from "framer-motion"
*
* export const Component = () => {
* const isPresent = useIsPresent()
*
* useEffect(() => {
* !isPresent && console.log("I've been removed!")
* }, [isPresent])
*
* return
* }
* ```
*
* @public
*/
function useIsPresent() {
return isPresent(React.useContext(PresenceContext));
}
function isPresent(context) {
return context === null ? true : context.isPresent;
}
function shallowCompare(next, prev) {
if (!Array.isArray(prev))
return false;
var prevLength = prev.length;
if (prevLength !== next.length)
return false;
for (var i = 0; i < prevLength; i++) {
if (prev[i] !== next[i])
return false;
}
return true;
}
/**
* Converts seconds to milliseconds
*
* @param seconds - Time in seconds.
* @return milliseconds - Converted time in milliseconds.
*/
var secondsToMilliseconds = function (seconds) { return seconds * 1000; };
var easingLookup = {
linear: popmotion.linear,
easeIn: popmotion.easeIn,
easeInOut: popmotion.easeInOut,
easeOut: popmotion.easeOut,
circIn: popmotion.circIn,
circInOut: popmotion.circInOut,
circOut: popmotion.circOut,
backIn: popmotion.backIn,
backInOut: popmotion.backInOut,
backOut: popmotion.backOut,
anticipate: popmotion.anticipate,
bounceIn: popmotion.bounceIn,
bounceInOut: popmotion.bounceInOut,
bounceOut: popmotion.bounceOut,
};
var easingDefinitionToFunction = function (definition) {
if (Array.isArray(definition)) {
// If cubic bezier definition, create bezier curve
heyListen.invariant(definition.length === 4, "Cubic bezier arrays must contain four numerical values.");
var _a = tslib.__read(definition, 4), x1 = _a[0], y1 = _a[1], x2 = _a[2], y2 = _a[3];
return popmotion.cubicBezier(x1, y1, x2, y2);
}
else if (typeof definition === "string") {
// Else lookup from table
heyListen.invariant(easingLookup[definition] !== undefined, "Invalid easing type '".concat(definition, "'"));
return easingLookup[definition];
}
return definition;
};
var isEasingArray = function (ease) {
return Array.isArray(ease) && typeof ease[0] !== "number";
};
/**
* 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.complex.test(value) && // And it contains numbers and/or colors
!value.startsWith("url(") // Unless it starts with "url("
) {
return true;
}
return false;
};
var underDampedSpring = function () { return ({
type: "spring",
stiffness: 500,
damping: 25,
restSpeed: 10,
}); };
var criticallyDampedSpring = function (to) { return ({
type: "spring",
stiffness: 550,
damping: to === 0 ? 2 * Math.sqrt(550) : 30,
restSpeed: 10,
}); };
var linearTween = function () { return ({
type: "keyframes",
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: criticallyDampedSpring,
scaleY: criticallyDampedSpring,
scale: criticallyDampedSpring,
opacity: linearTween,
backgroundColor: linearTween,
color: linearTween,
default: criticallyDampedSpring,
};
var getDefaultTransition = function (valueKey, to) {
var transitionFactory;
if (isKeyframesTarget(to)) {
transitionFactory = keyframes;
}
else {
transitionFactory =
defaultTransitions[valueKey] || defaultTransitions.default;
}
return tslib.__assign({ to: to }, transitionFactory(to));
};
/**
* A map of default value types for common values
*/
var defaultValueTypes = tslib.__assign(tslib.__assign({}, numberValueTypes), {
// Color props
color: styleValueTypes.color, backgroundColor: styleValueTypes.color, outlineColor: styleValueTypes.color, fill: styleValueTypes.color, stroke: styleValueTypes.color,
// Border props
borderColor: styleValueTypes.color, borderTopColor: styleValueTypes.color, borderRightColor: styleValueTypes.color, borderBottomColor: styleValueTypes.color, borderLeftColor: styleValueTypes.color, filter: styleValueTypes.filter, WebkitFilter: styleValueTypes.filter });
/**
* Gets the default ValueType for the provided value key
*/
var getDefaultValueType = function (key) { return defaultValueTypes[key]; };
function getAnimatableNone(key, value) {
var _a;
var defaultValueType = getDefaultValueType(key);
if (defaultValueType !== styleValueTypes.filter)
defaultValueType = styleValueTypes.complex;
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
return (_a = defaultValueType.getAnimatableNone) === null || _a === void 0 ? void 0 : _a.call(defaultValueType, value);
}
var instantAnimationState = {
current: false,
};
/**
* Decide whether a transition is defined on a given Transition.
* This filters out orchestration options and returns true
* if any options are left.
*/
function isTransitionDefined(_a) {
_a.when; _a.delay; _a.delayChildren; _a.staggerChildren; _a.staggerDirection; _a.repeat; _a.repeatType; _a.repeatDelay; _a.from; var transition = tslib.__rest(_a, ["when", "delay", "delayChildren", "staggerChildren", "staggerDirection", "repeat", "repeatType", "repeatDelay", "from"]);
return !!Object.keys(transition).length;
}
var legacyRepeatWarning = false;
/**
* Convert Framer Motion's Transition type into Popmotion-compatible options.
*/
function convertTransitionToAnimationOptions(_a) {
var ease = _a.ease, times = _a.times, yoyo = _a.yoyo, flip = _a.flip, loop = _a.loop, transition = tslib.__rest(_a, ["ease", "times", "yoyo", "flip", "loop"]);
var options = tslib.__assign({}, transition);
if (times)
options["offset"] = times;
/**
* Convert any existing durations from seconds to milliseconds
*/
if (transition.duration)
options["duration"] = secondsToMilliseconds(transition.duration);
if (transition.repeatDelay)
options.repeatDelay = secondsToMilliseconds(transition.repeatDelay);
/**
* Map easing names to Popmotion's easing functions
*/
if (ease) {
options["ease"] = isEasingArray(ease)
? ease.map(easingDefinitionToFunction)
: easingDefinitionToFunction(ease);
}
/**
* Support legacy transition API
*/
if (transition.type === "tween")
options.type = "keyframes";
/**
* TODO: These options are officially removed from the API.
*/
if (yoyo || loop || flip) {
heyListen.warning(!legacyRepeatWarning, "yoyo, loop and flip have been removed from the API. Replace with repeat and repeatType options.");
legacyRepeatWarning = true;
if (yoyo) {
options.repeatType = "reverse";
}
else if (loop) {
options.repeatType = "loop";
}
else if (flip) {
options.repeatType = "mirror";
}
options.repeat = loop || yoyo || flip || transition.repeat;
}
/**
* TODO: Popmotion 9 has the ability to automatically detect whether to use
* a keyframes or spring animation, but does so by detecting velocity and other spring options.
* It'd be good to introduce a similar thing here.
*/
if (transition.type !== "spring")
options.type = "keyframes";
return options;
}
/**
* Get the delay for a value by checking Transition with decreasing specificity.
*/
function getDelayFromTransition(transition, key) {
var _a, _b;
var valueTransition = getValueTransition(transition, key) || {};
return (_b = (_a = valueTransition.delay) !== null && _a !== void 0 ? _a : transition.delay) !== null && _b !== void 0 ? _b : 0;
}
function hydrateKeyframes(options) {
if (Array.isArray(options.to) && options.to[0] === null) {
options.to = tslib.__spreadArray([], tslib.__read(options.to), false);
options.to[0] = options.from;
}
return options;
}
function getPopmotionAnimationOptions(transition, options, key) {
var _a;
if (Array.isArray(options.to)) {
(_a = transition.duration) !== null && _a !== void 0 ? _a : (transition.duration = 0.8);
}
hydrateKeyframes(options);
/**
* Get a default transition if none is determined to be defined.
*/
if (!isTransitionDefined(transition)) {
transition = tslib.__assign(tslib.__assign({}, transition), getDefaultTransition(key, options.to));
}
return tslib.__assign(tslib.__assign({}, options), convertTransitionToAnimationOptions(transition));
}
/**
*
*/
function getAnimation(key, value, target, transition, onComplete) {
var _a;
var valueTransition = getValueTransition(transition, key);
var origin = (_a = valueTransition.from) !== null && _a !== void 0 ? _a : value.get();
var isTargetAnimatable = isAnimatable(key, target);
if (origin === "none" && isTargetAnimatable && typeof target === "string") {
/**
* If we're trying to animate from "none", try and get an animatable version
* of the target. This could be improved to work both ways.
*/
origin = getAnimatableNone(key, target);
}
else if (isZero(origin) && typeof target === "string") {
origin = getZeroUnit(target);
}
else if (!Array.isArray(target) &&
isZero(target) &&
typeof origin === "string") {
target = getZeroUnit(origin);
}
var isOriginAnimatable = isAnimatable(key, origin);
heyListen.warning(isOriginAnimatable === isTargetAnimatable, "You are trying to animate ".concat(key, " from \"").concat(origin, "\" to \"").concat(target, "\". ").concat(origin, " is not an animatable value - to enable this animation set ").concat(origin, " to a value animatable to ").concat(target, " via the `style` property."));
function start() {
var options = {
from: origin,
to: target,
velocity: value.getVelocity(),
onComplete: onComplete,
onUpdate: function (v) { return value.set(v); },
};
return valueTransition.type === "inertia" ||
valueTransition.type === "decay"
? popmotion.inertia(tslib.__assign(tslib.__assign({}, options), valueTransition))
: popmotion.animate(tslib.__assign(tslib.__assign({}, getPopmotionAnimationOptions(valueTransition, options, key)), { onUpdate: function (v) {
var _a;
options.onUpdate(v);
(_a = valueTransition.onUpdate) === null || _a === void 0 ? void 0 : _a.call(valueTransition, v);
}, onComplete: function () {
var _a;
options.onComplete();
(_a = valueTransition.onComplete) === null || _a === void 0 ? void 0 : _a.call(valueTransition);
} }));
}
function set() {
var _a, _b;
var finalTarget = resolveFinalValueInKeyframes(target);
value.set(finalTarget);
onComplete();
(_a = valueTransition === null || valueTransition === void 0 ? void 0 : valueTransition.onUpdate) === null || _a === void 0 ? void 0 : _a.call(valueTransition, finalTarget);
(_b = valueTransition === null || valueTransition === void 0 ? void 0 : valueTransition.onComplete) === null || _b === void 0 ? void 0 : _b.call(valueTransition);
return { stop: function () { } };
}
return !isOriginAnimatable ||
!isTargetAnimatable ||
valueTransition.type === false
? set
: start;
}
function isZero(value) {
return (value === 0 ||
(typeof value === "string" &&
parseFloat(value) === 0 &&
value.indexOf(" ") === -1));
}
function getZeroUnit(potentialUnitType) {
return typeof potentialUnitType === "number"
? 0
: getAnimatableNone("", potentialUnitType);
}
function getValueTransition(transition, key) {
return transition[key] || transition["default"] || transition;
}
/**
* Start animation on a MotionValue. This function is an interface between
* Framer Motion and Popmotion
*/
function startAnimation(key, value, target, transition) {
if (transition === void 0) { transition = {}; }
if (instantAnimationState.current) {
transition = { type: false };
}
return value.start(function (onComplete) {
var delayTimer;
var controls;
var animation = getAnimation(key, value, target, transition, onComplete);
var delay = getDelayFromTransition(transition, key);
var start = function () { return (controls = animation()); };
if (delay) {
delayTimer = window.setTimeout(start, secondsToMilliseconds(delay));
}
else {
start();
}
return function () {
clearTimeout(delayTimer);
controls === null || controls === void 0 ? void 0 : controls.stop();
};
});
}
/**
* Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
*/
var isNumericalString = function (v) { return /^\-?\d*\.?\d+$/.test(v); };
/**
* Check if the value is a zero value string like "0px" or "0%"
*/
var isZeroValueString = function (v) { return /^0[^.\s]+$/.test(v); };
function addUniqueItem(arr, item) {
arr.indexOf(item) === -1 && arr.push(item);
}
function removeItem(arr, item) {
var index = arr.indexOf(item);
index > -1 && arr.splice(index, 1);
}
// Adapted from array-move
function moveItem(_a, fromIndex, toIndex) {
var _b = tslib.__read(_a), arr = _b.slice(0);
var startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
if (startIndex >= 0 && startIndex < arr.length) {
var endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
var _c = tslib.__read(arr.splice(fromIndex, 1), 1), item = _c[0];
arr.splice(endIndex, 0, item);
}
return arr;
}
var SubscriptionManager = /** @class */ (function () {
function SubscriptionManager() {
this.subscriptions = [];
}
SubscriptionManager.prototype.add = function (handler) {
var _this = this;
addUniqueItem(this.subscriptions, handler);
return function () { return removeItem(_this.subscriptions, handler); };
};
SubscriptionManager.prototype.notify = function (a, b, c) {
var numSubscriptions = this.subscriptions.length;
if (!numSubscriptions)
return;
if (numSubscriptions === 1) {
/**
* If there's only a single handler we can just call it without invoking a loop.
*/
this.subscriptions[0](a, b, c);
}
else {
for (var i = 0; i < numSubscriptions; i++) {
/**
* Check whether the handler exists before firing as it's possible
* the subscriptions were modified during this loop running.
*/
var handler = this.subscriptions[i];
handler && handler(a, b, c);
}
}
};
SubscriptionManager.prototype.getSize = function () {
return this.subscriptions.length;
};
SubscriptionManager.prototype.clear = function () {
this.subscriptions.length = 0;
};
return SubscriptionManager;
}());
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) {
var _this = this;
/**
* This will be replaced by the build step with the latest version number.
* When MotionValues are provided to motion components, warn if versions are mixed.
*/
this.version = "6.5.1";
/**
* Duration, in milliseconds, since last updating frame.
*
* @internal
*/
this.timeDelta = 0;
/**
* Timestamp of the last time this `MotionValue` was updated.
*
* @internal
*/
this.lastUpdated = 0;
/**
* Functions to notify when the `MotionValue` updates.
*
* @internal
*/
this.updateSubscribers = new SubscriptionManager();
/**
* Functions to notify when the velocity updates.
*
* @internal
*/
this.velocityUpdateSubscribers = new SubscriptionManager();
/**
* Functions to notify when the `MotionValue` updates and `render` is set to `true`.
*
* @internal
*/
this.renderSubscribers = new SubscriptionManager();
/**
* 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 = v;
// Update timestamp
var _a = sync.getFrameData(), delta = _a.delta, timestamp = _a.timestamp;
if (_this.lastUpdated !== timestamp) {
_this.timeDelta = delta;
_this.lastUpdated = timestamp;
sync__default["default"].postRender(_this.scheduleVelocityCheck);
}
// Update update subscribers
if (_this.prev !== _this.current) {
_this.updateSubscribers.notify(_this.current);
}
// Update velocity subscribers
if (_this.velocityUpdateSubscribers.getSize()) {
_this.velocityUpdateSubscribers.notify(_this.getVelocity());
}
// Update render subscribers
if (render) {
_this.renderSubscribers.notify(_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 sync__default["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;
_this.velocityUpdateSubscribers.notify(_this.getVelocity());
}
};
this.hasAnimated = false;
this.prev = this.current = init;
this.canTrackVelocity = isFloat(this.current);
}
/**
* 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..
*
* ```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
* }
* ```
*
* @privateRemarks
*
* 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) {
return this.updateSubscribers.add(subscription);
};
MotionValue.prototype.clearListeners = function () {
this.updateSubscribers.clear();
};
/**
* 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) {
// Render immediately
subscription(this.get());
return this.renderSubscribers.add(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;
};
/**
* @public
*/
MotionValue.prototype.getPrevious = function () {
return this.prev;
};
/**
* 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
popmotion.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.hasAnimated = true;
_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.clear();
this.renderSubscribers.clear();
this.stop();
};
return MotionValue;
}());
function motionValue(init) {
return new MotionValue(init);
}
/**
* Tests a provided value against a ValueType
*/
var testValueType = function (v) { return function (type) { return type.test(v); }; };
/**
* ValueType for "auto"
*/
var auto = {
test: function (v) { return v === "auto"; },
parse: function (v) { return v; },
};
/**
* A list of value types commonly used for dimensions
*/
var dimensionValueTypes = [styleValueTypes.number, styleValueTypes.px, styleValueTypes.percent, styleValueTypes.degrees, styleValueTypes.vw, styleValueTypes.vh, auto];
/**
* Tests a dimensional value against the list of dimension ValueTypes
*/
var findDimensionValueType = function (v) {
return dimensionValueTypes.find(testValueType(v));
};
/**
* A list of all ValueTypes
*/
var valueTypes = tslib.__spreadArray(tslib.__spreadArray([], tslib.__read(dimensionValueTypes), false), [styleValueTypes.color, styleValueTypes.complex], false);
/**
* Tests a value against the list of ValueTypes
*/
var findValueType = function (v) { return valueTypes.find(testValueType(v)); };
/**
* Set VisualElement's MotionValue, creating a new MotionValue for it if
* it doesn't exist.
*/
function setMotionValue(visualElement, key, value) {
if (visualElement.hasValue(key)) {
visualElement.getValue(key).set(value);
}
else {
visualElement.addValue(key, motionValue(value));
}
}
function setTarget(visualElement, definition) {
var resolved = resolveVariant(visualElement, definition);
var _a = resolved ? visualElement.makeTargetAnimatable(resolved, false) : {}, _b = _a.transitionEnd, transitionEnd = _b === void 0 ? {} : _b; _a.transition; var target = tslib.__rest(_a, ["transitionEnd", "transition"]);
target = tslib.__assign(tslib.__assign({}, target), transitionEnd);
for (var key in target) {
var value = resolveFinalValueInKeyframes(target[key]);
setMotionValue(visualElement, key, value);
}
}
function setVariants(visualElement, variantLabels) {
var reversedLabels = tslib.__spreadArray([], tslib.__read(variantLabels), false).reverse();
reversedLabels.forEach(function (key) {
var _a;
var variant = visualElement.getVariant(key);
variant && setTarget(visualElement, variant);
(_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.forEach(function (child) {
setVariants(child, variantLabels);
});
});
}
function setValues(visualElement, definition) {
if (Array.isArray(definition)) {
return setVariants(visualElement, definition);
}
else if (typeof definition === "string") {
return setVariants(visualElement, [definition]);
}
else {
setTarget(visualElement, definition);
}
}
function checkTargetForNewValues(visualElement, target, origin) {
var _a, _b, _c;
var _d;
var newValueKeys = Object.keys(target).filter(function (key) { return !visualElement.hasValue(key); });
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 the target is a series of keyframes, we can use the first value
* in the array. If this first value is null, we'll still need to read from the DOM.
*/
if (Array.isArray(targetValue)) {
value = targetValue[0];
}
/**
* If the target isn't keyframes, or the first keyframe was null, we need to
* first check if an origin value was explicitly defined in the transition as "from",
* if not read the value from the DOM. As an absolute fallback, take the defined target value.
*/
if (value === null) {
value = (_b = (_a = origin[key]) !== null && _a !== void 0 ? _a : visualElement.readValue(key)) !== null && _b !== void 0 ? _b : target[key];
}
/**
* If value is still undefined or null, ignore it. Preferably this would throw,
* but this was causing issues in Framer.
*/
if (value === undefined || value === null)
continue;
if (typeof value === "string" &&
(isNumericalString(value) || isZeroValueString(value))) {
// If this is a number read as a string, ie "0" or "200", convert it to a number
value = parseFloat(value);
}
else if (!findValueType(value) && styleValueTypes.complex.test(targetValue)) {
value = getAnimatableNone(key, targetValue);
}
visualElement.addValue(key, motionValue(value));
(_c = (_d = origin)[key]) !== null && _c !== void 0 ? _c : (_d[key] = value);
visualElement.setBaseTarget(key, value);
}
}
function getOriginFromTransition(key, transition) {
if (!transition)
return;
var valueTransition = transition[key] || transition["default"] || transition;
return valueTransition.from;
}
function getOrigin(target, transition, visualElement) {
var _a, _b;
var origin = {};
for (var key in target) {
origin[key] =
(_a = getOriginFromTransition(key, transition)) !== null && _a !== void 0 ? _a : (_b = visualElement.getValue(key)) === null || _b === void 0 ? void 0 : _b.get();
}
return origin;
}
function animateVisualElement(visualElement, definition, options) {
if (options === void 0) { options = {}; }
visualElement.notifyAnimationStart(definition);
var animation;
if (Array.isArray(definition)) {
var animations = definition.map(function (variant) {
return animateVariant(visualElement, variant, options);
});
animation = Promise.all(animations);
}
else if (typeof definition === "string") {
animation = animateVariant(visualElement, definition, options);
}
else {
var resolvedDefinition = typeof definition === "function"
? resolveVariant(visualElement, definition, options.custom)
: definition;
animation = animateTarget(visualElement, resolvedDefinition, options);
}
return animation.then(function () {
return visualElement.notifyAnimationComplete(definition);
});
}
function animateVariant(visualElement, variant, options) {
var _a;
if (options === void 0) { options = {}; }
var resolved = resolveVariant(visualElement, variant, options.custom);
var _b = (resolved || {}).transition, transition = _b === void 0 ? visualElement.getDefaultTransition() || {} : _b;
if (options.transitionOverride) {
transition = options.transitionOverride;
}
/**
* If we have a variant, create a callback that runs it as an animation.
* Otherwise, we resolve a Promise immediately for a composable no-op.
*/
var getAnimation = resolved
? function () { return animateTarget(visualElement, resolved, options); }
: function () { return Promise.resolve(); };
/**
* If we have children, create a callback that runs all their animations.
* Otherwise, we resolve a Promise immediately for a composable no-op.
*/
var getChildAnimations = ((_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.size)
? function (forwardDelay) {
if (forwardDelay === void 0) { forwardDelay = 0; }
var _a = transition.delayChildren, delayChildren = _a === void 0 ? 0 : _a, staggerChildren = transition.staggerChildren, staggerDirection = transition.staggerDirection;
return animateChildren(visualElement, variant, delayChildren + forwardDelay, staggerChildren, staggerDirection, options);
}
: function () { return Promise.resolve(); };
/**
* If the transition explicitly defines a "when" option, we need to resolve either
* this animation or all children animations before playing the other.
*/
var when = transition.when;
if (when) {
var _c = tslib.__read(when === "beforeChildren"
? [getAnimation, getChildAnimations]
: [getChildAnimations, getAnimation], 2), first = _c[0], last = _c[1];
return first().then(last);
}
else {
return Promise.all([getAnimation(), getChildAnimations(options.delay)]);
}
}
/**
* @internal
*/
function animateTarget(visualElement, definition, _a) {
var _b;
var _c = _a === void 0 ? {} : _a, _d = _c.delay, delay = _d === void 0 ? 0 : _d, transitionOverride = _c.transitionOverride, type = _c.type;
var _e = visualElement.makeTargetAnimatable(definition), _f = _e.transition, transition = _f === void 0 ? visualElement.getDefaultTransition() : _f, transitionEnd = _e.transitionEnd, target = tslib.__rest(_e, ["transition", "transitionEnd"]);
if (transitionOverride)
transition = transitionOverride;
var animations = [];
var animationTypeState = type && ((_b = visualElement.animationState) === null || _b === void 0 ? void 0 : _b.getState()[type]);
for (var key in target) {
var value = visualElement.getValue(key);
var valueTarget = target[key];
if (!value ||
valueTarget === undefined ||
(animationTypeState &&
shouldBlockAnimation(animationTypeState, key))) {
continue;
}
var valueTransition = tslib.__assign({ delay: delay }, transition);
/**
* Make animation instant if this is a transform prop and we should reduce motion.
*/
if (visualElement.shouldReduceMotion && isTransformProp(key)) {
valueTransition = tslib.__assign(tslib.__assign({}, valueTransition), { type: false, delay: 0 });
}
var animation = startAnimation(key, value, valueTarget, valueTransition);
animations.push(animation);
}
return Promise.all(animations).then(function () {
transitionEnd && setTarget(visualElement, transitionEnd);
});
}
function animateChildren(visualElement, variant, delayChildren, staggerChildren, staggerDirection, options) {
if (delayChildren === void 0) { delayChildren = 0; }
if (staggerChildren === void 0) { staggerChildren = 0; }
if (staggerDirection === void 0) { staggerDirection = 1; }
var animations = [];
var maxStaggerDuration = (visualElement.variantChildren.size - 1) * staggerChildren;
var generateStaggerDuration = staggerDirection === 1
? function (i) {
if (i === void 0) { i = 0; }
return i * staggerChildren;
}
: function (i) {
if (i === void 0) { i = 0; }
return maxStaggerDuration - i * staggerChildren;
};
Array.from(visualElement.variantChildren)
.sort(sortByTreeOrder)
.forEach(function (child, i) {
animations.push(animateVariant(child, variant, tslib.__assign(tslib.__assign({}, options), { delay: delayChildren + generateStaggerDuration(i) })).then(function () { return child.notifyAnimationComplete(variant); }));
});
return Promise.all(animations);
}
function stopAnimation(visualElement) {
visualElement.forEachValue(function (value) { return value.stop(); });
}
function sortByTreeOrder(a, b) {
return a.sortNodePosition(b);
}
/**
* Decide whether we should block this animation. Previously, we achieved this
* just by checking whether the key was listed in protectedKeys, but this
* posed problems if an animation was triggered by afterChildren and protectedKeys
* had been set to true in the meantime.
*/
function shouldBlockAnimation(_a, key) {
var protectedKeys = _a.protectedKeys, needsAnimating = _a.needsAnimating;
var shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true;
needsAnimating[key] = false;
return shouldBlock;
}
var variantPriorityOrder = [
exports.AnimationType.Animate,
exports.AnimationType.InView,
exports.AnimationType.Focus,
exports.AnimationType.Hover,
exports.AnimationType.Tap,
exports.AnimationType.Drag,
exports.AnimationType.Exit,
];
var reversePriorityOrder = tslib.__spreadArray([], tslib.__read(variantPriorityOrder), false).reverse();
var numAnimationTypes = variantPriorityOrder.length;
function animateList(visualElement) {
return function (animations) {
return Promise.all(animations.map(function (_a) {
var animation = _a.animation, options = _a.options;
return animateVisualElement(visualElement, animation, options);
}));
};
}
function createAnimationState(visualElement) {
var animate = animateList(visualElement);
var state = createState();
var allAnimatedKeys = {};
var isInitialRender = true;
/**
* This function will be used to reduce the animation definitions for
* each active animation type into an object of resolved values for it.
*/
var buildResolvedTypeValues = function (acc, definition) {
var resolved = resolveVariant(visualElement, definition);
if (resolved) {
resolved.transition; var transitionEnd = resolved.transitionEnd, target = tslib.__rest(resolved, ["transition", "transitionEnd"]);
acc = tslib.__assign(tslib.__assign(tslib.__assign({}, acc), target), transitionEnd);
}
return acc;
};
function isAnimated(key) {
return allAnimatedKeys[key] !== undefined;
}
/**
* This just allows us to inject mocked animation functions
* @internal
*/
function setAnimateFunction(makeAnimator) {
animate = makeAnimator(visualElement);
}
/**
* When we receive new props, we need to:
* 1. Create a list of protected keys for each type. This is a directory of
* value keys that are currently being "handled" by types of a higher priority
* so that whenever an animation is played of a given type, these values are
* protected from being animated.
* 2. Determine if an animation type needs animating.
* 3. Determine if any values have been removed from a type and figure out
* what to animate those to.
*/
function animateChanges(options, changedActiveType) {
var _a;
var props = visualElement.getProps();
var context = visualElement.getVariantContext(true) || {};
/**
* A list of animations that we'll build into as we iterate through the animation
* types. This will get executed at the end of the function.
*/
var animations = [];
/**
* Keep track of which values have been removed. Then, as we hit lower priority
* animation types, we can check if they contain removed values and animate to that.
*/
var removedKeys = new Set();
/**
* A dictionary of all encountered keys. This is an object to let us build into and
* copy it without iteration. Each time we hit an animation type we set its protected
* keys - the keys its not allowed to animate - to the latest version of this object.
*/
var encounteredKeys = {};
/**
* If a variant has been removed at a given index, and this component is controlling
* variant animations, we want to ensure lower-priority variants are forced to animate.
*/
var removedVariantIndex = Infinity;
var _loop_1 = function (i) {
var type = reversePriorityOrder[i];
var typeState = state[type];
var prop = (_a = props[type]) !== null && _a !== void 0 ? _a : context[type];
var propIsVariant = isVariantLabel(prop);
/**
* If this type has *just* changed isActive status, set activeDelta
* to that status. Otherwise set to null.
*/
var activeDelta = type === changedActiveType ? typeState.isActive : null;
if (activeDelta === false)
removedVariantIndex = i;
/**
* If this prop is an inherited variant, rather than been set directly on the
* component itself, we want to make sure we allow the parent to trigger animations.
*
* TODO: Can probably change this to a !isControllingVariants check
*/
var isInherited = prop === context[type] && prop !== props[type] && propIsVariant;
/**
*
*/
if (isInherited &&
isInitialRender &&
visualElement.manuallyAnimateOnMount) {
isInherited = false;
}
/**
* Set all encountered keys so far as the protected keys for this type. This will
* be any key that has been animated or otherwise handled by active, higher-priortiy types.
*/
typeState.protectedKeys = tslib.__assign({}, encounteredKeys);
// Check if we can skip analysing this prop early
if (
// If it isn't active and hasn't *just* been set as inactive
(!typeState.isActive && activeDelta === null) ||
// If we didn't and don't have any defined prop for this animation type
(!prop && !typeState.prevProp) ||
// Or if the prop doesn't define an animation
isAnimationControls(prop) ||
typeof prop === "boolean") {
return "continue";
}
/**
* As we go look through the values defined on this type, if we detect
* a changed value or a value that was removed in a higher priority, we set
* this to true and add this prop to the animation list.
*/
var variantDidChange = checkVariantsDidChange(typeState.prevProp, prop);
var shouldAnimateType = variantDidChange ||
// If we're making this variant active, we want to always make it active
(type === changedActiveType &&
typeState.isActive &&
!isInherited &&
propIsVariant) ||
// If we removed a higher-priority variant (i is in reverse order)
(i > removedVariantIndex && propIsVariant);
/**
* As animations can be set as variant lists, variants or target objects, we
* coerce everything to an array if it isn't one already
*/
var definitionList = Array.isArray(prop) ? prop : [prop];
/**
* Build an object of all the resolved values. We'll use this in the subsequent
* animateChanges calls to determine whether a value has changed.
*/
var resolvedValues = definitionList.reduce(buildResolvedTypeValues, {});
if (activeDelta === false)
resolvedValues = {};
/**
* Now we need to loop through all the keys in the prev prop and this prop,
* and decide:
* 1. If the value has changed, and needs animating
* 2. If it has been removed, and needs adding to the removedKeys set
* 3. If it has been removed in a higher priority type and needs animating
* 4. If it hasn't been removed in a higher priority but hasn't changed, and
* needs adding to the type's protectedKeys list.
*/
var _b = typeState.prevResolvedValues, prevResolvedValues = _b === void 0 ? {} : _b;
var allKeys = tslib.__assign(tslib.__assign({}, prevResolvedValues), resolvedValues);
var markToAnimate = function (key) {
shouldAnimateType = true;
removedKeys.delete(key);
typeState.needsAnimating[key] = true;
};
for (var key in allKeys) {
var next = resolvedValues[key];
var prev = prevResolvedValues[key];
// If we've already handled this we can just skip ahead
if (encounteredKeys.hasOwnProperty(key))
continue;
/**
* If the value has changed, we probably want to animate it.
*/
if (next !== prev) {
/**
* If both values are keyframes, we need to shallow compare them to
* detect whether any value has changed. If it has, we animate it.
*/
if (isKeyframesTarget(next) && isKeyframesTarget(prev)) {
if (!shallowCompare(next, prev) || variantDidChange) {
markToAnimate(key);
}
else {
/**
* If it hasn't changed, we want to ensure it doesn't animate by
* adding it to the list of protected keys.
*/
typeState.protectedKeys[key] = true;
}
}
else if (next !== undefined) {
// If next is defined and doesn't equal prev, it needs animating
markToAnimate(key);
}
else {
// If it's undefined, it's been removed.
removedKeys.add(key);
}
}
else if (next !== undefined && removedKeys.has(key)) {
/**
* If next hasn't changed and it isn't undefined, we want to check if it's
* been removed by a higher priority
*/
markToAnimate(key);
}
else {
/**
* If it hasn't changed, we add it to the list of protected values
* to ensure it doesn't get animated.
*/
typeState.protectedKeys[key] = true;
}
}
/**
* Update the typeState so next time animateChanges is called we can compare the
* latest prop and resolvedValues to these.
*/
typeState.prevProp = prop;
typeState.prevResolvedValues = resolvedValues;
/**
*
*/
if (typeState.isActive) {
encounteredKeys = tslib.__assign(tslib.__assign({}, encounteredKeys), resolvedValues);
}
if (isInitialRender && visualElement.blockInitialAnimation) {
shouldAnimateType = false;
}
/**
* If this is an inherited prop we want to hard-block animations
* TODO: Test as this should probably still handle animations triggered
* by removed values?
*/
if (shouldAnimateType && !isInherited) {
animations.push.apply(animations, tslib.__spreadArray([], tslib.__read(definitionList.map(function (animation) { return ({
animation: animation,
options: tslib.__assign({ type: type }, options),
}); })), false));
}
};
/**
* Iterate through all animation types in reverse priority order. For each, we want to
* detect which values it's handling and whether or not they've changed (and therefore
* need to be animated). If any values have been removed, we want to detect those in
* lower priority props and flag for animation.
*/
for (var i = 0; i < numAnimationTypes; i++) {
_loop_1(i);
}
allAnimatedKeys = tslib.__assign({}, encounteredKeys);
/**
* If there are some removed value that haven't been dealt with,
* we need to create a new animation that falls back either to the value
* defined in the style prop, or the last read value.
*/
if (removedKeys.size) {
var fallbackAnimation_1 = {};
removedKeys.forEach(function (key) {
var fallbackTarget = visualElement.getBaseTarget(key);
if (fallbackTarget !== undefined) {
fallbackAnimation_1[key] = fallbackTarget;
}
});
animations.push({ animation: fallbackAnimation_1 });
}
var shouldAnimate = Boolean(animations.length);
if (isInitialRender &&
props.initial === false &&
!visualElement.manuallyAnimateOnMount) {
shouldAnimate = false;
}
isInitialRender = false;
return shouldAnimate ? animate(animations) : Promise.resolve();
}
/**
* Change whether a certain animation type is active.
*/
function setActive(type, isActive, options) {
var _a;
// If the active state hasn't changed, we can safely do nothing here
if (state[type].isActive === isActive)
return Promise.resolve();
// Propagate active change to children
(_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.forEach(function (child) { var _a; return (_a = child.animationState) === null || _a === void 0 ? void 0 : _a.setActive(type, isActive); });
state[type].isActive = isActive;
var animations = animateChanges(options, type);
for (var key in state) {
state[key].protectedKeys = {};
}
return animations;
}
return {
isAnimated: isAnimated,
animateChanges: animateChanges,
setActive: setActive,
setAnimateFunction: setAnimateFunction,
getState: function () { return state; },
};
}
function checkVariantsDidChange(prev, next) {
if (typeof next === "string") {
return next !== prev;
}
else if (isVariantLabels(next)) {
return !shallowCompare(next, prev);
}
return false;
}
function createTypeState(isActive) {
if (isActive === void 0) { isActive = false; }
return {
isActive: isActive,
protectedKeys: {},
needsAnimating: {},
prevResolvedValues: {},
};
}
function createState() {
var _a;
return _a = {},
_a[exports.AnimationType.Animate] = createTypeState(true),
_a[exports.AnimationType.InView] = createTypeState(),
_a[exports.AnimationType.Hover] = createTypeState(),
_a[exports.AnimationType.Tap] = createTypeState(),
_a[exports.AnimationType.Drag] = createTypeState(),
_a[exports.AnimationType.Focus] = createTypeState(),
_a[exports.AnimationType.Exit] = createTypeState(),
_a;
}
var animations = {
animation: makeRenderlessComponent(function (_a) {
var visualElement = _a.visualElement, animate = _a.animate;
/**
* We dynamically generate the AnimationState manager as it contains a reference
* to the underlying animation library. We only want to load that if we load this,
* so people can optionally code split it out using the `m` component.
*/
visualElement.animationState || (visualElement.animationState = createAnimationState(visualElement));
/**
* Subscribe any provided AnimationControls to the component's VisualElement
*/
if (isAnimationControls(animate)) {
React.useEffect(function () { return animate.subscribe(visualElement); }, [animate]);
}
}),
exit: makeRenderlessComponent(function (props) {
var custom = props.custom, visualElement = props.visualElement;
var _a = tslib.__read(usePresence(), 2), isPresent = _a[0], safeToRemove = _a[1];
var presenceContext = React.useContext(PresenceContext);
React.useEffect(function () {
var _a, _b;
visualElement.isPresent = isPresent;
var animation = (_a = visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(exports.AnimationType.Exit, !isPresent, { custom: (_b = presenceContext === null || presenceContext === void 0 ? void 0 : presenceContext.custom) !== null && _b !== void 0 ? _b : custom });
!isPresent && (animation === null || animation === void 0 ? void 0 : animation.then(safeToRemove));
}, [isPresent]);
}),
};
/**
* @internal
*/
var PanSession = /** @class */ (function () {
function PanSession(event, handlers, _a) {
var _this = this;
var _b = _a === void 0 ? {} : _a, transformPagePoint = _b.transformPagePoint;
/**
* @internal
*/
this.startEvent = null;
/**
* @internal
*/
this.lastMoveEvent = null;
/**
* @internal
*/
this.lastMoveEventInfo = null;
/**
* @internal
*/
this.handlers = {};
this.updatePoint = function () {
if (!(_this.lastMoveEvent && _this.lastMoveEventInfo))
return;
var info = getPanInfo(_this.lastMoveEventInfo, _this.history);
var isPanStarted = _this.startEvent !== null;
// Only start panning if the offset is larger than 3 pixels. If we make it
// any larger than this we'll want to reset the pointer history
// on the first update to avoid visual snapping to the cursoe.
var isDistancePastThreshold = popmotion.distance(info.offset, { x: 0, y: 0 }) >= 3;
if (!isPanStarted && !isDistancePastThreshold)
return;
var point = info.point;
var timestamp = sync.getFrameData().timestamp;
_this.history.push(tslib.__assign(tslib.__assign({}, point), { timestamp: timestamp }));
var _a = _this.handlers, onStart = _a.onStart, onMove = _a.onMove;
if (!isPanStarted) {
onStart && onStart(_this.lastMoveEvent, info);
_this.startEvent = _this.lastMoveEvent;
}
onMove && onMove(_this.lastMoveEvent, info);
};
this.handlePointerMove = function (event, info) {
_this.lastMoveEvent = event;
_this.lastMoveEventInfo = transformPoint(info, _this.transformPagePoint);
// Because Safari doesn't trigger mouseup events when it's above a `