 85bf1341f3
			
		
	
	85bf1341f3
	
	
	
		
			
			Frontend Enhancements: - Complete React TypeScript frontend with modern UI components - Distributed workflows management interface with real-time updates - Socket.IO integration for live agent status monitoring - Agent management dashboard with cluster visualization - Project management interface with metrics and task tracking - Responsive design with proper error handling and loading states Backend Infrastructure: - Distributed coordinator for multi-agent workflow orchestration - Cluster management API with comprehensive agent operations - Enhanced database models for agents and projects - Project service for filesystem-based project discovery - Performance monitoring and metrics collection - Comprehensive API documentation and error handling Documentation: - Complete distributed development guide (README_DISTRIBUTED.md) - Comprehensive development report with architecture insights - System configuration templates and deployment guides The platform now provides a complete web interface for managing the distributed AI cluster with real-time monitoring, workflow orchestration, and agent coordination capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			5718 lines
		
	
	
		
			194 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			5718 lines
		
	
	
		
			194 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const isBrowser = typeof document !== "undefined";
 | |
| 
 | |
| /**
 | |
|  * Convert camelCase to dash-case properties.
 | |
|  */
 | |
| const camelToDash = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
 | |
| 
 | |
| const optimizedAppearDataId = "framerAppearId";
 | |
| const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
 | |
| 
 | |
| function isRefObject(ref) {
 | |
|     return (ref &&
 | |
|         typeof ref === "object" &&
 | |
|         Object.prototype.hasOwnProperty.call(ref, "current"));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Decides if the supplied variable is variant label
 | |
|  */
 | |
| function isVariantLabel(v) {
 | |
|     return typeof v === "string" || Array.isArray(v);
 | |
| }
 | |
| 
 | |
| function isAnimationControls(v) {
 | |
|     return (v !== null &&
 | |
|         typeof v === "object" &&
 | |
|         typeof v.start === "function");
 | |
| }
 | |
| 
 | |
| const variantPriorityOrder = [
 | |
|     "animate",
 | |
|     "whileInView",
 | |
|     "whileFocus",
 | |
|     "whileHover",
 | |
|     "whileTap",
 | |
|     "whileDrag",
 | |
|     "exit",
 | |
| ];
 | |
| const variantProps = ["initial", ...variantPriorityOrder];
 | |
| 
 | |
| function isControllingVariants(props) {
 | |
|     return (isAnimationControls(props.animate) ||
 | |
|         variantProps.some((name) => isVariantLabel(props[name])));
 | |
| }
 | |
| function isVariantNode(props) {
 | |
|     return Boolean(isControllingVariants(props) || props.variants);
 | |
| }
 | |
| 
 | |
| const featureProps = {
 | |
|     animation: [
 | |
|         "animate",
 | |
|         "variants",
 | |
|         "whileHover",
 | |
|         "whileTap",
 | |
|         "exit",
 | |
|         "whileInView",
 | |
|         "whileFocus",
 | |
|         "whileDrag",
 | |
|     ],
 | |
|     exit: ["exit"],
 | |
|     drag: ["drag", "dragControls"],
 | |
|     focus: ["whileFocus"],
 | |
|     hover: ["whileHover", "onHoverStart", "onHoverEnd"],
 | |
|     tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"],
 | |
|     pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"],
 | |
|     inView: ["whileInView", "onViewportEnter", "onViewportLeave"],
 | |
|     layout: ["layout", "layoutId"],
 | |
| };
 | |
| const featureDefinitions = {};
 | |
| for (const key in featureProps) {
 | |
|     featureDefinitions[key] = {
 | |
|         isEnabled: (props) => featureProps[key].some((name) => !!props[name]),
 | |
|     };
 | |
| }
 | |
| 
 | |
| const scaleCorrectors = {};
 | |
| function addScaleCorrector(correctors) {
 | |
|     Object.assign(scaleCorrectors, correctors);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Generate a list of every possible transform key.
 | |
|  */
 | |
| const transformPropOrder = [
 | |
|     "transformPerspective",
 | |
|     "x",
 | |
|     "y",
 | |
|     "z",
 | |
|     "translateX",
 | |
|     "translateY",
 | |
|     "translateZ",
 | |
|     "scale",
 | |
|     "scaleX",
 | |
|     "scaleY",
 | |
|     "rotate",
 | |
|     "rotateX",
 | |
|     "rotateY",
 | |
|     "rotateZ",
 | |
|     "skew",
 | |
|     "skewX",
 | |
|     "skewY",
 | |
| ];
 | |
| /**
 | |
|  * A quick lookup for transform props.
 | |
|  */
 | |
| const transformProps = new Set(transformPropOrder);
 | |
| 
 | |
| function isForcedMotionValue(key, { layout, layoutId }) {
 | |
|     return (transformProps.has(key) ||
 | |
|         key.startsWith("origin") ||
 | |
|         ((layout || layoutId !== undefined) &&
 | |
|             (!!scaleCorrectors[key] || key === "opacity")));
 | |
| }
 | |
| 
 | |
| const isMotionValue = (value) => Boolean(value && value.getVelocity);
 | |
| 
 | |
| const translateAlias = {
 | |
|     x: "translateX",
 | |
|     y: "translateY",
 | |
|     z: "translateZ",
 | |
|     transformPerspective: "perspective",
 | |
| };
 | |
| const numTransforms = transformPropOrder.length;
 | |
| /**
 | |
|  * 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(transform, { enableHardwareAcceleration = true, allowTransformNone = true, }, transformIsDefault, transformTemplate) {
 | |
|     // The transform string we're going to build into.
 | |
|     let transformString = "";
 | |
|     /**
 | |
|      * Loop over all possible transforms in order, adding the ones that
 | |
|      * are present to the transform string.
 | |
|      */
 | |
|     for (let i = 0; i < numTransforms; i++) {
 | |
|         const key = transformPropOrder[i];
 | |
|         if (transform[key] !== undefined) {
 | |
|             const transformName = translateAlias[key] || key;
 | |
|             transformString += `${transformName}(${transform[key]}) `;
 | |
|         }
 | |
|     }
 | |
|     if (enableHardwareAcceleration && !transform.z) {
 | |
|         transformString += "translateZ(0)";
 | |
|     }
 | |
|     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;
 | |
| }
 | |
| 
 | |
| const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token);
 | |
| const isCSSVariableName = checkStringStartsWith("--");
 | |
| const isCSSVariableToken = checkStringStartsWith("var(--");
 | |
| const cssVariableRegex = /var\s*\(\s*--[\w-]+(\s*,\s*(?:(?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)+)?\s*\)/g;
 | |
| 
 | |
| /**
 | |
|  * Provided a value and a ValueType, returns the value as that value type.
 | |
|  */
 | |
| const getValueAsType = (value, type) => {
 | |
|     return type && typeof value === "number"
 | |
|         ? type.transform(value)
 | |
|         : value;
 | |
| };
 | |
| 
 | |
| const clamp = (min, max, v) => Math.min(Math.max(v, min), max);
 | |
| 
 | |
| const number = {
 | |
|     test: (v) => typeof v === "number",
 | |
|     parse: parseFloat,
 | |
|     transform: (v) => v,
 | |
| };
 | |
| const alpha = {
 | |
|     ...number,
 | |
|     transform: (v) => clamp(0, 1, v),
 | |
| };
 | |
| const scale = {
 | |
|     ...number,
 | |
|     default: 1,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * TODO: When we move from string as a source of truth to data models
 | |
|  * everything in this folder should probably be referred to as models vs types
 | |
|  */
 | |
| // If this number is a decimal, make it just five decimal places
 | |
| // to avoid exponents
 | |
| const sanitize = (v) => Math.round(v * 100000) / 100000;
 | |
| const floatRegex = /(-)?([\d]*\.?[\d])+/g;
 | |
| const colorRegex = /(#[0-9a-f]{3,8}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2}(-?[\d\.]+%?)\s*[\,\/]?\s*[\d\.]*%?\))/gi;
 | |
| const singleColorRegex = /^(#[0-9a-f]{3,8}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2}(-?[\d\.]+%?)\s*[\,\/]?\s*[\d\.]*%?\))$/i;
 | |
| function isString(v) {
 | |
|     return typeof v === "string";
 | |
| }
 | |
| 
 | |
| const createUnitType = (unit) => ({
 | |
|     test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
 | |
|     parse: parseFloat,
 | |
|     transform: (v) => `${v}${unit}`,
 | |
| });
 | |
| const degrees = createUnitType("deg");
 | |
| const percent = createUnitType("%");
 | |
| const px = createUnitType("px");
 | |
| const vh = createUnitType("vh");
 | |
| const vw = createUnitType("vw");
 | |
| const progressPercentage = {
 | |
|     ...percent,
 | |
|     parse: (v) => percent.parse(v) / 100,
 | |
|     transform: (v) => percent.transform(v * 100),
 | |
| };
 | |
| 
 | |
| const int = {
 | |
|     ...number,
 | |
|     transform: Math.round,
 | |
| };
 | |
| 
 | |
| const numberValueTypes = {
 | |
|     // Border props
 | |
|     borderWidth: px,
 | |
|     borderTopWidth: px,
 | |
|     borderRightWidth: px,
 | |
|     borderBottomWidth: px,
 | |
|     borderLeftWidth: px,
 | |
|     borderRadius: px,
 | |
|     radius: px,
 | |
|     borderTopLeftRadius: px,
 | |
|     borderTopRightRadius: px,
 | |
|     borderBottomRightRadius: px,
 | |
|     borderBottomLeftRadius: px,
 | |
|     // Positioning props
 | |
|     width: px,
 | |
|     maxWidth: px,
 | |
|     height: px,
 | |
|     maxHeight: px,
 | |
|     size: px,
 | |
|     top: px,
 | |
|     right: px,
 | |
|     bottom: px,
 | |
|     left: px,
 | |
|     // Spacing props
 | |
|     padding: px,
 | |
|     paddingTop: px,
 | |
|     paddingRight: px,
 | |
|     paddingBottom: px,
 | |
|     paddingLeft: px,
 | |
|     margin: px,
 | |
|     marginTop: px,
 | |
|     marginRight: px,
 | |
|     marginBottom: px,
 | |
|     marginLeft: px,
 | |
|     // Transform props
 | |
|     rotate: degrees,
 | |
|     rotateX: degrees,
 | |
|     rotateY: degrees,
 | |
|     rotateZ: degrees,
 | |
|     scale,
 | |
|     scaleX: scale,
 | |
|     scaleY: scale,
 | |
|     scaleZ: scale,
 | |
|     skew: degrees,
 | |
|     skewX: degrees,
 | |
|     skewY: degrees,
 | |
|     distance: px,
 | |
|     translateX: px,
 | |
|     translateY: px,
 | |
|     translateZ: px,
 | |
|     x: px,
 | |
|     y: px,
 | |
|     z: px,
 | |
|     perspective: px,
 | |
|     transformPerspective: px,
 | |
|     opacity: alpha,
 | |
|     originX: progressPercentage,
 | |
|     originY: progressPercentage,
 | |
|     originZ: px,
 | |
|     // Misc
 | |
|     zIndex: int,
 | |
|     // SVG
 | |
|     fillOpacity: alpha,
 | |
|     strokeOpacity: alpha,
 | |
|     numOctaves: int,
 | |
| };
 | |
| 
 | |
| function buildHTMLStyles(state, latestValues, options, transformTemplate) {
 | |
|     const { style, vars, transform, transformOrigin } = state;
 | |
|     // Track whether we encounter any transform or transformOrigin values.
 | |
|     let hasTransform = false;
 | |
|     let hasTransformOrigin = false;
 | |
|     // Does the calculated transform essentially equal "none"?
 | |
|     let 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 (const key in latestValues) {
 | |
|         const value = latestValues[key];
 | |
|         /**
 | |
|          * If this is a CSS variable we don't do any further processing.
 | |
|          */
 | |
|         if (isCSSVariableName(key)) {
 | |
|             vars[key] = value;
 | |
|             continue;
 | |
|         }
 | |
|         // Convert the value to its default value type, ie 0 -> "0px"
 | |
|         const valueType = numberValueTypes[key];
 | |
|         const valueAsType = getValueAsType(value, valueType);
 | |
|         if (transformProps.has(key)) {
 | |
|             // If this is a transform, flag to enable further transform processing
 | |
|             hasTransform = true;
 | |
|             transform[key] = valueAsType;
 | |
|             // 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 !== (valueType.default || 0))
 | |
|                 transformIsNone = false;
 | |
|         }
 | |
|         else if (key.startsWith("origin")) {
 | |
|             // If this is a transform origin, flag and enable further transform-origin processing
 | |
|             hasTransformOrigin = true;
 | |
|             transformOrigin[key] = valueAsType;
 | |
|         }
 | |
|         else {
 | |
|             style[key] = valueAsType;
 | |
|         }
 | |
|     }
 | |
|     if (!latestValues.transform) {
 | |
|         if (hasTransform || transformTemplate) {
 | |
|             style.transform = buildTransform(state.transform, options, transformIsNone, transformTemplate);
 | |
|         }
 | |
|         else if (style.transform) {
 | |
|             /**
 | |
|              * If we have previously created a transform but currently don't have any,
 | |
|              * reset transform style to none.
 | |
|              */
 | |
|             style.transform = "none";
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Build a transformOrigin style. Uses the same defaults as the browser for
 | |
|      * undefined origins.
 | |
|      */
 | |
|     if (hasTransformOrigin) {
 | |
|         const { originX = "50%", originY = "50%", originZ = 0, } = transformOrigin;
 | |
|         style.transformOrigin = `${originX} ${originY} ${originZ}`;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function calcOrigin(origin, offset, size) {
 | |
|     return typeof origin === "string"
 | |
|         ? origin
 | |
|         : 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) {
 | |
|     const pxOriginX = calcOrigin(originX, dimensions.x, dimensions.width);
 | |
|     const pxOriginY = calcOrigin(originY, dimensions.y, dimensions.height);
 | |
|     return `${pxOriginX} ${pxOriginY}`;
 | |
| }
 | |
| 
 | |
| const dashKeys = {
 | |
|     offset: "stroke-dashoffset",
 | |
|     array: "stroke-dasharray",
 | |
| };
 | |
| const 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 = 1, offset = 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.
 | |
|     const keys = useDashCase ? dashKeys : camelKeys;
 | |
|     // Build the dash offset
 | |
|     attrs[keys.offset] = px.transform(-offset);
 | |
|     // Build the dash array
 | |
|     const pathLength = px.transform(length);
 | |
|     const pathSpacing = px.transform(spacing);
 | |
|     attrs[keys.array] = `${pathLength} ${pathSpacing}`;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Build SVG visual attrbutes, like cx and style.transform
 | |
|  */
 | |
| function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0, 
 | |
| // This is object creation, which we try to avoid per-frame.
 | |
| ...latest }, options, isSVGTag, transformTemplate) {
 | |
|     buildHTMLStyles(state, latest, options, transformTemplate);
 | |
|     /**
 | |
|      * For svg tags we just want to make sure viewBox is animatable and treat all the styles
 | |
|      * as normal HTML tags.
 | |
|      */
 | |
|     if (isSVGTag) {
 | |
|         if (state.style.viewBox) {
 | |
|             state.attrs.viewBox = state.style.viewBox;
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     state.attrs = state.style;
 | |
|     state.style = {};
 | |
|     const { attrs, style, dimensions } = state;
 | |
|     /**
 | |
|      * 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);
 | |
|     }
 | |
|     // Render attrX/attrY/attrScale as attributes
 | |
|     if (attrX !== undefined)
 | |
|         attrs.x = attrX;
 | |
|     if (attrY !== undefined)
 | |
|         attrs.y = attrY;
 | |
|     if (attrScale !== undefined)
 | |
|         attrs.scale = attrScale;
 | |
|     // Build SVG path if one has been defined
 | |
|     if (pathLength !== undefined) {
 | |
|         buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false);
 | |
|     }
 | |
| }
 | |
| 
 | |
| const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg";
 | |
| 
 | |
| function renderHTML(element, { style, vars }, styleProp, projection) {
 | |
|     Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
 | |
|     // Loop over any CSS variables and assign those.
 | |
|     for (const key in vars) {
 | |
|         element.style.setProperty(key, vars[key]);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A set of attribute names that are always read/written as camel case.
 | |
|  */
 | |
| const camelCaseAttributes = new Set([
 | |
|     "baseFrequency",
 | |
|     "diffuseConstant",
 | |
|     "kernelMatrix",
 | |
|     "kernelUnitLength",
 | |
|     "keySplines",
 | |
|     "keyTimes",
 | |
|     "limitingConeAngle",
 | |
|     "markerHeight",
 | |
|     "markerWidth",
 | |
|     "numOctaves",
 | |
|     "targetX",
 | |
|     "targetY",
 | |
|     "surfaceScale",
 | |
|     "specularConstant",
 | |
|     "specularExponent",
 | |
|     "stdDeviation",
 | |
|     "tableValues",
 | |
|     "viewBox",
 | |
|     "gradientTransform",
 | |
|     "pathLength",
 | |
|     "startOffset",
 | |
|     "textLength",
 | |
|     "lengthAdjust",
 | |
| ]);
 | |
| 
 | |
| function renderSVG(element, renderState, _styleProp, projection) {
 | |
|     renderHTML(element, renderState, undefined, projection);
 | |
|     for (const key in renderState.attrs) {
 | |
|         element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function scrapeMotionValuesFromProps$1(props, prevProps) {
 | |
|     const { style } = props;
 | |
|     const newValues = {};
 | |
|     for (const key in style) {
 | |
|         if (isMotionValue(style[key]) ||
 | |
|             (prevProps.style && isMotionValue(prevProps.style[key])) ||
 | |
|             isForcedMotionValue(key, props)) {
 | |
|             newValues[key] = style[key];
 | |
|         }
 | |
|     }
 | |
|     return newValues;
 | |
| }
 | |
| 
 | |
| function scrapeMotionValuesFromProps(props, prevProps) {
 | |
|     const newValues = scrapeMotionValuesFromProps$1(props, prevProps);
 | |
|     for (const key in props) {
 | |
|         if (isMotionValue(props[key]) || isMotionValue(prevProps[key])) {
 | |
|             const targetKey = transformPropOrder.indexOf(key) !== -1
 | |
|                 ? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
 | |
|                 : key;
 | |
|             newValues[targetKey] = props[key];
 | |
|         }
 | |
|     }
 | |
|     return newValues;
 | |
| }
 | |
| 
 | |
| function resolveVariantFromProps(props, definition, custom, currentValues = {}, currentVelocity = {}) {
 | |
|     /**
 | |
|      * If the variant definition is a function, resolve.
 | |
|      */
 | |
|     if (typeof definition === "function") {
 | |
|         definition = definition(custom !== undefined ? 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 = props.variants && props.variants[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 !== undefined ? custom : props.custom, currentValues, currentVelocity);
 | |
|     }
 | |
|     return definition;
 | |
| }
 | |
| 
 | |
| const isKeyframesTarget = (v) => {
 | |
|     return Array.isArray(v);
 | |
| };
 | |
| 
 | |
| const isCustomValue = (v) => {
 | |
|     return Boolean(v && typeof v === "object" && v.mix && v.toValue);
 | |
| };
 | |
| const resolveFinalValueInKeyframes = (v) => {
 | |
|     // TODO maybe throw if v.length - 1 is placeholder token?
 | |
|     return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
 | |
| };
 | |
| 
 | |
| const noop = (any) => any;
 | |
| 
 | |
| class Queue {
 | |
|     constructor() {
 | |
|         this.order = [];
 | |
|         this.scheduled = new Set();
 | |
|     }
 | |
|     add(process) {
 | |
|         if (!this.scheduled.has(process)) {
 | |
|             this.scheduled.add(process);
 | |
|             this.order.push(process);
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
|     remove(process) {
 | |
|         const index = this.order.indexOf(process);
 | |
|         if (index !== -1) {
 | |
|             this.order.splice(index, 1);
 | |
|             this.scheduled.delete(process);
 | |
|         }
 | |
|     }
 | |
|     clear() {
 | |
|         this.order.length = 0;
 | |
|         this.scheduled.clear();
 | |
|     }
 | |
| }
 | |
| function createRenderStep(runNextFrame) {
 | |
|     /**
 | |
|      * We create and reuse two queues, one to queue jobs for the current frame
 | |
|      * and one for the next. We reuse to avoid triggering GC after x frames.
 | |
|      */
 | |
|     let thisFrame = new Queue();
 | |
|     let nextFrame = new Queue();
 | |
|     let numToRun = 0;
 | |
|     /**
 | |
|      * Track whether we're currently processing jobs in this step. This way
 | |
|      * we can decide whether to schedule new jobs for this frame or next.
 | |
|      */
 | |
|     let isProcessing = false;
 | |
|     let flushNextFrame = false;
 | |
|     /**
 | |
|      * A set of processes which were marked keepAlive when scheduled.
 | |
|      */
 | |
|     const toKeepAlive = new WeakSet();
 | |
|     const step = {
 | |
|         /**
 | |
|          * Schedule a process to run on the next frame.
 | |
|          */
 | |
|         schedule: (callback, keepAlive = false, immediate = false) => {
 | |
|             const addToCurrentFrame = immediate && isProcessing;
 | |
|             const queue = addToCurrentFrame ? thisFrame : nextFrame;
 | |
|             if (keepAlive)
 | |
|                 toKeepAlive.add(callback);
 | |
|             if (queue.add(callback) && addToCurrentFrame && isProcessing) {
 | |
|                 // If we're adding it to the currently running queue, update its measured size
 | |
|                 numToRun = thisFrame.order.length;
 | |
|             }
 | |
|             return callback;
 | |
|         },
 | |
|         /**
 | |
|          * Cancel the provided callback from running on the next frame.
 | |
|          */
 | |
|         cancel: (callback) => {
 | |
|             nextFrame.remove(callback);
 | |
|             toKeepAlive.delete(callback);
 | |
|         },
 | |
|         /**
 | |
|          * Execute all schedule callbacks.
 | |
|          */
 | |
|         process: (frameData) => {
 | |
|             /**
 | |
|              * If we're already processing we've probably been triggered by a flushSync
 | |
|              * inside an existing process. Instead of executing, mark flushNextFrame
 | |
|              * as true and ensure we flush the following frame at the end of this one.
 | |
|              */
 | |
|             if (isProcessing) {
 | |
|                 flushNextFrame = true;
 | |
|                 return;
 | |
|             }
 | |
|             isProcessing = true;
 | |
|             [thisFrame, nextFrame] = [nextFrame, thisFrame];
 | |
|             // Clear the next frame queue
 | |
|             nextFrame.clear();
 | |
|             // Execute this frame
 | |
|             numToRun = thisFrame.order.length;
 | |
|             if (numToRun) {
 | |
|                 for (let i = 0; i < numToRun; i++) {
 | |
|                     const callback = thisFrame.order[i];
 | |
|                     callback(frameData);
 | |
|                     if (toKeepAlive.has(callback)) {
 | |
|                         step.schedule(callback);
 | |
|                         runNextFrame();
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             isProcessing = false;
 | |
|             if (flushNextFrame) {
 | |
|                 flushNextFrame = false;
 | |
|                 step.process(frameData);
 | |
|             }
 | |
|         },
 | |
|     };
 | |
|     return step;
 | |
| }
 | |
| 
 | |
| const stepsOrder = [
 | |
|     "prepare",
 | |
|     "read",
 | |
|     "update",
 | |
|     "preRender",
 | |
|     "render",
 | |
|     "postRender",
 | |
| ];
 | |
| const maxElapsed$1 = 40;
 | |
| function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
 | |
|     let runNextFrame = false;
 | |
|     let useDefaultElapsed = true;
 | |
|     const state = {
 | |
|         delta: 0,
 | |
|         timestamp: 0,
 | |
|         isProcessing: false,
 | |
|     };
 | |
|     const steps = stepsOrder.reduce((acc, key) => {
 | |
|         acc[key] = createRenderStep(() => (runNextFrame = true));
 | |
|         return acc;
 | |
|     }, {});
 | |
|     const processStep = (stepId) => steps[stepId].process(state);
 | |
|     const processBatch = () => {
 | |
|         const timestamp = performance.now();
 | |
|         runNextFrame = false;
 | |
|         state.delta = useDefaultElapsed
 | |
|             ? 1000 / 60
 | |
|             : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
 | |
|         state.timestamp = timestamp;
 | |
|         state.isProcessing = true;
 | |
|         stepsOrder.forEach(processStep);
 | |
|         state.isProcessing = false;
 | |
|         if (runNextFrame && allowKeepAlive) {
 | |
|             useDefaultElapsed = false;
 | |
|             scheduleNextBatch(processBatch);
 | |
|         }
 | |
|     };
 | |
|     const wake = () => {
 | |
|         runNextFrame = true;
 | |
|         useDefaultElapsed = true;
 | |
|         if (!state.isProcessing) {
 | |
|             scheduleNextBatch(processBatch);
 | |
|         }
 | |
|     };
 | |
|     const schedule = stepsOrder.reduce((acc, key) => {
 | |
|         const step = steps[key];
 | |
|         acc[key] = (process, keepAlive = false, immediate = false) => {
 | |
|             if (!runNextFrame)
 | |
|                 wake();
 | |
|             return step.schedule(process, keepAlive, immediate);
 | |
|         };
 | |
|         return acc;
 | |
|     }, {});
 | |
|     const cancel = (process) => stepsOrder.forEach((key) => steps[key].cancel(process));
 | |
|     return { schedule, cancel, state, steps };
 | |
| }
 | |
| 
 | |
| const { schedule: frame, cancel: cancelFrame, state: frameData, steps, } = createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
 | |
| 
 | |
| /**
 | |
|  * Pipe
 | |
|  * Compose other transformers to run linearily
 | |
|  * pipe(min(20), max(40))
 | |
|  * @param  {...functions} transformers
 | |
|  * @return {function}
 | |
|  */
 | |
| const combineFunctions = (a, b) => (v) => b(a(v));
 | |
| const pipe = (...transformers) => transformers.reduce(combineFunctions);
 | |
| 
 | |
| /**
 | |
|  * Creates an object containing the latest state of every MotionValue on a VisualElement
 | |
|  */
 | |
| function getCurrent(visualElement) {
 | |
|     const current = {};
 | |
|     visualElement.values.forEach((value, key) => (current[key] = value.get()));
 | |
|     return current;
 | |
| }
 | |
| /**
 | |
|  * Creates an object containing the latest velocity of every MotionValue on a VisualElement
 | |
|  */
 | |
| function getVelocity(visualElement) {
 | |
|     const velocity = {};
 | |
|     visualElement.values.forEach((value, key) => (velocity[key] = value.getVelocity()));
 | |
|     return velocity;
 | |
| }
 | |
| function resolveVariant(visualElement, definition, custom) {
 | |
|     const props = visualElement.getProps();
 | |
|     return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, getCurrent(visualElement), getVelocity(visualElement));
 | |
| }
 | |
| 
 | |
| exports.warning = noop;
 | |
| exports.invariant = noop;
 | |
| if (process.env.NODE_ENV !== "production") {
 | |
|     exports.warning = (check, message) => {
 | |
|         if (!check && typeof console !== "undefined") {
 | |
|             console.warn(message);
 | |
|         }
 | |
|     };
 | |
|     exports.invariant = (check, message) => {
 | |
|         if (!check) {
 | |
|             throw new Error(message);
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Converts seconds to milliseconds
 | |
|  *
 | |
|  * @param seconds - Time in seconds.
 | |
|  * @return milliseconds - Converted time in milliseconds.
 | |
|  */
 | |
| const secondsToMilliseconds = (seconds) => seconds * 1000;
 | |
| const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
 | |
| 
 | |
| const instantAnimationState = {
 | |
|     current: false,
 | |
| };
 | |
| 
 | |
| const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
 | |
| 
 | |
| function isWaapiSupportedEasing(easing) {
 | |
|     return Boolean(!easing ||
 | |
|         (typeof easing === "string" && supportedWaapiEasing[easing]) ||
 | |
|         isBezierDefinition(easing) ||
 | |
|         (Array.isArray(easing) && easing.every(isWaapiSupportedEasing)));
 | |
| }
 | |
| const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
 | |
| const supportedWaapiEasing = {
 | |
|     linear: "linear",
 | |
|     ease: "ease",
 | |
|     easeIn: "ease-in",
 | |
|     easeOut: "ease-out",
 | |
|     easeInOut: "ease-in-out",
 | |
|     circIn: cubicBezierAsString([0, 0.65, 0.55, 1]),
 | |
|     circOut: cubicBezierAsString([0.55, 0, 1, 0.45]),
 | |
|     backIn: cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
 | |
|     backOut: cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
 | |
| };
 | |
| function mapEasingToNativeEasing(easing) {
 | |
|     if (!easing)
 | |
|         return undefined;
 | |
|     return isBezierDefinition(easing)
 | |
|         ? cubicBezierAsString(easing)
 | |
|         : Array.isArray(easing)
 | |
|             ? easing.map(mapEasingToNativeEasing)
 | |
|             : supportedWaapiEasing[easing];
 | |
| }
 | |
| 
 | |
| function animateStyle(element, valueName, keyframes, { delay = 0, duration, repeat = 0, repeatType = "loop", ease, times, } = {}) {
 | |
|     const keyframeOptions = { [valueName]: keyframes };
 | |
|     if (times)
 | |
|         keyframeOptions.offset = times;
 | |
|     const easing = mapEasingToNativeEasing(ease);
 | |
|     /**
 | |
|      * If this is an easing array, apply to keyframes, not animation as a whole
 | |
|      */
 | |
|     if (Array.isArray(easing))
 | |
|         keyframeOptions.easing = easing;
 | |
|     return element.animate(keyframeOptions, {
 | |
|         delay,
 | |
|         duration,
 | |
|         easing: !Array.isArray(easing) ? easing : "linear",
 | |
|         fill: "both",
 | |
|         iterations: repeat + 1,
 | |
|         direction: repeatType === "reverse" ? "alternate" : "normal",
 | |
|     });
 | |
| }
 | |
| 
 | |
| function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }) {
 | |
|     const index = repeat && repeatType !== "loop" && repeat % 2 === 1
 | |
|         ? 0
 | |
|         : keyframes.length - 1;
 | |
|     return keyframes[index];
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Bezier function generator
 | |
|   This has been modified from Gaëtan Renaudeau's BezierEasing
 | |
|   https://github.com/gre/bezier-easing/blob/master/src/index.js
 | |
|   https://github.com/gre/bezier-easing/blob/master/LICENSE
 | |
|   
 | |
|   I've removed the newtonRaphsonIterate algo because in benchmarking it
 | |
|   wasn't noticiably faster than binarySubdivision, indeed removing it
 | |
|   usually improved times, depending on the curve.
 | |
|   I also removed the lookup table, as for the added bundle size and loop we're
 | |
|   only cutting ~4 or so subdivision iterations. I bumped the max iterations up
 | |
|   to 12 to compensate and this still tended to be faster for no perceivable
 | |
|   loss in accuracy.
 | |
|   Usage
 | |
|     const easeOut = cubicBezier(.17,.67,.83,.67);
 | |
|     const x = easeOut(0.5); // returns 0.627...
 | |
| */
 | |
| // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
 | |
| const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
 | |
|     t;
 | |
| const subdivisionPrecision = 0.0000001;
 | |
| const subdivisionMaxIterations = 12;
 | |
| function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
 | |
|     let currentX;
 | |
|     let currentT;
 | |
|     let i = 0;
 | |
|     do {
 | |
|         currentT = lowerBound + (upperBound - lowerBound) / 2.0;
 | |
|         currentX = calcBezier(currentT, mX1, mX2) - x;
 | |
|         if (currentX > 0.0) {
 | |
|             upperBound = currentT;
 | |
|         }
 | |
|         else {
 | |
|             lowerBound = currentT;
 | |
|         }
 | |
|     } while (Math.abs(currentX) > subdivisionPrecision &&
 | |
|         ++i < subdivisionMaxIterations);
 | |
|     return currentT;
 | |
| }
 | |
| function cubicBezier(mX1, mY1, mX2, mY2) {
 | |
|     // If this is a linear gradient, return linear easing
 | |
|     if (mX1 === mY1 && mX2 === mY2)
 | |
|         return noop;
 | |
|     const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
 | |
|     // If animation is at start/end, return t without easing
 | |
|     return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
 | |
| }
 | |
| 
 | |
| const easeIn = cubicBezier(0.42, 0, 1, 1);
 | |
| const easeOut = cubicBezier(0, 0, 0.58, 1);
 | |
| const easeInOut = cubicBezier(0.42, 0, 0.58, 1);
 | |
| 
 | |
| const isEasingArray = (ease) => {
 | |
|     return Array.isArray(ease) && typeof ease[0] !== "number";
 | |
| };
 | |
| 
 | |
| // Accepts an easing function and returns a new one that outputs mirrored values for
 | |
| // the second half of the animation. Turns easeIn into easeInOut.
 | |
| const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
 | |
| 
 | |
| // Accepts an easing function and returns a new one that outputs reversed values.
 | |
| // Turns easeIn into easeOut.
 | |
| const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
 | |
| 
 | |
| const circIn = (p) => 1 - Math.sin(Math.acos(p));
 | |
| const circOut = reverseEasing(circIn);
 | |
| const circInOut = mirrorEasing(circIn);
 | |
| 
 | |
| const backOut = cubicBezier(0.33, 1.53, 0.69, 0.99);
 | |
| const backIn = reverseEasing(backOut);
 | |
| const backInOut = mirrorEasing(backIn);
 | |
| 
 | |
| const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
 | |
| 
 | |
| const easingLookup = {
 | |
|     linear: noop,
 | |
|     easeIn,
 | |
|     easeInOut,
 | |
|     easeOut,
 | |
|     circIn,
 | |
|     circInOut,
 | |
|     circOut,
 | |
|     backIn,
 | |
|     backInOut,
 | |
|     backOut,
 | |
|     anticipate,
 | |
| };
 | |
| const easingDefinitionToFunction = (definition) => {
 | |
|     if (Array.isArray(definition)) {
 | |
|         // If cubic bezier definition, create bezier curve
 | |
|         exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
 | |
|         const [x1, y1, x2, y2] = definition;
 | |
|         return cubicBezier(x1, y1, x2, y2);
 | |
|     }
 | |
|     else if (typeof definition === "string") {
 | |
|         // Else lookup from table
 | |
|         exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
 | |
|         return easingLookup[definition];
 | |
|     }
 | |
|     return definition;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000,
 | |
|  * but false if a number or multiple colors
 | |
|  */
 | |
| const isColorString = (type, testProp) => (v) => {
 | |
|     return Boolean((isString(v) && singleColorRegex.test(v) && v.startsWith(type)) ||
 | |
|         (testProp && Object.prototype.hasOwnProperty.call(v, testProp)));
 | |
| };
 | |
| const splitColor = (aName, bName, cName) => (v) => {
 | |
|     if (!isString(v))
 | |
|         return v;
 | |
|     const [a, b, c, alpha] = v.match(floatRegex);
 | |
|     return {
 | |
|         [aName]: parseFloat(a),
 | |
|         [bName]: parseFloat(b),
 | |
|         [cName]: parseFloat(c),
 | |
|         alpha: alpha !== undefined ? parseFloat(alpha) : 1,
 | |
|     };
 | |
| };
 | |
| 
 | |
| const clampRgbUnit = (v) => clamp(0, 255, v);
 | |
| const rgbUnit = {
 | |
|     ...number,
 | |
|     transform: (v) => Math.round(clampRgbUnit(v)),
 | |
| };
 | |
| const rgba = {
 | |
|     test: isColorString("rgb", "red"),
 | |
|     parse: splitColor("red", "green", "blue"),
 | |
|     transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
 | |
|         rgbUnit.transform(red) +
 | |
|         ", " +
 | |
|         rgbUnit.transform(green) +
 | |
|         ", " +
 | |
|         rgbUnit.transform(blue) +
 | |
|         ", " +
 | |
|         sanitize(alpha.transform(alpha$1)) +
 | |
|         ")",
 | |
| };
 | |
| 
 | |
| function parseHex(v) {
 | |
|     let r = "";
 | |
|     let g = "";
 | |
|     let b = "";
 | |
|     let a = "";
 | |
|     // If we have 6 characters, ie #FF0000
 | |
|     if (v.length > 5) {
 | |
|         r = v.substring(1, 3);
 | |
|         g = v.substring(3, 5);
 | |
|         b = v.substring(5, 7);
 | |
|         a = v.substring(7, 9);
 | |
|         // Or we have 3 characters, ie #F00
 | |
|     }
 | |
|     else {
 | |
|         r = v.substring(1, 2);
 | |
|         g = v.substring(2, 3);
 | |
|         b = v.substring(3, 4);
 | |
|         a = v.substring(4, 5);
 | |
|         r += r;
 | |
|         g += g;
 | |
|         b += b;
 | |
|         a += a;
 | |
|     }
 | |
|     return {
 | |
|         red: parseInt(r, 16),
 | |
|         green: parseInt(g, 16),
 | |
|         blue: parseInt(b, 16),
 | |
|         alpha: a ? parseInt(a, 16) / 255 : 1,
 | |
|     };
 | |
| }
 | |
| const hex = {
 | |
|     test: isColorString("#"),
 | |
|     parse: parseHex,
 | |
|     transform: rgba.transform,
 | |
| };
 | |
| 
 | |
| const hsla = {
 | |
|     test: isColorString("hsl", "hue"),
 | |
|     parse: splitColor("hue", "saturation", "lightness"),
 | |
|     transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
 | |
|         return ("hsla(" +
 | |
|             Math.round(hue) +
 | |
|             ", " +
 | |
|             percent.transform(sanitize(saturation)) +
 | |
|             ", " +
 | |
|             percent.transform(sanitize(lightness)) +
 | |
|             ", " +
 | |
|             sanitize(alpha.transform(alpha$1)) +
 | |
|             ")");
 | |
|     },
 | |
| };
 | |
| 
 | |
| const color = {
 | |
|     test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
 | |
|     parse: (v) => {
 | |
|         if (rgba.test(v)) {
 | |
|             return rgba.parse(v);
 | |
|         }
 | |
|         else if (hsla.test(v)) {
 | |
|             return hsla.parse(v);
 | |
|         }
 | |
|         else {
 | |
|             return hex.parse(v);
 | |
|         }
 | |
|     },
 | |
|     transform: (v) => {
 | |
|         return isString(v)
 | |
|             ? v
 | |
|             : v.hasOwnProperty("red")
 | |
|                 ? rgba.transform(v)
 | |
|                 : hsla.transform(v);
 | |
|     },
 | |
| };
 | |
| 
 | |
| /*
 | |
|   Value in range from progress
 | |
| 
 | |
|   Given a lower limit and an upper limit, we return the value within
 | |
|   that range as expressed by progress (usually a number from 0 to 1)
 | |
| 
 | |
|   So progress = 0.5 would change
 | |
| 
 | |
|   from -------- to
 | |
| 
 | |
|   to
 | |
| 
 | |
|   from ---- to
 | |
| 
 | |
|   E.g. from = 10, to = 20, progress = 0.5 => 15
 | |
| 
 | |
|   @param [number]: Lower limit of range
 | |
|   @param [number]: Upper limit of range
 | |
|   @param [number]: The progress between lower and upper limits expressed 0-1
 | |
|   @return [number]: Value as calculated from progress within range (not limited within range)
 | |
| */
 | |
| const mix = (from, to, progress) => -progress * from + progress * to + from;
 | |
| 
 | |
| // Adapted from https://gist.github.com/mjackson/5311256
 | |
| function hueToRgb(p, q, t) {
 | |
|     if (t < 0)
 | |
|         t += 1;
 | |
|     if (t > 1)
 | |
|         t -= 1;
 | |
|     if (t < 1 / 6)
 | |
|         return p + (q - p) * 6 * t;
 | |
|     if (t < 1 / 2)
 | |
|         return q;
 | |
|     if (t < 2 / 3)
 | |
|         return p + (q - p) * (2 / 3 - t) * 6;
 | |
|     return p;
 | |
| }
 | |
| function hslaToRgba({ hue, saturation, lightness, alpha }) {
 | |
|     hue /= 360;
 | |
|     saturation /= 100;
 | |
|     lightness /= 100;
 | |
|     let red = 0;
 | |
|     let green = 0;
 | |
|     let blue = 0;
 | |
|     if (!saturation) {
 | |
|         red = green = blue = lightness;
 | |
|     }
 | |
|     else {
 | |
|         const q = lightness < 0.5
 | |
|             ? lightness * (1 + saturation)
 | |
|             : lightness + saturation - lightness * saturation;
 | |
|         const p = 2 * lightness - q;
 | |
|         red = hueToRgb(p, q, hue + 1 / 3);
 | |
|         green = hueToRgb(p, q, hue);
 | |
|         blue = hueToRgb(p, q, hue - 1 / 3);
 | |
|     }
 | |
|     return {
 | |
|         red: Math.round(red * 255),
 | |
|         green: Math.round(green * 255),
 | |
|         blue: Math.round(blue * 255),
 | |
|         alpha,
 | |
|     };
 | |
| }
 | |
| 
 | |
| // Linear color space blending
 | |
| // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
 | |
| // Demonstrated http://codepen.io/osublake/pen/xGVVaN
 | |
| const mixLinearColor = (from, to, v) => {
 | |
|     const fromExpo = from * from;
 | |
|     return Math.sqrt(Math.max(0, v * (to * to - fromExpo) + fromExpo));
 | |
| };
 | |
| const colorTypes = [hex, rgba, hsla];
 | |
| const getColorType = (v) => colorTypes.find((type) => type.test(v));
 | |
| function asRGBA(color) {
 | |
|     const type = getColorType(color);
 | |
|     exports.invariant(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
 | |
|     let model = type.parse(color);
 | |
|     if (type === hsla) {
 | |
|         // TODO Remove this cast - needed since Framer Motion's stricter typing
 | |
|         model = hslaToRgba(model);
 | |
|     }
 | |
|     return model;
 | |
| }
 | |
| const mixColor = (from, to) => {
 | |
|     const fromRGBA = asRGBA(from);
 | |
|     const toRGBA = asRGBA(to);
 | |
|     const blended = { ...fromRGBA };
 | |
|     return (v) => {
 | |
|         blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
 | |
|         blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
 | |
|         blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
 | |
|         blended.alpha = mix(fromRGBA.alpha, toRGBA.alpha, v);
 | |
|         return rgba.transform(blended);
 | |
|     };
 | |
| };
 | |
| 
 | |
| function test(v) {
 | |
|     var _a, _b;
 | |
|     return (isNaN(v) &&
 | |
|         isString(v) &&
 | |
|         (((_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) || 0) +
 | |
|             (((_b = v.match(colorRegex)) === null || _b === void 0 ? void 0 : _b.length) || 0) >
 | |
|             0);
 | |
| }
 | |
| const cssVarTokeniser = {
 | |
|     regex: cssVariableRegex,
 | |
|     countKey: "Vars",
 | |
|     token: "${v}",
 | |
|     parse: noop,
 | |
| };
 | |
| const colorTokeniser = {
 | |
|     regex: colorRegex,
 | |
|     countKey: "Colors",
 | |
|     token: "${c}",
 | |
|     parse: color.parse,
 | |
| };
 | |
| const numberTokeniser = {
 | |
|     regex: floatRegex,
 | |
|     countKey: "Numbers",
 | |
|     token: "${n}",
 | |
|     parse: number.parse,
 | |
| };
 | |
| function tokenise(info, { regex, countKey, token, parse }) {
 | |
|     const matches = info.tokenised.match(regex);
 | |
|     if (!matches)
 | |
|         return;
 | |
|     info["num" + countKey] = matches.length;
 | |
|     info.tokenised = info.tokenised.replace(regex, token);
 | |
|     info.values.push(...matches.map(parse));
 | |
| }
 | |
| function analyseComplexValue(value) {
 | |
|     const originalValue = value.toString();
 | |
|     const info = {
 | |
|         value: originalValue,
 | |
|         tokenised: originalValue,
 | |
|         values: [],
 | |
|         numVars: 0,
 | |
|         numColors: 0,
 | |
|         numNumbers: 0,
 | |
|     };
 | |
|     if (info.value.includes("var(--"))
 | |
|         tokenise(info, cssVarTokeniser);
 | |
|     tokenise(info, colorTokeniser);
 | |
|     tokenise(info, numberTokeniser);
 | |
|     return info;
 | |
| }
 | |
| function parseComplexValue(v) {
 | |
|     return analyseComplexValue(v).values;
 | |
| }
 | |
| function createTransformer(source) {
 | |
|     const { values, numColors, numVars, tokenised } = analyseComplexValue(source);
 | |
|     const numValues = values.length;
 | |
|     return (v) => {
 | |
|         let output = tokenised;
 | |
|         for (let i = 0; i < numValues; i++) {
 | |
|             if (i < numVars) {
 | |
|                 output = output.replace(cssVarTokeniser.token, v[i]);
 | |
|             }
 | |
|             else if (i < numVars + numColors) {
 | |
|                 output = output.replace(colorTokeniser.token, color.transform(v[i]));
 | |
|             }
 | |
|             else {
 | |
|                 output = output.replace(numberTokeniser.token, sanitize(v[i]));
 | |
|             }
 | |
|         }
 | |
|         return output;
 | |
|     };
 | |
| }
 | |
| const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
 | |
| function getAnimatableNone$1(v) {
 | |
|     const parsed = parseComplexValue(v);
 | |
|     const transformer = createTransformer(v);
 | |
|     return transformer(parsed.map(convertNumbersToZero));
 | |
| }
 | |
| const complex = {
 | |
|     test,
 | |
|     parse: parseComplexValue,
 | |
|     createTransformer,
 | |
|     getAnimatableNone: getAnimatableNone$1,
 | |
| };
 | |
| 
 | |
| const mixImmediate = (origin, target) => (p) => `${p > 0 ? target : origin}`;
 | |
| function getMixer$1(origin, target) {
 | |
|     if (typeof origin === "number") {
 | |
|         return (v) => mix(origin, target, v);
 | |
|     }
 | |
|     else if (color.test(origin)) {
 | |
|         return mixColor(origin, target);
 | |
|     }
 | |
|     else {
 | |
|         return origin.startsWith("var(")
 | |
|             ? mixImmediate(origin, target)
 | |
|             : mixComplex(origin, target);
 | |
|     }
 | |
| }
 | |
| const mixArray = (from, to) => {
 | |
|     const output = [...from];
 | |
|     const numValues = output.length;
 | |
|     const blendValue = from.map((fromThis, i) => getMixer$1(fromThis, to[i]));
 | |
|     return (v) => {
 | |
|         for (let i = 0; i < numValues; i++) {
 | |
|             output[i] = blendValue[i](v);
 | |
|         }
 | |
|         return output;
 | |
|     };
 | |
| };
 | |
| const mixObject = (origin, target) => {
 | |
|     const output = { ...origin, ...target };
 | |
|     const blendValue = {};
 | |
|     for (const key in output) {
 | |
|         if (origin[key] !== undefined && target[key] !== undefined) {
 | |
|             blendValue[key] = getMixer$1(origin[key], target[key]);
 | |
|         }
 | |
|     }
 | |
|     return (v) => {
 | |
|         for (const key in blendValue) {
 | |
|             output[key] = blendValue[key](v);
 | |
|         }
 | |
|         return output;
 | |
|     };
 | |
| };
 | |
| const mixComplex = (origin, target) => {
 | |
|     const template = complex.createTransformer(target);
 | |
|     const originStats = analyseComplexValue(origin);
 | |
|     const targetStats = analyseComplexValue(target);
 | |
|     const canInterpolate = originStats.numVars === targetStats.numVars &&
 | |
|         originStats.numColors === targetStats.numColors &&
 | |
|         originStats.numNumbers >= targetStats.numNumbers;
 | |
|     if (canInterpolate) {
 | |
|         return pipe(mixArray(originStats.values, targetStats.values), template);
 | |
|     }
 | |
|     else {
 | |
|         exports.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
 | |
|         return mixImmediate(origin, target);
 | |
|     }
 | |
| };
 | |
| 
 | |
| /*
 | |
|   Progress within given range
 | |
| 
 | |
|   Given a lower limit and an upper limit, we return the progress
 | |
|   (expressed as a number 0-1) represented by the given value, and
 | |
|   limit that progress to within 0-1.
 | |
| 
 | |
|   @param [number]: Lower limit
 | |
|   @param [number]: Upper limit
 | |
|   @param [number]: Value to find progress within given range
 | |
|   @return [number]: Progress of value within range as expressed 0-1
 | |
| */
 | |
| const progress = (from, to, value) => {
 | |
|     const toFromDifference = to - from;
 | |
|     return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
 | |
| };
 | |
| 
 | |
| const mixNumber = (from, to) => (p) => mix(from, to, p);
 | |
| function detectMixerFactory(v) {
 | |
|     if (typeof v === "number") {
 | |
|         return mixNumber;
 | |
|     }
 | |
|     else if (typeof v === "string") {
 | |
|         return color.test(v) ? mixColor : mixComplex;
 | |
|     }
 | |
|     else if (Array.isArray(v)) {
 | |
|         return mixArray;
 | |
|     }
 | |
|     else if (typeof v === "object") {
 | |
|         return mixObject;
 | |
|     }
 | |
|     return mixNumber;
 | |
| }
 | |
| function createMixers(output, ease, customMixer) {
 | |
|     const mixers = [];
 | |
|     const mixerFactory = customMixer || detectMixerFactory(output[0]);
 | |
|     const numMixers = output.length - 1;
 | |
|     for (let i = 0; i < numMixers; i++) {
 | |
|         let mixer = mixerFactory(output[i], output[i + 1]);
 | |
|         if (ease) {
 | |
|             const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease;
 | |
|             mixer = pipe(easingFunction, mixer);
 | |
|         }
 | |
|         mixers.push(mixer);
 | |
|     }
 | |
|     return mixers;
 | |
| }
 | |
| /**
 | |
|  * Create a function that maps from a numerical input array to a generic output array.
 | |
|  *
 | |
|  * Accepts:
 | |
|  *   - Numbers
 | |
|  *   - Colors (hex, hsl, hsla, rgb, rgba)
 | |
|  *   - Complex (combinations of one or more numbers or strings)
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const mixColor = interpolate([0, 1], ['#fff', '#000'])
 | |
|  *
 | |
|  * mixColor(0.5) // 'rgba(128, 128, 128, 1)'
 | |
|  * ```
 | |
|  *
 | |
|  * TODO Revist this approach once we've moved to data models for values,
 | |
|  * probably not needed to pregenerate mixer functions.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
 | |
|     const inputLength = input.length;
 | |
|     exports.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
 | |
|     /**
 | |
|      * If we're only provided a single input, we can just make a function
 | |
|      * that returns the output.
 | |
|      */
 | |
|     if (inputLength === 1)
 | |
|         return () => output[0];
 | |
|     // If input runs highest -> lowest, reverse both arrays
 | |
|     if (input[0] > input[inputLength - 1]) {
 | |
|         input = [...input].reverse();
 | |
|         output = [...output].reverse();
 | |
|     }
 | |
|     const mixers = createMixers(output, ease, mixer);
 | |
|     const numMixers = mixers.length;
 | |
|     const interpolator = (v) => {
 | |
|         let i = 0;
 | |
|         if (numMixers > 1) {
 | |
|             for (; i < input.length - 2; i++) {
 | |
|                 if (v < input[i + 1])
 | |
|                     break;
 | |
|             }
 | |
|         }
 | |
|         const progressInRange = progress(input[i], input[i + 1], v);
 | |
|         return mixers[i](progressInRange);
 | |
|     };
 | |
|     return isClamp
 | |
|         ? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
 | |
|         : interpolator;
 | |
| }
 | |
| 
 | |
| function fillOffset(offset, remaining) {
 | |
|     const min = offset[offset.length - 1];
 | |
|     for (let i = 1; i <= remaining; i++) {
 | |
|         const offsetProgress = progress(0, remaining, i);
 | |
|         offset.push(mix(min, 1, offsetProgress));
 | |
|     }
 | |
| }
 | |
| 
 | |
| function defaultOffset$1(arr) {
 | |
|     const offset = [0];
 | |
|     fillOffset(offset, arr.length - 1);
 | |
|     return offset;
 | |
| }
 | |
| 
 | |
| function convertOffsetToTimes(offset, duration) {
 | |
|     return offset.map((o) => o * duration);
 | |
| }
 | |
| 
 | |
| function defaultEasing(values, easing) {
 | |
|     return values.map(() => easing || easeInOut).splice(0, values.length - 1);
 | |
| }
 | |
| function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
 | |
|     /**
 | |
|      * Easing functions can be externally defined as strings. Here we convert them
 | |
|      * into actual functions.
 | |
|      */
 | |
|     const easingFunctions = isEasingArray(ease)
 | |
|         ? ease.map(easingDefinitionToFunction)
 | |
|         : easingDefinitionToFunction(ease);
 | |
|     /**
 | |
|      * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
 | |
|      * to reduce GC during animation.
 | |
|      */
 | |
|     const state = {
 | |
|         done: false,
 | |
|         value: keyframeValues[0],
 | |
|     };
 | |
|     /**
 | |
|      * Create a times array based on the provided 0-1 offsets
 | |
|      */
 | |
|     const absoluteTimes = convertOffsetToTimes(
 | |
|     // Only use the provided offsets if they're the correct length
 | |
|     // TODO Maybe we should warn here if there's a length mismatch
 | |
|     times && times.length === keyframeValues.length
 | |
|         ? times
 | |
|         : defaultOffset$1(keyframeValues), duration);
 | |
|     const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
 | |
|         ease: Array.isArray(easingFunctions)
 | |
|             ? easingFunctions
 | |
|             : defaultEasing(keyframeValues, easingFunctions),
 | |
|     });
 | |
|     return {
 | |
|         calculatedDuration: duration,
 | |
|         next: (t) => {
 | |
|             state.value = mapTimeToKeyframe(t);
 | |
|             state.done = t >= duration;
 | |
|             return state;
 | |
|         },
 | |
|     };
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Convert velocity into velocity per second
 | |
| 
 | |
|   @param [number]: Unit per frame
 | |
|   @param [number]: Frame duration in ms
 | |
| */
 | |
| function velocityPerSecond(velocity, frameDuration) {
 | |
|     return frameDuration ? velocity * (1000 / frameDuration) : 0;
 | |
| }
 | |
| 
 | |
| const velocitySampleDuration = 5; // ms
 | |
| function calcGeneratorVelocity(resolveValue, t, current) {
 | |
|     const prevT = Math.max(t - velocitySampleDuration, 0);
 | |
|     return velocityPerSecond(current - resolveValue(prevT), t - prevT);
 | |
| }
 | |
| 
 | |
| const safeMin = 0.001;
 | |
| const minDuration = 0.01;
 | |
| const maxDuration$1 = 10.0;
 | |
| const minDamping = 0.05;
 | |
| const maxDamping = 1;
 | |
| function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
 | |
|     let envelope;
 | |
|     let derivative;
 | |
|     exports.warning(duration <= secondsToMilliseconds(maxDuration$1), "Spring duration must be 10 seconds or less");
 | |
|     let dampingRatio = 1 - bounce;
 | |
|     /**
 | |
|      * Restrict dampingRatio and duration to within acceptable ranges.
 | |
|      */
 | |
|     dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
 | |
|     duration = clamp(minDuration, maxDuration$1, millisecondsToSeconds(duration));
 | |
|     if (dampingRatio < 1) {
 | |
|         /**
 | |
|          * Underdamped spring
 | |
|          */
 | |
|         envelope = (undampedFreq) => {
 | |
|             const exponentialDecay = undampedFreq * dampingRatio;
 | |
|             const delta = exponentialDecay * duration;
 | |
|             const a = exponentialDecay - velocity;
 | |
|             const b = calcAngularFreq(undampedFreq, dampingRatio);
 | |
|             const c = Math.exp(-delta);
 | |
|             return safeMin - (a / b) * c;
 | |
|         };
 | |
|         derivative = (undampedFreq) => {
 | |
|             const exponentialDecay = undampedFreq * dampingRatio;
 | |
|             const delta = exponentialDecay * duration;
 | |
|             const d = delta * velocity + velocity;
 | |
|             const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
 | |
|             const f = Math.exp(-delta);
 | |
|             const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
 | |
|             const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
 | |
|             return (factor * ((d - e) * f)) / g;
 | |
|         };
 | |
|     }
 | |
|     else {
 | |
|         /**
 | |
|          * Critically-damped spring
 | |
|          */
 | |
|         envelope = (undampedFreq) => {
 | |
|             const a = Math.exp(-undampedFreq * duration);
 | |
|             const b = (undampedFreq - velocity) * duration + 1;
 | |
|             return -safeMin + a * b;
 | |
|         };
 | |
|         derivative = (undampedFreq) => {
 | |
|             const a = Math.exp(-undampedFreq * duration);
 | |
|             const b = (velocity - undampedFreq) * (duration * duration);
 | |
|             return a * b;
 | |
|         };
 | |
|     }
 | |
|     const initialGuess = 5 / duration;
 | |
|     const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
 | |
|     duration = secondsToMilliseconds(duration);
 | |
|     if (isNaN(undampedFreq)) {
 | |
|         return {
 | |
|             stiffness: 100,
 | |
|             damping: 10,
 | |
|             duration,
 | |
|         };
 | |
|     }
 | |
|     else {
 | |
|         const stiffness = Math.pow(undampedFreq, 2) * mass;
 | |
|         return {
 | |
|             stiffness,
 | |
|             damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
 | |
|             duration,
 | |
|         };
 | |
|     }
 | |
| }
 | |
| const rootIterations = 12;
 | |
| function approximateRoot(envelope, derivative, initialGuess) {
 | |
|     let result = initialGuess;
 | |
|     for (let i = 1; i < rootIterations; i++) {
 | |
|         result = result - envelope(result) / derivative(result);
 | |
|     }
 | |
|     return result;
 | |
| }
 | |
| function calcAngularFreq(undampedFreq, dampingRatio) {
 | |
|     return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
 | |
| }
 | |
| 
 | |
| const durationKeys = ["duration", "bounce"];
 | |
| const physicsKeys = ["stiffness", "damping", "mass"];
 | |
| function isSpringType(options, keys) {
 | |
|     return keys.some((key) => options[key] !== undefined);
 | |
| }
 | |
| function getSpringOptions(options) {
 | |
|     let springOptions = {
 | |
|         velocity: 0.0,
 | |
|         stiffness: 100,
 | |
|         damping: 10,
 | |
|         mass: 1.0,
 | |
|         isResolvedFromDuration: false,
 | |
|         ...options,
 | |
|     };
 | |
|     // stiffness/damping/mass overrides duration/bounce
 | |
|     if (!isSpringType(options, physicsKeys) &&
 | |
|         isSpringType(options, durationKeys)) {
 | |
|         const derived = findSpring(options);
 | |
|         springOptions = {
 | |
|             ...springOptions,
 | |
|             ...derived,
 | |
|             mass: 1.0,
 | |
|         };
 | |
|         springOptions.isResolvedFromDuration = true;
 | |
|     }
 | |
|     return springOptions;
 | |
| }
 | |
| function spring({ keyframes, restDelta, restSpeed, ...options }) {
 | |
|     const origin = keyframes[0];
 | |
|     const target = keyframes[keyframes.length - 1];
 | |
|     /**
 | |
|      * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
 | |
|      * to reduce GC during animation.
 | |
|      */
 | |
|     const state = { done: false, value: origin };
 | |
|     const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
 | |
|         ...options,
 | |
|         velocity: -millisecondsToSeconds(options.velocity || 0),
 | |
|     });
 | |
|     const initialVelocity = velocity || 0.0;
 | |
|     const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
 | |
|     const initialDelta = target - origin;
 | |
|     const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
 | |
|     /**
 | |
|      * If we're working on a granular scale, use smaller defaults for determining
 | |
|      * when the spring is finished.
 | |
|      *
 | |
|      * These defaults have been selected emprically based on what strikes a good
 | |
|      * ratio between feeling good and finishing as soon as changes are imperceptible.
 | |
|      */
 | |
|     const isGranularScale = Math.abs(initialDelta) < 5;
 | |
|     restSpeed || (restSpeed = isGranularScale ? 0.01 : 2);
 | |
|     restDelta || (restDelta = isGranularScale ? 0.005 : 0.5);
 | |
|     let resolveSpring;
 | |
|     if (dampingRatio < 1) {
 | |
|         const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
 | |
|         // Underdamped spring
 | |
|         resolveSpring = (t) => {
 | |
|             const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
 | |
|             return (target -
 | |
|                 envelope *
 | |
|                     (((initialVelocity +
 | |
|                         dampingRatio * undampedAngularFreq * initialDelta) /
 | |
|                         angularFreq) *
 | |
|                         Math.sin(angularFreq * t) +
 | |
|                         initialDelta * Math.cos(angularFreq * t)));
 | |
|         };
 | |
|     }
 | |
|     else if (dampingRatio === 1) {
 | |
|         // Critically damped spring
 | |
|         resolveSpring = (t) => target -
 | |
|             Math.exp(-undampedAngularFreq * t) *
 | |
|                 (initialDelta +
 | |
|                     (initialVelocity + undampedAngularFreq * initialDelta) * t);
 | |
|     }
 | |
|     else {
 | |
|         // Overdamped spring
 | |
|         const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
 | |
|         resolveSpring = (t) => {
 | |
|             const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
 | |
|             // When performing sinh or cosh values can hit Infinity so we cap them here
 | |
|             const freqForT = Math.min(dampedAngularFreq * t, 300);
 | |
|             return (target -
 | |
|                 (envelope *
 | |
|                     ((initialVelocity +
 | |
|                         dampingRatio * undampedAngularFreq * initialDelta) *
 | |
|                         Math.sinh(freqForT) +
 | |
|                         dampedAngularFreq *
 | |
|                             initialDelta *
 | |
|                             Math.cosh(freqForT))) /
 | |
|                     dampedAngularFreq);
 | |
|         };
 | |
|     }
 | |
|     return {
 | |
|         calculatedDuration: isResolvedFromDuration ? duration || null : null,
 | |
|         next: (t) => {
 | |
|             const current = resolveSpring(t);
 | |
|             if (!isResolvedFromDuration) {
 | |
|                 let currentVelocity = initialVelocity;
 | |
|                 if (t !== 0) {
 | |
|                     /**
 | |
|                      * We only need to calculate velocity for under-damped springs
 | |
|                      * as over- and critically-damped springs can't overshoot, so
 | |
|                      * checking only for displacement is enough.
 | |
|                      */
 | |
|                     if (dampingRatio < 1) {
 | |
|                         currentVelocity = calcGeneratorVelocity(resolveSpring, t, current);
 | |
|                     }
 | |
|                     else {
 | |
|                         currentVelocity = 0;
 | |
|                     }
 | |
|                 }
 | |
|                 const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
 | |
|                 const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
 | |
|                 state.done =
 | |
|                     isBelowVelocityThreshold && isBelowDisplacementThreshold;
 | |
|             }
 | |
|             else {
 | |
|                 state.done = t >= duration;
 | |
|             }
 | |
|             state.value = state.done ? target : current;
 | |
|             return state;
 | |
|         },
 | |
|     };
 | |
| }
 | |
| 
 | |
| function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
 | |
|     const origin = keyframes[0];
 | |
|     const state = {
 | |
|         done: false,
 | |
|         value: origin,
 | |
|     };
 | |
|     const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
 | |
|     const nearestBoundary = (v) => {
 | |
|         if (min === undefined)
 | |
|             return max;
 | |
|         if (max === undefined)
 | |
|             return min;
 | |
|         return Math.abs(min - v) < Math.abs(max - v) ? min : max;
 | |
|     };
 | |
|     let amplitude = power * velocity;
 | |
|     const ideal = origin + amplitude;
 | |
|     const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
 | |
|     /**
 | |
|      * If the target has changed we need to re-calculate the amplitude, otherwise
 | |
|      * the animation will start from the wrong position.
 | |
|      */
 | |
|     if (target !== ideal)
 | |
|         amplitude = target - origin;
 | |
|     const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
 | |
|     const calcLatest = (t) => target + calcDelta(t);
 | |
|     const applyFriction = (t) => {
 | |
|         const delta = calcDelta(t);
 | |
|         const latest = calcLatest(t);
 | |
|         state.done = Math.abs(delta) <= restDelta;
 | |
|         state.value = state.done ? target : latest;
 | |
|     };
 | |
|     /**
 | |
|      * Ideally this would resolve for t in a stateless way, we could
 | |
|      * do that by always precalculating the animation but as we know
 | |
|      * this will be done anyway we can assume that spring will
 | |
|      * be discovered during that.
 | |
|      */
 | |
|     let timeReachedBoundary;
 | |
|     let spring$1;
 | |
|     const checkCatchBoundary = (t) => {
 | |
|         if (!isOutOfBounds(state.value))
 | |
|             return;
 | |
|         timeReachedBoundary = t;
 | |
|         spring$1 = spring({
 | |
|             keyframes: [state.value, nearestBoundary(state.value)],
 | |
|             velocity: calcGeneratorVelocity(calcLatest, t, state.value),
 | |
|             damping: bounceDamping,
 | |
|             stiffness: bounceStiffness,
 | |
|             restDelta,
 | |
|             restSpeed,
 | |
|         });
 | |
|     };
 | |
|     checkCatchBoundary(0);
 | |
|     return {
 | |
|         calculatedDuration: null,
 | |
|         next: (t) => {
 | |
|             /**
 | |
|              * We need to resolve the friction to figure out if we need a
 | |
|              * spring but we don't want to do this twice per frame. So here
 | |
|              * we flag if we updated for this frame and later if we did
 | |
|              * we can skip doing it again.
 | |
|              */
 | |
|             let hasUpdatedFrame = false;
 | |
|             if (!spring$1 && timeReachedBoundary === undefined) {
 | |
|                 hasUpdatedFrame = true;
 | |
|                 applyFriction(t);
 | |
|                 checkCatchBoundary(t);
 | |
|             }
 | |
|             /**
 | |
|              * If we have a spring and the provided t is beyond the moment the friction
 | |
|              * animation crossed the min/max boundary, use the spring.
 | |
|              */
 | |
|             if (timeReachedBoundary !== undefined && t > timeReachedBoundary) {
 | |
|                 return spring$1.next(t - timeReachedBoundary);
 | |
|             }
 | |
|             else {
 | |
|                 !hasUpdatedFrame && applyFriction(t);
 | |
|                 return state;
 | |
|             }
 | |
|         },
 | |
|     };
 | |
| }
 | |
| 
 | |
| const frameloopDriver = (update) => {
 | |
|     const passTimestamp = ({ timestamp }) => update(timestamp);
 | |
|     return {
 | |
|         start: () => frame.update(passTimestamp, true),
 | |
|         stop: () => cancelFrame(passTimestamp),
 | |
|         /**
 | |
|          * If we're processing this frame we can use the
 | |
|          * framelocked timestamp to keep things in sync.
 | |
|          */
 | |
|         now: () => frameData.isProcessing ? frameData.timestamp : performance.now(),
 | |
|     };
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Implement a practical max duration for keyframe generation
 | |
|  * to prevent infinite loops
 | |
|  */
 | |
| const maxGeneratorDuration = 20000;
 | |
| function calcGeneratorDuration(generator) {
 | |
|     let duration = 0;
 | |
|     const timeStep = 50;
 | |
|     let state = generator.next(duration);
 | |
|     while (!state.done && duration < maxGeneratorDuration) {
 | |
|         duration += timeStep;
 | |
|         state = generator.next(duration);
 | |
|     }
 | |
|     return duration >= maxGeneratorDuration ? Infinity : duration;
 | |
| }
 | |
| 
 | |
| const types = {
 | |
|     decay: inertia,
 | |
|     inertia,
 | |
|     tween: keyframes,
 | |
|     keyframes: keyframes,
 | |
|     spring,
 | |
| };
 | |
| /**
 | |
|  * Animate a single value on the main thread.
 | |
|  *
 | |
|  * This function is written, where functionality overlaps,
 | |
|  * to be largely spec-compliant with WAAPI to allow fungibility
 | |
|  * between the two.
 | |
|  */
 | |
| function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, keyframes: keyframes$1, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", onPlay, onStop, onComplete, onUpdate, ...options }) {
 | |
|     let speed = 1;
 | |
|     let hasStopped = false;
 | |
|     let resolveFinishedPromise;
 | |
|     let currentFinishedPromise;
 | |
|     /**
 | |
|      * Resolve the current Promise every time we enter the
 | |
|      * finished state. This is WAAPI-compatible behaviour.
 | |
|      */
 | |
|     const updateFinishedPromise = () => {
 | |
|         currentFinishedPromise = new Promise((resolve) => {
 | |
|             resolveFinishedPromise = resolve;
 | |
|         });
 | |
|     };
 | |
|     // Create the first finished promise
 | |
|     updateFinishedPromise();
 | |
|     let animationDriver;
 | |
|     const generatorFactory = types[type] || keyframes;
 | |
|     /**
 | |
|      * If this isn't the keyframes generator and we've been provided
 | |
|      * strings as keyframes, we need to interpolate these.
 | |
|      */
 | |
|     let mapNumbersToKeyframes;
 | |
|     if (generatorFactory !== keyframes &&
 | |
|         typeof keyframes$1[0] !== "number") {
 | |
|         if (process.env.NODE_ENV !== "production") {
 | |
|             exports.invariant(keyframes$1.length === 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
 | |
|         }
 | |
|         mapNumbersToKeyframes = interpolate([0, 100], keyframes$1, {
 | |
|             clamp: false,
 | |
|         });
 | |
|         keyframes$1 = [0, 100];
 | |
|     }
 | |
|     const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
 | |
|     let mirroredGenerator;
 | |
|     if (repeatType === "mirror") {
 | |
|         mirroredGenerator = generatorFactory({
 | |
|             ...options,
 | |
|             keyframes: [...keyframes$1].reverse(),
 | |
|             velocity: -(options.velocity || 0),
 | |
|         });
 | |
|     }
 | |
|     let playState = "idle";
 | |
|     let holdTime = null;
 | |
|     let startTime = null;
 | |
|     let cancelTime = null;
 | |
|     /**
 | |
|      * If duration is undefined and we have repeat options,
 | |
|      * we need to calculate a duration from the generator.
 | |
|      *
 | |
|      * We set it to the generator itself to cache the duration.
 | |
|      * Any timeline resolver will need to have already precalculated
 | |
|      * the duration by this step.
 | |
|      */
 | |
|     if (generator.calculatedDuration === null && repeat) {
 | |
|         generator.calculatedDuration = calcGeneratorDuration(generator);
 | |
|     }
 | |
|     const { calculatedDuration } = generator;
 | |
|     let resolvedDuration = Infinity;
 | |
|     let totalDuration = Infinity;
 | |
|     if (calculatedDuration !== null) {
 | |
|         resolvedDuration = calculatedDuration + repeatDelay;
 | |
|         totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
 | |
|     }
 | |
|     let currentTime = 0;
 | |
|     const tick = (timestamp) => {
 | |
|         if (startTime === null)
 | |
|             return;
 | |
|         /**
 | |
|          * requestAnimationFrame timestamps can come through as lower than
 | |
|          * the startTime as set by performance.now(). Here we prevent this,
 | |
|          * though in the future it could be possible to make setting startTime
 | |
|          * a pending operation that gets resolved here.
 | |
|          */
 | |
|         if (speed > 0)
 | |
|             startTime = Math.min(startTime, timestamp);
 | |
|         if (speed < 0)
 | |
|             startTime = Math.min(timestamp - totalDuration / speed, startTime);
 | |
|         if (holdTime !== null) {
 | |
|             currentTime = holdTime;
 | |
|         }
 | |
|         else {
 | |
|             // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
 | |
|             // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
 | |
|             // example.
 | |
|             currentTime = Math.round(timestamp - startTime) * speed;
 | |
|         }
 | |
|         // Rebase on delay
 | |
|         const timeWithoutDelay = currentTime - delay * (speed >= 0 ? 1 : -1);
 | |
|         const isInDelayPhase = speed >= 0 ? timeWithoutDelay < 0 : timeWithoutDelay > totalDuration;
 | |
|         currentTime = Math.max(timeWithoutDelay, 0);
 | |
|         /**
 | |
|          * If this animation has finished, set the current time
 | |
|          * to the total duration.
 | |
|          */
 | |
|         if (playState === "finished" && holdTime === null) {
 | |
|             currentTime = totalDuration;
 | |
|         }
 | |
|         let elapsed = currentTime;
 | |
|         let frameGenerator = generator;
 | |
|         if (repeat) {
 | |
|             /**
 | |
|              * Get the current progress (0-1) of the animation. If t is >
 | |
|              * than duration we'll get values like 2.5 (midway through the
 | |
|              * third iteration)
 | |
|              */
 | |
|             const progress = Math.min(currentTime, totalDuration) / resolvedDuration;
 | |
|             /**
 | |
|              * Get the current iteration (0 indexed). For instance the floor of
 | |
|              * 2.5 is 2.
 | |
|              */
 | |
|             let currentIteration = Math.floor(progress);
 | |
|             /**
 | |
|              * Get the current progress of the iteration by taking the remainder
 | |
|              * so 2.5 is 0.5 through iteration 2
 | |
|              */
 | |
|             let iterationProgress = progress % 1.0;
 | |
|             /**
 | |
|              * If iteration progress is 1 we count that as the end
 | |
|              * of the previous iteration.
 | |
|              */
 | |
|             if (!iterationProgress && progress >= 1) {
 | |
|                 iterationProgress = 1;
 | |
|             }
 | |
|             iterationProgress === 1 && currentIteration--;
 | |
|             currentIteration = Math.min(currentIteration, repeat + 1);
 | |
|             /**
 | |
|              * Reverse progress if we're not running in "normal" direction
 | |
|              */
 | |
|             const isOddIteration = Boolean(currentIteration % 2);
 | |
|             if (isOddIteration) {
 | |
|                 if (repeatType === "reverse") {
 | |
|                     iterationProgress = 1 - iterationProgress;
 | |
|                     if (repeatDelay) {
 | |
|                         iterationProgress -= repeatDelay / resolvedDuration;
 | |
|                     }
 | |
|                 }
 | |
|                 else if (repeatType === "mirror") {
 | |
|                     frameGenerator = mirroredGenerator;
 | |
|                 }
 | |
|             }
 | |
|             elapsed = clamp(0, 1, iterationProgress) * resolvedDuration;
 | |
|         }
 | |
|         /**
 | |
|          * If we're in negative time, set state as the initial keyframe.
 | |
|          * This prevents delay: x, duration: 0 animations from finishing
 | |
|          * instantly.
 | |
|          */
 | |
|         const state = isInDelayPhase
 | |
|             ? { done: false, value: keyframes$1[0] }
 | |
|             : frameGenerator.next(elapsed);
 | |
|         if (mapNumbersToKeyframes) {
 | |
|             state.value = mapNumbersToKeyframes(state.value);
 | |
|         }
 | |
|         let { done } = state;
 | |
|         if (!isInDelayPhase && calculatedDuration !== null) {
 | |
|             done = speed >= 0 ? currentTime >= totalDuration : currentTime <= 0;
 | |
|         }
 | |
|         const isAnimationFinished = holdTime === null &&
 | |
|             (playState === "finished" || (playState === "running" && done));
 | |
|         if (onUpdate) {
 | |
|             onUpdate(state.value);
 | |
|         }
 | |
|         if (isAnimationFinished) {
 | |
|             finish();
 | |
|         }
 | |
|         return state;
 | |
|     };
 | |
|     const stopAnimationDriver = () => {
 | |
|         animationDriver && animationDriver.stop();
 | |
|         animationDriver = undefined;
 | |
|     };
 | |
|     const cancel = () => {
 | |
|         playState = "idle";
 | |
|         stopAnimationDriver();
 | |
|         resolveFinishedPromise();
 | |
|         updateFinishedPromise();
 | |
|         startTime = cancelTime = null;
 | |
|     };
 | |
|     const finish = () => {
 | |
|         playState = "finished";
 | |
|         onComplete && onComplete();
 | |
|         stopAnimationDriver();
 | |
|         resolveFinishedPromise();
 | |
|     };
 | |
|     const play = () => {
 | |
|         if (hasStopped)
 | |
|             return;
 | |
|         if (!animationDriver)
 | |
|             animationDriver = driver(tick);
 | |
|         const now = animationDriver.now();
 | |
|         onPlay && onPlay();
 | |
|         if (holdTime !== null) {
 | |
|             startTime = now - holdTime;
 | |
|         }
 | |
|         else if (!startTime || playState === "finished") {
 | |
|             startTime = now;
 | |
|         }
 | |
|         if (playState === "finished") {
 | |
|             updateFinishedPromise();
 | |
|         }
 | |
|         cancelTime = startTime;
 | |
|         holdTime = null;
 | |
|         /**
 | |
|          * Set playState to running only after we've used it in
 | |
|          * the previous logic.
 | |
|          */
 | |
|         playState = "running";
 | |
|         animationDriver.start();
 | |
|     };
 | |
|     if (autoplay) {
 | |
|         play();
 | |
|     }
 | |
|     const controls = {
 | |
|         then(resolve, reject) {
 | |
|             return currentFinishedPromise.then(resolve, reject);
 | |
|         },
 | |
|         get time() {
 | |
|             return millisecondsToSeconds(currentTime);
 | |
|         },
 | |
|         set time(newTime) {
 | |
|             newTime = secondsToMilliseconds(newTime);
 | |
|             currentTime = newTime;
 | |
|             if (holdTime !== null || !animationDriver || speed === 0) {
 | |
|                 holdTime = newTime;
 | |
|             }
 | |
|             else {
 | |
|                 startTime = animationDriver.now() - newTime / speed;
 | |
|             }
 | |
|         },
 | |
|         get duration() {
 | |
|             const duration = generator.calculatedDuration === null
 | |
|                 ? calcGeneratorDuration(generator)
 | |
|                 : generator.calculatedDuration;
 | |
|             return millisecondsToSeconds(duration);
 | |
|         },
 | |
|         get speed() {
 | |
|             return speed;
 | |
|         },
 | |
|         set speed(newSpeed) {
 | |
|             if (newSpeed === speed || !animationDriver)
 | |
|                 return;
 | |
|             speed = newSpeed;
 | |
|             controls.time = millisecondsToSeconds(currentTime);
 | |
|         },
 | |
|         get state() {
 | |
|             return playState;
 | |
|         },
 | |
|         play,
 | |
|         pause: () => {
 | |
|             playState = "paused";
 | |
|             holdTime = currentTime;
 | |
|         },
 | |
|         stop: () => {
 | |
|             hasStopped = true;
 | |
|             if (playState === "idle")
 | |
|                 return;
 | |
|             playState = "idle";
 | |
|             onStop && onStop();
 | |
|             cancel();
 | |
|         },
 | |
|         cancel: () => {
 | |
|             if (cancelTime !== null)
 | |
|                 tick(cancelTime);
 | |
|             cancel();
 | |
|         },
 | |
|         complete: () => {
 | |
|             playState = "finished";
 | |
|         },
 | |
|         sample: (elapsed) => {
 | |
|             startTime = 0;
 | |
|             return tick(elapsed);
 | |
|         },
 | |
|     };
 | |
|     return controls;
 | |
| }
 | |
| 
 | |
| function memo(callback) {
 | |
|     let result;
 | |
|     return () => {
 | |
|         if (result === undefined)
 | |
|             result = callback();
 | |
|         return result;
 | |
|     };
 | |
| }
 | |
| 
 | |
| const supportsWaapi = memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
 | |
| /**
 | |
|  * A list of values that can be hardware-accelerated.
 | |
|  */
 | |
| const acceleratedValues = new Set([
 | |
|     "opacity",
 | |
|     "clipPath",
 | |
|     "filter",
 | |
|     "transform",
 | |
|     "backgroundColor",
 | |
| ]);
 | |
| /**
 | |
|  * 10ms is chosen here as it strikes a balance between smooth
 | |
|  * results (more than one keyframe per frame at 60fps) and
 | |
|  * keyframe quantity.
 | |
|  */
 | |
| const sampleDelta = 10; //ms
 | |
| /**
 | |
|  * Implement a practical max duration for keyframe generation
 | |
|  * to prevent infinite loops
 | |
|  */
 | |
| const maxDuration = 20000;
 | |
| const requiresPregeneratedKeyframes = (valueName, options) => options.type === "spring" ||
 | |
|     valueName === "backgroundColor" ||
 | |
|     !isWaapiSupportedEasing(options.ease);
 | |
| function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ...options }) {
 | |
|     const canAccelerateAnimation = supportsWaapi() &&
 | |
|         acceleratedValues.has(valueName) &&
 | |
|         !options.repeatDelay &&
 | |
|         options.repeatType !== "mirror" &&
 | |
|         options.damping !== 0 &&
 | |
|         options.type !== "inertia";
 | |
|     if (!canAccelerateAnimation)
 | |
|         return false;
 | |
|     /**
 | |
|      * TODO: Unify with js/index
 | |
|      */
 | |
|     let hasStopped = false;
 | |
|     let resolveFinishedPromise;
 | |
|     let currentFinishedPromise;
 | |
|     /**
 | |
|      * Cancelling an animation will write to the DOM. For safety we want to defer
 | |
|      * this until the next `update` frame lifecycle. This flag tracks whether we
 | |
|      * have a pending cancel, if so we shouldn't allow animations to finish.
 | |
|      */
 | |
|     let pendingCancel = false;
 | |
|     /**
 | |
|      * Resolve the current Promise every time we enter the
 | |
|      * finished state. This is WAAPI-compatible behaviour.
 | |
|      */
 | |
|     const updateFinishedPromise = () => {
 | |
|         currentFinishedPromise = new Promise((resolve) => {
 | |
|             resolveFinishedPromise = resolve;
 | |
|         });
 | |
|     };
 | |
|     // Create the first finished promise
 | |
|     updateFinishedPromise();
 | |
|     let { keyframes, duration = 300, ease, times } = options;
 | |
|     /**
 | |
|      * If this animation needs pre-generated keyframes then generate.
 | |
|      */
 | |
|     if (requiresPregeneratedKeyframes(valueName, options)) {
 | |
|         const sampleAnimation = animateValue({
 | |
|             ...options,
 | |
|             repeat: 0,
 | |
|             delay: 0,
 | |
|         });
 | |
|         let state = { done: false, value: keyframes[0] };
 | |
|         const pregeneratedKeyframes = [];
 | |
|         /**
 | |
|          * Bail after 20 seconds of pre-generated keyframes as it's likely
 | |
|          * we're heading for an infinite loop.
 | |
|          */
 | |
|         let t = 0;
 | |
|         while (!state.done && t < maxDuration) {
 | |
|             state = sampleAnimation.sample(t);
 | |
|             pregeneratedKeyframes.push(state.value);
 | |
|             t += sampleDelta;
 | |
|         }
 | |
|         times = undefined;
 | |
|         keyframes = pregeneratedKeyframes;
 | |
|         duration = t - sampleDelta;
 | |
|         ease = "linear";
 | |
|     }
 | |
|     const animation = animateStyle(value.owner.current, valueName, keyframes, {
 | |
|         ...options,
 | |
|         duration,
 | |
|         /**
 | |
|          * This function is currently not called if ease is provided
 | |
|          * as a function so the cast is safe.
 | |
|          *
 | |
|          * However it would be possible for a future refinement to port
 | |
|          * in easing pregeneration from Motion One for browsers that
 | |
|          * support the upcoming `linear()` easing function.
 | |
|          */
 | |
|         ease: ease,
 | |
|         times,
 | |
|     });
 | |
|     const cancelAnimation = () => {
 | |
|         pendingCancel = false;
 | |
|         animation.cancel();
 | |
|     };
 | |
|     const safeCancel = () => {
 | |
|         pendingCancel = true;
 | |
|         frame.update(cancelAnimation);
 | |
|         resolveFinishedPromise();
 | |
|         updateFinishedPromise();
 | |
|     };
 | |
|     /**
 | |
|      * Prefer the `onfinish` prop as it's more widely supported than
 | |
|      * the `finished` promise.
 | |
|      *
 | |
|      * Here, we synchronously set the provided MotionValue to the end
 | |
|      * keyframe. If we didn't, when the WAAPI animation is finished it would
 | |
|      * be removed from the element which would then revert to its old styles.
 | |
|      */
 | |
|     animation.onfinish = () => {
 | |
|         if (pendingCancel)
 | |
|             return;
 | |
|         value.set(getFinalKeyframe(keyframes, options));
 | |
|         onComplete && onComplete();
 | |
|         safeCancel();
 | |
|     };
 | |
|     /**
 | |
|      * Animation interrupt callback.
 | |
|      */
 | |
|     const controls = {
 | |
|         then(resolve, reject) {
 | |
|             return currentFinishedPromise.then(resolve, reject);
 | |
|         },
 | |
|         attachTimeline(timeline) {
 | |
|             animation.timeline = timeline;
 | |
|             animation.onfinish = null;
 | |
|             return noop;
 | |
|         },
 | |
|         get time() {
 | |
|             return millisecondsToSeconds(animation.currentTime || 0);
 | |
|         },
 | |
|         set time(newTime) {
 | |
|             animation.currentTime = secondsToMilliseconds(newTime);
 | |
|         },
 | |
|         get speed() {
 | |
|             return animation.playbackRate;
 | |
|         },
 | |
|         set speed(newSpeed) {
 | |
|             animation.playbackRate = newSpeed;
 | |
|         },
 | |
|         get duration() {
 | |
|             return millisecondsToSeconds(duration);
 | |
|         },
 | |
|         play: () => {
 | |
|             if (hasStopped)
 | |
|                 return;
 | |
|             animation.play();
 | |
|             /**
 | |
|              * Cancel any pending cancel tasks
 | |
|              */
 | |
|             cancelFrame(cancelAnimation);
 | |
|         },
 | |
|         pause: () => animation.pause(),
 | |
|         stop: () => {
 | |
|             hasStopped = true;
 | |
|             if (animation.playState === "idle")
 | |
|                 return;
 | |
|             /**
 | |
|              * WAAPI doesn't natively have any interruption capabilities.
 | |
|              *
 | |
|              * Rather than read commited styles back out of the DOM, we can
 | |
|              * create a renderless JS animation and sample it twice to calculate
 | |
|              * its current value, "previous" value, and therefore allow
 | |
|              * Motion to calculate velocity for any subsequent animation.
 | |
|              */
 | |
|             const { currentTime } = animation;
 | |
|             if (currentTime) {
 | |
|                 const sampleAnimation = animateValue({
 | |
|                     ...options,
 | |
|                     autoplay: false,
 | |
|                 });
 | |
|                 value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
 | |
|             }
 | |
|             safeCancel();
 | |
|         },
 | |
|         complete: () => {
 | |
|             if (pendingCancel)
 | |
|                 return;
 | |
|             animation.finish();
 | |
|         },
 | |
|         cancel: safeCancel,
 | |
|     };
 | |
|     return controls;
 | |
| }
 | |
| 
 | |
| function createInstantAnimation({ keyframes, delay, onUpdate, onComplete, }) {
 | |
|     const setValue = () => {
 | |
|         onUpdate && onUpdate(keyframes[keyframes.length - 1]);
 | |
|         onComplete && onComplete();
 | |
|         /**
 | |
|          * TODO: As this API grows it could make sense to always return
 | |
|          * animateValue. This will be a bigger project as animateValue
 | |
|          * is frame-locked whereas this function resolves instantly.
 | |
|          * This is a behavioural change and also has ramifications regarding
 | |
|          * assumptions within tests.
 | |
|          */
 | |
|         return {
 | |
|             time: 0,
 | |
|             speed: 1,
 | |
|             duration: 0,
 | |
|             play: (noop),
 | |
|             pause: (noop),
 | |
|             stop: (noop),
 | |
|             then: (resolve) => {
 | |
|                 resolve();
 | |
|                 return Promise.resolve();
 | |
|             },
 | |
|             cancel: (noop),
 | |
|             complete: (noop),
 | |
|         };
 | |
|     };
 | |
|     return delay
 | |
|         ? animateValue({
 | |
|             keyframes: [0, 1],
 | |
|             duration: 0,
 | |
|             delay,
 | |
|             onComplete: setValue,
 | |
|         })
 | |
|         : setValue();
 | |
| }
 | |
| 
 | |
| const underDampedSpring = {
 | |
|     type: "spring",
 | |
|     stiffness: 500,
 | |
|     damping: 25,
 | |
|     restSpeed: 10,
 | |
| };
 | |
| const criticallyDampedSpring = (target) => ({
 | |
|     type: "spring",
 | |
|     stiffness: 550,
 | |
|     damping: target === 0 ? 2 * Math.sqrt(550) : 30,
 | |
|     restSpeed: 10,
 | |
| });
 | |
| const keyframesTransition = {
 | |
|     type: "keyframes",
 | |
|     duration: 0.8,
 | |
| };
 | |
| /**
 | |
|  * Default easing curve is a slightly shallower version of
 | |
|  * the default browser easing curve.
 | |
|  */
 | |
| const ease = {
 | |
|     type: "keyframes",
 | |
|     ease: [0.25, 0.1, 0.35, 1],
 | |
|     duration: 0.3,
 | |
| };
 | |
| const getDefaultTransition = (valueKey, { keyframes }) => {
 | |
|     if (keyframes.length > 2) {
 | |
|         return keyframesTransition;
 | |
|     }
 | |
|     else if (transformProps.has(valueKey)) {
 | |
|         return valueKey.startsWith("scale")
 | |
|             ? criticallyDampedSpring(keyframes[1])
 | |
|             : underDampedSpring;
 | |
|     }
 | |
|     return ease;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Check if a value is animatable. Examples:
 | |
|  *
 | |
|  * ✅: 100, "100px", "#fff"
 | |
|  * ❌: "block", "url(2.jpg)"
 | |
|  * @param value
 | |
|  *
 | |
|  * @internal
 | |
|  */
 | |
| const isAnimatable = (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
 | |
|         (complex.test(value) || value === "0") && // And it contains numbers and/or colors
 | |
|         !value.startsWith("url(") // Unless it starts with "url("
 | |
|     ) {
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Properties that should default to 1 or 100%
 | |
|  */
 | |
| const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
 | |
| function applyDefaultFilter(v) {
 | |
|     const [name, value] = v.slice(0, -1).split("(");
 | |
|     if (name === "drop-shadow")
 | |
|         return v;
 | |
|     const [number] = value.match(floatRegex) || [];
 | |
|     if (!number)
 | |
|         return v;
 | |
|     const unit = value.replace(number, "");
 | |
|     let defaultValue = maxDefaults.has(name) ? 1 : 0;
 | |
|     if (number !== value)
 | |
|         defaultValue *= 100;
 | |
|     return name + "(" + defaultValue + unit + ")";
 | |
| }
 | |
| const functionRegex = /([a-z-]*)\(.*?\)/g;
 | |
| const filter = {
 | |
|     ...complex,
 | |
|     getAnimatableNone: (v) => {
 | |
|         const functions = v.match(functionRegex);
 | |
|         return functions ? functions.map(applyDefaultFilter).join(" ") : v;
 | |
|     },
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * A map of default value types for common values
 | |
|  */
 | |
| const defaultValueTypes = {
 | |
|     ...numberValueTypes,
 | |
|     // Color props
 | |
|     color,
 | |
|     backgroundColor: color,
 | |
|     outlineColor: color,
 | |
|     fill: color,
 | |
|     stroke: color,
 | |
|     // Border props
 | |
|     borderColor: color,
 | |
|     borderTopColor: color,
 | |
|     borderRightColor: color,
 | |
|     borderBottomColor: color,
 | |
|     borderLeftColor: color,
 | |
|     filter,
 | |
|     WebkitFilter: filter,
 | |
| };
 | |
| /**
 | |
|  * Gets the default ValueType for the provided value key
 | |
|  */
 | |
| const getDefaultValueType = (key) => defaultValueTypes[key];
 | |
| 
 | |
| function getAnimatableNone(key, value) {
 | |
|     let defaultValueType = getDefaultValueType(key);
 | |
|     if (defaultValueType !== filter)
 | |
|         defaultValueType = complex;
 | |
|     // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
 | |
|     return defaultValueType.getAnimatableNone
 | |
|         ? defaultValueType.getAnimatableNone(value)
 | |
|         : undefined;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check if the value is a zero value string like "0px" or "0%"
 | |
|  */
 | |
| const isZeroValueString = (v) => /^0[^.\s]+$/.test(v);
 | |
| 
 | |
| function isNone(value) {
 | |
|     if (typeof value === "number") {
 | |
|         return value === 0;
 | |
|     }
 | |
|     else if (value !== null) {
 | |
|         return value === "none" || value === "0" || isZeroValueString(value);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function getKeyframes(value, valueName, target, transition) {
 | |
|     const isTargetAnimatable = isAnimatable(valueName, target);
 | |
|     let keyframes;
 | |
|     if (Array.isArray(target)) {
 | |
|         keyframes = [...target];
 | |
|     }
 | |
|     else {
 | |
|         keyframes = [null, target];
 | |
|     }
 | |
|     const defaultOrigin = transition.from !== undefined ? transition.from : value.get();
 | |
|     let animatableTemplateValue = undefined;
 | |
|     const noneKeyframeIndexes = [];
 | |
|     for (let i = 0; i < keyframes.length; i++) {
 | |
|         /**
 | |
|          * Fill null/wildcard keyframes
 | |
|          */
 | |
|         if (keyframes[i] === null) {
 | |
|             keyframes[i] = i === 0 ? defaultOrigin : keyframes[i - 1];
 | |
|         }
 | |
|         if (isNone(keyframes[i])) {
 | |
|             noneKeyframeIndexes.push(i);
 | |
|         }
 | |
|         // TODO: Clean this conditional, it works for now
 | |
|         if (typeof keyframes[i] === "string" &&
 | |
|             keyframes[i] !== "none" &&
 | |
|             keyframes[i] !== "0") {
 | |
|             animatableTemplateValue = keyframes[i];
 | |
|         }
 | |
|     }
 | |
|     if (isTargetAnimatable &&
 | |
|         noneKeyframeIndexes.length &&
 | |
|         animatableTemplateValue) {
 | |
|         for (let i = 0; i < noneKeyframeIndexes.length; i++) {
 | |
|             const index = noneKeyframeIndexes[i];
 | |
|             keyframes[index] = getAnimatableNone(valueName, animatableTemplateValue);
 | |
|         }
 | |
|     }
 | |
|     return keyframes;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
 | |
|     return !!Object.keys(transition).length;
 | |
| }
 | |
| function getValueTransition$1(transition, key) {
 | |
|     return transition[key] || transition["default"] || transition;
 | |
| }
 | |
| 
 | |
| const MotionGlobalConfig = {
 | |
|     skipAnimations: false,
 | |
| };
 | |
| 
 | |
| const animateMotionValue = (valueName, value, target, transition = {}) => {
 | |
|     return (onComplete) => {
 | |
|         const valueTransition = getValueTransition$1(transition, valueName) || {};
 | |
|         /**
 | |
|          * Most transition values are currently completely overwritten by value-specific
 | |
|          * transitions. In the future it'd be nicer to blend these transitions. But for now
 | |
|          * delay actually does inherit from the root transition if not value-specific.
 | |
|          */
 | |
|         const delay = valueTransition.delay || transition.delay || 0;
 | |
|         /**
 | |
|          * Elapsed isn't a public transition option but can be passed through from
 | |
|          * optimized appear effects in milliseconds.
 | |
|          */
 | |
|         let { elapsed = 0 } = transition;
 | |
|         elapsed = elapsed - secondsToMilliseconds(delay);
 | |
|         const keyframes = getKeyframes(value, valueName, target, valueTransition);
 | |
|         /**
 | |
|          * Check if we're able to animate between the start and end keyframes,
 | |
|          * and throw a warning if we're attempting to animate between one that's
 | |
|          * animatable and another that isn't.
 | |
|          */
 | |
|         const originKeyframe = keyframes[0];
 | |
|         const targetKeyframe = keyframes[keyframes.length - 1];
 | |
|         const isOriginAnimatable = isAnimatable(valueName, originKeyframe);
 | |
|         const isTargetAnimatable = isAnimatable(valueName, targetKeyframe);
 | |
|         exports.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${valueName} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
 | |
|         let options = {
 | |
|             keyframes,
 | |
|             velocity: value.getVelocity(),
 | |
|             ease: "easeOut",
 | |
|             ...valueTransition,
 | |
|             delay: -elapsed,
 | |
|             onUpdate: (v) => {
 | |
|                 value.set(v);
 | |
|                 valueTransition.onUpdate && valueTransition.onUpdate(v);
 | |
|             },
 | |
|             onComplete: () => {
 | |
|                 onComplete();
 | |
|                 valueTransition.onComplete && valueTransition.onComplete();
 | |
|             },
 | |
|         };
 | |
|         /**
 | |
|          * If there's no transition defined for this value, we can generate
 | |
|          * unqiue transition settings for this value.
 | |
|          */
 | |
|         if (!isTransitionDefined(valueTransition)) {
 | |
|             options = {
 | |
|                 ...options,
 | |
|                 ...getDefaultTransition(valueName, options),
 | |
|             };
 | |
|         }
 | |
|         /**
 | |
|          * Both WAAPI and our internal animation functions use durations
 | |
|          * as defined by milliseconds, while our external API defines them
 | |
|          * as seconds.
 | |
|          */
 | |
|         if (options.duration) {
 | |
|             options.duration = secondsToMilliseconds(options.duration);
 | |
|         }
 | |
|         if (options.repeatDelay) {
 | |
|             options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
 | |
|         }
 | |
|         if (!isOriginAnimatable ||
 | |
|             !isTargetAnimatable ||
 | |
|             instantAnimationState.current ||
 | |
|             valueTransition.type === false ||
 | |
|             MotionGlobalConfig.skipAnimations) {
 | |
|             /**
 | |
|              * If we can't animate this value, or the global instant animation flag is set,
 | |
|              * or this is simply defined as an instant transition, return an instant transition.
 | |
|              */
 | |
|             return createInstantAnimation(instantAnimationState.current
 | |
|                 ? { ...options, delay: 0 }
 | |
|                 : options);
 | |
|         }
 | |
|         /**
 | |
|          * Animate via WAAPI if possible.
 | |
|          */
 | |
|         if (
 | |
|         /**
 | |
|          * If this is a handoff animation, the optimised animation will be running via
 | |
|          * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
 | |
|          * optimised animation.
 | |
|          */
 | |
|         !transition.isHandoff &&
 | |
|             value.owner &&
 | |
|             value.owner.current instanceof HTMLElement &&
 | |
|             /**
 | |
|              * If we're outputting values to onUpdate then we can't use WAAPI as there's
 | |
|              * no way to read the value from WAAPI every frame.
 | |
|              */
 | |
|             !value.owner.getProps().onUpdate) {
 | |
|             const acceleratedAnimation = createAcceleratedAnimation(value, valueName, options);
 | |
|             if (acceleratedAnimation)
 | |
|                 return acceleratedAnimation;
 | |
|         }
 | |
|         /**
 | |
|          * If we didn't create an accelerated animation, create a JS animation
 | |
|          */
 | |
|         return animateValue(options);
 | |
|     };
 | |
| };
 | |
| 
 | |
| function isWillChangeMotionValue(value) {
 | |
|     return Boolean(isMotionValue(value) && value.add);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
 | |
|  */
 | |
| const isNumericalString = (v) => /^\-?\d*\.?\d+$/.test(v);
 | |
| 
 | |
| function addUniqueItem(arr, item) {
 | |
|     if (arr.indexOf(item) === -1)
 | |
|         arr.push(item);
 | |
| }
 | |
| function removeItem(arr, item) {
 | |
|     const index = arr.indexOf(item);
 | |
|     if (index > -1)
 | |
|         arr.splice(index, 1);
 | |
| }
 | |
| // Adapted from array-move
 | |
| function moveItem([...arr], fromIndex, toIndex) {
 | |
|     const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
 | |
|     if (startIndex >= 0 && startIndex < arr.length) {
 | |
|         const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
 | |
|         const [item] = arr.splice(fromIndex, 1);
 | |
|         arr.splice(endIndex, 0, item);
 | |
|     }
 | |
|     return arr;
 | |
| }
 | |
| 
 | |
| class SubscriptionManager {
 | |
|     constructor() {
 | |
|         this.subscriptions = [];
 | |
|     }
 | |
|     add(handler) {
 | |
|         addUniqueItem(this.subscriptions, handler);
 | |
|         return () => removeItem(this.subscriptions, handler);
 | |
|     }
 | |
|     notify(a, b, c) {
 | |
|         const 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 (let i = 0; i < numSubscriptions; i++) {
 | |
|                 /**
 | |
|                  * Check whether the handler exists before firing as it's possible
 | |
|                  * the subscriptions were modified during this loop running.
 | |
|                  */
 | |
|                 const handler = this.subscriptions[i];
 | |
|                 handler && handler(a, b, c);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     getSize() {
 | |
|         return this.subscriptions.length;
 | |
|     }
 | |
|     clear() {
 | |
|         this.subscriptions.length = 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| const 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);
 | |
| }
 | |
| 
 | |
| const isFloat = (value) => {
 | |
|     return !isNaN(parseFloat(value));
 | |
| };
 | |
| const collectMotionValues = {
 | |
|     current: undefined,
 | |
| };
 | |
| /**
 | |
|  * `MotionValue` is used to track the state and velocity of motion values.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| class MotionValue {
 | |
|     /**
 | |
|      * @param init - The initiating value
 | |
|      * @param config - Optional configuration options
 | |
|      *
 | |
|      * -  `transformer`: A function to transform incoming values with.
 | |
|      *
 | |
|      * @internal
 | |
|      */
 | |
|     constructor(init, options = {}) {
 | |
|         /**
 | |
|          * 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 = "10.18.0";
 | |
|         /**
 | |
|          * Duration, in milliseconds, since last updating frame.
 | |
|          *
 | |
|          * @internal
 | |
|          */
 | |
|         this.timeDelta = 0;
 | |
|         /**
 | |
|          * Timestamp of the last time this `MotionValue` was updated.
 | |
|          *
 | |
|          * @internal
 | |
|          */
 | |
|         this.lastUpdated = 0;
 | |
|         /**
 | |
|          * Tracks whether this value can output a velocity. Currently this is only true
 | |
|          * if the value is numerical, but we might be able to widen the scope here and support
 | |
|          * other value types.
 | |
|          *
 | |
|          * @internal
 | |
|          */
 | |
|         this.canTrackVelocity = false;
 | |
|         /**
 | |
|          * An object containing a SubscriptionManager for each active event.
 | |
|          */
 | |
|         this.events = {};
 | |
|         this.updateAndNotify = (v, render = true) => {
 | |
|             this.prev = this.current;
 | |
|             this.current = v;
 | |
|             // Update timestamp
 | |
|             const { delta, timestamp } = frameData;
 | |
|             if (this.lastUpdated !== timestamp) {
 | |
|                 this.timeDelta = delta;
 | |
|                 this.lastUpdated = timestamp;
 | |
|                 frame.postRender(this.scheduleVelocityCheck);
 | |
|             }
 | |
|             // Update update subscribers
 | |
|             if (this.prev !== this.current && this.events.change) {
 | |
|                 this.events.change.notify(this.current);
 | |
|             }
 | |
|             // Update velocity subscribers
 | |
|             if (this.events.velocityChange) {
 | |
|                 this.events.velocityChange.notify(this.getVelocity());
 | |
|             }
 | |
|             // Update render subscribers
 | |
|             if (render && this.events.renderRequest) {
 | |
|                 this.events.renderRequest.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 = () => frame.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 = ({ timestamp }) => {
 | |
|             if (timestamp !== this.lastUpdated) {
 | |
|                 this.prev = this.current;
 | |
|                 if (this.events.velocityChange) {
 | |
|                     this.events.velocityChange.notify(this.getVelocity());
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|         this.hasAnimated = false;
 | |
|         this.prev = this.current = init;
 | |
|         this.canTrackVelocity = isFloat(this.current);
 | |
|         this.owner = options.owner;
 | |
|     }
 | |
|     /**
 | |
|      * 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.on("change", updateOpacity)
 | |
|      *     const unsubscribeY = y.on("change", updateOpacity)
 | |
|      *
 | |
|      *     return () => {
 | |
|      *       unsubscribeX()
 | |
|      *       unsubscribeY()
 | |
|      *     }
 | |
|      *   }, [])
 | |
|      *
 | |
|      *   return <motion.div style={{ x }} />
 | |
|      * }
 | |
|      * ```
 | |
|      *
 | |
|      * @param subscriber - A function that receives the latest value.
 | |
|      * @returns A function that, when called, will cancel this subscription.
 | |
|      *
 | |
|      * @deprecated
 | |
|      */
 | |
|     onChange(subscription) {
 | |
|         if (process.env.NODE_ENV !== "production") {
 | |
|             warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`);
 | |
|         }
 | |
|         return this.on("change", subscription);
 | |
|     }
 | |
|     on(eventName, callback) {
 | |
|         if (!this.events[eventName]) {
 | |
|             this.events[eventName] = new SubscriptionManager();
 | |
|         }
 | |
|         const unsubscribe = this.events[eventName].add(callback);
 | |
|         if (eventName === "change") {
 | |
|             return () => {
 | |
|                 unsubscribe();
 | |
|                 /**
 | |
|                  * If we have no more change listeners by the start
 | |
|                  * of the next frame, stop active animations.
 | |
|                  */
 | |
|                 frame.read(() => {
 | |
|                     if (!this.events.change.getSize()) {
 | |
|                         this.stop();
 | |
|                     }
 | |
|                 });
 | |
|             };
 | |
|         }
 | |
|         return unsubscribe;
 | |
|     }
 | |
|     clearListeners() {
 | |
|         for (const eventManagers in this.events) {
 | |
|             this.events[eventManagers].clear();
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Attaches a passive effect to the `MotionValue`.
 | |
|      *
 | |
|      * @internal
 | |
|      */
 | |
|     attach(passiveEffect, stopPassiveEffect) {
 | |
|         this.passiveEffect = passiveEffect;
 | |
|         this.stopPassiveEffect = stopPassiveEffect;
 | |
|     }
 | |
|     /**
 | |
|      * 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
 | |
|      */
 | |
|     set(v, render = true) {
 | |
|         if (!render || !this.passiveEffect) {
 | |
|             this.updateAndNotify(v, render);
 | |
|         }
 | |
|         else {
 | |
|             this.passiveEffect(v, this.updateAndNotify);
 | |
|         }
 | |
|     }
 | |
|     setWithVelocity(prev, current, delta) {
 | |
|         this.set(current);
 | |
|         this.prev = prev;
 | |
|         this.timeDelta = delta;
 | |
|     }
 | |
|     /**
 | |
|      * Set the state of the `MotionValue`, stopping any active animations,
 | |
|      * effects, and resets velocity to `0`.
 | |
|      */
 | |
|     jump(v) {
 | |
|         this.updateAndNotify(v);
 | |
|         this.prev = v;
 | |
|         this.stop();
 | |
|         if (this.stopPassiveEffect)
 | |
|             this.stopPassiveEffect();
 | |
|     }
 | |
|     /**
 | |
|      * Returns the latest state of `MotionValue`
 | |
|      *
 | |
|      * @returns - The latest state of `MotionValue`
 | |
|      *
 | |
|      * @public
 | |
|      */
 | |
|     get() {
 | |
|         if (collectMotionValues.current) {
 | |
|             collectMotionValues.current.push(this);
 | |
|         }
 | |
|         return this.current;
 | |
|     }
 | |
|     /**
 | |
|      * @public
 | |
|      */
 | |
|     getPrevious() {
 | |
|         return this.prev;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the latest velocity of `MotionValue`
 | |
|      *
 | |
|      * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
 | |
|      *
 | |
|      * @public
 | |
|      */
 | |
|     getVelocity() {
 | |
|         // 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
 | |
|                 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
 | |
|      */
 | |
|     start(startAnimation) {
 | |
|         this.stop();
 | |
|         return new Promise((resolve) => {
 | |
|             this.hasAnimated = true;
 | |
|             this.animation = startAnimation(resolve);
 | |
|             if (this.events.animationStart) {
 | |
|                 this.events.animationStart.notify();
 | |
|             }
 | |
|         }).then(() => {
 | |
|             if (this.events.animationComplete) {
 | |
|                 this.events.animationComplete.notify();
 | |
|             }
 | |
|             this.clearAnimation();
 | |
|         });
 | |
|     }
 | |
|     /**
 | |
|      * Stop the currently active animation.
 | |
|      *
 | |
|      * @public
 | |
|      */
 | |
|     stop() {
 | |
|         if (this.animation) {
 | |
|             this.animation.stop();
 | |
|             if (this.events.animationCancel) {
 | |
|                 this.events.animationCancel.notify();
 | |
|             }
 | |
|         }
 | |
|         this.clearAnimation();
 | |
|     }
 | |
|     /**
 | |
|      * Returns `true` if this value is currently animating.
 | |
|      *
 | |
|      * @public
 | |
|      */
 | |
|     isAnimating() {
 | |
|         return !!this.animation;
 | |
|     }
 | |
|     clearAnimation() {
 | |
|         delete this.animation;
 | |
|     }
 | |
|     /**
 | |
|      * 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
 | |
|      */
 | |
|     destroy() {
 | |
|         this.clearListeners();
 | |
|         this.stop();
 | |
|         if (this.stopPassiveEffect) {
 | |
|             this.stopPassiveEffect();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| function motionValue(init, options) {
 | |
|     return new MotionValue(init, options);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Tests a provided value against a ValueType
 | |
|  */
 | |
| const testValueType = (v) => (type) => type.test(v);
 | |
| 
 | |
| /**
 | |
|  * ValueType for "auto"
 | |
|  */
 | |
| const auto = {
 | |
|     test: (v) => v === "auto",
 | |
|     parse: (v) => v,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * A list of value types commonly used for dimensions
 | |
|  */
 | |
| const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
 | |
| /**
 | |
|  * Tests a dimensional value against the list of dimension ValueTypes
 | |
|  */
 | |
| const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
 | |
| 
 | |
| /**
 | |
|  * A list of all ValueTypes
 | |
|  */
 | |
| const valueTypes = [...dimensionValueTypes, color, complex];
 | |
| /**
 | |
|  * Tests a value against the list of ValueTypes
 | |
|  */
 | |
| const findValueType = (v) => 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) {
 | |
|     const resolved = resolveVariant(visualElement, definition);
 | |
|     let { transitionEnd = {}, transition = {}, ...target } = resolved ? visualElement.makeTargetAnimatable(resolved, false) : {};
 | |
|     target = { ...target, ...transitionEnd };
 | |
|     for (const key in target) {
 | |
|         const value = resolveFinalValueInKeyframes(target[key]);
 | |
|         setMotionValue(visualElement, key, value);
 | |
|     }
 | |
| }
 | |
| function setVariants(visualElement, variantLabels) {
 | |
|     const reversedLabels = [...variantLabels].reverse();
 | |
|     reversedLabels.forEach((key) => {
 | |
|         const variant = visualElement.getVariant(key);
 | |
|         variant && setTarget(visualElement, variant);
 | |
|         if (visualElement.variantChildren) {
 | |
|             visualElement.variantChildren.forEach((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;
 | |
|     const newValueKeys = Object.keys(target).filter((key) => !visualElement.hasValue(key));
 | |
|     const numNewValues = newValueKeys.length;
 | |
|     if (!numNewValues)
 | |
|         return;
 | |
|     for (let i = 0; i < numNewValues; i++) {
 | |
|         const key = newValueKeys[i];
 | |
|         const targetValue = target[key];
 | |
|         let 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) && complex.test(targetValue)) {
 | |
|             value = getAnimatableNone(key, targetValue);
 | |
|         }
 | |
|         visualElement.addValue(key, motionValue(value, { owner: visualElement }));
 | |
|         if (origin[key] === undefined) {
 | |
|             origin[key] = value;
 | |
|         }
 | |
|         if (value !== null)
 | |
|             visualElement.setBaseTarget(key, value);
 | |
|     }
 | |
| }
 | |
| function getOriginFromTransition(key, transition) {
 | |
|     if (!transition)
 | |
|         return;
 | |
|     const valueTransition = transition[key] || transition["default"] || transition;
 | |
|     return valueTransition.from;
 | |
| }
 | |
| function getOrigin(target, transition, visualElement) {
 | |
|     const origin = {};
 | |
|     for (const key in target) {
 | |
|         const transitionOrigin = getOriginFromTransition(key, transition);
 | |
|         if (transitionOrigin !== undefined) {
 | |
|             origin[key] = transitionOrigin;
 | |
|         }
 | |
|         else {
 | |
|             const value = visualElement.getValue(key);
 | |
|             if (value) {
 | |
|                 origin[key] = value.get();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return origin;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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({ protectedKeys, needsAnimating }, key) {
 | |
|     const shouldBlock = protectedKeys.hasOwnProperty(key) && needsAnimating[key] !== true;
 | |
|     needsAnimating[key] = false;
 | |
|     return shouldBlock;
 | |
| }
 | |
| function hasKeyframesChanged(value, target) {
 | |
|     const current = value.get();
 | |
|     if (Array.isArray(target)) {
 | |
|         for (let i = 0; i < target.length; i++) {
 | |
|             if (target[i] !== current)
 | |
|                 return true;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         return current !== target;
 | |
|     }
 | |
| }
 | |
| function animateTarget(visualElement, definition, { delay = 0, transitionOverride, type } = {}) {
 | |
|     let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = visualElement.makeTargetAnimatable(definition);
 | |
|     const willChange = visualElement.getValue("willChange");
 | |
|     if (transitionOverride)
 | |
|         transition = transitionOverride;
 | |
|     const animations = [];
 | |
|     const animationTypeState = type &&
 | |
|         visualElement.animationState &&
 | |
|         visualElement.animationState.getState()[type];
 | |
|     for (const key in target) {
 | |
|         const value = visualElement.getValue(key);
 | |
|         const valueTarget = target[key];
 | |
|         if (!value ||
 | |
|             valueTarget === undefined ||
 | |
|             (animationTypeState &&
 | |
|                 shouldBlockAnimation(animationTypeState, key))) {
 | |
|             continue;
 | |
|         }
 | |
|         const valueTransition = {
 | |
|             delay,
 | |
|             elapsed: 0,
 | |
|             ...getValueTransition$1(transition || {}, key),
 | |
|         };
 | |
|         /**
 | |
|          * If this is the first time a value is being animated, check
 | |
|          * to see if we're handling off from an existing animation.
 | |
|          */
 | |
|         if (window.HandoffAppearAnimations) {
 | |
|             const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
 | |
|             if (appearId) {
 | |
|                 const elapsed = window.HandoffAppearAnimations(appearId, key, value, frame);
 | |
|                 if (elapsed !== null) {
 | |
|                     valueTransition.elapsed = elapsed;
 | |
|                     valueTransition.isHandoff = true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         let canSkip = !valueTransition.isHandoff &&
 | |
|             !hasKeyframesChanged(value, valueTarget);
 | |
|         if (valueTransition.type === "spring" &&
 | |
|             (value.getVelocity() || valueTransition.velocity)) {
 | |
|             canSkip = false;
 | |
|         }
 | |
|         /**
 | |
|          * Temporarily disable skipping animations if there's an animation in
 | |
|          * progress. Better would be to track the current target of a value
 | |
|          * and compare that against valueTarget.
 | |
|          */
 | |
|         if (value.animation) {
 | |
|             canSkip = false;
 | |
|         }
 | |
|         if (canSkip)
 | |
|             continue;
 | |
|         value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && transformProps.has(key)
 | |
|             ? { type: false }
 | |
|             : valueTransition));
 | |
|         const animation = value.animation;
 | |
|         if (isWillChangeMotionValue(willChange)) {
 | |
|             willChange.add(key);
 | |
|             animation.then(() => willChange.remove(key));
 | |
|         }
 | |
|         animations.push(animation);
 | |
|     }
 | |
|     if (transitionEnd) {
 | |
|         Promise.all(animations).then(() => {
 | |
|             transitionEnd && setTarget(visualElement, transitionEnd);
 | |
|         });
 | |
|     }
 | |
|     return animations;
 | |
| }
 | |
| 
 | |
| const distance = (a, b) => Math.abs(a - b);
 | |
| function distance2D(a, b) {
 | |
|     // Multi-dimensional
 | |
|     const xDelta = distance(a.x, b.x);
 | |
|     const yDelta = distance(a.y, b.y);
 | |
|     return Math.sqrt(xDelta ** 2 + yDelta ** 2);
 | |
| }
 | |
| 
 | |
| const createAxisDelta = () => ({
 | |
|     translate: 0,
 | |
|     scale: 1,
 | |
|     origin: 0,
 | |
|     originPoint: 0,
 | |
| });
 | |
| const createDelta = () => ({
 | |
|     x: createAxisDelta(),
 | |
|     y: createAxisDelta(),
 | |
| });
 | |
| const createAxis = () => ({ min: 0, max: 0 });
 | |
| const createBox = () => ({
 | |
|     x: createAxis(),
 | |
|     y: createAxis(),
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * Bounding boxes tend to be defined as top, left, right, bottom. For various operations
 | |
|  * it's easier to consider each axis individually. This function returns a bounding box
 | |
|  * as a map of single-axis min/max values.
 | |
|  */
 | |
| function convertBoundingBoxToBox({ top, left, right, bottom, }) {
 | |
|     return {
 | |
|         x: { min: left, max: right },
 | |
|         y: { min: top, max: bottom },
 | |
|     };
 | |
| }
 | |
| function convertBoxToBoundingBox({ x, y }) {
 | |
|     return { top: y.min, right: x.max, bottom: y.max, left: x.min };
 | |
| }
 | |
| /**
 | |
|  * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function
 | |
|  * provided by Framer to allow measured points to be corrected for device scaling. This is used
 | |
|  * when measuring DOM elements and DOM event points.
 | |
|  */
 | |
| function transformBoxPoints(point, transformPoint) {
 | |
|     if (!transformPoint)
 | |
|         return point;
 | |
|     const topLeft = transformPoint({ x: point.left, y: point.top });
 | |
|     const bottomRight = transformPoint({ x: point.right, y: point.bottom });
 | |
|     return {
 | |
|         top: topLeft.y,
 | |
|         left: topLeft.x,
 | |
|         bottom: bottomRight.y,
 | |
|         right: bottomRight.x,
 | |
|     };
 | |
| }
 | |
| 
 | |
| function isIdentityScale(scale) {
 | |
|     return scale === undefined || scale === 1;
 | |
| }
 | |
| function hasScale({ scale, scaleX, scaleY }) {
 | |
|     return (!isIdentityScale(scale) ||
 | |
|         !isIdentityScale(scaleX) ||
 | |
|         !isIdentityScale(scaleY));
 | |
| }
 | |
| function hasTransform(values) {
 | |
|     return (hasScale(values) ||
 | |
|         has2DTranslate(values) ||
 | |
|         values.z ||
 | |
|         values.rotate ||
 | |
|         values.rotateX ||
 | |
|         values.rotateY);
 | |
| }
 | |
| function has2DTranslate(values) {
 | |
|     return is2DTranslate(values.x) || is2DTranslate(values.y);
 | |
| }
 | |
| function is2DTranslate(value) {
 | |
|     return value && value !== "0%";
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Scales a point based on a factor and an originPoint
 | |
|  */
 | |
| function scalePoint(point, scale, originPoint) {
 | |
|     const distanceFromOrigin = point - originPoint;
 | |
|     const scaled = scale * distanceFromOrigin;
 | |
|     return originPoint + scaled;
 | |
| }
 | |
| /**
 | |
|  * Applies a translate/scale delta to a point
 | |
|  */
 | |
| function applyPointDelta(point, translate, scale, originPoint, boxScale) {
 | |
|     if (boxScale !== undefined) {
 | |
|         point = scalePoint(point, boxScale, originPoint);
 | |
|     }
 | |
|     return scalePoint(point, scale, originPoint) + translate;
 | |
| }
 | |
| /**
 | |
|  * Applies a translate/scale delta to an axis
 | |
|  */
 | |
| function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) {
 | |
|     axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale);
 | |
|     axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale);
 | |
| }
 | |
| /**
 | |
|  * Applies a translate/scale delta to a box
 | |
|  */
 | |
| function applyBoxDelta(box, { x, y }) {
 | |
|     applyAxisDelta(box.x, x.translate, x.scale, x.originPoint);
 | |
|     applyAxisDelta(box.y, y.translate, y.scale, y.originPoint);
 | |
| }
 | |
| /**
 | |
|  * Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms
 | |
|  * in a tree upon our box before then calculating how to project it into our desired viewport-relative box
 | |
|  *
 | |
|  * This is the final nested loop within updateLayoutDelta for future refactoring
 | |
|  */
 | |
| function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
 | |
|     const treeLength = treePath.length;
 | |
|     if (!treeLength)
 | |
|         return;
 | |
|     // Reset the treeScale
 | |
|     treeScale.x = treeScale.y = 1;
 | |
|     let node;
 | |
|     let delta;
 | |
|     for (let i = 0; i < treeLength; i++) {
 | |
|         node = treePath[i];
 | |
|         delta = node.projectionDelta;
 | |
|         /**
 | |
|          * TODO: Prefer to remove this, but currently we have motion components with
 | |
|          * display: contents in Framer.
 | |
|          */
 | |
|         const instance = node.instance;
 | |
|         if (instance &&
 | |
|             instance.style &&
 | |
|             instance.style.display === "contents") {
 | |
|             continue;
 | |
|         }
 | |
|         if (isSharedTransition &&
 | |
|             node.options.layoutScroll &&
 | |
|             node.scroll &&
 | |
|             node !== node.root) {
 | |
|             transformBox(box, {
 | |
|                 x: -node.scroll.offset.x,
 | |
|                 y: -node.scroll.offset.y,
 | |
|             });
 | |
|         }
 | |
|         if (delta) {
 | |
|             // Incoporate each ancestor's scale into a culmulative treeScale for this component
 | |
|             treeScale.x *= delta.x.scale;
 | |
|             treeScale.y *= delta.y.scale;
 | |
|             // Apply each ancestor's calculated delta into this component's recorded layout box
 | |
|             applyBoxDelta(box, delta);
 | |
|         }
 | |
|         if (isSharedTransition && hasTransform(node.latestValues)) {
 | |
|             transformBox(box, node.latestValues);
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Snap tree scale back to 1 if it's within a non-perceivable threshold.
 | |
|      * This will help reduce useless scales getting rendered.
 | |
|      */
 | |
|     treeScale.x = snapToDefault(treeScale.x);
 | |
|     treeScale.y = snapToDefault(treeScale.y);
 | |
| }
 | |
| function snapToDefault(scale) {
 | |
|     if (Number.isInteger(scale))
 | |
|         return scale;
 | |
|     return scale > 1.0000000000001 || scale < 0.999999999999 ? scale : 1;
 | |
| }
 | |
| function translateAxis(axis, distance) {
 | |
|     axis.min = axis.min + distance;
 | |
|     axis.max = axis.max + distance;
 | |
| }
 | |
| /**
 | |
|  * Apply a transform to an axis from the latest resolved motion values.
 | |
|  * This function basically acts as a bridge between a flat motion value map
 | |
|  * and applyAxisDelta
 | |
|  */
 | |
| function transformAxis(axis, transforms, [key, scaleKey, originKey]) {
 | |
|     const axisOrigin = transforms[originKey] !== undefined ? transforms[originKey] : 0.5;
 | |
|     const originPoint = mix(axis.min, axis.max, axisOrigin);
 | |
|     // Apply the axis delta to the final axis
 | |
|     applyAxisDelta(axis, transforms[key], transforms[scaleKey], originPoint, transforms.scale);
 | |
| }
 | |
| /**
 | |
|  * The names of the motion values we want to apply as translation, scale and origin.
 | |
|  */
 | |
| const xKeys = ["x", "scaleX", "originX"];
 | |
| const yKeys = ["y", "scaleY", "originY"];
 | |
| /**
 | |
|  * Apply a transform to a box from the latest resolved motion values.
 | |
|  */
 | |
| function transformBox(box, transform) {
 | |
|     transformAxis(box.x, transform, xKeys);
 | |
|     transformAxis(box.y, transform, yKeys);
 | |
| }
 | |
| 
 | |
| function measureViewportBox(instance, transformPoint) {
 | |
|     return convertBoundingBoxToBox(transformBoxPoints(instance.getBoundingClientRect(), transformPoint));
 | |
| }
 | |
| function measurePageBox(element, rootProjectionNode, transformPagePoint) {
 | |
|     const viewportBox = measureViewportBox(element, transformPagePoint);
 | |
|     const { scroll } = rootProjectionNode;
 | |
|     if (scroll) {
 | |
|         translateAxis(viewportBox.x, scroll.offset.x);
 | |
|         translateAxis(viewportBox.y, scroll.offset.y);
 | |
|     }
 | |
|     return viewportBox;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Timeout defined in ms
 | |
|  */
 | |
| function delay(callback, timeout) {
 | |
|     const start = performance.now();
 | |
|     const checkElapsed = ({ timestamp }) => {
 | |
|         const elapsed = timestamp - start;
 | |
|         if (elapsed >= timeout) {
 | |
|             cancelFrame(checkElapsed);
 | |
|             callback(elapsed - timeout);
 | |
|         }
 | |
|     };
 | |
|     frame.read(checkElapsed, true);
 | |
|     return () => cancelFrame(checkElapsed);
 | |
| }
 | |
| 
 | |
| function resolveElements(elements, scope, selectorCache) {
 | |
|     var _a;
 | |
|     if (typeof elements === "string") {
 | |
|         let root = document;
 | |
|         if (scope) {
 | |
|             exports.invariant(Boolean(scope.current), "Scope provided, but no element detected.");
 | |
|             root = scope.current;
 | |
|         }
 | |
|         if (selectorCache) {
 | |
|             (_a = selectorCache[elements]) !== null && _a !== void 0 ? _a : (selectorCache[elements] = root.querySelectorAll(elements));
 | |
|             elements = selectorCache[elements];
 | |
|         }
 | |
|         else {
 | |
|             elements = root.querySelectorAll(elements);
 | |
|         }
 | |
|     }
 | |
|     else if (elements instanceof Element) {
 | |
|         elements = [elements];
 | |
|     }
 | |
|     /**
 | |
|      * Return an empty array
 | |
|      */
 | |
|     return Array.from(elements || []);
 | |
| }
 | |
| 
 | |
| const visualElementStore = new WeakMap();
 | |
| 
 | |
| function observeTimeline(update, timeline) {
 | |
|     let prevProgress;
 | |
|     const onFrame = () => {
 | |
|         const { currentTime } = timeline;
 | |
|         const percentage = currentTime === null ? 0 : currentTime.value;
 | |
|         const progress = percentage / 100;
 | |
|         if (prevProgress !== progress) {
 | |
|             update(progress);
 | |
|         }
 | |
|         prevProgress = progress;
 | |
|     };
 | |
|     frame.update(onFrame, true);
 | |
|     return () => cancelFrame(onFrame);
 | |
| }
 | |
| 
 | |
| const supportsScrollTimeline = memo(() => window.ScrollTimeline !== undefined);
 | |
| 
 | |
| class GroupPlaybackControls {
 | |
|     constructor(animations) {
 | |
|         this.animations = animations.filter(Boolean);
 | |
|     }
 | |
|     then(onResolve, onReject) {
 | |
|         return Promise.all(this.animations).then(onResolve).catch(onReject);
 | |
|     }
 | |
|     /**
 | |
|      * TODO: Filter out cancelled or stopped animations before returning
 | |
|      */
 | |
|     getAll(propName) {
 | |
|         return this.animations[0][propName];
 | |
|     }
 | |
|     setAll(propName, newValue) {
 | |
|         for (let i = 0; i < this.animations.length; i++) {
 | |
|             this.animations[i][propName] = newValue;
 | |
|         }
 | |
|     }
 | |
|     attachTimeline(timeline) {
 | |
|         const cancelAll = this.animations.map((animation) => {
 | |
|             if (supportsScrollTimeline() && animation.attachTimeline) {
 | |
|                 animation.attachTimeline(timeline);
 | |
|             }
 | |
|             else {
 | |
|                 animation.pause();
 | |
|                 return observeTimeline((progress) => {
 | |
|                     animation.time = animation.duration * progress;
 | |
|                 }, timeline);
 | |
|             }
 | |
|         });
 | |
|         return () => {
 | |
|             cancelAll.forEach((cancelTimeline, i) => {
 | |
|                 if (cancelTimeline)
 | |
|                     cancelTimeline();
 | |
|                 this.animations[i].stop();
 | |
|             });
 | |
|         };
 | |
|     }
 | |
|     get time() {
 | |
|         return this.getAll("time");
 | |
|     }
 | |
|     set time(time) {
 | |
|         this.setAll("time", time);
 | |
|     }
 | |
|     get speed() {
 | |
|         return this.getAll("speed");
 | |
|     }
 | |
|     set speed(speed) {
 | |
|         this.setAll("speed", speed);
 | |
|     }
 | |
|     get duration() {
 | |
|         let max = 0;
 | |
|         for (let i = 0; i < this.animations.length; i++) {
 | |
|             max = Math.max(max, this.animations[i].duration);
 | |
|         }
 | |
|         return max;
 | |
|     }
 | |
|     runAll(methodName) {
 | |
|         this.animations.forEach((controls) => controls[methodName]());
 | |
|     }
 | |
|     play() {
 | |
|         this.runAll("play");
 | |
|     }
 | |
|     pause() {
 | |
|         this.runAll("pause");
 | |
|     }
 | |
|     stop() {
 | |
|         this.runAll("stop");
 | |
|     }
 | |
|     cancel() {
 | |
|         this.runAll("cancel");
 | |
|     }
 | |
|     complete() {
 | |
|         this.runAll("complete");
 | |
|     }
 | |
| }
 | |
| 
 | |
| function isDOMKeyframes(keyframes) {
 | |
|     return typeof keyframes === "object" && !Array.isArray(keyframes);
 | |
| }
 | |
| 
 | |
| function isSVGElement(element) {
 | |
|     return element instanceof SVGElement && element.tagName !== "svg";
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse Framer's special CSS variable format into a CSS token and a fallback.
 | |
|  *
 | |
|  * ```
 | |
|  * `var(--foo, #fff)` => [`--foo`, '#fff']
 | |
|  * ```
 | |
|  *
 | |
|  * @param current
 | |
|  */
 | |
| const splitCSSVariableRegex = /var\((--[a-zA-Z0-9-_]+),? ?([a-zA-Z0-9 ()%#.,-]+)?\)/;
 | |
| function parseCSSVariable(current) {
 | |
|     const match = splitCSSVariableRegex.exec(current);
 | |
|     if (!match)
 | |
|         return [,];
 | |
|     const [, token, fallback] = match;
 | |
|     return [token, fallback];
 | |
| }
 | |
| const maxDepth = 4;
 | |
| function getVariableValue(current, element, depth = 1) {
 | |
|     exports.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
 | |
|     const [token, fallback] = parseCSSVariable(current);
 | |
|     // No CSS variable detected
 | |
|     if (!token)
 | |
|         return;
 | |
|     // Attempt to read this CSS variable off the element
 | |
|     const resolved = window.getComputedStyle(element).getPropertyValue(token);
 | |
|     if (resolved) {
 | |
|         const trimmed = resolved.trim();
 | |
|         return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
 | |
|     }
 | |
|     else if (isCSSVariableToken(fallback)) {
 | |
|         // The fallback might itself be a CSS variable, in which case we attempt to resolve it too.
 | |
|         return getVariableValue(fallback, element, depth + 1);
 | |
|     }
 | |
|     else {
 | |
|         return fallback;
 | |
|     }
 | |
| }
 | |
| /**
 | |
|  * Resolve CSS variables from
 | |
|  *
 | |
|  * @internal
 | |
|  */
 | |
| function resolveCSSVariables(visualElement, { ...target }, transitionEnd) {
 | |
|     const element = visualElement.current;
 | |
|     if (!(element instanceof Element))
 | |
|         return { target, transitionEnd };
 | |
|     // If `transitionEnd` isn't `undefined`, clone it. We could clone `target` and `transitionEnd`
 | |
|     // only if they change but I think this reads clearer and this isn't a performance-critical path.
 | |
|     if (transitionEnd) {
 | |
|         transitionEnd = { ...transitionEnd };
 | |
|     }
 | |
|     // Go through existing `MotionValue`s and ensure any existing CSS variables are resolved
 | |
|     visualElement.values.forEach((value) => {
 | |
|         const current = value.get();
 | |
|         if (!isCSSVariableToken(current))
 | |
|             return;
 | |
|         const resolved = getVariableValue(current, element);
 | |
|         if (resolved)
 | |
|             value.set(resolved);
 | |
|     });
 | |
|     // Cycle through every target property and resolve CSS variables. Currently
 | |
|     // we only read single-var properties like `var(--foo)`, not `calc(var(--foo) + 20px)`
 | |
|     for (const key in target) {
 | |
|         const current = target[key];
 | |
|         if (!isCSSVariableToken(current))
 | |
|             continue;
 | |
|         const resolved = getVariableValue(current, element);
 | |
|         if (!resolved)
 | |
|             continue;
 | |
|         // Clone target if it hasn't already been
 | |
|         target[key] = resolved;
 | |
|         if (!transitionEnd)
 | |
|             transitionEnd = {};
 | |
|         // If the user hasn't already set this key on `transitionEnd`, set it to the unresolved
 | |
|         // CSS variable. This will ensure that after the animation the component will reflect
 | |
|         // changes in the value of the CSS variable.
 | |
|         if (transitionEnd[key] === undefined) {
 | |
|             transitionEnd[key] = current;
 | |
|         }
 | |
|     }
 | |
|     return { target, transitionEnd };
 | |
| }
 | |
| 
 | |
| const positionalKeys = new Set([
 | |
|     "width",
 | |
|     "height",
 | |
|     "top",
 | |
|     "left",
 | |
|     "right",
 | |
|     "bottom",
 | |
|     "x",
 | |
|     "y",
 | |
|     "translateX",
 | |
|     "translateY",
 | |
| ]);
 | |
| const isPositionalKey = (key) => positionalKeys.has(key);
 | |
| const hasPositionalKey = (target) => {
 | |
|     return Object.keys(target).some(isPositionalKey);
 | |
| };
 | |
| const isNumOrPxType = (v) => v === number || v === px;
 | |
| const getPosFromMatrix = (matrix, pos) => parseFloat(matrix.split(", ")[pos]);
 | |
| const getTranslateFromMatrix = (pos2, pos3) => (_bbox, { transform }) => {
 | |
|     if (transform === "none" || !transform)
 | |
|         return 0;
 | |
|     const matrix3d = transform.match(/^matrix3d\((.+)\)$/);
 | |
|     if (matrix3d) {
 | |
|         return getPosFromMatrix(matrix3d[1], pos3);
 | |
|     }
 | |
|     else {
 | |
|         const matrix = transform.match(/^matrix\((.+)\)$/);
 | |
|         if (matrix) {
 | |
|             return getPosFromMatrix(matrix[1], pos2);
 | |
|         }
 | |
|         else {
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
| };
 | |
| const transformKeys = new Set(["x", "y", "z"]);
 | |
| const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
 | |
| function removeNonTranslationalTransform(visualElement) {
 | |
|     const removedTransforms = [];
 | |
|     nonTranslationalTransformKeys.forEach((key) => {
 | |
|         const value = visualElement.getValue(key);
 | |
|         if (value !== undefined) {
 | |
|             removedTransforms.push([key, value.get()]);
 | |
|             value.set(key.startsWith("scale") ? 1 : 0);
 | |
|         }
 | |
|     });
 | |
|     // Apply changes to element before measurement
 | |
|     if (removedTransforms.length)
 | |
|         visualElement.render();
 | |
|     return removedTransforms;
 | |
| }
 | |
| const positionalValues = {
 | |
|     // Dimensions
 | |
|     width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
 | |
|     height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
 | |
|     top: (_bbox, { top }) => parseFloat(top),
 | |
|     left: (_bbox, { left }) => parseFloat(left),
 | |
|     bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
 | |
|     right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
 | |
|     // Transform
 | |
|     x: getTranslateFromMatrix(4, 13),
 | |
|     y: getTranslateFromMatrix(5, 14),
 | |
| };
 | |
| // Alias translate longform names
 | |
| positionalValues.translateX = positionalValues.x;
 | |
| positionalValues.translateY = positionalValues.y;
 | |
| const convertChangedValueTypes = (target, visualElement, changedKeys) => {
 | |
|     const originBbox = visualElement.measureViewportBox();
 | |
|     const element = visualElement.current;
 | |
|     const elementComputedStyle = getComputedStyle(element);
 | |
|     const { display } = elementComputedStyle;
 | |
|     const origin = {};
 | |
|     // If the element is currently set to display: "none", make it visible before
 | |
|     // measuring the target bounding box
 | |
|     if (display === "none") {
 | |
|         visualElement.setStaticValue("display", target.display || "block");
 | |
|     }
 | |
|     /**
 | |
|      * Record origins before we render and update styles
 | |
|      */
 | |
|     changedKeys.forEach((key) => {
 | |
|         origin[key] = positionalValues[key](originBbox, elementComputedStyle);
 | |
|     });
 | |
|     // Apply the latest values (as set in checkAndConvertChangedValueTypes)
 | |
|     visualElement.render();
 | |
|     const targetBbox = visualElement.measureViewportBox();
 | |
|     changedKeys.forEach((key) => {
 | |
|         // Restore styles to their **calculated computed style**, not their actual
 | |
|         // originally set style. This allows us to animate between equivalent pixel units.
 | |
|         const value = visualElement.getValue(key);
 | |
|         value && value.jump(origin[key]);
 | |
|         target[key] = positionalValues[key](targetBbox, elementComputedStyle);
 | |
|     });
 | |
|     return target;
 | |
| };
 | |
| const checkAndConvertChangedValueTypes = (visualElement, target, origin = {}, transitionEnd = {}) => {
 | |
|     target = { ...target };
 | |
|     transitionEnd = { ...transitionEnd };
 | |
|     const targetPositionalKeys = Object.keys(target).filter(isPositionalKey);
 | |
|     // We want to remove any transform values that could affect the element's bounding box before
 | |
|     // it's measured. We'll reapply these later.
 | |
|     let removedTransformValues = [];
 | |
|     let hasAttemptedToRemoveTransformValues = false;
 | |
|     const changedValueTypeKeys = [];
 | |
|     targetPositionalKeys.forEach((key) => {
 | |
|         const value = visualElement.getValue(key);
 | |
|         if (!visualElement.hasValue(key))
 | |
|             return;
 | |
|         let from = origin[key];
 | |
|         let fromType = findDimensionValueType(from);
 | |
|         const to = target[key];
 | |
|         let toType;
 | |
|         // TODO: The current implementation of this basically throws an error
 | |
|         // if you try and do value conversion via keyframes. There's probably
 | |
|         // a way of doing this but the performance implications would need greater scrutiny,
 | |
|         // as it'd be doing multiple resize-remeasure operations.
 | |
|         if (isKeyframesTarget(to)) {
 | |
|             const numKeyframes = to.length;
 | |
|             const fromIndex = to[0] === null ? 1 : 0;
 | |
|             from = to[fromIndex];
 | |
|             fromType = findDimensionValueType(from);
 | |
|             for (let i = fromIndex; i < numKeyframes; i++) {
 | |
|                 /**
 | |
|                  * Don't allow wildcard keyframes to be used to detect
 | |
|                  * a difference in value types.
 | |
|                  */
 | |
|                 if (to[i] === null)
 | |
|                     break;
 | |
|                 if (!toType) {
 | |
|                     toType = findDimensionValueType(to[i]);
 | |
|                     exports.invariant(toType === fromType ||
 | |
|                         (isNumOrPxType(fromType) && isNumOrPxType(toType)), "Keyframes must be of the same dimension as the current value");
 | |
|                 }
 | |
|                 else {
 | |
|                     exports.invariant(findDimensionValueType(to[i]) === toType, "All keyframes must be of the same type");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             toType = findDimensionValueType(to);
 | |
|         }
 | |
|         if (fromType !== toType) {
 | |
|             // If they're both just number or px, convert them both to numbers rather than
 | |
|             // relying on resize/remeasure to convert (which is wasteful in this situation)
 | |
|             if (isNumOrPxType(fromType) && isNumOrPxType(toType)) {
 | |
|                 const current = value.get();
 | |
|                 if (typeof current === "string") {
 | |
|                     value.set(parseFloat(current));
 | |
|                 }
 | |
|                 if (typeof to === "string") {
 | |
|                     target[key] = parseFloat(to);
 | |
|                 }
 | |
|                 else if (Array.isArray(to) && toType === px) {
 | |
|                     target[key] = to.map(parseFloat);
 | |
|                 }
 | |
|             }
 | |
|             else if ((fromType === null || fromType === void 0 ? void 0 : fromType.transform) &&
 | |
|                 (toType === null || toType === void 0 ? void 0 : toType.transform) &&
 | |
|                 (from === 0 || to === 0)) {
 | |
|                 // If one or the other value is 0, it's safe to coerce it to the
 | |
|                 // type of the other without measurement
 | |
|                 if (from === 0) {
 | |
|                     value.set(toType.transform(from));
 | |
|                 }
 | |
|                 else {
 | |
|                     target[key] = fromType.transform(to);
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 // If we're going to do value conversion via DOM measurements, we first
 | |
|                 // need to remove non-positional transform values that could affect the bbox measurements.
 | |
|                 if (!hasAttemptedToRemoveTransformValues) {
 | |
|                     removedTransformValues =
 | |
|                         removeNonTranslationalTransform(visualElement);
 | |
|                     hasAttemptedToRemoveTransformValues = true;
 | |
|                 }
 | |
|                 changedValueTypeKeys.push(key);
 | |
|                 transitionEnd[key] =
 | |
|                     transitionEnd[key] !== undefined
 | |
|                         ? transitionEnd[key]
 | |
|                         : target[key];
 | |
|                 value.jump(to);
 | |
|             }
 | |
|         }
 | |
|     });
 | |
|     if (changedValueTypeKeys.length) {
 | |
|         const scrollY = changedValueTypeKeys.indexOf("height") >= 0
 | |
|             ? window.pageYOffset
 | |
|             : null;
 | |
|         const convertedTarget = convertChangedValueTypes(target, visualElement, changedValueTypeKeys);
 | |
|         // If we removed transform values, reapply them before the next render
 | |
|         if (removedTransformValues.length) {
 | |
|             removedTransformValues.forEach(([key, value]) => {
 | |
|                 visualElement.getValue(key).set(value);
 | |
|             });
 | |
|         }
 | |
|         // Reapply original values
 | |
|         visualElement.render();
 | |
|         // Restore scroll position
 | |
|         if (isBrowser && scrollY !== null) {
 | |
|             window.scrollTo({ top: scrollY });
 | |
|         }
 | |
|         return { target: convertedTarget, transitionEnd };
 | |
|     }
 | |
|     else {
 | |
|         return { target, transitionEnd };
 | |
|     }
 | |
| };
 | |
| /**
 | |
|  * Convert value types for x/y/width/height/top/left/bottom/right
 | |
|  *
 | |
|  * Allows animation between `'auto'` -> `'100%'` or `0` -> `'calc(50% - 10vw)'`
 | |
|  *
 | |
|  * @internal
 | |
|  */
 | |
| function unitConversion(visualElement, target, origin, transitionEnd) {
 | |
|     return hasPositionalKey(target)
 | |
|         ? checkAndConvertChangedValueTypes(visualElement, target, origin, transitionEnd)
 | |
|         : { target, transitionEnd };
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Parse a DOM variant to make it animatable. This involves resolving CSS variables
 | |
|  * and ensuring animations like "20%" => "calc(50vw)" are performed in pixels.
 | |
|  */
 | |
| const parseDomVariant = (visualElement, target, origin, transitionEnd) => {
 | |
|     const resolved = resolveCSSVariables(visualElement, target, transitionEnd);
 | |
|     target = resolved.target;
 | |
|     transitionEnd = resolved.transitionEnd;
 | |
|     return unitConversion(visualElement, target, origin, transitionEnd);
 | |
| };
 | |
| 
 | |
| // Does this device prefer reduced motion? Returns `null` server-side.
 | |
| const prefersReducedMotion = { current: null };
 | |
| const hasReducedMotionListener = { current: false };
 | |
| 
 | |
| function initPrefersReducedMotion() {
 | |
|     hasReducedMotionListener.current = true;
 | |
|     if (!isBrowser)
 | |
|         return;
 | |
|     if (window.matchMedia) {
 | |
|         const motionMediaQuery = window.matchMedia("(prefers-reduced-motion)");
 | |
|         const setReducedMotionPreferences = () => (prefersReducedMotion.current = motionMediaQuery.matches);
 | |
|         motionMediaQuery.addListener(setReducedMotionPreferences);
 | |
|         setReducedMotionPreferences();
 | |
|     }
 | |
|     else {
 | |
|         prefersReducedMotion.current = false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function updateMotionValuesFromProps(element, next, prev) {
 | |
|     const { willChange } = next;
 | |
|     for (const key in next) {
 | |
|         const nextValue = next[key];
 | |
|         const prevValue = prev[key];
 | |
|         if (isMotionValue(nextValue)) {
 | |
|             /**
 | |
|              * If this is a motion value found in props or style, we want to add it
 | |
|              * to our visual element's motion value map.
 | |
|              */
 | |
|             element.addValue(key, nextValue);
 | |
|             if (isWillChangeMotionValue(willChange)) {
 | |
|                 willChange.add(key);
 | |
|             }
 | |
|             /**
 | |
|              * Check the version of the incoming motion value with this version
 | |
|              * and warn against mismatches.
 | |
|              */
 | |
|             if (process.env.NODE_ENV === "development") {
 | |
|                 warnOnce(nextValue.version === "10.18.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 10.18.0 may not work as expected.`);
 | |
|             }
 | |
|         }
 | |
|         else if (isMotionValue(prevValue)) {
 | |
|             /**
 | |
|              * If we're swapping from a motion value to a static value,
 | |
|              * create a new motion value from that
 | |
|              */
 | |
|             element.addValue(key, motionValue(nextValue, { owner: element }));
 | |
|             if (isWillChangeMotionValue(willChange)) {
 | |
|                 willChange.remove(key);
 | |
|             }
 | |
|         }
 | |
|         else if (prevValue !== nextValue) {
 | |
|             /**
 | |
|              * If this is a flat value that has changed, update the motion value
 | |
|              * or create one if it doesn't exist. We only want to do this if we're
 | |
|              * not handling the value with our animation state.
 | |
|              */
 | |
|             if (element.hasValue(key)) {
 | |
|                 const existingValue = element.getValue(key);
 | |
|                 // TODO: Only update values that aren't being animated or even looked at
 | |
|                 !existingValue.hasAnimated && existingValue.set(nextValue);
 | |
|             }
 | |
|             else {
 | |
|                 const latestValue = element.getStaticValue(key);
 | |
|                 element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element }));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     // Handle removed values
 | |
|     for (const key in prev) {
 | |
|         if (next[key] === undefined)
 | |
|             element.removeValue(key);
 | |
|     }
 | |
|     return next;
 | |
| }
 | |
| 
 | |
| const featureNames = Object.keys(featureDefinitions);
 | |
| const numFeatures = featureNames.length;
 | |
| const propEventHandlers = [
 | |
|     "AnimationStart",
 | |
|     "AnimationComplete",
 | |
|     "Update",
 | |
|     "BeforeLayoutMeasure",
 | |
|     "LayoutMeasure",
 | |
|     "LayoutAnimationStart",
 | |
|     "LayoutAnimationComplete",
 | |
| ];
 | |
| const numVariantProps = variantProps.length;
 | |
| /**
 | |
|  * A VisualElement is an imperative abstraction around UI elements such as
 | |
|  * HTMLElement, SVGElement, Three.Object3D etc.
 | |
|  */
 | |
| class VisualElement {
 | |
|     constructor({ parent, props, presenceContext, reducedMotionConfig, visualState, }, options = {}) {
 | |
|         /**
 | |
|          * A reference to the current underlying Instance, e.g. a HTMLElement
 | |
|          * or Three.Mesh etc.
 | |
|          */
 | |
|         this.current = null;
 | |
|         /**
 | |
|          * A set containing references to this VisualElement's children.
 | |
|          */
 | |
|         this.children = new Set();
 | |
|         /**
 | |
|          * Determine what role this visual element should take in the variant tree.
 | |
|          */
 | |
|         this.isVariantNode = false;
 | |
|         this.isControllingVariants = false;
 | |
|         /**
 | |
|          * Decides whether this VisualElement should animate in reduced motion
 | |
|          * mode.
 | |
|          *
 | |
|          * TODO: This is currently set on every individual VisualElement but feels
 | |
|          * like it could be set globally.
 | |
|          */
 | |
|         this.shouldReduceMotion = null;
 | |
|         /**
 | |
|          * A map of all motion values attached to this visual element. Motion
 | |
|          * values are source of truth for any given animated value. A motion
 | |
|          * value might be provided externally by the component via props.
 | |
|          */
 | |
|         this.values = new Map();
 | |
|         /**
 | |
|          * Cleanup functions for active features (hover/tap/exit etc)
 | |
|          */
 | |
|         this.features = {};
 | |
|         /**
 | |
|          * A map of every subscription that binds the provided or generated
 | |
|          * motion values onChange listeners to this visual element.
 | |
|          */
 | |
|         this.valueSubscriptions = new Map();
 | |
|         /**
 | |
|          * A reference to the previously-provided motion values as returned
 | |
|          * from scrapeMotionValuesFromProps. We use the keys in here to determine
 | |
|          * if any motion values need to be removed after props are updated.
 | |
|          */
 | |
|         this.prevMotionValues = {};
 | |
|         /**
 | |
|          * An object containing a SubscriptionManager for each active event.
 | |
|          */
 | |
|         this.events = {};
 | |
|         /**
 | |
|          * An object containing an unsubscribe function for each prop event subscription.
 | |
|          * For example, every "Update" event can have multiple subscribers via
 | |
|          * VisualElement.on(), but only one of those can be defined via the onUpdate prop.
 | |
|          */
 | |
|         this.propEventSubscriptions = {};
 | |
|         this.notifyUpdate = () => this.notify("Update", this.latestValues);
 | |
|         this.render = () => {
 | |
|             if (!this.current)
 | |
|                 return;
 | |
|             this.triggerBuild();
 | |
|             this.renderInstance(this.current, this.renderState, this.props.style, this.projection);
 | |
|         };
 | |
|         this.scheduleRender = () => frame.render(this.render, false, true);
 | |
|         const { latestValues, renderState } = visualState;
 | |
|         this.latestValues = latestValues;
 | |
|         this.baseTarget = { ...latestValues };
 | |
|         this.initialValues = props.initial ? { ...latestValues } : {};
 | |
|         this.renderState = renderState;
 | |
|         this.parent = parent;
 | |
|         this.props = props;
 | |
|         this.presenceContext = presenceContext;
 | |
|         this.depth = parent ? parent.depth + 1 : 0;
 | |
|         this.reducedMotionConfig = reducedMotionConfig;
 | |
|         this.options = options;
 | |
|         this.isControllingVariants = isControllingVariants(props);
 | |
|         this.isVariantNode = isVariantNode(props);
 | |
|         if (this.isVariantNode) {
 | |
|             this.variantChildren = new Set();
 | |
|         }
 | |
|         this.manuallyAnimateOnMount = Boolean(parent && parent.current);
 | |
|         /**
 | |
|          * Any motion values that are provided to the element when created
 | |
|          * aren't yet bound to the element, as this would technically be impure.
 | |
|          * However, we iterate through the motion values and set them to the
 | |
|          * initial values for this component.
 | |
|          *
 | |
|          * TODO: This is impure and we should look at changing this to run on mount.
 | |
|          * Doing so will break some tests but this isn't neccessarily a breaking change,
 | |
|          * more a reflection of the test.
 | |
|          */
 | |
|         const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {});
 | |
|         for (const key in initialMotionValues) {
 | |
|             const value = initialMotionValues[key];
 | |
|             if (latestValues[key] !== undefined && isMotionValue(value)) {
 | |
|                 value.set(latestValues[key], false);
 | |
|                 if (isWillChangeMotionValue(willChange)) {
 | |
|                     willChange.add(key);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * This method takes React props and returns found MotionValues. For example, HTML
 | |
|      * MotionValues will be found within the style prop, whereas for Three.js within attribute arrays.
 | |
|      *
 | |
|      * This isn't an abstract method as it needs calling in the constructor, but it is
 | |
|      * intended to be one.
 | |
|      */
 | |
|     scrapeMotionValuesFromProps(_props, _prevProps) {
 | |
|         return {};
 | |
|     }
 | |
|     mount(instance) {
 | |
|         this.current = instance;
 | |
|         visualElementStore.set(instance, this);
 | |
|         if (this.projection && !this.projection.instance) {
 | |
|             this.projection.mount(instance);
 | |
|         }
 | |
|         if (this.parent && this.isVariantNode && !this.isControllingVariants) {
 | |
|             this.removeFromVariantTree = this.parent.addVariantChild(this);
 | |
|         }
 | |
|         this.values.forEach((value, key) => this.bindToMotionValue(key, value));
 | |
|         if (!hasReducedMotionListener.current) {
 | |
|             initPrefersReducedMotion();
 | |
|         }
 | |
|         this.shouldReduceMotion =
 | |
|             this.reducedMotionConfig === "never"
 | |
|                 ? false
 | |
|                 : this.reducedMotionConfig === "always"
 | |
|                     ? true
 | |
|                     : prefersReducedMotion.current;
 | |
|         if (process.env.NODE_ENV !== "production") {
 | |
|             warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
 | |
|         }
 | |
|         if (this.parent)
 | |
|             this.parent.children.add(this);
 | |
|         this.update(this.props, this.presenceContext);
 | |
|     }
 | |
|     unmount() {
 | |
|         visualElementStore.delete(this.current);
 | |
|         this.projection && this.projection.unmount();
 | |
|         cancelFrame(this.notifyUpdate);
 | |
|         cancelFrame(this.render);
 | |
|         this.valueSubscriptions.forEach((remove) => remove());
 | |
|         this.removeFromVariantTree && this.removeFromVariantTree();
 | |
|         this.parent && this.parent.children.delete(this);
 | |
|         for (const key in this.events) {
 | |
|             this.events[key].clear();
 | |
|         }
 | |
|         for (const key in this.features) {
 | |
|             this.features[key].unmount();
 | |
|         }
 | |
|         this.current = null;
 | |
|     }
 | |
|     bindToMotionValue(key, value) {
 | |
|         const valueIsTransform = transformProps.has(key);
 | |
|         const removeOnChange = value.on("change", (latestValue) => {
 | |
|             this.latestValues[key] = latestValue;
 | |
|             this.props.onUpdate &&
 | |
|                 frame.update(this.notifyUpdate, false, true);
 | |
|             if (valueIsTransform && this.projection) {
 | |
|                 this.projection.isTransformDirty = true;
 | |
|             }
 | |
|         });
 | |
|         const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender);
 | |
|         this.valueSubscriptions.set(key, () => {
 | |
|             removeOnChange();
 | |
|             removeOnRenderRequest();
 | |
|         });
 | |
|     }
 | |
|     sortNodePosition(other) {
 | |
|         /**
 | |
|          * If these nodes aren't even of the same type we can't compare their depth.
 | |
|          */
 | |
|         if (!this.current ||
 | |
|             !this.sortInstanceNodePosition ||
 | |
|             this.type !== other.type) {
 | |
|             return 0;
 | |
|         }
 | |
|         return this.sortInstanceNodePosition(this.current, other.current);
 | |
|     }
 | |
|     loadFeatures({ children, ...renderedProps }, isStrict, preloadedFeatures, initialLayoutGroupConfig) {
 | |
|         let ProjectionNodeConstructor;
 | |
|         let MeasureLayout;
 | |
|         /**
 | |
|          * 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 (process.env.NODE_ENV !== "production" &&
 | |
|             preloadedFeatures &&
 | |
|             isStrict) {
 | |
|             const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.";
 | |
|             renderedProps.ignoreStrict
 | |
|                 ? exports.warning(false, strictMessage)
 | |
|                 : exports.invariant(false, strictMessage);
 | |
|         }
 | |
|         for (let i = 0; i < numFeatures; i++) {
 | |
|             const name = featureNames[i];
 | |
|             const { isEnabled, Feature: FeatureConstructor, ProjectionNode, MeasureLayout: MeasureLayoutComponent, } = featureDefinitions[name];
 | |
|             if (ProjectionNode)
 | |
|                 ProjectionNodeConstructor = ProjectionNode;
 | |
|             if (isEnabled(renderedProps)) {
 | |
|                 if (!this.features[name] && FeatureConstructor) {
 | |
|                     this.features[name] = new FeatureConstructor(this);
 | |
|                 }
 | |
|                 if (MeasureLayoutComponent) {
 | |
|                     MeasureLayout = MeasureLayoutComponent;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if ((this.type === "html" || this.type === "svg") &&
 | |
|             !this.projection &&
 | |
|             ProjectionNodeConstructor) {
 | |
|             this.projection = new ProjectionNodeConstructor(this.latestValues, this.parent && this.parent.projection);
 | |
|             const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, } = renderedProps;
 | |
|             this.projection.setOptions({
 | |
|                 layoutId,
 | |
|                 layout,
 | |
|                 alwaysMeasureLayout: Boolean(drag) ||
 | |
|                     (dragConstraints && isRefObject(dragConstraints)),
 | |
|                 visualElement: this,
 | |
|                 scheduleRender: () => this.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: initialLayoutGroupConfig,
 | |
|                 layoutScroll,
 | |
|                 layoutRoot,
 | |
|             });
 | |
|         }
 | |
|         return MeasureLayout;
 | |
|     }
 | |
|     updateFeatures() {
 | |
|         for (const key in this.features) {
 | |
|             const feature = this.features[key];
 | |
|             if (feature.isMounted) {
 | |
|                 feature.update();
 | |
|             }
 | |
|             else {
 | |
|                 feature.mount();
 | |
|                 feature.isMounted = true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     triggerBuild() {
 | |
|         this.build(this.renderState, this.latestValues, this.options, this.props);
 | |
|     }
 | |
|     /**
 | |
|      * Measure the current viewport box with or without transforms.
 | |
|      * Only measures axis-aligned boxes, rotate and skew must be manually
 | |
|      * removed with a re-render to work.
 | |
|      */
 | |
|     measureViewportBox() {
 | |
|         return this.current
 | |
|             ? this.measureInstanceViewportBox(this.current, this.props)
 | |
|             : createBox();
 | |
|     }
 | |
|     getStaticValue(key) {
 | |
|         return this.latestValues[key];
 | |
|     }
 | |
|     setStaticValue(key, value) {
 | |
|         this.latestValues[key] = value;
 | |
|     }
 | |
|     /**
 | |
|      * Make a target animatable by Popmotion. For instance, if we're
 | |
|      * trying to animate width from 100px to 100vw we need to measure 100vw
 | |
|      * in pixels to determine what we really need to animate to. This is also
 | |
|      * pluggable to support Framer's custom value types like Color,
 | |
|      * and CSS variables.
 | |
|      */
 | |
|     makeTargetAnimatable(target, canMutate = true) {
 | |
|         return this.makeTargetAnimatableFromInstance(target, this.props, canMutate);
 | |
|     }
 | |
|     /**
 | |
|      * Update the provided props. Ensure any newly-added motion values are
 | |
|      * added to our map, old ones removed, and listeners updated.
 | |
|      */
 | |
|     update(props, presenceContext) {
 | |
|         if (props.transformTemplate || this.props.transformTemplate) {
 | |
|             this.scheduleRender();
 | |
|         }
 | |
|         this.prevProps = this.props;
 | |
|         this.props = props;
 | |
|         this.prevPresenceContext = this.presenceContext;
 | |
|         this.presenceContext = presenceContext;
 | |
|         /**
 | |
|          * Update prop event handlers ie onAnimationStart, onAnimationComplete
 | |
|          */
 | |
|         for (let i = 0; i < propEventHandlers.length; i++) {
 | |
|             const key = propEventHandlers[i];
 | |
|             if (this.propEventSubscriptions[key]) {
 | |
|                 this.propEventSubscriptions[key]();
 | |
|                 delete this.propEventSubscriptions[key];
 | |
|             }
 | |
|             const listener = props["on" + key];
 | |
|             if (listener) {
 | |
|                 this.propEventSubscriptions[key] = this.on(key, listener);
 | |
|             }
 | |
|         }
 | |
|         this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps), this.prevMotionValues);
 | |
|         if (this.handleChildMotionValue) {
 | |
|             this.handleChildMotionValue();
 | |
|         }
 | |
|     }
 | |
|     getProps() {
 | |
|         return this.props;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the variant definition with a given name.
 | |
|      */
 | |
|     getVariant(name) {
 | |
|         return this.props.variants ? this.props.variants[name] : undefined;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the defined default transition on this component.
 | |
|      */
 | |
|     getDefaultTransition() {
 | |
|         return this.props.transition;
 | |
|     }
 | |
|     getTransformPagePoint() {
 | |
|         return this.props.transformPagePoint;
 | |
|     }
 | |
|     getClosestVariantNode() {
 | |
|         return this.isVariantNode
 | |
|             ? this
 | |
|             : this.parent
 | |
|                 ? this.parent.getClosestVariantNode()
 | |
|                 : undefined;
 | |
|     }
 | |
|     getVariantContext(startAtParent = false) {
 | |
|         if (startAtParent) {
 | |
|             return this.parent ? this.parent.getVariantContext() : undefined;
 | |
|         }
 | |
|         if (!this.isControllingVariants) {
 | |
|             const context = this.parent
 | |
|                 ? this.parent.getVariantContext() || {}
 | |
|                 : {};
 | |
|             if (this.props.initial !== undefined) {
 | |
|                 context.initial = this.props.initial;
 | |
|             }
 | |
|             return context;
 | |
|         }
 | |
|         const context = {};
 | |
|         for (let i = 0; i < numVariantProps; i++) {
 | |
|             const name = variantProps[i];
 | |
|             const prop = this.props[name];
 | |
|             if (isVariantLabel(prop) || prop === false) {
 | |
|                 context[name] = prop;
 | |
|             }
 | |
|         }
 | |
|         return context;
 | |
|     }
 | |
|     /**
 | |
|      * Add a child visual element to our set of children.
 | |
|      */
 | |
|     addVariantChild(child) {
 | |
|         const closestVariantNode = this.getClosestVariantNode();
 | |
|         if (closestVariantNode) {
 | |
|             closestVariantNode.variantChildren &&
 | |
|                 closestVariantNode.variantChildren.add(child);
 | |
|             return () => closestVariantNode.variantChildren.delete(child);
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Add a motion value and bind it to this visual element.
 | |
|      */
 | |
|     addValue(key, value) {
 | |
|         // Remove existing value if it exists
 | |
|         if (value !== this.values.get(key)) {
 | |
|             this.removeValue(key);
 | |
|             this.bindToMotionValue(key, value);
 | |
|         }
 | |
|         this.values.set(key, value);
 | |
|         this.latestValues[key] = value.get();
 | |
|     }
 | |
|     /**
 | |
|      * Remove a motion value and unbind any active subscriptions.
 | |
|      */
 | |
|     removeValue(key) {
 | |
|         this.values.delete(key);
 | |
|         const unsubscribe = this.valueSubscriptions.get(key);
 | |
|         if (unsubscribe) {
 | |
|             unsubscribe();
 | |
|             this.valueSubscriptions.delete(key);
 | |
|         }
 | |
|         delete this.latestValues[key];
 | |
|         this.removeValueFromRenderState(key, this.renderState);
 | |
|     }
 | |
|     /**
 | |
|      * Check whether we have a motion value for this key
 | |
|      */
 | |
|     hasValue(key) {
 | |
|         return this.values.has(key);
 | |
|     }
 | |
|     getValue(key, defaultValue) {
 | |
|         if (this.props.values && this.props.values[key]) {
 | |
|             return this.props.values[key];
 | |
|         }
 | |
|         let value = this.values.get(key);
 | |
|         if (value === undefined && defaultValue !== undefined) {
 | |
|             value = motionValue(defaultValue, { owner: this });
 | |
|             this.addValue(key, value);
 | |
|         }
 | |
|         return value;
 | |
|     }
 | |
|     /**
 | |
|      * If we're trying to animate to a previously unencountered value,
 | |
|      * we need to check for it in our state and as a last resort read it
 | |
|      * directly from the instance (which might have performance implications).
 | |
|      */
 | |
|     readValue(key) {
 | |
|         var _a;
 | |
|         return this.latestValues[key] !== undefined || !this.current
 | |
|             ? this.latestValues[key]
 | |
|             : (_a = this.getBaseTargetFromProps(this.props, key)) !== null && _a !== void 0 ? _a : this.readValueFromInstance(this.current, key, this.options);
 | |
|     }
 | |
|     /**
 | |
|      * Set the base target to later animate back to. This is currently
 | |
|      * only hydrated on creation and when we first read a value.
 | |
|      */
 | |
|     setBaseTarget(key, value) {
 | |
|         this.baseTarget[key] = value;
 | |
|     }
 | |
|     /**
 | |
|      * Find the base target for a value thats been removed from all animation
 | |
|      * props.
 | |
|      */
 | |
|     getBaseTarget(key) {
 | |
|         var _a;
 | |
|         const { initial } = this.props;
 | |
|         const valueFromInitial = typeof initial === "string" || typeof initial === "object"
 | |
|             ? (_a = resolveVariantFromProps(this.props, initial)) === null || _a === void 0 ? void 0 : _a[key]
 | |
|             : undefined;
 | |
|         /**
 | |
|          * If this value still exists in the current initial variant, read that.
 | |
|          */
 | |
|         if (initial && valueFromInitial !== undefined) {
 | |
|             return valueFromInitial;
 | |
|         }
 | |
|         /**
 | |
|          * Alternatively, if this VisualElement config has defined a getBaseTarget
 | |
|          * so we can read the value from an alternative source, try that.
 | |
|          */
 | |
|         const target = this.getBaseTargetFromProps(this.props, key);
 | |
|         if (target !== undefined && !isMotionValue(target))
 | |
|             return target;
 | |
|         /**
 | |
|          * If the value was initially defined on initial, but it doesn't any more,
 | |
|          * return undefined. Otherwise return the value as initially read from the DOM.
 | |
|          */
 | |
|         return this.initialValues[key] !== undefined &&
 | |
|             valueFromInitial === undefined
 | |
|             ? undefined
 | |
|             : this.baseTarget[key];
 | |
|     }
 | |
|     on(eventName, callback) {
 | |
|         if (!this.events[eventName]) {
 | |
|             this.events[eventName] = new SubscriptionManager();
 | |
|         }
 | |
|         return this.events[eventName].add(callback);
 | |
|     }
 | |
|     notify(eventName, ...args) {
 | |
|         if (this.events[eventName]) {
 | |
|             this.events[eventName].notify(...args);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class DOMVisualElement extends VisualElement {
 | |
|     sortInstanceNodePosition(a, b) {
 | |
|         /**
 | |
|          * compareDocumentPosition returns a bitmask, by using the bitwise &
 | |
|          * we're returning true if 2 in that bitmask is set to true. 2 is set
 | |
|          * to true if b preceeds a.
 | |
|          */
 | |
|         return a.compareDocumentPosition(b) & 2 ? 1 : -1;
 | |
|     }
 | |
|     getBaseTargetFromProps(props, key) {
 | |
|         return props.style ? props.style[key] : undefined;
 | |
|     }
 | |
|     removeValueFromRenderState(key, { vars, style }) {
 | |
|         delete vars[key];
 | |
|         delete style[key];
 | |
|     }
 | |
|     makeTargetAnimatableFromInstance({ transition, transitionEnd, ...target }, { transformValues }, isMounted) {
 | |
|         let origin = getOrigin(target, transition || {}, this);
 | |
|         /**
 | |
|          * If Framer has provided a function to convert `Color` etc value types, convert them
 | |
|          */
 | |
|         if (transformValues) {
 | |
|             if (transitionEnd)
 | |
|                 transitionEnd = transformValues(transitionEnd);
 | |
|             if (target)
 | |
|                 target = transformValues(target);
 | |
|             if (origin)
 | |
|                 origin = transformValues(origin);
 | |
|         }
 | |
|         if (isMounted) {
 | |
|             checkTargetForNewValues(this, target, origin);
 | |
|             const parsed = parseDomVariant(this, target, origin, transitionEnd);
 | |
|             transitionEnd = parsed.transitionEnd;
 | |
|             target = parsed.target;
 | |
|         }
 | |
|         return {
 | |
|             transition,
 | |
|             transitionEnd,
 | |
|             ...target,
 | |
|         };
 | |
|     }
 | |
| }
 | |
| 
 | |
| class SVGVisualElement extends DOMVisualElement {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.type = "svg";
 | |
|         this.isSVGTag = false;
 | |
|     }
 | |
|     getBaseTargetFromProps(props, key) {
 | |
|         return props[key];
 | |
|     }
 | |
|     readValueFromInstance(instance, key) {
 | |
|         if (transformProps.has(key)) {
 | |
|             const defaultType = getDefaultValueType(key);
 | |
|             return defaultType ? defaultType.default || 0 : 0;
 | |
|         }
 | |
|         key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
 | |
|         return instance.getAttribute(key);
 | |
|     }
 | |
|     measureInstanceViewportBox() {
 | |
|         return createBox();
 | |
|     }
 | |
|     scrapeMotionValuesFromProps(props, prevProps) {
 | |
|         return scrapeMotionValuesFromProps(props, prevProps);
 | |
|     }
 | |
|     build(renderState, latestValues, options, props) {
 | |
|         buildSVGAttrs(renderState, latestValues, options, this.isSVGTag, props.transformTemplate);
 | |
|     }
 | |
|     renderInstance(instance, renderState, styleProp, projection) {
 | |
|         renderSVG(instance, renderState, styleProp, projection);
 | |
|     }
 | |
|     mount(instance) {
 | |
|         this.isSVGTag = isSVGTag(instance.tagName);
 | |
|         super.mount(instance);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function getComputedStyle$1(element) {
 | |
|     return window.getComputedStyle(element);
 | |
| }
 | |
| class HTMLVisualElement extends DOMVisualElement {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.type = "html";
 | |
|     }
 | |
|     readValueFromInstance(instance, key) {
 | |
|         if (transformProps.has(key)) {
 | |
|             const defaultType = getDefaultValueType(key);
 | |
|             return defaultType ? defaultType.default || 0 : 0;
 | |
|         }
 | |
|         else {
 | |
|             const computedStyle = getComputedStyle$1(instance);
 | |
|             const value = (isCSSVariableName(key)
 | |
|                 ? computedStyle.getPropertyValue(key)
 | |
|                 : computedStyle[key]) || 0;
 | |
|             return typeof value === "string" ? value.trim() : value;
 | |
|         }
 | |
|     }
 | |
|     measureInstanceViewportBox(instance, { transformPagePoint }) {
 | |
|         return measureViewportBox(instance, transformPagePoint);
 | |
|     }
 | |
|     build(renderState, latestValues, options, props) {
 | |
|         buildHTMLStyles(renderState, latestValues, options, props.transformTemplate);
 | |
|     }
 | |
|     scrapeMotionValuesFromProps(props, prevProps) {
 | |
|         return scrapeMotionValuesFromProps$1(props, prevProps);
 | |
|     }
 | |
|     handleChildMotionValue() {
 | |
|         if (this.childSubscription) {
 | |
|             this.childSubscription();
 | |
|             delete this.childSubscription;
 | |
|         }
 | |
|         const { children } = this.props;
 | |
|         if (isMotionValue(children)) {
 | |
|             this.childSubscription = children.on("change", (latest) => {
 | |
|                 if (this.current)
 | |
|                     this.current.textContent = `${latest}`;
 | |
|             });
 | |
|         }
 | |
|     }
 | |
|     renderInstance(instance, renderState, styleProp, projection) {
 | |
|         renderHTML(instance, renderState, styleProp, projection);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function createVisualElement(element) {
 | |
|     const options = {
 | |
|         presenceContext: null,
 | |
|         props: {},
 | |
|         visualState: {
 | |
|             renderState: {
 | |
|                 transform: {},
 | |
|                 transformOrigin: {},
 | |
|                 style: {},
 | |
|                 vars: {},
 | |
|                 attrs: {},
 | |
|             },
 | |
|             latestValues: {},
 | |
|         },
 | |
|     };
 | |
|     const node = isSVGElement(element)
 | |
|         ? new SVGVisualElement(options, {
 | |
|             enableHardwareAcceleration: false,
 | |
|         })
 | |
|         : new HTMLVisualElement(options, {
 | |
|             enableHardwareAcceleration: true,
 | |
|         });
 | |
|     node.mount(element);
 | |
|     visualElementStore.set(element, node);
 | |
| }
 | |
| 
 | |
| function animateSingleValue(value, keyframes, options) {
 | |
|     const motionValue$1 = isMotionValue(value) ? value : motionValue(value);
 | |
|     motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options));
 | |
|     return motionValue$1.animation;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create a progress => progress easing function from a generator.
 | |
|  */
 | |
| function createGeneratorEasing(options, scale = 100) {
 | |
|     const generator = spring({ keyframes: [0, scale], ...options });
 | |
|     const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
 | |
|     return {
 | |
|         type: "keyframes",
 | |
|         ease: (progress) => generator.next(duration * progress).value / scale,
 | |
|         duration: millisecondsToSeconds(duration),
 | |
|     };
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Given a absolute or relative time definition and current/prev time state of the sequence,
 | |
|  * calculate an absolute time for the next keyframes.
 | |
|  */
 | |
| function calcNextTime(current, next, prev, labels) {
 | |
|     var _a;
 | |
|     if (typeof next === "number") {
 | |
|         return next;
 | |
|     }
 | |
|     else if (next.startsWith("-") || next.startsWith("+")) {
 | |
|         return Math.max(0, current + parseFloat(next));
 | |
|     }
 | |
|     else if (next === "<") {
 | |
|         return prev;
 | |
|     }
 | |
|     else {
 | |
|         return (_a = labels.get(next)) !== null && _a !== void 0 ? _a : current;
 | |
|     }
 | |
| }
 | |
| 
 | |
| const wrap = (min, max, v) => {
 | |
|     const rangeSize = max - min;
 | |
|     return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
 | |
| };
 | |
| 
 | |
| function getEasingForSegment(easing, i) {
 | |
|     return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing;
 | |
| }
 | |
| 
 | |
| function eraseKeyframes(sequence, startTime, endTime) {
 | |
|     for (let i = 0; i < sequence.length; i++) {
 | |
|         const keyframe = sequence[i];
 | |
|         if (keyframe.at > startTime && keyframe.at < endTime) {
 | |
|             removeItem(sequence, keyframe);
 | |
|             // If we remove this item we have to push the pointer back one
 | |
|             i--;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
 | |
|     /**
 | |
|      * Erase every existing value between currentTime and targetTime,
 | |
|      * this will essentially splice this timeline into any currently
 | |
|      * defined ones.
 | |
|      */
 | |
|     eraseKeyframes(sequence, startTime, endTime);
 | |
|     for (let i = 0; i < keyframes.length; i++) {
 | |
|         sequence.push({
 | |
|             value: keyframes[i],
 | |
|             at: mix(startTime, endTime, offset[i]),
 | |
|             easing: getEasingForSegment(easing, i),
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| function compareByTime(a, b) {
 | |
|     if (a.at === b.at) {
 | |
|         if (a.value === null)
 | |
|             return 1;
 | |
|         if (b.value === null)
 | |
|             return -1;
 | |
|         return 0;
 | |
|     }
 | |
|     else {
 | |
|         return a.at - b.at;
 | |
|     }
 | |
| }
 | |
| 
 | |
| const defaultSegmentEasing = "easeInOut";
 | |
| function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope) {
 | |
|     const defaultDuration = defaultTransition.duration || 0.3;
 | |
|     const animationDefinitions = new Map();
 | |
|     const sequences = new Map();
 | |
|     const elementCache = {};
 | |
|     const timeLabels = new Map();
 | |
|     let prevTime = 0;
 | |
|     let currentTime = 0;
 | |
|     let totalDuration = 0;
 | |
|     /**
 | |
|      * Build the timeline by mapping over the sequence array and converting
 | |
|      * the definitions into keyframes and offsets with absolute time values.
 | |
|      * These will later get converted into relative offsets in a second pass.
 | |
|      */
 | |
|     for (let i = 0; i < sequence.length; i++) {
 | |
|         const segment = sequence[i];
 | |
|         /**
 | |
|          * If this is a timeline label, mark it and skip the rest of this iteration.
 | |
|          */
 | |
|         if (typeof segment === "string") {
 | |
|             timeLabels.set(segment, currentTime);
 | |
|             continue;
 | |
|         }
 | |
|         else if (!Array.isArray(segment)) {
 | |
|             timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
 | |
|             continue;
 | |
|         }
 | |
|         let [subject, keyframes, transition = {}] = segment;
 | |
|         /**
 | |
|          * If a relative or absolute time value has been specified we need to resolve
 | |
|          * it in relation to the currentTime.
 | |
|          */
 | |
|         if (transition.at !== undefined) {
 | |
|             currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
 | |
|         }
 | |
|         /**
 | |
|          * Keep track of the maximum duration in this definition. This will be
 | |
|          * applied to currentTime once the definition has been parsed.
 | |
|          */
 | |
|         let maxDuration = 0;
 | |
|         const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numElements = 0) => {
 | |
|             const valueKeyframesAsList = keyframesAsList(valueKeyframes);
 | |
|             const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", ...remainingTransition } = valueTransition;
 | |
|             let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
 | |
|             /**
 | |
|              * Resolve stagger() if defined.
 | |
|              */
 | |
|             const calculatedDelay = typeof delay === "function"
 | |
|                 ? delay(elementIndex, numElements)
 | |
|                 : delay;
 | |
|             /**
 | |
|              * If this animation should and can use a spring, generate a spring easing function.
 | |
|              */
 | |
|             const numKeyframes = valueKeyframesAsList.length;
 | |
|             if (numKeyframes <= 2 && type === "spring") {
 | |
|                 /**
 | |
|                  * As we're creating an easing function from a spring,
 | |
|                  * ideally we want to generate it using the real distance
 | |
|                  * between the two keyframes. However this isn't always
 | |
|                  * possible - in these situations we use 0-100.
 | |
|                  */
 | |
|                 let absoluteDelta = 100;
 | |
|                 if (numKeyframes === 2 &&
 | |
|                     isNumberKeyframesArray(valueKeyframesAsList)) {
 | |
|                     const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
 | |
|                     absoluteDelta = Math.abs(delta);
 | |
|                 }
 | |
|                 const springTransition = { ...remainingTransition };
 | |
|                 if (duration !== undefined) {
 | |
|                     springTransition.duration = secondsToMilliseconds(duration);
 | |
|                 }
 | |
|                 const springEasing = createGeneratorEasing(springTransition, absoluteDelta);
 | |
|                 ease = springEasing.ease;
 | |
|                 duration = springEasing.duration;
 | |
|             }
 | |
|             duration !== null && duration !== void 0 ? duration : (duration = defaultDuration);
 | |
|             const startTime = currentTime + calculatedDelay;
 | |
|             const targetTime = startTime + duration;
 | |
|             /**
 | |
|              * If there's only one time offset of 0, fill in a second with length 1
 | |
|              */
 | |
|             if (times.length === 1 && times[0] === 0) {
 | |
|                 times[1] = 1;
 | |
|             }
 | |
|             /**
 | |
|              * Fill out if offset if fewer offsets than keyframes
 | |
|              */
 | |
|             const remainder = times.length - valueKeyframesAsList.length;
 | |
|             remainder > 0 && fillOffset(times, remainder);
 | |
|             /**
 | |
|              * If only one value has been set, ie [1], push a null to the start of
 | |
|              * the keyframe array. This will let us mark a keyframe at this point
 | |
|              * that will later be hydrated with the previous value.
 | |
|              */
 | |
|             valueKeyframesAsList.length === 1 &&
 | |
|                 valueKeyframesAsList.unshift(null);
 | |
|             /**
 | |
|              * Add keyframes, mapping offsets to absolute time.
 | |
|              */
 | |
|             addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
 | |
|             maxDuration = Math.max(calculatedDelay + duration, maxDuration);
 | |
|             totalDuration = Math.max(targetTime, totalDuration);
 | |
|         };
 | |
|         if (isMotionValue(subject)) {
 | |
|             const subjectSequence = getSubjectSequence(subject, sequences);
 | |
|             resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
 | |
|         }
 | |
|         else {
 | |
|             /**
 | |
|              * Find all the elements specified in the definition and parse value
 | |
|              * keyframes from their timeline definitions.
 | |
|              */
 | |
|             const elements = resolveElements(subject, scope, elementCache);
 | |
|             const numElements = elements.length;
 | |
|             /**
 | |
|              * For every element in this segment, process the defined values.
 | |
|              */
 | |
|             for (let elementIndex = 0; elementIndex < numElements; elementIndex++) {
 | |
|                 /**
 | |
|                  * Cast necessary, but we know these are of this type
 | |
|                  */
 | |
|                 keyframes = keyframes;
 | |
|                 transition = transition;
 | |
|                 const element = elements[elementIndex];
 | |
|                 const subjectSequence = getSubjectSequence(element, sequences);
 | |
|                 for (const key in keyframes) {
 | |
|                     resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), elementIndex, numElements);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         prevTime = currentTime;
 | |
|         currentTime += maxDuration;
 | |
|     }
 | |
|     /**
 | |
|      * For every element and value combination create a new animation.
 | |
|      */
 | |
|     sequences.forEach((valueSequences, element) => {
 | |
|         for (const key in valueSequences) {
 | |
|             const valueSequence = valueSequences[key];
 | |
|             /**
 | |
|              * Arrange all the keyframes in ascending time order.
 | |
|              */
 | |
|             valueSequence.sort(compareByTime);
 | |
|             const keyframes = [];
 | |
|             const valueOffset = [];
 | |
|             const valueEasing = [];
 | |
|             /**
 | |
|              * For each keyframe, translate absolute times into
 | |
|              * relative offsets based on the total duration of the timeline.
 | |
|              */
 | |
|             for (let i = 0; i < valueSequence.length; i++) {
 | |
|                 const { at, value, easing } = valueSequence[i];
 | |
|                 keyframes.push(value);
 | |
|                 valueOffset.push(progress(0, totalDuration, at));
 | |
|                 valueEasing.push(easing || "easeOut");
 | |
|             }
 | |
|             /**
 | |
|              * If the first keyframe doesn't land on offset: 0
 | |
|              * provide one by duplicating the initial keyframe. This ensures
 | |
|              * it snaps to the first keyframe when the animation starts.
 | |
|              */
 | |
|             if (valueOffset[0] !== 0) {
 | |
|                 valueOffset.unshift(0);
 | |
|                 keyframes.unshift(keyframes[0]);
 | |
|                 valueEasing.unshift(defaultSegmentEasing);
 | |
|             }
 | |
|             /**
 | |
|              * If the last keyframe doesn't land on offset: 1
 | |
|              * provide one with a null wildcard value. This will ensure it
 | |
|              * stays static until the end of the animation.
 | |
|              */
 | |
|             if (valueOffset[valueOffset.length - 1] !== 1) {
 | |
|                 valueOffset.push(1);
 | |
|                 keyframes.push(null);
 | |
|             }
 | |
|             if (!animationDefinitions.has(element)) {
 | |
|                 animationDefinitions.set(element, {
 | |
|                     keyframes: {},
 | |
|                     transition: {},
 | |
|                 });
 | |
|             }
 | |
|             const definition = animationDefinitions.get(element);
 | |
|             definition.keyframes[key] = keyframes;
 | |
|             definition.transition[key] = {
 | |
|                 ...defaultTransition,
 | |
|                 duration: totalDuration,
 | |
|                 ease: valueEasing,
 | |
|                 times: valueOffset,
 | |
|                 ...sequenceTransition,
 | |
|             };
 | |
|         }
 | |
|     });
 | |
|     return animationDefinitions;
 | |
| }
 | |
| function getSubjectSequence(subject, sequences) {
 | |
|     !sequences.has(subject) && sequences.set(subject, {});
 | |
|     return sequences.get(subject);
 | |
| }
 | |
| function getValueSequence(name, sequences) {
 | |
|     if (!sequences[name])
 | |
|         sequences[name] = [];
 | |
|     return sequences[name];
 | |
| }
 | |
| function keyframesAsList(keyframes) {
 | |
|     return Array.isArray(keyframes) ? keyframes : [keyframes];
 | |
| }
 | |
| function getValueTransition(transition, key) {
 | |
|     return transition[key]
 | |
|         ? { ...transition, ...transition[key] }
 | |
|         : { ...transition };
 | |
| }
 | |
| const isNumber = (keyframe) => typeof keyframe === "number";
 | |
| const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
 | |
| 
 | |
| function animateElements(elementOrSelector, keyframes, options, scope) {
 | |
|     const elements = resolveElements(elementOrSelector, scope);
 | |
|     const numElements = elements.length;
 | |
|     exports.invariant(Boolean(numElements), "No valid element provided.");
 | |
|     const animations = [];
 | |
|     for (let i = 0; i < numElements; i++) {
 | |
|         const element = elements[i];
 | |
|         /**
 | |
|          * Check each element for an associated VisualElement. If none exists,
 | |
|          * we need to create one.
 | |
|          */
 | |
|         if (!visualElementStore.has(element)) {
 | |
|             /**
 | |
|              * TODO: We only need render-specific parts of the VisualElement.
 | |
|              * With some additional work the size of the animate() function
 | |
|              * could be reduced significantly.
 | |
|              */
 | |
|             createVisualElement(element);
 | |
|         }
 | |
|         const visualElement = visualElementStore.get(element);
 | |
|         const transition = { ...options };
 | |
|         /**
 | |
|          * Resolve stagger function if provided.
 | |
|          */
 | |
|         if (typeof transition.delay === "function") {
 | |
|             transition.delay = transition.delay(i, numElements);
 | |
|         }
 | |
|         animations.push(...animateTarget(visualElement, { ...keyframes, transition }, {}));
 | |
|     }
 | |
|     return new GroupPlaybackControls(animations);
 | |
| }
 | |
| const isSequence = (value) => Array.isArray(value) && Array.isArray(value[0]);
 | |
| function animateSequence(sequence, options, scope) {
 | |
|     const animations = [];
 | |
|     const animationDefinitions = createAnimationsFromSequence(sequence, options, scope);
 | |
|     animationDefinitions.forEach(({ keyframes, transition }, subject) => {
 | |
|         let animation;
 | |
|         if (isMotionValue(subject)) {
 | |
|             animation = animateSingleValue(subject, keyframes.default, transition.default);
 | |
|         }
 | |
|         else {
 | |
|             animation = animateElements(subject, keyframes, transition);
 | |
|         }
 | |
|         animations.push(animation);
 | |
|     });
 | |
|     return new GroupPlaybackControls(animations);
 | |
| }
 | |
| const createScopedAnimate = (scope) => {
 | |
|     /**
 | |
|      * Implementation
 | |
|      */
 | |
|     function scopedAnimate(valueOrElementOrSequence, keyframes, options) {
 | |
|         let animation;
 | |
|         if (isSequence(valueOrElementOrSequence)) {
 | |
|             animation = animateSequence(valueOrElementOrSequence, keyframes, scope);
 | |
|         }
 | |
|         else if (isDOMKeyframes(keyframes)) {
 | |
|             animation = animateElements(valueOrElementOrSequence, keyframes, options, scope);
 | |
|         }
 | |
|         else {
 | |
|             animation = animateSingleValue(valueOrElementOrSequence, keyframes, options);
 | |
|         }
 | |
|         if (scope) {
 | |
|             scope.animations.push(animation);
 | |
|         }
 | |
|         return animation;
 | |
|     }
 | |
|     return scopedAnimate;
 | |
| };
 | |
| const animate = createScopedAnimate();
 | |
| 
 | |
| const resizeHandlers = new WeakMap();
 | |
| let observer;
 | |
| function getElementSize(target, borderBoxSize) {
 | |
|     if (borderBoxSize) {
 | |
|         const { inlineSize, blockSize } = borderBoxSize[0];
 | |
|         return { width: inlineSize, height: blockSize };
 | |
|     }
 | |
|     else if (target instanceof SVGElement && "getBBox" in target) {
 | |
|         return target.getBBox();
 | |
|     }
 | |
|     else {
 | |
|         return {
 | |
|             width: target.offsetWidth,
 | |
|             height: target.offsetHeight,
 | |
|         };
 | |
|     }
 | |
| }
 | |
| function notifyTarget({ target, contentRect, borderBoxSize, }) {
 | |
|     var _a;
 | |
|     (_a = resizeHandlers.get(target)) === null || _a === void 0 ? void 0 : _a.forEach((handler) => {
 | |
|         handler({
 | |
|             target,
 | |
|             contentSize: contentRect,
 | |
|             get size() {
 | |
|                 return getElementSize(target, borderBoxSize);
 | |
|             },
 | |
|         });
 | |
|     });
 | |
| }
 | |
| function notifyAll(entries) {
 | |
|     entries.forEach(notifyTarget);
 | |
| }
 | |
| function createResizeObserver() {
 | |
|     if (typeof ResizeObserver === "undefined")
 | |
|         return;
 | |
|     observer = new ResizeObserver(notifyAll);
 | |
| }
 | |
| function resizeElement(target, handler) {
 | |
|     if (!observer)
 | |
|         createResizeObserver();
 | |
|     const elements = resolveElements(target);
 | |
|     elements.forEach((element) => {
 | |
|         let elementHandlers = resizeHandlers.get(element);
 | |
|         if (!elementHandlers) {
 | |
|             elementHandlers = new Set();
 | |
|             resizeHandlers.set(element, elementHandlers);
 | |
|         }
 | |
|         elementHandlers.add(handler);
 | |
|         observer === null || observer === void 0 ? void 0 : observer.observe(element);
 | |
|     });
 | |
|     return () => {
 | |
|         elements.forEach((element) => {
 | |
|             const elementHandlers = resizeHandlers.get(element);
 | |
|             elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.delete(handler);
 | |
|             if (!(elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.size)) {
 | |
|                 observer === null || observer === void 0 ? void 0 : observer.unobserve(element);
 | |
|             }
 | |
|         });
 | |
|     };
 | |
| }
 | |
| 
 | |
| const windowCallbacks = new Set();
 | |
| let windowResizeHandler;
 | |
| function createWindowResizeHandler() {
 | |
|     windowResizeHandler = () => {
 | |
|         const size = {
 | |
|             width: window.innerWidth,
 | |
|             height: window.innerHeight,
 | |
|         };
 | |
|         const info = {
 | |
|             target: window,
 | |
|             size,
 | |
|             contentSize: size,
 | |
|         };
 | |
|         windowCallbacks.forEach((callback) => callback(info));
 | |
|     };
 | |
|     window.addEventListener("resize", windowResizeHandler);
 | |
| }
 | |
| function resizeWindow(callback) {
 | |
|     windowCallbacks.add(callback);
 | |
|     if (!windowResizeHandler)
 | |
|         createWindowResizeHandler();
 | |
|     return () => {
 | |
|         windowCallbacks.delete(callback);
 | |
|         if (!windowCallbacks.size && windowResizeHandler) {
 | |
|             windowResizeHandler = undefined;
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| function resize(a, b) {
 | |
|     return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A time in milliseconds, beyond which we consider the scroll velocity to be 0.
 | |
|  */
 | |
| const maxElapsed = 50;
 | |
| const createAxisInfo = () => ({
 | |
|     current: 0,
 | |
|     offset: [],
 | |
|     progress: 0,
 | |
|     scrollLength: 0,
 | |
|     targetOffset: 0,
 | |
|     targetLength: 0,
 | |
|     containerLength: 0,
 | |
|     velocity: 0,
 | |
| });
 | |
| const createScrollInfo = () => ({
 | |
|     time: 0,
 | |
|     x: createAxisInfo(),
 | |
|     y: createAxisInfo(),
 | |
| });
 | |
| const keys = {
 | |
|     x: {
 | |
|         length: "Width",
 | |
|         position: "Left",
 | |
|     },
 | |
|     y: {
 | |
|         length: "Height",
 | |
|         position: "Top",
 | |
|     },
 | |
| };
 | |
| function updateAxisInfo(element, axisName, info, time) {
 | |
|     const axis = info[axisName];
 | |
|     const { length, position } = keys[axisName];
 | |
|     const prev = axis.current;
 | |
|     const prevTime = info.time;
 | |
|     axis.current = element["scroll" + position];
 | |
|     axis.scrollLength = element["scroll" + length] - element["client" + length];
 | |
|     axis.offset.length = 0;
 | |
|     axis.offset[0] = 0;
 | |
|     axis.offset[1] = axis.scrollLength;
 | |
|     axis.progress = progress(0, axis.scrollLength, axis.current);
 | |
|     const elapsed = time - prevTime;
 | |
|     axis.velocity =
 | |
|         elapsed > maxElapsed
 | |
|             ? 0
 | |
|             : velocityPerSecond(axis.current - prev, elapsed);
 | |
| }
 | |
| function updateScrollInfo(element, info, time) {
 | |
|     updateAxisInfo(element, "x", info, time);
 | |
|     updateAxisInfo(element, "y", info, time);
 | |
|     info.time = time;
 | |
| }
 | |
| 
 | |
| function calcInset(element, container) {
 | |
|     const inset = { x: 0, y: 0 };
 | |
|     let current = element;
 | |
|     while (current && current !== container) {
 | |
|         if (current instanceof HTMLElement) {
 | |
|             inset.x += current.offsetLeft;
 | |
|             inset.y += current.offsetTop;
 | |
|             current = current.offsetParent;
 | |
|         }
 | |
|         else if (current.tagName === "svg") {
 | |
|             /**
 | |
|              * This isn't an ideal approach to measuring the offset of <svg /> tags.
 | |
|              * It would be preferable, given they behave like HTMLElements in most ways
 | |
|              * to use offsetLeft/Top. But these don't exist on <svg />. Likewise we
 | |
|              * can't use .getBBox() like most SVG elements as these provide the offset
 | |
|              * relative to the SVG itself, which for <svg /> is usually 0x0.
 | |
|              */
 | |
|             const svgBoundingBox = current.getBoundingClientRect();
 | |
|             current = current.parentElement;
 | |
|             const parentBoundingBox = current.getBoundingClientRect();
 | |
|             inset.x += svgBoundingBox.left - parentBoundingBox.left;
 | |
|             inset.y += svgBoundingBox.top - parentBoundingBox.top;
 | |
|         }
 | |
|         else if (current instanceof SVGGraphicsElement) {
 | |
|             const { x, y } = current.getBBox();
 | |
|             inset.x += x;
 | |
|             inset.y += y;
 | |
|             let svg = null;
 | |
|             let parent = current.parentNode;
 | |
|             while (!svg) {
 | |
|                 if (parent.tagName === "svg") {
 | |
|                     svg = parent;
 | |
|                 }
 | |
|                 parent = current.parentNode;
 | |
|             }
 | |
|             current = svg;
 | |
|         }
 | |
|         else {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     return inset;
 | |
| }
 | |
| 
 | |
| const ScrollOffset = {
 | |
|     Enter: [
 | |
|         [0, 1],
 | |
|         [1, 1],
 | |
|     ],
 | |
|     Exit: [
 | |
|         [0, 0],
 | |
|         [1, 0],
 | |
|     ],
 | |
|     Any: [
 | |
|         [1, 0],
 | |
|         [0, 1],
 | |
|     ],
 | |
|     All: [
 | |
|         [0, 0],
 | |
|         [1, 1],
 | |
|     ],
 | |
| };
 | |
| 
 | |
| const namedEdges = {
 | |
|     start: 0,
 | |
|     center: 0.5,
 | |
|     end: 1,
 | |
| };
 | |
| function resolveEdge(edge, length, inset = 0) {
 | |
|     let delta = 0;
 | |
|     /**
 | |
|      * If we have this edge defined as a preset, replace the definition
 | |
|      * with the numerical value.
 | |
|      */
 | |
|     if (namedEdges[edge] !== undefined) {
 | |
|         edge = namedEdges[edge];
 | |
|     }
 | |
|     /**
 | |
|      * Handle unit values
 | |
|      */
 | |
|     if (typeof edge === "string") {
 | |
|         const asNumber = parseFloat(edge);
 | |
|         if (edge.endsWith("px")) {
 | |
|             delta = asNumber;
 | |
|         }
 | |
|         else if (edge.endsWith("%")) {
 | |
|             edge = asNumber / 100;
 | |
|         }
 | |
|         else if (edge.endsWith("vw")) {
 | |
|             delta = (asNumber / 100) * document.documentElement.clientWidth;
 | |
|         }
 | |
|         else if (edge.endsWith("vh")) {
 | |
|             delta = (asNumber / 100) * document.documentElement.clientHeight;
 | |
|         }
 | |
|         else {
 | |
|             edge = asNumber;
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * If the edge is defined as a number, handle as a progress value.
 | |
|      */
 | |
|     if (typeof edge === "number") {
 | |
|         delta = length * edge;
 | |
|     }
 | |
|     return inset + delta;
 | |
| }
 | |
| 
 | |
| const defaultOffset = [0, 0];
 | |
| function resolveOffset(offset, containerLength, targetLength, targetInset) {
 | |
|     let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
 | |
|     let targetPoint = 0;
 | |
|     let containerPoint = 0;
 | |
|     if (typeof offset === "number") {
 | |
|         /**
 | |
|          * If we're provided offset: [0, 0.5, 1] then each number x should become
 | |
|          * [x, x], so we default to the behaviour of mapping 0 => 0 of both target
 | |
|          * and container etc.
 | |
|          */
 | |
|         offsetDefinition = [offset, offset];
 | |
|     }
 | |
|     else if (typeof offset === "string") {
 | |
|         offset = offset.trim();
 | |
|         if (offset.includes(" ")) {
 | |
|             offsetDefinition = offset.split(" ");
 | |
|         }
 | |
|         else {
 | |
|             /**
 | |
|              * If we're provided a definition like "100px" then we want to apply
 | |
|              * that only to the top of the target point, leaving the container at 0.
 | |
|              * Whereas a named offset like "end" should be applied to both.
 | |
|              */
 | |
|             offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
 | |
|         }
 | |
|     }
 | |
|     targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
 | |
|     containerPoint = resolveEdge(offsetDefinition[1], containerLength);
 | |
|     return targetPoint - containerPoint;
 | |
| }
 | |
| 
 | |
| const point = { x: 0, y: 0 };
 | |
| function getTargetSize(target) {
 | |
|     return "getBBox" in target && target.tagName !== "svg"
 | |
|         ? target.getBBox()
 | |
|         : { width: target.clientWidth, height: target.clientHeight };
 | |
| }
 | |
| function resolveOffsets(container, info, options) {
 | |
|     let { offset: offsetDefinition = ScrollOffset.All } = options;
 | |
|     const { target = container, axis = "y" } = options;
 | |
|     const lengthLabel = axis === "y" ? "height" : "width";
 | |
|     const inset = target !== container ? calcInset(target, container) : point;
 | |
|     /**
 | |
|      * Measure the target and container. If they're the same thing then we
 | |
|      * use the container's scrollWidth/Height as the target, from there
 | |
|      * all other calculations can remain the same.
 | |
|      */
 | |
|     const targetSize = target === container
 | |
|         ? { width: container.scrollWidth, height: container.scrollHeight }
 | |
|         : getTargetSize(target);
 | |
|     const containerSize = {
 | |
|         width: container.clientWidth,
 | |
|         height: container.clientHeight,
 | |
|     };
 | |
|     /**
 | |
|      * Reset the length of the resolved offset array rather than creating a new one.
 | |
|      * TODO: More reusable data structures for targetSize/containerSize would also be good.
 | |
|      */
 | |
|     info[axis].offset.length = 0;
 | |
|     /**
 | |
|      * Populate the offset array by resolving the user's offset definition into
 | |
|      * a list of pixel scroll offets.
 | |
|      */
 | |
|     let hasChanged = !info[axis].interpolate;
 | |
|     const numOffsets = offsetDefinition.length;
 | |
|     for (let i = 0; i < numOffsets; i++) {
 | |
|         const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
 | |
|         if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
 | |
|             hasChanged = true;
 | |
|         }
 | |
|         info[axis].offset[i] = offset;
 | |
|     }
 | |
|     /**
 | |
|      * If the pixel scroll offsets have changed, create a new interpolator function
 | |
|      * to map scroll value into a progress.
 | |
|      */
 | |
|     if (hasChanged) {
 | |
|         info[axis].interpolate = interpolate(info[axis].offset, defaultOffset$1(offsetDefinition));
 | |
|         info[axis].interpolatorOffsets = [...info[axis].offset];
 | |
|     }
 | |
|     info[axis].progress = info[axis].interpolate(info[axis].current);
 | |
| }
 | |
| 
 | |
| function measure(container, target = container, info) {
 | |
|     /**
 | |
|      * Find inset of target within scrollable container
 | |
|      */
 | |
|     info.x.targetOffset = 0;
 | |
|     info.y.targetOffset = 0;
 | |
|     if (target !== container) {
 | |
|         let node = target;
 | |
|         while (node && node !== container) {
 | |
|             info.x.targetOffset += node.offsetLeft;
 | |
|             info.y.targetOffset += node.offsetTop;
 | |
|             node = node.offsetParent;
 | |
|         }
 | |
|     }
 | |
|     info.x.targetLength =
 | |
|         target === container ? target.scrollWidth : target.clientWidth;
 | |
|     info.y.targetLength =
 | |
|         target === container ? target.scrollHeight : target.clientHeight;
 | |
|     info.x.containerLength = container.clientWidth;
 | |
|     info.y.containerLength = container.clientHeight;
 | |
|     /**
 | |
|      * In development mode ensure scroll containers aren't position: static as this makes
 | |
|      * it difficult to measure their relative positions.
 | |
|      */
 | |
|     if (process.env.NODE_ENV !== "production") {
 | |
|         if (container && target && target !== container) {
 | |
|             warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly.");
 | |
|         }
 | |
|     }
 | |
| }
 | |
| function createOnScrollHandler(element, onScroll, info, options = {}) {
 | |
|     return {
 | |
|         measure: () => measure(element, options.target, info),
 | |
|         update: (time) => {
 | |
|             updateScrollInfo(element, info, time);
 | |
|             if (options.offset || options.target) {
 | |
|                 resolveOffsets(element, info, options);
 | |
|             }
 | |
|         },
 | |
|         notify: () => onScroll(info),
 | |
|     };
 | |
| }
 | |
| 
 | |
| const scrollListeners = new WeakMap();
 | |
| const resizeListeners = new WeakMap();
 | |
| const onScrollHandlers = new WeakMap();
 | |
| const getEventTarget = (element) => element === document.documentElement ? window : element;
 | |
| function scrollInfo(onScroll, { container = document.documentElement, ...options } = {}) {
 | |
|     let containerHandlers = onScrollHandlers.get(container);
 | |
|     /**
 | |
|      * Get the onScroll handlers for this container.
 | |
|      * If one isn't found, create a new one.
 | |
|      */
 | |
|     if (!containerHandlers) {
 | |
|         containerHandlers = new Set();
 | |
|         onScrollHandlers.set(container, containerHandlers);
 | |
|     }
 | |
|     /**
 | |
|      * Create a new onScroll handler for the provided callback.
 | |
|      */
 | |
|     const info = createScrollInfo();
 | |
|     const containerHandler = createOnScrollHandler(container, onScroll, info, options);
 | |
|     containerHandlers.add(containerHandler);
 | |
|     /**
 | |
|      * Check if there's a scroll event listener for this container.
 | |
|      * If not, create one.
 | |
|      */
 | |
|     if (!scrollListeners.has(container)) {
 | |
|         const measureAll = () => {
 | |
|             for (const handler of containerHandlers)
 | |
|                 handler.measure();
 | |
|         };
 | |
|         const updateAll = () => {
 | |
|             for (const handler of containerHandlers) {
 | |
|                 handler.update(frameData.timestamp);
 | |
|             }
 | |
|         };
 | |
|         const notifyAll = () => {
 | |
|             for (const handler of containerHandlers)
 | |
|                 handler.notify();
 | |
|         };
 | |
|         const listener = () => {
 | |
|             frame.read(measureAll, false, true);
 | |
|             frame.read(updateAll, false, true);
 | |
|             frame.update(notifyAll, false, true);
 | |
|         };
 | |
|         scrollListeners.set(container, listener);
 | |
|         const target = getEventTarget(container);
 | |
|         window.addEventListener("resize", listener, { passive: true });
 | |
|         if (container !== document.documentElement) {
 | |
|             resizeListeners.set(container, resize(container, listener));
 | |
|         }
 | |
|         target.addEventListener("scroll", listener, { passive: true });
 | |
|     }
 | |
|     const listener = scrollListeners.get(container);
 | |
|     frame.read(listener, false, true);
 | |
|     return () => {
 | |
|         var _a;
 | |
|         cancelFrame(listener);
 | |
|         /**
 | |
|          * Check if we even have any handlers for this container.
 | |
|          */
 | |
|         const currentHandlers = onScrollHandlers.get(container);
 | |
|         if (!currentHandlers)
 | |
|             return;
 | |
|         currentHandlers.delete(containerHandler);
 | |
|         if (currentHandlers.size)
 | |
|             return;
 | |
|         /**
 | |
|          * If no more handlers, remove the scroll listener too.
 | |
|          */
 | |
|         const scrollListener = scrollListeners.get(container);
 | |
|         scrollListeners.delete(container);
 | |
|         if (scrollListener) {
 | |
|             getEventTarget(container).removeEventListener("scroll", scrollListener);
 | |
|             (_a = resizeListeners.get(container)) === null || _a === void 0 ? void 0 : _a();
 | |
|             window.removeEventListener("resize", scrollListener);
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| function scrollTimelineFallback({ source, axis = "y" }) {
 | |
|     // ScrollTimeline records progress as a percentage CSSUnitValue
 | |
|     const currentTime = { value: 0 };
 | |
|     const cancel = scrollInfo((info) => {
 | |
|         currentTime.value = info[axis].progress * 100;
 | |
|     }, { container: source, axis });
 | |
|     return { currentTime, cancel };
 | |
| }
 | |
| const timelineCache = new Map();
 | |
| function getTimeline({ source = document.documentElement, axis = "y", } = {}) {
 | |
|     if (!timelineCache.has(source)) {
 | |
|         timelineCache.set(source, {});
 | |
|     }
 | |
|     const elementCache = timelineCache.get(source);
 | |
|     if (!elementCache[axis]) {
 | |
|         elementCache[axis] = supportsScrollTimeline()
 | |
|             ? new ScrollTimeline({ source, axis })
 | |
|             : scrollTimelineFallback({ source, axis });
 | |
|     }
 | |
|     return elementCache[axis];
 | |
| }
 | |
| function scroll(onScroll, options) {
 | |
|     const timeline = getTimeline(options);
 | |
|     if (typeof onScroll === "function") {
 | |
|         return observeTimeline(onScroll, timeline);
 | |
|     }
 | |
|     else {
 | |
|         return onScroll.attachTimeline(timeline);
 | |
|     }
 | |
| }
 | |
| 
 | |
| const thresholds = {
 | |
|     some: 0,
 | |
|     all: 1,
 | |
| };
 | |
| function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) {
 | |
|     const elements = resolveElements(elementOrSelector);
 | |
|     const activeIntersections = new WeakMap();
 | |
|     const onIntersectionChange = (entries) => {
 | |
|         entries.forEach((entry) => {
 | |
|             const onEnd = activeIntersections.get(entry.target);
 | |
|             /**
 | |
|              * If there's no change to the intersection, we don't need to
 | |
|              * do anything here.
 | |
|              */
 | |
|             if (entry.isIntersecting === Boolean(onEnd))
 | |
|                 return;
 | |
|             if (entry.isIntersecting) {
 | |
|                 const newOnEnd = onStart(entry);
 | |
|                 if (typeof newOnEnd === "function") {
 | |
|                     activeIntersections.set(entry.target, newOnEnd);
 | |
|                 }
 | |
|                 else {
 | |
|                     observer.unobserve(entry.target);
 | |
|                 }
 | |
|             }
 | |
|             else if (onEnd) {
 | |
|                 onEnd(entry);
 | |
|                 activeIntersections.delete(entry.target);
 | |
|             }
 | |
|         });
 | |
|     };
 | |
|     const observer = new IntersectionObserver(onIntersectionChange, {
 | |
|         root,
 | |
|         rootMargin,
 | |
|         threshold: typeof amount === "number" ? amount : thresholds[amount],
 | |
|     });
 | |
|     elements.forEach((element) => observer.observe(element));
 | |
|     return () => observer.disconnect();
 | |
| }
 | |
| 
 | |
| function getOriginIndex(from, total) {
 | |
|     if (from === "first") {
 | |
|         return 0;
 | |
|     }
 | |
|     else {
 | |
|         const lastIndex = total - 1;
 | |
|         return from === "last" ? lastIndex : lastIndex / 2;
 | |
|     }
 | |
| }
 | |
| function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) {
 | |
|     return (i, total) => {
 | |
|         const fromIndex = typeof from === "number" ? from : getOriginIndex(from, total);
 | |
|         const distance = Math.abs(fromIndex - i);
 | |
|         let delay = duration * distance;
 | |
|         if (ease) {
 | |
|             const maxDelay = total * duration;
 | |
|             const easingFunction = easingDefinitionToFunction(ease);
 | |
|             delay = easingFunction(delay / maxDelay) * maxDelay;
 | |
|         }
 | |
|         return startDelay + delay;
 | |
|     };
 | |
| }
 | |
| 
 | |
| const isCustomValueType = (v) => {
 | |
|     return v && typeof v === "object" && v.mix;
 | |
| };
 | |
| const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
 | |
| function transform(...args) {
 | |
|     const useImmediate = !Array.isArray(args[0]);
 | |
|     const argOffset = useImmediate ? 0 : -1;
 | |
|     const inputValue = args[0 + argOffset];
 | |
|     const inputRange = args[1 + argOffset];
 | |
|     const outputRange = args[2 + argOffset];
 | |
|     const options = args[3 + argOffset];
 | |
|     const interpolator = interpolate(inputRange, outputRange, {
 | |
|         mixer: getMixer(outputRange[0]),
 | |
|         ...options,
 | |
|     });
 | |
|     return useImmediate ? interpolator(inputValue) : interpolator;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @deprecated
 | |
|  *
 | |
|  * Import as `frame` instead.
 | |
|  */
 | |
| const sync = frame;
 | |
| /**
 | |
|  * @deprecated
 | |
|  *
 | |
|  * Use cancelFrame(callback) instead.
 | |
|  */
 | |
| const cancelSync = stepsOrder.reduce((acc, key) => {
 | |
|     acc[key] = (process) => cancelFrame(process);
 | |
|     return acc;
 | |
| }, {});
 | |
| 
 | |
| exports.HTMLVisualElement = HTMLVisualElement;
 | |
| exports.MotionGlobalConfig = MotionGlobalConfig;
 | |
| exports.MotionValue = MotionValue;
 | |
| exports.SVGVisualElement = SVGVisualElement;
 | |
| exports.SubscriptionManager = SubscriptionManager;
 | |
| exports.VisualElement = VisualElement;
 | |
| exports.addScaleCorrector = addScaleCorrector;
 | |
| exports.addUniqueItem = addUniqueItem;
 | |
| exports.animate = animate;
 | |
| exports.animateMotionValue = animateMotionValue;
 | |
| exports.animateSingleValue = animateSingleValue;
 | |
| exports.animateStyle = animateStyle;
 | |
| exports.animateTarget = animateTarget;
 | |
| exports.animateValue = animateValue;
 | |
| exports.anticipate = anticipate;
 | |
| exports.applyBoxDelta = applyBoxDelta;
 | |
| exports.applyTreeDeltas = applyTreeDeltas;
 | |
| exports.backIn = backIn;
 | |
| exports.backInOut = backInOut;
 | |
| exports.backOut = backOut;
 | |
| exports.buildHTMLStyles = buildHTMLStyles;
 | |
| exports.buildSVGAttrs = buildSVGAttrs;
 | |
| exports.buildTransform = buildTransform;
 | |
| exports.camelToDash = camelToDash;
 | |
| exports.cancelFrame = cancelFrame;
 | |
| exports.cancelSync = cancelSync;
 | |
| exports.checkTargetForNewValues = checkTargetForNewValues;
 | |
| exports.circIn = circIn;
 | |
| exports.circInOut = circInOut;
 | |
| exports.circOut = circOut;
 | |
| exports.clamp = clamp;
 | |
| exports.collectMotionValues = collectMotionValues;
 | |
| exports.color = color;
 | |
| exports.complex = complex;
 | |
| exports.convertBoundingBoxToBox = convertBoundingBoxToBox;
 | |
| exports.convertBoxToBoundingBox = convertBoxToBoundingBox;
 | |
| exports.createBox = createBox;
 | |
| exports.createDelta = createDelta;
 | |
| exports.createScopedAnimate = createScopedAnimate;
 | |
| exports.cubicBezier = cubicBezier;
 | |
| exports.delay = delay;
 | |
| exports.distance = distance;
 | |
| exports.distance2D = distance2D;
 | |
| exports.easeIn = easeIn;
 | |
| exports.easeInOut = easeInOut;
 | |
| exports.easeOut = easeOut;
 | |
| exports.featureDefinitions = featureDefinitions;
 | |
| exports.frame = frame;
 | |
| exports.frameData = frameData;
 | |
| exports.getOrigin = getOrigin;
 | |
| exports.getValueTransition = getValueTransition$1;
 | |
| exports.has2DTranslate = has2DTranslate;
 | |
| exports.hasReducedMotionListener = hasReducedMotionListener;
 | |
| exports.hasScale = hasScale;
 | |
| exports.hasTransform = hasTransform;
 | |
| exports.inView = inView;
 | |
| exports.initPrefersReducedMotion = initPrefersReducedMotion;
 | |
| exports.instantAnimationState = instantAnimationState;
 | |
| exports.interpolate = interpolate;
 | |
| exports.isAnimationControls = isAnimationControls;
 | |
| exports.isBrowser = isBrowser;
 | |
| exports.isCSSVariableName = isCSSVariableName;
 | |
| exports.isControllingVariants = isControllingVariants;
 | |
| exports.isCustomValue = isCustomValue;
 | |
| exports.isForcedMotionValue = isForcedMotionValue;
 | |
| exports.isKeyframesTarget = isKeyframesTarget;
 | |
| exports.isMotionValue = isMotionValue;
 | |
| exports.isRefObject = isRefObject;
 | |
| exports.isSVGElement = isSVGElement;
 | |
| exports.isSVGTag = isSVGTag;
 | |
| exports.isVariantLabel = isVariantLabel;
 | |
| exports.isVariantNode = isVariantNode;
 | |
| exports.measurePageBox = measurePageBox;
 | |
| exports.millisecondsToSeconds = millisecondsToSeconds;
 | |
| exports.mirrorEasing = mirrorEasing;
 | |
| exports.mix = mix;
 | |
| exports.motionValue = motionValue;
 | |
| exports.moveItem = moveItem;
 | |
| exports.noop = noop;
 | |
| exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
 | |
| exports.optimizedAppearDataId = optimizedAppearDataId;
 | |
| exports.percent = percent;
 | |
| exports.pipe = pipe;
 | |
| exports.prefersReducedMotion = prefersReducedMotion;
 | |
| exports.progress = progress;
 | |
| exports.px = px;
 | |
| exports.removeItem = removeItem;
 | |
| exports.renderSVG = renderSVG;
 | |
| exports.resolveVariant = resolveVariant;
 | |
| exports.resolveVariantFromProps = resolveVariantFromProps;
 | |
| exports.reverseEasing = reverseEasing;
 | |
| exports.scaleCorrectors = scaleCorrectors;
 | |
| exports.scalePoint = scalePoint;
 | |
| exports.scrapeMotionValuesFromProps = scrapeMotionValuesFromProps;
 | |
| exports.scrapeMotionValuesFromProps$1 = scrapeMotionValuesFromProps$1;
 | |
| exports.scroll = scroll;
 | |
| exports.scrollInfo = scrollInfo;
 | |
| exports.secondsToMilliseconds = secondsToMilliseconds;
 | |
| exports.setValues = setValues;
 | |
| exports.spring = spring;
 | |
| exports.stagger = stagger;
 | |
| exports.steps = steps;
 | |
| exports.sync = sync;
 | |
| exports.transform = transform;
 | |
| exports.transformBox = transformBox;
 | |
| exports.transformProps = transformProps;
 | |
| exports.translateAxis = translateAxis;
 | |
| exports.variantPriorityOrder = variantPriorityOrder;
 | |
| exports.visualElementStore = visualElementStore;
 | |
| exports.warnOnce = warnOnce;
 | |
| exports.wrap = wrap;
 |