 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>
		
			
				
	
	
		
			6314 lines
		
	
	
		
			240 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			6314 lines
		
	
	
		
			240 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| Object.defineProperty(exports, '__esModule', { value: true });
 | |
| 
 | |
| var React = require('react');
 | |
| var indexLegacy = require('./index-legacy-87714a68.js');
 | |
| 
 | |
| function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
 | |
| 
 | |
| function _interopNamespace(e) {
 | |
|     if (e && e.__esModule) return e;
 | |
|     var n = Object.create(null);
 | |
|     if (e) {
 | |
|         Object.keys(e).forEach(function (k) {
 | |
|             if (k !== 'default') {
 | |
|                 var d = Object.getOwnPropertyDescriptor(e, k);
 | |
|                 Object.defineProperty(n, k, d.get ? d : {
 | |
|                     enumerable: true,
 | |
|                     get: function () { return e[k]; }
 | |
|                 });
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     n["default"] = e;
 | |
|     return Object.freeze(n);
 | |
| }
 | |
| 
 | |
| var React__namespace = /*#__PURE__*/_interopNamespace(React);
 | |
| var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
 | |
| 
 | |
| /**
 | |
|  * @public
 | |
|  */
 | |
| const MotionConfigContext = React.createContext({
 | |
|     transformPagePoint: (p) => p,
 | |
|     isStatic: false,
 | |
|     reducedMotion: "never",
 | |
| });
 | |
| 
 | |
| const MotionContext = React.createContext({});
 | |
| 
 | |
| /**
 | |
|  * @public
 | |
|  */
 | |
| const PresenceContext = React.createContext(null);
 | |
| 
 | |
| const useIsomorphicLayoutEffect = indexLegacy.isBrowser ? React.useLayoutEffect : React.useEffect;
 | |
| 
 | |
| const LazyContext = React.createContext({ strict: false });
 | |
| 
 | |
| function useVisualElement(Component, visualState, props, createVisualElement) {
 | |
|     const { visualElement: parent } = React.useContext(MotionContext);
 | |
|     const lazyContext = React.useContext(LazyContext);
 | |
|     const presenceContext = React.useContext(PresenceContext);
 | |
|     const reducedMotionConfig = React.useContext(MotionConfigContext).reducedMotion;
 | |
|     const visualElementRef = React.useRef();
 | |
|     /**
 | |
|      * If we haven't preloaded a renderer, check to see if we have one lazy-loaded
 | |
|      */
 | |
|     createVisualElement = createVisualElement || lazyContext.renderer;
 | |
|     if (!visualElementRef.current && createVisualElement) {
 | |
|         visualElementRef.current = createVisualElement(Component, {
 | |
|             visualState,
 | |
|             parent,
 | |
|             props,
 | |
|             presenceContext,
 | |
|             blockInitialAnimation: presenceContext
 | |
|                 ? presenceContext.initial === false
 | |
|                 : false,
 | |
|             reducedMotionConfig,
 | |
|         });
 | |
|     }
 | |
|     const visualElement = visualElementRef.current;
 | |
|     React.useInsertionEffect(() => {
 | |
|         visualElement && visualElement.update(props, presenceContext);
 | |
|     });
 | |
|     /**
 | |
|      * Cache this value as we want to know whether HandoffAppearAnimations
 | |
|      * was present on initial render - it will be deleted after this.
 | |
|      */
 | |
|     const wantsHandoff = React.useRef(Boolean(props[indexLegacy.optimizedAppearDataAttribute] && !window.HandoffComplete));
 | |
|     useIsomorphicLayoutEffect(() => {
 | |
|         if (!visualElement)
 | |
|             return;
 | |
|         visualElement.render();
 | |
|         /**
 | |
|          * Ideally this function would always run in a useEffect.
 | |
|          *
 | |
|          * However, if we have optimised appear animations to handoff from,
 | |
|          * it needs to happen synchronously to ensure there's no flash of
 | |
|          * incorrect styles in the event of a hydration error.
 | |
|          *
 | |
|          * So if we detect a situtation where optimised appear animations
 | |
|          * are running, we use useLayoutEffect to trigger animations.
 | |
|          */
 | |
|         if (wantsHandoff.current && visualElement.animationState) {
 | |
|             visualElement.animationState.animateChanges();
 | |
|         }
 | |
|     });
 | |
|     React.useEffect(() => {
 | |
|         if (!visualElement)
 | |
|             return;
 | |
|         visualElement.updateFeatures();
 | |
|         if (!wantsHandoff.current && visualElement.animationState) {
 | |
|             visualElement.animationState.animateChanges();
 | |
|         }
 | |
|         if (wantsHandoff.current) {
 | |
|             wantsHandoff.current = false;
 | |
|             // This ensures all future calls to animateChanges() will run in useEffect
 | |
|             window.HandoffComplete = true;
 | |
|         }
 | |
|     });
 | |
|     return visualElement;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates a ref function that, when called, hydrates the provided
 | |
|  * external ref and VisualElement.
 | |
|  */
 | |
| function useMotionRef(visualState, visualElement, externalRef) {
 | |
|     return React.useCallback((instance) => {
 | |
|         instance && visualState.mount && visualState.mount(instance);
 | |
|         if (visualElement) {
 | |
|             instance
 | |
|                 ? visualElement.mount(instance)
 | |
|                 : visualElement.unmount();
 | |
|         }
 | |
|         if (externalRef) {
 | |
|             if (typeof externalRef === "function") {
 | |
|                 externalRef(instance);
 | |
|             }
 | |
|             else if (indexLegacy.isRefObject(externalRef)) {
 | |
|                 externalRef.current = instance;
 | |
|             }
 | |
|         }
 | |
|     }, 
 | |
|     /**
 | |
|      * Only pass a new ref callback to React if we've received a visual element
 | |
|      * factory. Otherwise we'll be mounting/remounting every time externalRef
 | |
|      * or other dependencies change.
 | |
|      */
 | |
|     [visualElement]);
 | |
| }
 | |
| 
 | |
| function getCurrentTreeVariants(props, context) {
 | |
|     if (indexLegacy.isControllingVariants(props)) {
 | |
|         const { initial, animate } = props;
 | |
|         return {
 | |
|             initial: initial === false || indexLegacy.isVariantLabel(initial)
 | |
|                 ? initial
 | |
|                 : undefined,
 | |
|             animate: indexLegacy.isVariantLabel(animate) ? animate : undefined,
 | |
|         };
 | |
|     }
 | |
|     return props.inherit !== false ? context : {};
 | |
| }
 | |
| 
 | |
| function useCreateMotionContext(props) {
 | |
|     const { initial, animate } = getCurrentTreeVariants(props, React.useContext(MotionContext));
 | |
|     return React.useMemo(() => ({ initial, animate }), [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]);
 | |
| }
 | |
| function variantLabelsAsDependency(prop) {
 | |
|     return Array.isArray(prop) ? prop.join(" ") : prop;
 | |
| }
 | |
| 
 | |
| function loadFeatures(features) {
 | |
|     for (const key in features) {
 | |
|         indexLegacy.featureDefinitions[key] = {
 | |
|             ...indexLegacy.featureDefinitions[key],
 | |
|             ...features[key],
 | |
|         };
 | |
|     }
 | |
| }
 | |
| 
 | |
| const LayoutGroupContext = React.createContext({});
 | |
| 
 | |
| /**
 | |
|  * Internal, exported only for usage in Framer
 | |
|  */
 | |
| const SwitchLayoutGroupContext = React.createContext({});
 | |
| 
 | |
| const motionComponentSymbol = Symbol.for("motionComponentSymbol");
 | |
| 
 | |
| /**
 | |
|  * Create a `motion` component.
 | |
|  *
 | |
|  * This function accepts a Component argument, which can be either a string (ie "div"
 | |
|  * for `motion.div`), or an actual React component.
 | |
|  *
 | |
|  * Alongside this is a config option which provides a way of rendering the provided
 | |
|  * component "offline", or outside the React render cycle.
 | |
|  */
 | |
| function createMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) {
 | |
|     preloadedFeatures && loadFeatures(preloadedFeatures);
 | |
|     function MotionComponent(props, externalRef) {
 | |
|         /**
 | |
|          * If we need to measure the element we load this functionality in a
 | |
|          * separate class component in order to gain access to getSnapshotBeforeUpdate.
 | |
|          */
 | |
|         let MeasureLayout;
 | |
|         const configAndProps = {
 | |
|             ...React.useContext(MotionConfigContext),
 | |
|             ...props,
 | |
|             layoutId: useLayoutId(props),
 | |
|         };
 | |
|         const { isStatic } = configAndProps;
 | |
|         const context = useCreateMotionContext(props);
 | |
|         const visualState = useVisualState(props, isStatic);
 | |
|         if (!isStatic && indexLegacy.isBrowser) {
 | |
|             /**
 | |
|              * Create a VisualElement for this component. A VisualElement provides a common
 | |
|              * interface to renderer-specific APIs (ie DOM/Three.js etc) as well as
 | |
|              * providing a way of rendering to these APIs outside of the React render loop
 | |
|              * for more performant animations and interactions
 | |
|              */
 | |
|             context.visualElement = useVisualElement(Component, visualState, configAndProps, createVisualElement);
 | |
|             /**
 | |
|              * Load Motion gesture and animation features. These are rendered as renderless
 | |
|              * components so each feature can optionally make use of React lifecycle methods.
 | |
|              */
 | |
|             const initialLayoutGroupConfig = React.useContext(SwitchLayoutGroupContext);
 | |
|             const isStrict = React.useContext(LazyContext).strict;
 | |
|             if (context.visualElement) {
 | |
|                 MeasureLayout = context.visualElement.loadFeatures(
 | |
|                 // Note: Pass the full new combined props to correctly re-render dynamic feature components.
 | |
|                 configAndProps, isStrict, preloadedFeatures, initialLayoutGroupConfig);
 | |
|             }
 | |
|         }
 | |
|         /**
 | |
|          * The mount order and hierarchy is specific to ensure our element ref
 | |
|          * is hydrated by the time features fire their effects.
 | |
|          */
 | |
|         return (React__namespace.createElement(MotionContext.Provider, { value: context },
 | |
|             MeasureLayout && context.visualElement ? (React__namespace.createElement(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null,
 | |
|             useRender(Component, props, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)));
 | |
|     }
 | |
|     const ForwardRefComponent = React.forwardRef(MotionComponent);
 | |
|     ForwardRefComponent[motionComponentSymbol] = Component;
 | |
|     return ForwardRefComponent;
 | |
| }
 | |
| function useLayoutId({ layoutId }) {
 | |
|     const layoutGroupId = React.useContext(LayoutGroupContext).id;
 | |
|     return layoutGroupId && layoutId !== undefined
 | |
|         ? layoutGroupId + "-" + layoutId
 | |
|         : layoutId;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Convert any React component into a `motion` component. The provided component
 | |
|  * **must** use `React.forwardRef` to the underlying DOM component you want to animate.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const Component = React.forwardRef((props, ref) => {
 | |
|  *   return <div ref={ref} />
 | |
|  * })
 | |
|  *
 | |
|  * const MotionComponent = motion(Component)
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function createMotionProxy(createConfig) {
 | |
|     function custom(Component, customMotionComponentConfig = {}) {
 | |
|         return createMotionComponent(createConfig(Component, customMotionComponentConfig));
 | |
|     }
 | |
|     if (typeof Proxy === "undefined") {
 | |
|         return custom;
 | |
|     }
 | |
|     /**
 | |
|      * A cache of generated `motion` components, e.g `motion.div`, `motion.input` etc.
 | |
|      * Rather than generating them anew every render.
 | |
|      */
 | |
|     const componentCache = new Map();
 | |
|     return new Proxy(custom, {
 | |
|         /**
 | |
|          * Called when `motion` is referenced with a prop: `motion.div`, `motion.input` etc.
 | |
|          * The prop name is passed through as `key` and we can use that to generate a `motion`
 | |
|          * DOM component with that name.
 | |
|          */
 | |
|         get: (_target, key) => {
 | |
|             /**
 | |
|              * If this element doesn't exist in the component cache, create it and cache.
 | |
|              */
 | |
|             if (!componentCache.has(key)) {
 | |
|                 componentCache.set(key, custom(key));
 | |
|             }
 | |
|             return componentCache.get(key);
 | |
|         },
 | |
|     });
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * We keep these listed seperately as we use the lowercase tag names as part
 | |
|  * of the runtime bundle to detect SVG components
 | |
|  */
 | |
| const lowercaseSVGElements = [
 | |
|     "animate",
 | |
|     "circle",
 | |
|     "defs",
 | |
|     "desc",
 | |
|     "ellipse",
 | |
|     "g",
 | |
|     "image",
 | |
|     "line",
 | |
|     "filter",
 | |
|     "marker",
 | |
|     "mask",
 | |
|     "metadata",
 | |
|     "path",
 | |
|     "pattern",
 | |
|     "polygon",
 | |
|     "polyline",
 | |
|     "rect",
 | |
|     "stop",
 | |
|     "switch",
 | |
|     "symbol",
 | |
|     "svg",
 | |
|     "text",
 | |
|     "tspan",
 | |
|     "use",
 | |
|     "view",
 | |
| ];
 | |
| 
 | |
| function isSVGComponent(Component) {
 | |
|     if (
 | |
|     /**
 | |
|      * If it's not a string, it's a custom React component. Currently we only support
 | |
|      * HTML custom React components.
 | |
|      */
 | |
|     typeof Component !== "string" ||
 | |
|         /**
 | |
|          * If it contains a dash, the element is a custom HTML webcomponent.
 | |
|          */
 | |
|         Component.includes("-")) {
 | |
|         return false;
 | |
|     }
 | |
|     else if (
 | |
|     /**
 | |
|      * If it's in our list of lowercase SVG tags, it's an SVG component
 | |
|      */
 | |
|     lowercaseSVGElements.indexOf(Component) > -1 ||
 | |
|         /**
 | |
|          * If it contains a capital letter, it's an SVG component
 | |
|          */
 | |
|         /[A-Z]/.test(Component)) {
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| const createHtmlRenderState = () => ({
 | |
|     style: {},
 | |
|     transform: {},
 | |
|     transformOrigin: {},
 | |
|     vars: {},
 | |
| });
 | |
| 
 | |
| function copyRawValuesOnly(target, source, props) {
 | |
|     for (const key in source) {
 | |
|         if (!indexLegacy.isMotionValue(source[key]) && !indexLegacy.isForcedMotionValue(key, props)) {
 | |
|             target[key] = source[key];
 | |
|         }
 | |
|     }
 | |
| }
 | |
| function useInitialMotionValues({ transformTemplate }, visualState, isStatic) {
 | |
|     return React.useMemo(() => {
 | |
|         const state = createHtmlRenderState();
 | |
|         indexLegacy.buildHTMLStyles(state, visualState, { enableHardwareAcceleration: !isStatic }, transformTemplate);
 | |
|         return Object.assign({}, state.vars, state.style);
 | |
|     }, [visualState]);
 | |
| }
 | |
| function useStyle(props, visualState, isStatic) {
 | |
|     const styleProp = props.style || {};
 | |
|     const style = {};
 | |
|     /**
 | |
|      * Copy non-Motion Values straight into style
 | |
|      */
 | |
|     copyRawValuesOnly(style, styleProp, props);
 | |
|     Object.assign(style, useInitialMotionValues(props, visualState, isStatic));
 | |
|     return props.transformValues ? props.transformValues(style) : style;
 | |
| }
 | |
| function useHTMLProps(props, visualState, isStatic) {
 | |
|     // The `any` isn't ideal but it is the type of createElement props argument
 | |
|     const htmlProps = {};
 | |
|     const style = useStyle(props, visualState, isStatic);
 | |
|     if (props.drag && props.dragListener !== false) {
 | |
|         // Disable the ghost element when a user drags
 | |
|         htmlProps.draggable = false;
 | |
|         // Disable text selection
 | |
|         style.userSelect =
 | |
|             style.WebkitUserSelect =
 | |
|                 style.WebkitTouchCallout =
 | |
|                     "none";
 | |
|         // Disable scrolling on the draggable direction
 | |
|         style.touchAction =
 | |
|             props.drag === true
 | |
|                 ? "none"
 | |
|                 : `pan-${props.drag === "x" ? "y" : "x"}`;
 | |
|     }
 | |
|     if (props.tabIndex === undefined &&
 | |
|         (props.onTap || props.onTapStart || props.whileTap)) {
 | |
|         htmlProps.tabIndex = 0;
 | |
|     }
 | |
|     htmlProps.style = style;
 | |
|     return htmlProps;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A list of all valid MotionProps.
 | |
|  *
 | |
|  * @privateRemarks
 | |
|  * This doesn't throw if a `MotionProp` name is missing - it should.
 | |
|  */
 | |
| const validMotionProps = new Set([
 | |
|     "animate",
 | |
|     "exit",
 | |
|     "variants",
 | |
|     "initial",
 | |
|     "style",
 | |
|     "values",
 | |
|     "variants",
 | |
|     "transition",
 | |
|     "transformTemplate",
 | |
|     "transformValues",
 | |
|     "custom",
 | |
|     "inherit",
 | |
|     "onBeforeLayoutMeasure",
 | |
|     "onAnimationStart",
 | |
|     "onAnimationComplete",
 | |
|     "onUpdate",
 | |
|     "onDragStart",
 | |
|     "onDrag",
 | |
|     "onDragEnd",
 | |
|     "onMeasureDragConstraints",
 | |
|     "onDirectionLock",
 | |
|     "onDragTransitionEnd",
 | |
|     "_dragX",
 | |
|     "_dragY",
 | |
|     "onHoverStart",
 | |
|     "onHoverEnd",
 | |
|     "onViewportEnter",
 | |
|     "onViewportLeave",
 | |
|     "globalTapTarget",
 | |
|     "ignoreStrict",
 | |
|     "viewport",
 | |
| ]);
 | |
| /**
 | |
|  * Check whether a prop name is a valid `MotionProp` key.
 | |
|  *
 | |
|  * @param key - Name of the property to check
 | |
|  * @returns `true` is key is a valid `MotionProp`.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function isValidMotionProp(key) {
 | |
|     return (key.startsWith("while") ||
 | |
|         (key.startsWith("drag") && key !== "draggable") ||
 | |
|         key.startsWith("layout") ||
 | |
|         key.startsWith("onTap") ||
 | |
|         key.startsWith("onPan") ||
 | |
|         key.startsWith("onLayout") ||
 | |
|         validMotionProps.has(key));
 | |
| }
 | |
| 
 | |
| let shouldForward = (key) => !isValidMotionProp(key);
 | |
| function loadExternalIsValidProp(isValidProp) {
 | |
|     if (!isValidProp)
 | |
|         return;
 | |
|     // Explicitly filter our events
 | |
|     shouldForward = (key) => key.startsWith("on") ? !isValidMotionProp(key) : isValidProp(key);
 | |
| }
 | |
| /**
 | |
|  * Emotion and Styled Components both allow users to pass through arbitrary props to their components
 | |
|  * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which
 | |
|  * of these should be passed to the underlying DOM node.
 | |
|  *
 | |
|  * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props
 | |
|  * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props
 | |
|  * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of
 | |
|  * `@emotion/is-prop-valid`, however to fix this problem we need to use it.
 | |
|  *
 | |
|  * By making it an optionalDependency we can offer this functionality only in the situations where it's
 | |
|  * actually required.
 | |
|  */
 | |
| try {
 | |
|     /**
 | |
|      * We attempt to import this package but require won't be defined in esm environments, in that case
 | |
|      * isPropValid will have to be provided via `MotionContext`. In a 6.0.0 this should probably be removed
 | |
|      * in favour of explicit injection.
 | |
|      */
 | |
|     loadExternalIsValidProp(require("@emotion/is-prop-valid").default);
 | |
| }
 | |
| catch (_a) {
 | |
|     // We don't need to actually do anything here - the fallback is the existing `isPropValid`.
 | |
| }
 | |
| function filterProps(props, isDom, forwardMotionProps) {
 | |
|     const filteredProps = {};
 | |
|     for (const key in props) {
 | |
|         /**
 | |
|          * values is considered a valid prop by Emotion, so if it's present
 | |
|          * this will be rendered out to the DOM unless explicitly filtered.
 | |
|          *
 | |
|          * We check the type as it could be used with the `feColorMatrix`
 | |
|          * element, which we support.
 | |
|          */
 | |
|         if (key === "values" && typeof props.values === "object")
 | |
|             continue;
 | |
|         if (shouldForward(key) ||
 | |
|             (forwardMotionProps === true && isValidMotionProp(key)) ||
 | |
|             (!isDom && !isValidMotionProp(key)) ||
 | |
|             // If trying to use native HTML drag events, forward drag listeners
 | |
|             (props["draggable"] && key.startsWith("onDrag"))) {
 | |
|             filteredProps[key] = props[key];
 | |
|         }
 | |
|     }
 | |
|     return filteredProps;
 | |
| }
 | |
| 
 | |
| const createSvgRenderState = () => ({
 | |
|     ...createHtmlRenderState(),
 | |
|     attrs: {},
 | |
| });
 | |
| 
 | |
| function useSVGProps(props, visualState, _isStatic, Component) {
 | |
|     const visualProps = React.useMemo(() => {
 | |
|         const state = createSvgRenderState();
 | |
|         indexLegacy.buildSVGAttrs(state, visualState, { enableHardwareAcceleration: false }, indexLegacy.isSVGTag(Component), props.transformTemplate);
 | |
|         return {
 | |
|             ...state.attrs,
 | |
|             style: { ...state.style },
 | |
|         };
 | |
|     }, [visualState]);
 | |
|     if (props.style) {
 | |
|         const rawStyles = {};
 | |
|         copyRawValuesOnly(rawStyles, props.style, props);
 | |
|         visualProps.style = { ...rawStyles, ...visualProps.style };
 | |
|     }
 | |
|     return visualProps;
 | |
| }
 | |
| 
 | |
| function createUseRender(forwardMotionProps = false) {
 | |
|     const useRender = (Component, props, ref, { latestValues }, isStatic) => {
 | |
|         const useVisualProps = isSVGComponent(Component)
 | |
|             ? useSVGProps
 | |
|             : useHTMLProps;
 | |
|         const visualProps = useVisualProps(props, latestValues, isStatic, Component);
 | |
|         const filteredProps = filterProps(props, typeof Component === "string", forwardMotionProps);
 | |
|         const elementProps = {
 | |
|             ...filteredProps,
 | |
|             ...visualProps,
 | |
|             ref,
 | |
|         };
 | |
|         /**
 | |
|          * If component has been handed a motion value as its child,
 | |
|          * memoise its initial value and render that. Subsequent updates
 | |
|          * will be handled by the onChange handler
 | |
|          */
 | |
|         const { children } = props;
 | |
|         const renderedChildren = React.useMemo(() => (indexLegacy.isMotionValue(children) ? children.get() : children), [children]);
 | |
|         return React.createElement(Component, {
 | |
|             ...elementProps,
 | |
|             children: renderedChildren,
 | |
|         });
 | |
|     };
 | |
|     return useRender;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates a constant value over the lifecycle of a component.
 | |
|  *
 | |
|  * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
 | |
|  * a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
 | |
|  * you can ensure that initialisers don't execute twice or more.
 | |
|  */
 | |
| function useConstant(init) {
 | |
|     const ref = React.useRef(null);
 | |
|     if (ref.current === null) {
 | |
|         ref.current = init();
 | |
|     }
 | |
|     return ref.current;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
 | |
|  *
 | |
|  * TODO: Remove and move to library
 | |
|  */
 | |
| function resolveMotionValue(value) {
 | |
|     const unwrappedValue = indexLegacy.isMotionValue(value) ? value.get() : value;
 | |
|     return indexLegacy.isCustomValue(unwrappedValue)
 | |
|         ? unwrappedValue.toValue()
 | |
|         : unwrappedValue;
 | |
| }
 | |
| 
 | |
| function makeState({ scrapeMotionValuesFromProps, createRenderState, onMount, }, props, context, presenceContext) {
 | |
|     const state = {
 | |
|         latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
 | |
|         renderState: createRenderState(),
 | |
|     };
 | |
|     if (onMount) {
 | |
|         state.mount = (instance) => onMount(props, instance, state);
 | |
|     }
 | |
|     return state;
 | |
| }
 | |
| const makeUseVisualState = (config) => (props, isStatic) => {
 | |
|     const context = React.useContext(MotionContext);
 | |
|     const presenceContext = React.useContext(PresenceContext);
 | |
|     const make = () => makeState(config, props, context, presenceContext);
 | |
|     return isStatic ? make() : useConstant(make);
 | |
| };
 | |
| function makeLatestValues(props, context, presenceContext, scrapeMotionValues) {
 | |
|     const values = {};
 | |
|     const motionValues = scrapeMotionValues(props, {});
 | |
|     for (const key in motionValues) {
 | |
|         values[key] = resolveMotionValue(motionValues[key]);
 | |
|     }
 | |
|     let { initial, animate } = props;
 | |
|     const isControllingVariants = indexLegacy.isControllingVariants(props);
 | |
|     const isVariantNode = indexLegacy.isVariantNode(props);
 | |
|     if (context &&
 | |
|         isVariantNode &&
 | |
|         !isControllingVariants &&
 | |
|         props.inherit !== false) {
 | |
|         if (initial === undefined)
 | |
|             initial = context.initial;
 | |
|         if (animate === undefined)
 | |
|             animate = context.animate;
 | |
|     }
 | |
|     let isInitialAnimationBlocked = presenceContext
 | |
|         ? presenceContext.initial === false
 | |
|         : false;
 | |
|     isInitialAnimationBlocked = isInitialAnimationBlocked || initial === false;
 | |
|     const variantToSet = isInitialAnimationBlocked ? animate : initial;
 | |
|     if (variantToSet &&
 | |
|         typeof variantToSet !== "boolean" &&
 | |
|         !indexLegacy.isAnimationControls(variantToSet)) {
 | |
|         const list = Array.isArray(variantToSet) ? variantToSet : [variantToSet];
 | |
|         list.forEach((definition) => {
 | |
|             const resolved = indexLegacy.resolveVariantFromProps(props, definition);
 | |
|             if (!resolved)
 | |
|                 return;
 | |
|             const { transitionEnd, transition, ...target } = resolved;
 | |
|             for (const key in target) {
 | |
|                 let valueTarget = target[key];
 | |
|                 if (Array.isArray(valueTarget)) {
 | |
|                     /**
 | |
|                      * Take final keyframe if the initial animation is blocked because
 | |
|                      * we want to initialise at the end of that blocked animation.
 | |
|                      */
 | |
|                     const index = isInitialAnimationBlocked
 | |
|                         ? valueTarget.length - 1
 | |
|                         : 0;
 | |
|                     valueTarget = valueTarget[index];
 | |
|                 }
 | |
|                 if (valueTarget !== null) {
 | |
|                     values[key] = valueTarget;
 | |
|                 }
 | |
|             }
 | |
|             for (const key in transitionEnd)
 | |
|                 values[key] = transitionEnd[key];
 | |
|         });
 | |
|     }
 | |
|     return values;
 | |
| }
 | |
| 
 | |
| const svgMotionConfig = {
 | |
|     useVisualState: makeUseVisualState({
 | |
|         scrapeMotionValuesFromProps: indexLegacy.scrapeMotionValuesFromProps,
 | |
|         createRenderState: createSvgRenderState,
 | |
|         onMount: (props, instance, { renderState, latestValues }) => {
 | |
|             indexLegacy.frame.read(() => {
 | |
|                 try {
 | |
|                     renderState.dimensions =
 | |
|                         typeof instance.getBBox ===
 | |
|                             "function"
 | |
|                             ? instance.getBBox()
 | |
|                             : instance.getBoundingClientRect();
 | |
|                 }
 | |
|                 catch (e) {
 | |
|                     // Most likely trying to measure an unrendered element under Firefox
 | |
|                     renderState.dimensions = {
 | |
|                         x: 0,
 | |
|                         y: 0,
 | |
|                         width: 0,
 | |
|                         height: 0,
 | |
|                     };
 | |
|                 }
 | |
|             });
 | |
|             indexLegacy.frame.render(() => {
 | |
|                 indexLegacy.buildSVGAttrs(renderState, latestValues, { enableHardwareAcceleration: false }, indexLegacy.isSVGTag(instance.tagName), props.transformTemplate);
 | |
|                 indexLegacy.renderSVG(instance, renderState);
 | |
|             });
 | |
|         },
 | |
|     }),
 | |
| };
 | |
| 
 | |
| const htmlMotionConfig = {
 | |
|     useVisualState: makeUseVisualState({
 | |
|         scrapeMotionValuesFromProps: indexLegacy.scrapeMotionValuesFromProps$1,
 | |
|         createRenderState: createHtmlRenderState,
 | |
|     }),
 | |
| };
 | |
| 
 | |
| function createDomMotionConfig(Component, { forwardMotionProps = false }, preloadedFeatures, createVisualElement) {
 | |
|     const baseConfig = isSVGComponent(Component)
 | |
|         ? svgMotionConfig
 | |
|         : htmlMotionConfig;
 | |
|     return {
 | |
|         ...baseConfig,
 | |
|         preloadedFeatures,
 | |
|         useRender: createUseRender(forwardMotionProps),
 | |
|         createVisualElement,
 | |
|         Component,
 | |
|     };
 | |
| }
 | |
| 
 | |
| function addDomEvent(target, eventName, handler, options = { passive: true }) {
 | |
|     target.addEventListener(eventName, handler, options);
 | |
|     return () => target.removeEventListener(eventName, handler);
 | |
| }
 | |
| 
 | |
| const isPrimaryPointer = (event) => {
 | |
|     if (event.pointerType === "mouse") {
 | |
|         return typeof event.button !== "number" || event.button <= 0;
 | |
|     }
 | |
|     else {
 | |
|         /**
 | |
|          * isPrimary is true for all mice buttons, whereas every touch point
 | |
|          * is regarded as its own input. So subsequent concurrent touch points
 | |
|          * will be false.
 | |
|          *
 | |
|          * Specifically match against false here as incomplete versions of
 | |
|          * PointerEvents in very old browser might have it set as undefined.
 | |
|          */
 | |
|         return event.isPrimary !== false;
 | |
|     }
 | |
| };
 | |
| 
 | |
| function extractEventInfo(event, pointType = "page") {
 | |
|     return {
 | |
|         point: {
 | |
|             x: event[pointType + "X"],
 | |
|             y: event[pointType + "Y"],
 | |
|         },
 | |
|     };
 | |
| }
 | |
| const addPointerInfo = (handler) => {
 | |
|     return (event) => isPrimaryPointer(event) && handler(event, extractEventInfo(event));
 | |
| };
 | |
| 
 | |
| function addPointerEvent(target, eventName, handler, options) {
 | |
|     return addDomEvent(target, eventName, addPointerInfo(handler), options);
 | |
| }
 | |
| 
 | |
| function createLock(name) {
 | |
|     let lock = null;
 | |
|     return () => {
 | |
|         const openLock = () => {
 | |
|             lock = null;
 | |
|         };
 | |
|         if (lock === null) {
 | |
|             lock = name;
 | |
|             return openLock;
 | |
|         }
 | |
|         return false;
 | |
|     };
 | |
| }
 | |
| const globalHorizontalLock = createLock("dragHorizontal");
 | |
| const globalVerticalLock = createLock("dragVertical");
 | |
| function getGlobalLock(drag) {
 | |
|     let lock = false;
 | |
|     if (drag === "y") {
 | |
|         lock = globalVerticalLock();
 | |
|     }
 | |
|     else if (drag === "x") {
 | |
|         lock = globalHorizontalLock();
 | |
|     }
 | |
|     else {
 | |
|         const openHorizontal = globalHorizontalLock();
 | |
|         const openVertical = globalVerticalLock();
 | |
|         if (openHorizontal && openVertical) {
 | |
|             lock = () => {
 | |
|                 openHorizontal();
 | |
|                 openVertical();
 | |
|             };
 | |
|         }
 | |
|         else {
 | |
|             // Release the locks because we don't use them
 | |
|             if (openHorizontal)
 | |
|                 openHorizontal();
 | |
|             if (openVertical)
 | |
|                 openVertical();
 | |
|         }
 | |
|     }
 | |
|     return lock;
 | |
| }
 | |
| function isDragActive() {
 | |
|     // Check the gesture lock - if we get it, it means no drag gesture is active
 | |
|     // and we can safely fire the tap gesture.
 | |
|     const openGestureLock = getGlobalLock(true);
 | |
|     if (!openGestureLock)
 | |
|         return true;
 | |
|     openGestureLock();
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| class Feature {
 | |
|     constructor(node) {
 | |
|         this.isMounted = false;
 | |
|         this.node = node;
 | |
|     }
 | |
|     update() { }
 | |
| }
 | |
| 
 | |
| function addHoverEvent(node, isActive) {
 | |
|     const eventName = "pointer" + (isActive ? "enter" : "leave");
 | |
|     const callbackName = "onHover" + (isActive ? "Start" : "End");
 | |
|     const handleEvent = (event, info) => {
 | |
|         if (event.pointerType === "touch" || isDragActive())
 | |
|             return;
 | |
|         const props = node.getProps();
 | |
|         if (node.animationState && props.whileHover) {
 | |
|             node.animationState.setActive("whileHover", isActive);
 | |
|         }
 | |
|         if (props[callbackName]) {
 | |
|             indexLegacy.frame.update(() => props[callbackName](event, info));
 | |
|         }
 | |
|     };
 | |
|     return addPointerEvent(node.current, eventName, handleEvent, {
 | |
|         passive: !node.getProps()[callbackName],
 | |
|     });
 | |
| }
 | |
| class HoverGesture extends Feature {
 | |
|     mount() {
 | |
|         this.unmount = indexLegacy.pipe(addHoverEvent(this.node, true), addHoverEvent(this.node, false));
 | |
|     }
 | |
|     unmount() { }
 | |
| }
 | |
| 
 | |
| class FocusGesture extends Feature {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.isActive = false;
 | |
|     }
 | |
|     onFocus() {
 | |
|         let isFocusVisible = false;
 | |
|         /**
 | |
|          * If this element doesn't match focus-visible then don't
 | |
|          * apply whileHover. But, if matches throws that focus-visible
 | |
|          * is not a valid selector then in that browser outline styles will be applied
 | |
|          * to the element by default and we want to match that behaviour with whileFocus.
 | |
|          */
 | |
|         try {
 | |
|             isFocusVisible = this.node.current.matches(":focus-visible");
 | |
|         }
 | |
|         catch (e) {
 | |
|             isFocusVisible = true;
 | |
|         }
 | |
|         if (!isFocusVisible || !this.node.animationState)
 | |
|             return;
 | |
|         this.node.animationState.setActive("whileFocus", true);
 | |
|         this.isActive = true;
 | |
|     }
 | |
|     onBlur() {
 | |
|         if (!this.isActive || !this.node.animationState)
 | |
|             return;
 | |
|         this.node.animationState.setActive("whileFocus", false);
 | |
|         this.isActive = false;
 | |
|     }
 | |
|     mount() {
 | |
|         this.unmount = indexLegacy.pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur()));
 | |
|     }
 | |
|     unmount() { }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Recursively traverse up the tree to check whether the provided child node
 | |
|  * is the parent or a descendant of it.
 | |
|  *
 | |
|  * @param parent - Element to find
 | |
|  * @param child - Element to test against parent
 | |
|  */
 | |
| const isNodeOrChild = (parent, child) => {
 | |
|     if (!child) {
 | |
|         return false;
 | |
|     }
 | |
|     else if (parent === child) {
 | |
|         return true;
 | |
|     }
 | |
|     else {
 | |
|         return isNodeOrChild(parent, child.parentElement);
 | |
|     }
 | |
| };
 | |
| 
 | |
| function fireSyntheticPointerEvent(name, handler) {
 | |
|     if (!handler)
 | |
|         return;
 | |
|     const syntheticPointerEvent = new PointerEvent("pointer" + name);
 | |
|     handler(syntheticPointerEvent, extractEventInfo(syntheticPointerEvent));
 | |
| }
 | |
| class PressGesture extends Feature {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.removeStartListeners = indexLegacy.noop;
 | |
|         this.removeEndListeners = indexLegacy.noop;
 | |
|         this.removeAccessibleListeners = indexLegacy.noop;
 | |
|         this.startPointerPress = (startEvent, startInfo) => {
 | |
|             if (this.isPressing)
 | |
|                 return;
 | |
|             this.removeEndListeners();
 | |
|             const props = this.node.getProps();
 | |
|             const endPointerPress = (endEvent, endInfo) => {
 | |
|                 if (!this.checkPressEnd())
 | |
|                     return;
 | |
|                 const { onTap, onTapCancel, globalTapTarget } = this.node.getProps();
 | |
|                 indexLegacy.frame.update(() => {
 | |
|                     /**
 | |
|                      * We only count this as a tap gesture if the event.target is the same
 | |
|                      * as, or a child of, this component's element
 | |
|                      */
 | |
|                     !globalTapTarget &&
 | |
|                         !isNodeOrChild(this.node.current, endEvent.target)
 | |
|                         ? onTapCancel && onTapCancel(endEvent, endInfo)
 | |
|                         : onTap && onTap(endEvent, endInfo);
 | |
|                 });
 | |
|             };
 | |
|             const removePointerUpListener = addPointerEvent(window, "pointerup", endPointerPress, { passive: !(props.onTap || props["onPointerUp"]) });
 | |
|             const removePointerCancelListener = addPointerEvent(window, "pointercancel", (cancelEvent, cancelInfo) => this.cancelPress(cancelEvent, cancelInfo), { passive: !(props.onTapCancel || props["onPointerCancel"]) });
 | |
|             this.removeEndListeners = indexLegacy.pipe(removePointerUpListener, removePointerCancelListener);
 | |
|             this.startPress(startEvent, startInfo);
 | |
|         };
 | |
|         this.startAccessiblePress = () => {
 | |
|             const handleKeydown = (keydownEvent) => {
 | |
|                 if (keydownEvent.key !== "Enter" || this.isPressing)
 | |
|                     return;
 | |
|                 const handleKeyup = (keyupEvent) => {
 | |
|                     if (keyupEvent.key !== "Enter" || !this.checkPressEnd())
 | |
|                         return;
 | |
|                     fireSyntheticPointerEvent("up", (event, info) => {
 | |
|                         const { onTap } = this.node.getProps();
 | |
|                         if (onTap) {
 | |
|                             indexLegacy.frame.update(() => onTap(event, info));
 | |
|                         }
 | |
|                     });
 | |
|                 };
 | |
|                 this.removeEndListeners();
 | |
|                 this.removeEndListeners = addDomEvent(this.node.current, "keyup", handleKeyup);
 | |
|                 fireSyntheticPointerEvent("down", (event, info) => {
 | |
|                     this.startPress(event, info);
 | |
|                 });
 | |
|             };
 | |
|             const removeKeydownListener = addDomEvent(this.node.current, "keydown", handleKeydown);
 | |
|             const handleBlur = () => {
 | |
|                 if (!this.isPressing)
 | |
|                     return;
 | |
|                 fireSyntheticPointerEvent("cancel", (cancelEvent, cancelInfo) => this.cancelPress(cancelEvent, cancelInfo));
 | |
|             };
 | |
|             const removeBlurListener = addDomEvent(this.node.current, "blur", handleBlur);
 | |
|             this.removeAccessibleListeners = indexLegacy.pipe(removeKeydownListener, removeBlurListener);
 | |
|         };
 | |
|     }
 | |
|     startPress(event, info) {
 | |
|         this.isPressing = true;
 | |
|         const { onTapStart, whileTap } = this.node.getProps();
 | |
|         /**
 | |
|          * Ensure we trigger animations before firing event callback
 | |
|          */
 | |
|         if (whileTap && this.node.animationState) {
 | |
|             this.node.animationState.setActive("whileTap", true);
 | |
|         }
 | |
|         if (onTapStart) {
 | |
|             indexLegacy.frame.update(() => onTapStart(event, info));
 | |
|         }
 | |
|     }
 | |
|     checkPressEnd() {
 | |
|         this.removeEndListeners();
 | |
|         this.isPressing = false;
 | |
|         const props = this.node.getProps();
 | |
|         if (props.whileTap && this.node.animationState) {
 | |
|             this.node.animationState.setActive("whileTap", false);
 | |
|         }
 | |
|         return !isDragActive();
 | |
|     }
 | |
|     cancelPress(event, info) {
 | |
|         if (!this.checkPressEnd())
 | |
|             return;
 | |
|         const { onTapCancel } = this.node.getProps();
 | |
|         if (onTapCancel) {
 | |
|             indexLegacy.frame.update(() => onTapCancel(event, info));
 | |
|         }
 | |
|     }
 | |
|     mount() {
 | |
|         const props = this.node.getProps();
 | |
|         const removePointerListener = addPointerEvent(props.globalTapTarget ? window : this.node.current, "pointerdown", this.startPointerPress, { passive: !(props.onTapStart || props["onPointerStart"]) });
 | |
|         const removeFocusListener = addDomEvent(this.node.current, "focus", this.startAccessiblePress);
 | |
|         this.removeStartListeners = indexLegacy.pipe(removePointerListener, removeFocusListener);
 | |
|     }
 | |
|     unmount() {
 | |
|         this.removeStartListeners();
 | |
|         this.removeEndListeners();
 | |
|         this.removeAccessibleListeners();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Map an IntersectionHandler callback to an element. We only ever make one handler for one
 | |
|  * element, so even though these handlers might all be triggered by different
 | |
|  * observers, we can keep them in the same map.
 | |
|  */
 | |
| const observerCallbacks = new WeakMap();
 | |
| /**
 | |
|  * Multiple observers can be created for multiple element/document roots. Each with
 | |
|  * different settings. So here we store dictionaries of observers to each root,
 | |
|  * using serialised settings (threshold/margin) as lookup keys.
 | |
|  */
 | |
| const observers = new WeakMap();
 | |
| const fireObserverCallback = (entry) => {
 | |
|     const callback = observerCallbacks.get(entry.target);
 | |
|     callback && callback(entry);
 | |
| };
 | |
| const fireAllObserverCallbacks = (entries) => {
 | |
|     entries.forEach(fireObserverCallback);
 | |
| };
 | |
| function initIntersectionObserver({ root, ...options }) {
 | |
|     const lookupRoot = root || document;
 | |
|     /**
 | |
|      * If we don't have an observer lookup map for this root, create one.
 | |
|      */
 | |
|     if (!observers.has(lookupRoot)) {
 | |
|         observers.set(lookupRoot, {});
 | |
|     }
 | |
|     const rootObservers = observers.get(lookupRoot);
 | |
|     const key = JSON.stringify(options);
 | |
|     /**
 | |
|      * If we don't have an observer for this combination of root and settings,
 | |
|      * create one.
 | |
|      */
 | |
|     if (!rootObservers[key]) {
 | |
|         rootObservers[key] = new IntersectionObserver(fireAllObserverCallbacks, { root, ...options });
 | |
|     }
 | |
|     return rootObservers[key];
 | |
| }
 | |
| function observeIntersection(element, options, callback) {
 | |
|     const rootInteresectionObserver = initIntersectionObserver(options);
 | |
|     observerCallbacks.set(element, callback);
 | |
|     rootInteresectionObserver.observe(element);
 | |
|     return () => {
 | |
|         observerCallbacks.delete(element);
 | |
|         rootInteresectionObserver.unobserve(element);
 | |
|     };
 | |
| }
 | |
| 
 | |
| const thresholdNames = {
 | |
|     some: 0,
 | |
|     all: 1,
 | |
| };
 | |
| class InViewFeature extends Feature {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.hasEnteredView = false;
 | |
|         this.isInView = false;
 | |
|     }
 | |
|     startObserver() {
 | |
|         this.unmount();
 | |
|         const { viewport = {} } = this.node.getProps();
 | |
|         const { root, margin: rootMargin, amount = "some", once } = viewport;
 | |
|         const options = {
 | |
|             root: root ? root.current : undefined,
 | |
|             rootMargin,
 | |
|             threshold: typeof amount === "number" ? amount : thresholdNames[amount],
 | |
|         };
 | |
|         const onIntersectionUpdate = (entry) => {
 | |
|             const { isIntersecting } = entry;
 | |
|             /**
 | |
|              * If there's been no change in the viewport state, early return.
 | |
|              */
 | |
|             if (this.isInView === isIntersecting)
 | |
|                 return;
 | |
|             this.isInView = isIntersecting;
 | |
|             /**
 | |
|              * Handle hasEnteredView. If this is only meant to run once, and
 | |
|              * element isn't visible, early return. Otherwise set hasEnteredView to true.
 | |
|              */
 | |
|             if (once && !isIntersecting && this.hasEnteredView) {
 | |
|                 return;
 | |
|             }
 | |
|             else if (isIntersecting) {
 | |
|                 this.hasEnteredView = true;
 | |
|             }
 | |
|             if (this.node.animationState) {
 | |
|                 this.node.animationState.setActive("whileInView", isIntersecting);
 | |
|             }
 | |
|             /**
 | |
|              * Use the latest committed props rather than the ones in scope
 | |
|              * when this observer is created
 | |
|              */
 | |
|             const { onViewportEnter, onViewportLeave } = this.node.getProps();
 | |
|             const callback = isIntersecting ? onViewportEnter : onViewportLeave;
 | |
|             callback && callback(entry);
 | |
|         };
 | |
|         return observeIntersection(this.node.current, options, onIntersectionUpdate);
 | |
|     }
 | |
|     mount() {
 | |
|         this.startObserver();
 | |
|     }
 | |
|     update() {
 | |
|         if (typeof IntersectionObserver === "undefined")
 | |
|             return;
 | |
|         const { props, prevProps } = this.node;
 | |
|         const hasOptionsChanged = ["amount", "margin", "root"].some(hasViewportOptionChanged(props, prevProps));
 | |
|         if (hasOptionsChanged) {
 | |
|             this.startObserver();
 | |
|         }
 | |
|     }
 | |
|     unmount() { }
 | |
| }
 | |
| function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) {
 | |
|     return (name) => viewport[name] !== prevViewport[name];
 | |
| }
 | |
| 
 | |
| const gestureAnimations = {
 | |
|     inView: {
 | |
|         Feature: InViewFeature,
 | |
|     },
 | |
|     tap: {
 | |
|         Feature: PressGesture,
 | |
|     },
 | |
|     focus: {
 | |
|         Feature: FocusGesture,
 | |
|     },
 | |
|     hover: {
 | |
|         Feature: HoverGesture,
 | |
|     },
 | |
| };
 | |
| 
 | |
| function shallowCompare(next, prev) {
 | |
|     if (!Array.isArray(prev))
 | |
|         return false;
 | |
|     const prevLength = prev.length;
 | |
|     if (prevLength !== next.length)
 | |
|         return false;
 | |
|     for (let i = 0; i < prevLength; i++) {
 | |
|         if (prev[i] !== next[i])
 | |
|             return false;
 | |
|     }
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| function animateVariant(visualElement, variant, options = {}) {
 | |
|     const resolved = indexLegacy.resolveVariant(visualElement, variant, options.custom);
 | |
|     let { transition = visualElement.getDefaultTransition() || {} } = resolved || {};
 | |
|     if (options.transitionOverride) {
 | |
|         transition = options.transitionOverride;
 | |
|     }
 | |
|     /**
 | |
|      * If we have a variant, create a callback that runs it as an animation.
 | |
|      * Otherwise, we resolve a Promise immediately for a composable no-op.
 | |
|      */
 | |
|     const getAnimation = resolved
 | |
|         ? () => Promise.all(indexLegacy.animateTarget(visualElement, resolved, options))
 | |
|         : () => Promise.resolve();
 | |
|     /**
 | |
|      * If we have children, create a callback that runs all their animations.
 | |
|      * Otherwise, we resolve a Promise immediately for a composable no-op.
 | |
|      */
 | |
|     const getChildAnimations = visualElement.variantChildren && visualElement.variantChildren.size
 | |
|         ? (forwardDelay = 0) => {
 | |
|             const { delayChildren = 0, staggerChildren, staggerDirection, } = transition;
 | |
|             return animateChildren(visualElement, variant, delayChildren + forwardDelay, staggerChildren, staggerDirection, options);
 | |
|         }
 | |
|         : () => Promise.resolve();
 | |
|     /**
 | |
|      * If the transition explicitly defines a "when" option, we need to resolve either
 | |
|      * this animation or all children animations before playing the other.
 | |
|      */
 | |
|     const { when } = transition;
 | |
|     if (when) {
 | |
|         const [first, last] = when === "beforeChildren"
 | |
|             ? [getAnimation, getChildAnimations]
 | |
|             : [getChildAnimations, getAnimation];
 | |
|         return first().then(() => last());
 | |
|     }
 | |
|     else {
 | |
|         return Promise.all([getAnimation(), getChildAnimations(options.delay)]);
 | |
|     }
 | |
| }
 | |
| function animateChildren(visualElement, variant, delayChildren = 0, staggerChildren = 0, staggerDirection = 1, options) {
 | |
|     const animations = [];
 | |
|     const maxStaggerDuration = (visualElement.variantChildren.size - 1) * staggerChildren;
 | |
|     const generateStaggerDuration = staggerDirection === 1
 | |
|         ? (i = 0) => i * staggerChildren
 | |
|         : (i = 0) => maxStaggerDuration - i * staggerChildren;
 | |
|     Array.from(visualElement.variantChildren)
 | |
|         .sort(sortByTreeOrder)
 | |
|         .forEach((child, i) => {
 | |
|         child.notify("AnimationStart", variant);
 | |
|         animations.push(animateVariant(child, variant, {
 | |
|             ...options,
 | |
|             delay: delayChildren + generateStaggerDuration(i),
 | |
|         }).then(() => child.notify("AnimationComplete", variant)));
 | |
|     });
 | |
|     return Promise.all(animations);
 | |
| }
 | |
| function sortByTreeOrder(a, b) {
 | |
|     return a.sortNodePosition(b);
 | |
| }
 | |
| 
 | |
| function animateVisualElement(visualElement, definition, options = {}) {
 | |
|     visualElement.notify("AnimationStart", definition);
 | |
|     let animation;
 | |
|     if (Array.isArray(definition)) {
 | |
|         const animations = definition.map((variant) => animateVariant(visualElement, variant, options));
 | |
|         animation = Promise.all(animations);
 | |
|     }
 | |
|     else if (typeof definition === "string") {
 | |
|         animation = animateVariant(visualElement, definition, options);
 | |
|     }
 | |
|     else {
 | |
|         const resolvedDefinition = typeof definition === "function"
 | |
|             ? indexLegacy.resolveVariant(visualElement, definition, options.custom)
 | |
|             : definition;
 | |
|         animation = Promise.all(indexLegacy.animateTarget(visualElement, resolvedDefinition, options));
 | |
|     }
 | |
|     return animation.then(() => visualElement.notify("AnimationComplete", definition));
 | |
| }
 | |
| 
 | |
| const reversePriorityOrder = [...indexLegacy.variantPriorityOrder].reverse();
 | |
| const numAnimationTypes = indexLegacy.variantPriorityOrder.length;
 | |
| function animateList(visualElement) {
 | |
|     return (animations) => Promise.all(animations.map(({ animation, options }) => animateVisualElement(visualElement, animation, options)));
 | |
| }
 | |
| function createAnimationState(visualElement) {
 | |
|     let animate = animateList(visualElement);
 | |
|     const state = createState();
 | |
|     let isInitialRender = true;
 | |
|     /**
 | |
|      * This function will be used to reduce the animation definitions for
 | |
|      * each active animation type into an object of resolved values for it.
 | |
|      */
 | |
|     const buildResolvedTypeValues = (acc, definition) => {
 | |
|         const resolved = indexLegacy.resolveVariant(visualElement, definition);
 | |
|         if (resolved) {
 | |
|             const { transition, transitionEnd, ...target } = resolved;
 | |
|             acc = { ...acc, ...target, ...transitionEnd };
 | |
|         }
 | |
|         return acc;
 | |
|     };
 | |
|     /**
 | |
|      * This just allows us to inject mocked animation functions
 | |
|      * @internal
 | |
|      */
 | |
|     function setAnimateFunction(makeAnimator) {
 | |
|         animate = makeAnimator(visualElement);
 | |
|     }
 | |
|     /**
 | |
|      * When we receive new props, we need to:
 | |
|      * 1. Create a list of protected keys for each type. This is a directory of
 | |
|      *    value keys that are currently being "handled" by types of a higher priority
 | |
|      *    so that whenever an animation is played of a given type, these values are
 | |
|      *    protected from being animated.
 | |
|      * 2. Determine if an animation type needs animating.
 | |
|      * 3. Determine if any values have been removed from a type and figure out
 | |
|      *    what to animate those to.
 | |
|      */
 | |
|     function animateChanges(options, changedActiveType) {
 | |
|         const props = visualElement.getProps();
 | |
|         const context = visualElement.getVariantContext(true) || {};
 | |
|         /**
 | |
|          * A list of animations that we'll build into as we iterate through the animation
 | |
|          * types. This will get executed at the end of the function.
 | |
|          */
 | |
|         const animations = [];
 | |
|         /**
 | |
|          * Keep track of which values have been removed. Then, as we hit lower priority
 | |
|          * animation types, we can check if they contain removed values and animate to that.
 | |
|          */
 | |
|         const removedKeys = new Set();
 | |
|         /**
 | |
|          * A dictionary of all encountered keys. This is an object to let us build into and
 | |
|          * copy it without iteration. Each time we hit an animation type we set its protected
 | |
|          * keys - the keys its not allowed to animate - to the latest version of this object.
 | |
|          */
 | |
|         let encounteredKeys = {};
 | |
|         /**
 | |
|          * If a variant has been removed at a given index, and this component is controlling
 | |
|          * variant animations, we want to ensure lower-priority variants are forced to animate.
 | |
|          */
 | |
|         let removedVariantIndex = Infinity;
 | |
|         /**
 | |
|          * Iterate through all animation types in reverse priority order. For each, we want to
 | |
|          * detect which values it's handling and whether or not they've changed (and therefore
 | |
|          * need to be animated). If any values have been removed, we want to detect those in
 | |
|          * lower priority props and flag for animation.
 | |
|          */
 | |
|         for (let i = 0; i < numAnimationTypes; i++) {
 | |
|             const type = reversePriorityOrder[i];
 | |
|             const typeState = state[type];
 | |
|             const prop = props[type] !== undefined ? props[type] : context[type];
 | |
|             const propIsVariant = indexLegacy.isVariantLabel(prop);
 | |
|             /**
 | |
|              * If this type has *just* changed isActive status, set activeDelta
 | |
|              * to that status. Otherwise set to null.
 | |
|              */
 | |
|             const activeDelta = type === changedActiveType ? typeState.isActive : null;
 | |
|             if (activeDelta === false)
 | |
|                 removedVariantIndex = i;
 | |
|             /**
 | |
|              * If this prop is an inherited variant, rather than been set directly on the
 | |
|              * component itself, we want to make sure we allow the parent to trigger animations.
 | |
|              *
 | |
|              * TODO: Can probably change this to a !isControllingVariants check
 | |
|              */
 | |
|             let isInherited = prop === context[type] && prop !== props[type] && propIsVariant;
 | |
|             /**
 | |
|              *
 | |
|              */
 | |
|             if (isInherited &&
 | |
|                 isInitialRender &&
 | |
|                 visualElement.manuallyAnimateOnMount) {
 | |
|                 isInherited = false;
 | |
|             }
 | |
|             /**
 | |
|              * Set all encountered keys so far as the protected keys for this type. This will
 | |
|              * be any key that has been animated or otherwise handled by active, higher-priortiy types.
 | |
|              */
 | |
|             typeState.protectedKeys = { ...encounteredKeys };
 | |
|             // Check if we can skip analysing this prop early
 | |
|             if (
 | |
|             // If it isn't active and hasn't *just* been set as inactive
 | |
|             (!typeState.isActive && activeDelta === null) ||
 | |
|                 // If we didn't and don't have any defined prop for this animation type
 | |
|                 (!prop && !typeState.prevProp) ||
 | |
|                 // Or if the prop doesn't define an animation
 | |
|                 indexLegacy.isAnimationControls(prop) ||
 | |
|                 typeof prop === "boolean") {
 | |
|                 continue;
 | |
|             }
 | |
|             /**
 | |
|              * As we go look through the values defined on this type, if we detect
 | |
|              * a changed value or a value that was removed in a higher priority, we set
 | |
|              * this to true and add this prop to the animation list.
 | |
|              */
 | |
|             const variantDidChange = checkVariantsDidChange(typeState.prevProp, prop);
 | |
|             let shouldAnimateType = variantDidChange ||
 | |
|                 // If we're making this variant active, we want to always make it active
 | |
|                 (type === changedActiveType &&
 | |
|                     typeState.isActive &&
 | |
|                     !isInherited &&
 | |
|                     propIsVariant) ||
 | |
|                 // If we removed a higher-priority variant (i is in reverse order)
 | |
|                 (i > removedVariantIndex && propIsVariant);
 | |
|             let handledRemovedValues = false;
 | |
|             /**
 | |
|              * As animations can be set as variant lists, variants or target objects, we
 | |
|              * coerce everything to an array if it isn't one already
 | |
|              */
 | |
|             const definitionList = Array.isArray(prop) ? prop : [prop];
 | |
|             /**
 | |
|              * Build an object of all the resolved values. We'll use this in the subsequent
 | |
|              * animateChanges calls to determine whether a value has changed.
 | |
|              */
 | |
|             let resolvedValues = definitionList.reduce(buildResolvedTypeValues, {});
 | |
|             if (activeDelta === false)
 | |
|                 resolvedValues = {};
 | |
|             /**
 | |
|              * Now we need to loop through all the keys in the prev prop and this prop,
 | |
|              * and decide:
 | |
|              * 1. If the value has changed, and needs animating
 | |
|              * 2. If it has been removed, and needs adding to the removedKeys set
 | |
|              * 3. If it has been removed in a higher priority type and needs animating
 | |
|              * 4. If it hasn't been removed in a higher priority but hasn't changed, and
 | |
|              *    needs adding to the type's protectedKeys list.
 | |
|              */
 | |
|             const { prevResolvedValues = {} } = typeState;
 | |
|             const allKeys = {
 | |
|                 ...prevResolvedValues,
 | |
|                 ...resolvedValues,
 | |
|             };
 | |
|             const markToAnimate = (key) => {
 | |
|                 shouldAnimateType = true;
 | |
|                 if (removedKeys.has(key)) {
 | |
|                     handledRemovedValues = true;
 | |
|                     removedKeys.delete(key);
 | |
|                 }
 | |
|                 typeState.needsAnimating[key] = true;
 | |
|             };
 | |
|             for (const key in allKeys) {
 | |
|                 const next = resolvedValues[key];
 | |
|                 const prev = prevResolvedValues[key];
 | |
|                 // If we've already handled this we can just skip ahead
 | |
|                 if (encounteredKeys.hasOwnProperty(key))
 | |
|                     continue;
 | |
|                 /**
 | |
|                  * If the value has changed, we probably want to animate it.
 | |
|                  */
 | |
|                 let valueHasChanged = false;
 | |
|                 if (indexLegacy.isKeyframesTarget(next) && indexLegacy.isKeyframesTarget(prev)) {
 | |
|                     valueHasChanged = !shallowCompare(next, prev);
 | |
|                 }
 | |
|                 else {
 | |
|                     valueHasChanged = next !== prev;
 | |
|                 }
 | |
|                 if (valueHasChanged) {
 | |
|                     if (next !== undefined) {
 | |
|                         // If next is defined and doesn't equal prev, it needs animating
 | |
|                         markToAnimate(key);
 | |
|                     }
 | |
|                     else {
 | |
|                         // If it's undefined, it's been removed.
 | |
|                         removedKeys.add(key);
 | |
|                     }
 | |
|                 }
 | |
|                 else if (next !== undefined && removedKeys.has(key)) {
 | |
|                     /**
 | |
|                      * If next hasn't changed and it isn't undefined, we want to check if it's
 | |
|                      * been removed by a higher priority
 | |
|                      */
 | |
|                     markToAnimate(key);
 | |
|                 }
 | |
|                 else {
 | |
|                     /**
 | |
|                      * If it hasn't changed, we add it to the list of protected values
 | |
|                      * to ensure it doesn't get animated.
 | |
|                      */
 | |
|                     typeState.protectedKeys[key] = true;
 | |
|                 }
 | |
|             }
 | |
|             /**
 | |
|              * Update the typeState so next time animateChanges is called we can compare the
 | |
|              * latest prop and resolvedValues to these.
 | |
|              */
 | |
|             typeState.prevProp = prop;
 | |
|             typeState.prevResolvedValues = resolvedValues;
 | |
|             /**
 | |
|              *
 | |
|              */
 | |
|             if (typeState.isActive) {
 | |
|                 encounteredKeys = { ...encounteredKeys, ...resolvedValues };
 | |
|             }
 | |
|             if (isInitialRender && visualElement.blockInitialAnimation) {
 | |
|                 shouldAnimateType = false;
 | |
|             }
 | |
|             /**
 | |
|              * If this is an inherited prop we want to hard-block animations
 | |
|              */
 | |
|             if (shouldAnimateType && (!isInherited || handledRemovedValues)) {
 | |
|                 animations.push(...definitionList.map((animation) => ({
 | |
|                     animation: animation,
 | |
|                     options: { type, ...options },
 | |
|                 })));
 | |
|             }
 | |
|         }
 | |
|         /**
 | |
|          * If there are some removed value that haven't been dealt with,
 | |
|          * we need to create a new animation that falls back either to the value
 | |
|          * defined in the style prop, or the last read value.
 | |
|          */
 | |
|         if (removedKeys.size) {
 | |
|             const fallbackAnimation = {};
 | |
|             removedKeys.forEach((key) => {
 | |
|                 const fallbackTarget = visualElement.getBaseTarget(key);
 | |
|                 if (fallbackTarget !== undefined) {
 | |
|                     fallbackAnimation[key] = fallbackTarget;
 | |
|                 }
 | |
|             });
 | |
|             animations.push({ animation: fallbackAnimation });
 | |
|         }
 | |
|         let shouldAnimate = Boolean(animations.length);
 | |
|         if (isInitialRender &&
 | |
|             (props.initial === false || props.initial === props.animate) &&
 | |
|             !visualElement.manuallyAnimateOnMount) {
 | |
|             shouldAnimate = false;
 | |
|         }
 | |
|         isInitialRender = false;
 | |
|         return shouldAnimate ? animate(animations) : Promise.resolve();
 | |
|     }
 | |
|     /**
 | |
|      * Change whether a certain animation type is active.
 | |
|      */
 | |
|     function setActive(type, isActive, options) {
 | |
|         var _a;
 | |
|         // If the active state hasn't changed, we can safely do nothing here
 | |
|         if (state[type].isActive === isActive)
 | |
|             return Promise.resolve();
 | |
|         // Propagate active change to children
 | |
|         (_a = visualElement.variantChildren) === null || _a === void 0 ? void 0 : _a.forEach((child) => { var _a; return (_a = child.animationState) === null || _a === void 0 ? void 0 : _a.setActive(type, isActive); });
 | |
|         state[type].isActive = isActive;
 | |
|         const animations = animateChanges(options, type);
 | |
|         for (const key in state) {
 | |
|             state[key].protectedKeys = {};
 | |
|         }
 | |
|         return animations;
 | |
|     }
 | |
|     return {
 | |
|         animateChanges,
 | |
|         setActive,
 | |
|         setAnimateFunction,
 | |
|         getState: () => state,
 | |
|     };
 | |
| }
 | |
| function checkVariantsDidChange(prev, next) {
 | |
|     if (typeof next === "string") {
 | |
|         return next !== prev;
 | |
|     }
 | |
|     else if (Array.isArray(next)) {
 | |
|         return !shallowCompare(next, prev);
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| function createTypeState(isActive = false) {
 | |
|     return {
 | |
|         isActive,
 | |
|         protectedKeys: {},
 | |
|         needsAnimating: {},
 | |
|         prevResolvedValues: {},
 | |
|     };
 | |
| }
 | |
| function createState() {
 | |
|     return {
 | |
|         animate: createTypeState(true),
 | |
|         whileInView: createTypeState(),
 | |
|         whileHover: createTypeState(),
 | |
|         whileTap: createTypeState(),
 | |
|         whileDrag: createTypeState(),
 | |
|         whileFocus: createTypeState(),
 | |
|         exit: createTypeState(),
 | |
|     };
 | |
| }
 | |
| 
 | |
| class AnimationFeature extends Feature {
 | |
|     /**
 | |
|      * We dynamically generate the AnimationState manager as it contains a reference
 | |
|      * to the underlying animation library. We only want to load that if we load this,
 | |
|      * so people can optionally code split it out using the `m` component.
 | |
|      */
 | |
|     constructor(node) {
 | |
|         super(node);
 | |
|         node.animationState || (node.animationState = createAnimationState(node));
 | |
|     }
 | |
|     updateAnimationControlsSubscription() {
 | |
|         const { animate } = this.node.getProps();
 | |
|         this.unmount();
 | |
|         if (indexLegacy.isAnimationControls(animate)) {
 | |
|             this.unmount = animate.subscribe(this.node);
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Subscribe any provided AnimationControls to the component's VisualElement
 | |
|      */
 | |
|     mount() {
 | |
|         this.updateAnimationControlsSubscription();
 | |
|     }
 | |
|     update() {
 | |
|         const { animate } = this.node.getProps();
 | |
|         const { animate: prevAnimate } = this.node.prevProps || {};
 | |
|         if (animate !== prevAnimate) {
 | |
|             this.updateAnimationControlsSubscription();
 | |
|         }
 | |
|     }
 | |
|     unmount() { }
 | |
| }
 | |
| 
 | |
| let id$2 = 0;
 | |
| class ExitAnimationFeature extends Feature {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.id = id$2++;
 | |
|     }
 | |
|     update() {
 | |
|         if (!this.node.presenceContext)
 | |
|             return;
 | |
|         const { isPresent, onExitComplete, custom } = this.node.presenceContext;
 | |
|         const { isPresent: prevIsPresent } = this.node.prevPresenceContext || {};
 | |
|         if (!this.node.animationState || isPresent === prevIsPresent) {
 | |
|             return;
 | |
|         }
 | |
|         const exitAnimation = this.node.animationState.setActive("exit", !isPresent, { custom: custom !== null && custom !== void 0 ? custom : this.node.getProps().custom });
 | |
|         if (onExitComplete && !isPresent) {
 | |
|             exitAnimation.then(() => onExitComplete(this.id));
 | |
|         }
 | |
|     }
 | |
|     mount() {
 | |
|         const { register } = this.node.presenceContext || {};
 | |
|         if (register) {
 | |
|             this.unmount = register(this.id);
 | |
|         }
 | |
|     }
 | |
|     unmount() { }
 | |
| }
 | |
| 
 | |
| const animations = {
 | |
|     animation: {
 | |
|         Feature: AnimationFeature,
 | |
|     },
 | |
|     exit: {
 | |
|         Feature: ExitAnimationFeature,
 | |
|     },
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @internal
 | |
|  */
 | |
| class PanSession {
 | |
|     constructor(event, handlers, { transformPagePoint, contextWindow, dragSnapToOrigin = false } = {}) {
 | |
|         /**
 | |
|          * @internal
 | |
|          */
 | |
|         this.startEvent = null;
 | |
|         /**
 | |
|          * @internal
 | |
|          */
 | |
|         this.lastMoveEvent = null;
 | |
|         /**
 | |
|          * @internal
 | |
|          */
 | |
|         this.lastMoveEventInfo = null;
 | |
|         /**
 | |
|          * @internal
 | |
|          */
 | |
|         this.handlers = {};
 | |
|         /**
 | |
|          * @internal
 | |
|          */
 | |
|         this.contextWindow = window;
 | |
|         this.updatePoint = () => {
 | |
|             if (!(this.lastMoveEvent && this.lastMoveEventInfo))
 | |
|                 return;
 | |
|             const info = getPanInfo(this.lastMoveEventInfo, this.history);
 | |
|             const isPanStarted = this.startEvent !== null;
 | |
|             // Only start panning if the offset is larger than 3 pixels. If we make it
 | |
|             // any larger than this we'll want to reset the pointer history
 | |
|             // on the first update to avoid visual snapping to the cursoe.
 | |
|             const isDistancePastThreshold = indexLegacy.distance2D(info.offset, { x: 0, y: 0 }) >= 3;
 | |
|             if (!isPanStarted && !isDistancePastThreshold)
 | |
|                 return;
 | |
|             const { point } = info;
 | |
|             const { timestamp } = indexLegacy.frameData;
 | |
|             this.history.push({ ...point, timestamp });
 | |
|             const { onStart, onMove } = this.handlers;
 | |
|             if (!isPanStarted) {
 | |
|                 onStart && onStart(this.lastMoveEvent, info);
 | |
|                 this.startEvent = this.lastMoveEvent;
 | |
|             }
 | |
|             onMove && onMove(this.lastMoveEvent, info);
 | |
|         };
 | |
|         this.handlePointerMove = (event, info) => {
 | |
|             this.lastMoveEvent = event;
 | |
|             this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
 | |
|             // Throttle mouse move event to once per frame
 | |
|             indexLegacy.frame.update(this.updatePoint, true);
 | |
|         };
 | |
|         this.handlePointerUp = (event, info) => {
 | |
|             this.end();
 | |
|             const { onEnd, onSessionEnd, resumeAnimation } = this.handlers;
 | |
|             if (this.dragSnapToOrigin)
 | |
|                 resumeAnimation && resumeAnimation();
 | |
|             if (!(this.lastMoveEvent && this.lastMoveEventInfo))
 | |
|                 return;
 | |
|             const panInfo = getPanInfo(event.type === "pointercancel"
 | |
|                 ? this.lastMoveEventInfo
 | |
|                 : transformPoint(info, this.transformPagePoint), this.history);
 | |
|             if (this.startEvent && onEnd) {
 | |
|                 onEnd(event, panInfo);
 | |
|             }
 | |
|             onSessionEnd && onSessionEnd(event, panInfo);
 | |
|         };
 | |
|         // If we have more than one touch, don't start detecting this gesture
 | |
|         if (!isPrimaryPointer(event))
 | |
|             return;
 | |
|         this.dragSnapToOrigin = dragSnapToOrigin;
 | |
|         this.handlers = handlers;
 | |
|         this.transformPagePoint = transformPagePoint;
 | |
|         this.contextWindow = contextWindow || window;
 | |
|         const info = extractEventInfo(event);
 | |
|         const initialInfo = transformPoint(info, this.transformPagePoint);
 | |
|         const { point } = initialInfo;
 | |
|         const { timestamp } = indexLegacy.frameData;
 | |
|         this.history = [{ ...point, timestamp }];
 | |
|         const { onSessionStart } = handlers;
 | |
|         onSessionStart &&
 | |
|             onSessionStart(event, getPanInfo(initialInfo, this.history));
 | |
|         this.removeListeners = indexLegacy.pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp));
 | |
|     }
 | |
|     updateHandlers(handlers) {
 | |
|         this.handlers = handlers;
 | |
|     }
 | |
|     end() {
 | |
|         this.removeListeners && this.removeListeners();
 | |
|         indexLegacy.cancelFrame(this.updatePoint);
 | |
|     }
 | |
| }
 | |
| function transformPoint(info, transformPagePoint) {
 | |
|     return transformPagePoint ? { point: transformPagePoint(info.point) } : info;
 | |
| }
 | |
| function subtractPoint(a, b) {
 | |
|     return { x: a.x - b.x, y: a.y - b.y };
 | |
| }
 | |
| function getPanInfo({ point }, history) {
 | |
|     return {
 | |
|         point,
 | |
|         delta: subtractPoint(point, lastDevicePoint(history)),
 | |
|         offset: subtractPoint(point, startDevicePoint(history)),
 | |
|         velocity: getVelocity(history, 0.1),
 | |
|     };
 | |
| }
 | |
| function startDevicePoint(history) {
 | |
|     return history[0];
 | |
| }
 | |
| function lastDevicePoint(history) {
 | |
|     return history[history.length - 1];
 | |
| }
 | |
| function getVelocity(history, timeDelta) {
 | |
|     if (history.length < 2) {
 | |
|         return { x: 0, y: 0 };
 | |
|     }
 | |
|     let i = history.length - 1;
 | |
|     let timestampedPoint = null;
 | |
|     const lastPoint = lastDevicePoint(history);
 | |
|     while (i >= 0) {
 | |
|         timestampedPoint = history[i];
 | |
|         if (lastPoint.timestamp - timestampedPoint.timestamp >
 | |
|             indexLegacy.secondsToMilliseconds(timeDelta)) {
 | |
|             break;
 | |
|         }
 | |
|         i--;
 | |
|     }
 | |
|     if (!timestampedPoint) {
 | |
|         return { x: 0, y: 0 };
 | |
|     }
 | |
|     const time = indexLegacy.millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp);
 | |
|     if (time === 0) {
 | |
|         return { x: 0, y: 0 };
 | |
|     }
 | |
|     const currentVelocity = {
 | |
|         x: (lastPoint.x - timestampedPoint.x) / time,
 | |
|         y: (lastPoint.y - timestampedPoint.y) / time,
 | |
|     };
 | |
|     if (currentVelocity.x === Infinity) {
 | |
|         currentVelocity.x = 0;
 | |
|     }
 | |
|     if (currentVelocity.y === Infinity) {
 | |
|         currentVelocity.y = 0;
 | |
|     }
 | |
|     return currentVelocity;
 | |
| }
 | |
| 
 | |
| function calcLength(axis) {
 | |
|     return axis.max - axis.min;
 | |
| }
 | |
| function isNear(value, target = 0, maxDistance = 0.01) {
 | |
|     return Math.abs(value - target) <= maxDistance;
 | |
| }
 | |
| function calcAxisDelta(delta, source, target, origin = 0.5) {
 | |
|     delta.origin = origin;
 | |
|     delta.originPoint = indexLegacy.mix(source.min, source.max, delta.origin);
 | |
|     delta.scale = calcLength(target) / calcLength(source);
 | |
|     if (isNear(delta.scale, 1, 0.0001) || isNaN(delta.scale))
 | |
|         delta.scale = 1;
 | |
|     delta.translate =
 | |
|         indexLegacy.mix(target.min, target.max, delta.origin) - delta.originPoint;
 | |
|     if (isNear(delta.translate) || isNaN(delta.translate))
 | |
|         delta.translate = 0;
 | |
| }
 | |
| function calcBoxDelta(delta, source, target, origin) {
 | |
|     calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
 | |
|     calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
 | |
| }
 | |
| function calcRelativeAxis(target, relative, parent) {
 | |
|     target.min = parent.min + relative.min;
 | |
|     target.max = target.min + calcLength(relative);
 | |
| }
 | |
| function calcRelativeBox(target, relative, parent) {
 | |
|     calcRelativeAxis(target.x, relative.x, parent.x);
 | |
|     calcRelativeAxis(target.y, relative.y, parent.y);
 | |
| }
 | |
| function calcRelativeAxisPosition(target, layout, parent) {
 | |
|     target.min = layout.min - parent.min;
 | |
|     target.max = target.min + calcLength(layout);
 | |
| }
 | |
| function calcRelativePosition(target, layout, parent) {
 | |
|     calcRelativeAxisPosition(target.x, layout.x, parent.x);
 | |
|     calcRelativeAxisPosition(target.y, layout.y, parent.y);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Apply constraints to a point. These constraints are both physical along an
 | |
|  * axis, and an elastic factor that determines how much to constrain the point
 | |
|  * by if it does lie outside the defined parameters.
 | |
|  */
 | |
| function applyConstraints(point, { min, max }, elastic) {
 | |
|     if (min !== undefined && point < min) {
 | |
|         // If we have a min point defined, and this is outside of that, constrain
 | |
|         point = elastic ? indexLegacy.mix(min, point, elastic.min) : Math.max(point, min);
 | |
|     }
 | |
|     else if (max !== undefined && point > max) {
 | |
|         // If we have a max point defined, and this is outside of that, constrain
 | |
|         point = elastic ? indexLegacy.mix(max, point, elastic.max) : Math.min(point, max);
 | |
|     }
 | |
|     return point;
 | |
| }
 | |
| /**
 | |
|  * Calculate constraints in terms of the viewport when defined relatively to the
 | |
|  * measured axis. This is measured from the nearest edge, so a max constraint of 200
 | |
|  * on an axis with a max value of 300 would return a constraint of 500 - axis length
 | |
|  */
 | |
| function calcRelativeAxisConstraints(axis, min, max) {
 | |
|     return {
 | |
|         min: min !== undefined ? axis.min + min : undefined,
 | |
|         max: max !== undefined
 | |
|             ? axis.max + max - (axis.max - axis.min)
 | |
|             : undefined,
 | |
|     };
 | |
| }
 | |
| /**
 | |
|  * Calculate constraints in terms of the viewport when
 | |
|  * defined relatively to the measured bounding box.
 | |
|  */
 | |
| function calcRelativeConstraints(layoutBox, { top, left, bottom, right }) {
 | |
|     return {
 | |
|         x: calcRelativeAxisConstraints(layoutBox.x, left, right),
 | |
|         y: calcRelativeAxisConstraints(layoutBox.y, top, bottom),
 | |
|     };
 | |
| }
 | |
| /**
 | |
|  * Calculate viewport constraints when defined as another viewport-relative axis
 | |
|  */
 | |
| function calcViewportAxisConstraints(layoutAxis, constraintsAxis) {
 | |
|     let min = constraintsAxis.min - layoutAxis.min;
 | |
|     let max = constraintsAxis.max - layoutAxis.max;
 | |
|     // If the constraints axis is actually smaller than the layout axis then we can
 | |
|     // flip the constraints
 | |
|     if (constraintsAxis.max - constraintsAxis.min <
 | |
|         layoutAxis.max - layoutAxis.min) {
 | |
|         [min, max] = [max, min];
 | |
|     }
 | |
|     return { min, max };
 | |
| }
 | |
| /**
 | |
|  * Calculate viewport constraints when defined as another viewport-relative box
 | |
|  */
 | |
| function calcViewportConstraints(layoutBox, constraintsBox) {
 | |
|     return {
 | |
|         x: calcViewportAxisConstraints(layoutBox.x, constraintsBox.x),
 | |
|         y: calcViewportAxisConstraints(layoutBox.y, constraintsBox.y),
 | |
|     };
 | |
| }
 | |
| /**
 | |
|  * Calculate a transform origin relative to the source axis, between 0-1, that results
 | |
|  * in an asthetically pleasing scale/transform needed to project from source to target.
 | |
|  */
 | |
| function calcOrigin(source, target) {
 | |
|     let origin = 0.5;
 | |
|     const sourceLength = calcLength(source);
 | |
|     const targetLength = calcLength(target);
 | |
|     if (targetLength > sourceLength) {
 | |
|         origin = indexLegacy.progress(target.min, target.max - sourceLength, source.min);
 | |
|     }
 | |
|     else if (sourceLength > targetLength) {
 | |
|         origin = indexLegacy.progress(source.min, source.max - targetLength, target.min);
 | |
|     }
 | |
|     return indexLegacy.clamp(0, 1, origin);
 | |
| }
 | |
| /**
 | |
|  * Rebase the calculated viewport constraints relative to the layout.min point.
 | |
|  */
 | |
| function rebaseAxisConstraints(layout, constraints) {
 | |
|     const relativeConstraints = {};
 | |
|     if (constraints.min !== undefined) {
 | |
|         relativeConstraints.min = constraints.min - layout.min;
 | |
|     }
 | |
|     if (constraints.max !== undefined) {
 | |
|         relativeConstraints.max = constraints.max - layout.min;
 | |
|     }
 | |
|     return relativeConstraints;
 | |
| }
 | |
| const defaultElastic = 0.35;
 | |
| /**
 | |
|  * Accepts a dragElastic prop and returns resolved elastic values for each axis.
 | |
|  */
 | |
| function resolveDragElastic(dragElastic = defaultElastic) {
 | |
|     if (dragElastic === false) {
 | |
|         dragElastic = 0;
 | |
|     }
 | |
|     else if (dragElastic === true) {
 | |
|         dragElastic = defaultElastic;
 | |
|     }
 | |
|     return {
 | |
|         x: resolveAxisElastic(dragElastic, "left", "right"),
 | |
|         y: resolveAxisElastic(dragElastic, "top", "bottom"),
 | |
|     };
 | |
| }
 | |
| function resolveAxisElastic(dragElastic, minLabel, maxLabel) {
 | |
|     return {
 | |
|         min: resolvePointElastic(dragElastic, minLabel),
 | |
|         max: resolvePointElastic(dragElastic, maxLabel),
 | |
|     };
 | |
| }
 | |
| function resolvePointElastic(dragElastic, label) {
 | |
|     return typeof dragElastic === "number"
 | |
|         ? dragElastic
 | |
|         : dragElastic[label] || 0;
 | |
| }
 | |
| 
 | |
| function eachAxis(callback) {
 | |
|     return [callback("x"), callback("y")];
 | |
| }
 | |
| 
 | |
| // Fixes https://github.com/framer/motion/issues/2270
 | |
| const getContextWindow = ({ current }) => {
 | |
|     return current ? current.ownerDocument.defaultView : null;
 | |
| };
 | |
| 
 | |
| const elementDragControls = new WeakMap();
 | |
| /**
 | |
|  *
 | |
|  */
 | |
| // let latestPointerEvent: PointerEvent
 | |
| class VisualElementDragControls {
 | |
|     constructor(visualElement) {
 | |
|         // This is a reference to the global drag gesture lock, ensuring only one component
 | |
|         // can "capture" the drag of one or both axes.
 | |
|         // TODO: Look into moving this into pansession?
 | |
|         this.openGlobalLock = null;
 | |
|         this.isDragging = false;
 | |
|         this.currentDirection = null;
 | |
|         this.originPoint = { x: 0, y: 0 };
 | |
|         /**
 | |
|          * The permitted boundaries of travel, in pixels.
 | |
|          */
 | |
|         this.constraints = false;
 | |
|         this.hasMutatedConstraints = false;
 | |
|         /**
 | |
|          * The per-axis resolved elastic values.
 | |
|          */
 | |
|         this.elastic = indexLegacy.createBox();
 | |
|         this.visualElement = visualElement;
 | |
|     }
 | |
|     start(originEvent, { snapToCursor = false } = {}) {
 | |
|         /**
 | |
|          * Don't start dragging if this component is exiting
 | |
|          */
 | |
|         const { presenceContext } = this.visualElement;
 | |
|         if (presenceContext && presenceContext.isPresent === false)
 | |
|             return;
 | |
|         const onSessionStart = (event) => {
 | |
|             const { dragSnapToOrigin } = this.getProps();
 | |
|             // Stop or pause any animations on both axis values immediately. This allows the user to throw and catch
 | |
|             // the component.
 | |
|             dragSnapToOrigin ? this.pauseAnimation() : this.stopAnimation();
 | |
|             if (snapToCursor) {
 | |
|                 this.snapToCursor(extractEventInfo(event, "page").point);
 | |
|             }
 | |
|         };
 | |
|         const onStart = (event, info) => {
 | |
|             // Attempt to grab the global drag gesture lock - maybe make this part of PanSession
 | |
|             const { drag, dragPropagation, onDragStart } = this.getProps();
 | |
|             if (drag && !dragPropagation) {
 | |
|                 if (this.openGlobalLock)
 | |
|                     this.openGlobalLock();
 | |
|                 this.openGlobalLock = getGlobalLock(drag);
 | |
|                 // If we don 't have the lock, don't start dragging
 | |
|                 if (!this.openGlobalLock)
 | |
|                     return;
 | |
|             }
 | |
|             this.isDragging = true;
 | |
|             this.currentDirection = null;
 | |
|             this.resolveConstraints();
 | |
|             if (this.visualElement.projection) {
 | |
|                 this.visualElement.projection.isAnimationBlocked = true;
 | |
|                 this.visualElement.projection.target = undefined;
 | |
|             }
 | |
|             /**
 | |
|              * Record gesture origin
 | |
|              */
 | |
|             eachAxis((axis) => {
 | |
|                 let current = this.getAxisMotionValue(axis).get() || 0;
 | |
|                 /**
 | |
|                  * If the MotionValue is a percentage value convert to px
 | |
|                  */
 | |
|                 if (indexLegacy.percent.test(current)) {
 | |
|                     const { projection } = this.visualElement;
 | |
|                     if (projection && projection.layout) {
 | |
|                         const measuredAxis = projection.layout.layoutBox[axis];
 | |
|                         if (measuredAxis) {
 | |
|                             const length = calcLength(measuredAxis);
 | |
|                             current = length * (parseFloat(current) / 100);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 this.originPoint[axis] = current;
 | |
|             });
 | |
|             // Fire onDragStart event
 | |
|             if (onDragStart) {
 | |
|                 indexLegacy.frame.update(() => onDragStart(event, info), false, true);
 | |
|             }
 | |
|             const { animationState } = this.visualElement;
 | |
|             animationState && animationState.setActive("whileDrag", true);
 | |
|         };
 | |
|         const onMove = (event, info) => {
 | |
|             // latestPointerEvent = event
 | |
|             const { dragPropagation, dragDirectionLock, onDirectionLock, onDrag, } = this.getProps();
 | |
|             // If we didn't successfully receive the gesture lock, early return.
 | |
|             if (!dragPropagation && !this.openGlobalLock)
 | |
|                 return;
 | |
|             const { offset } = info;
 | |
|             // Attempt to detect drag direction if directionLock is true
 | |
|             if (dragDirectionLock && this.currentDirection === null) {
 | |
|                 this.currentDirection = getCurrentDirection(offset);
 | |
|                 // If we've successfully set a direction, notify listener
 | |
|                 if (this.currentDirection !== null) {
 | |
|                     onDirectionLock && onDirectionLock(this.currentDirection);
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             // Update each point with the latest position
 | |
|             this.updateAxis("x", info.point, offset);
 | |
|             this.updateAxis("y", info.point, offset);
 | |
|             /**
 | |
|              * Ideally we would leave the renderer to fire naturally at the end of
 | |
|              * this frame but if the element is about to change layout as the result
 | |
|              * of a re-render we want to ensure the browser can read the latest
 | |
|              * bounding box to ensure the pointer and element don't fall out of sync.
 | |
|              */
 | |
|             this.visualElement.render();
 | |
|             /**
 | |
|              * This must fire after the render call as it might trigger a state
 | |
|              * change which itself might trigger a layout update.
 | |
|              */
 | |
|             onDrag && onDrag(event, info);
 | |
|         };
 | |
|         const onSessionEnd = (event, info) => this.stop(event, info);
 | |
|         const resumeAnimation = () => eachAxis((axis) => {
 | |
|             var _a;
 | |
|             return this.getAnimationState(axis) === "paused" &&
 | |
|                 ((_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.play());
 | |
|         });
 | |
|         const { dragSnapToOrigin } = this.getProps();
 | |
|         this.panSession = new PanSession(originEvent, {
 | |
|             onSessionStart,
 | |
|             onStart,
 | |
|             onMove,
 | |
|             onSessionEnd,
 | |
|             resumeAnimation,
 | |
|         }, {
 | |
|             transformPagePoint: this.visualElement.getTransformPagePoint(),
 | |
|             dragSnapToOrigin,
 | |
|             contextWindow: getContextWindow(this.visualElement),
 | |
|         });
 | |
|     }
 | |
|     stop(event, info) {
 | |
|         const isDragging = this.isDragging;
 | |
|         this.cancel();
 | |
|         if (!isDragging)
 | |
|             return;
 | |
|         const { velocity } = info;
 | |
|         this.startAnimation(velocity);
 | |
|         const { onDragEnd } = this.getProps();
 | |
|         if (onDragEnd) {
 | |
|             indexLegacy.frame.update(() => onDragEnd(event, info));
 | |
|         }
 | |
|     }
 | |
|     cancel() {
 | |
|         this.isDragging = false;
 | |
|         const { projection, animationState } = this.visualElement;
 | |
|         if (projection) {
 | |
|             projection.isAnimationBlocked = false;
 | |
|         }
 | |
|         this.panSession && this.panSession.end();
 | |
|         this.panSession = undefined;
 | |
|         const { dragPropagation } = this.getProps();
 | |
|         if (!dragPropagation && this.openGlobalLock) {
 | |
|             this.openGlobalLock();
 | |
|             this.openGlobalLock = null;
 | |
|         }
 | |
|         animationState && animationState.setActive("whileDrag", false);
 | |
|     }
 | |
|     updateAxis(axis, _point, offset) {
 | |
|         const { drag } = this.getProps();
 | |
|         // If we're not dragging this axis, do an early return.
 | |
|         if (!offset || !shouldDrag(axis, drag, this.currentDirection))
 | |
|             return;
 | |
|         const axisValue = this.getAxisMotionValue(axis);
 | |
|         let next = this.originPoint[axis] + offset[axis];
 | |
|         // Apply constraints
 | |
|         if (this.constraints && this.constraints[axis]) {
 | |
|             next = applyConstraints(next, this.constraints[axis], this.elastic[axis]);
 | |
|         }
 | |
|         axisValue.set(next);
 | |
|     }
 | |
|     resolveConstraints() {
 | |
|         var _a;
 | |
|         const { dragConstraints, dragElastic } = this.getProps();
 | |
|         const layout = this.visualElement.projection &&
 | |
|             !this.visualElement.projection.layout
 | |
|             ? this.visualElement.projection.measure(false)
 | |
|             : (_a = this.visualElement.projection) === null || _a === void 0 ? void 0 : _a.layout;
 | |
|         const prevConstraints = this.constraints;
 | |
|         if (dragConstraints && indexLegacy.isRefObject(dragConstraints)) {
 | |
|             if (!this.constraints) {
 | |
|                 this.constraints = this.resolveRefConstraints();
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             if (dragConstraints && layout) {
 | |
|                 this.constraints = calcRelativeConstraints(layout.layoutBox, dragConstraints);
 | |
|             }
 | |
|             else {
 | |
|                 this.constraints = false;
 | |
|             }
 | |
|         }
 | |
|         this.elastic = resolveDragElastic(dragElastic);
 | |
|         /**
 | |
|          * If we're outputting to external MotionValues, we want to rebase the measured constraints
 | |
|          * from viewport-relative to component-relative.
 | |
|          */
 | |
|         if (prevConstraints !== this.constraints &&
 | |
|             layout &&
 | |
|             this.constraints &&
 | |
|             !this.hasMutatedConstraints) {
 | |
|             eachAxis((axis) => {
 | |
|                 if (this.getAxisMotionValue(axis)) {
 | |
|                     this.constraints[axis] = rebaseAxisConstraints(layout.layoutBox[axis], this.constraints[axis]);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|     }
 | |
|     resolveRefConstraints() {
 | |
|         const { dragConstraints: constraints, onMeasureDragConstraints } = this.getProps();
 | |
|         if (!constraints || !indexLegacy.isRefObject(constraints))
 | |
|             return false;
 | |
|         const constraintsElement = constraints.current;
 | |
|         indexLegacy.invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop.");
 | |
|         const { projection } = this.visualElement;
 | |
|         // TODO
 | |
|         if (!projection || !projection.layout)
 | |
|             return false;
 | |
|         const constraintsBox = indexLegacy.measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint());
 | |
|         let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox);
 | |
|         /**
 | |
|          * If there's an onMeasureDragConstraints listener we call it and
 | |
|          * if different constraints are returned, set constraints to that
 | |
|          */
 | |
|         if (onMeasureDragConstraints) {
 | |
|             const userConstraints = onMeasureDragConstraints(indexLegacy.convertBoxToBoundingBox(measuredConstraints));
 | |
|             this.hasMutatedConstraints = !!userConstraints;
 | |
|             if (userConstraints) {
 | |
|                 measuredConstraints = indexLegacy.convertBoundingBoxToBox(userConstraints);
 | |
|             }
 | |
|         }
 | |
|         return measuredConstraints;
 | |
|     }
 | |
|     startAnimation(velocity) {
 | |
|         const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps();
 | |
|         const constraints = this.constraints || {};
 | |
|         const momentumAnimations = eachAxis((axis) => {
 | |
|             if (!shouldDrag(axis, drag, this.currentDirection)) {
 | |
|                 return;
 | |
|             }
 | |
|             let transition = (constraints && constraints[axis]) || {};
 | |
|             if (dragSnapToOrigin)
 | |
|                 transition = { min: 0, max: 0 };
 | |
|             /**
 | |
|              * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame
 | |
|              * of spring animations so we should look into adding a disable spring option to `inertia`.
 | |
|              * We could do something here where we affect the `bounceStiffness` and `bounceDamping`
 | |
|              * using the value of `dragElastic`.
 | |
|              */
 | |
|             const bounceStiffness = dragElastic ? 200 : 1000000;
 | |
|             const bounceDamping = dragElastic ? 40 : 10000000;
 | |
|             const inertia = {
 | |
|                 type: "inertia",
 | |
|                 velocity: dragMomentum ? velocity[axis] : 0,
 | |
|                 bounceStiffness,
 | |
|                 bounceDamping,
 | |
|                 timeConstant: 750,
 | |
|                 restDelta: 1,
 | |
|                 restSpeed: 10,
 | |
|                 ...dragTransition,
 | |
|                 ...transition,
 | |
|             };
 | |
|             // If we're not animating on an externally-provided `MotionValue` we can use the
 | |
|             // component's animation controls which will handle interactions with whileHover (etc),
 | |
|             // otherwise we just have to animate the `MotionValue` itself.
 | |
|             return this.startAxisValueAnimation(axis, inertia);
 | |
|         });
 | |
|         // Run all animations and then resolve the new drag constraints.
 | |
|         return Promise.all(momentumAnimations).then(onDragTransitionEnd);
 | |
|     }
 | |
|     startAxisValueAnimation(axis, transition) {
 | |
|         const axisValue = this.getAxisMotionValue(axis);
 | |
|         return axisValue.start(indexLegacy.animateMotionValue(axis, axisValue, 0, transition));
 | |
|     }
 | |
|     stopAnimation() {
 | |
|         eachAxis((axis) => this.getAxisMotionValue(axis).stop());
 | |
|     }
 | |
|     pauseAnimation() {
 | |
|         eachAxis((axis) => { var _a; return (_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.pause(); });
 | |
|     }
 | |
|     getAnimationState(axis) {
 | |
|         var _a;
 | |
|         return (_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.state;
 | |
|     }
 | |
|     /**
 | |
|      * Drag works differently depending on which props are provided.
 | |
|      *
 | |
|      * - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values.
 | |
|      * - Otherwise, we apply the delta to the x/y motion values.
 | |
|      */
 | |
|     getAxisMotionValue(axis) {
 | |
|         const dragKey = "_drag" + axis.toUpperCase();
 | |
|         const props = this.visualElement.getProps();
 | |
|         const externalMotionValue = props[dragKey];
 | |
|         return externalMotionValue
 | |
|             ? externalMotionValue
 | |
|             : this.visualElement.getValue(axis, (props.initial ? props.initial[axis] : undefined) || 0);
 | |
|     }
 | |
|     snapToCursor(point) {
 | |
|         eachAxis((axis) => {
 | |
|             const { drag } = this.getProps();
 | |
|             // If we're not dragging this axis, do an early return.
 | |
|             if (!shouldDrag(axis, drag, this.currentDirection))
 | |
|                 return;
 | |
|             const { projection } = this.visualElement;
 | |
|             const axisValue = this.getAxisMotionValue(axis);
 | |
|             if (projection && projection.layout) {
 | |
|                 const { min, max } = projection.layout.layoutBox[axis];
 | |
|                 axisValue.set(point[axis] - indexLegacy.mix(min, max, 0.5));
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     /**
 | |
|      * When the viewport resizes we want to check if the measured constraints
 | |
|      * have changed and, if so, reposition the element within those new constraints
 | |
|      * relative to where it was before the resize.
 | |
|      */
 | |
|     scalePositionWithinConstraints() {
 | |
|         if (!this.visualElement.current)
 | |
|             return;
 | |
|         const { drag, dragConstraints } = this.getProps();
 | |
|         const { projection } = this.visualElement;
 | |
|         if (!indexLegacy.isRefObject(dragConstraints) || !projection || !this.constraints)
 | |
|             return;
 | |
|         /**
 | |
|          * Stop current animations as there can be visual glitching if we try to do
 | |
|          * this mid-animation
 | |
|          */
 | |
|         this.stopAnimation();
 | |
|         /**
 | |
|          * Record the relative position of the dragged element relative to the
 | |
|          * constraints box and save as a progress value.
 | |
|          */
 | |
|         const boxProgress = { x: 0, y: 0 };
 | |
|         eachAxis((axis) => {
 | |
|             const axisValue = this.getAxisMotionValue(axis);
 | |
|             if (axisValue) {
 | |
|                 const latest = axisValue.get();
 | |
|                 boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]);
 | |
|             }
 | |
|         });
 | |
|         /**
 | |
|          * Update the layout of this element and resolve the latest drag constraints
 | |
|          */
 | |
|         const { transformTemplate } = this.visualElement.getProps();
 | |
|         this.visualElement.current.style.transform = transformTemplate
 | |
|             ? transformTemplate({}, "")
 | |
|             : "none";
 | |
|         projection.root && projection.root.updateScroll();
 | |
|         projection.updateLayout();
 | |
|         this.resolveConstraints();
 | |
|         /**
 | |
|          * For each axis, calculate the current progress of the layout axis
 | |
|          * within the new constraints.
 | |
|          */
 | |
|         eachAxis((axis) => {
 | |
|             if (!shouldDrag(axis, drag, null))
 | |
|                 return;
 | |
|             /**
 | |
|              * Calculate a new transform based on the previous box progress
 | |
|              */
 | |
|             const axisValue = this.getAxisMotionValue(axis);
 | |
|             const { min, max } = this.constraints[axis];
 | |
|             axisValue.set(indexLegacy.mix(min, max, boxProgress[axis]));
 | |
|         });
 | |
|     }
 | |
|     addListeners() {
 | |
|         if (!this.visualElement.current)
 | |
|             return;
 | |
|         elementDragControls.set(this.visualElement, this);
 | |
|         const element = this.visualElement.current;
 | |
|         /**
 | |
|          * Attach a pointerdown event listener on this DOM element to initiate drag tracking.
 | |
|          */
 | |
|         const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => {
 | |
|             const { drag, dragListener = true } = this.getProps();
 | |
|             drag && dragListener && this.start(event);
 | |
|         });
 | |
|         const measureDragConstraints = () => {
 | |
|             const { dragConstraints } = this.getProps();
 | |
|             if (indexLegacy.isRefObject(dragConstraints)) {
 | |
|                 this.constraints = this.resolveRefConstraints();
 | |
|             }
 | |
|         };
 | |
|         const { projection } = this.visualElement;
 | |
|         const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
 | |
|         if (projection && !projection.layout) {
 | |
|             projection.root && projection.root.updateScroll();
 | |
|             projection.updateLayout();
 | |
|         }
 | |
|         measureDragConstraints();
 | |
|         /**
 | |
|          * Attach a window resize listener to scale the draggable target within its defined
 | |
|          * constraints as the window resizes.
 | |
|          */
 | |
|         const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints());
 | |
|         /**
 | |
|          * If the element's layout changes, calculate the delta and apply that to
 | |
|          * the drag gesture's origin point.
 | |
|          */
 | |
|         const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => {
 | |
|             if (this.isDragging && hasLayoutChanged) {
 | |
|                 eachAxis((axis) => {
 | |
|                     const motionValue = this.getAxisMotionValue(axis);
 | |
|                     if (!motionValue)
 | |
|                         return;
 | |
|                     this.originPoint[axis] += delta[axis].translate;
 | |
|                     motionValue.set(motionValue.get() + delta[axis].translate);
 | |
|                 });
 | |
|                 this.visualElement.render();
 | |
|             }
 | |
|         }));
 | |
|         return () => {
 | |
|             stopResizeListener();
 | |
|             stopPointerListener();
 | |
|             stopMeasureLayoutListener();
 | |
|             stopLayoutUpdateListener && stopLayoutUpdateListener();
 | |
|         };
 | |
|     }
 | |
|     getProps() {
 | |
|         const props = this.visualElement.getProps();
 | |
|         const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props;
 | |
|         return {
 | |
|             ...props,
 | |
|             drag,
 | |
|             dragDirectionLock,
 | |
|             dragPropagation,
 | |
|             dragConstraints,
 | |
|             dragElastic,
 | |
|             dragMomentum,
 | |
|         };
 | |
|     }
 | |
| }
 | |
| function shouldDrag(direction, drag, currentDirection) {
 | |
|     return ((drag === true || drag === direction) &&
 | |
|         (currentDirection === null || currentDirection === direction));
 | |
| }
 | |
| /**
 | |
|  * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower
 | |
|  * than the provided threshold, return `null`.
 | |
|  *
 | |
|  * @param offset - The x/y offset from origin.
 | |
|  * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction.
 | |
|  */
 | |
| function getCurrentDirection(offset, lockThreshold = 10) {
 | |
|     let direction = null;
 | |
|     if (Math.abs(offset.y) > lockThreshold) {
 | |
|         direction = "y";
 | |
|     }
 | |
|     else if (Math.abs(offset.x) > lockThreshold) {
 | |
|         direction = "x";
 | |
|     }
 | |
|     return direction;
 | |
| }
 | |
| 
 | |
| class DragGesture extends Feature {
 | |
|     constructor(node) {
 | |
|         super(node);
 | |
|         this.removeGroupControls = indexLegacy.noop;
 | |
|         this.removeListeners = indexLegacy.noop;
 | |
|         this.controls = new VisualElementDragControls(node);
 | |
|     }
 | |
|     mount() {
 | |
|         // If we've been provided a DragControls for manual control over the drag gesture,
 | |
|         // subscribe this component to it on mount.
 | |
|         const { dragControls } = this.node.getProps();
 | |
|         if (dragControls) {
 | |
|             this.removeGroupControls = dragControls.subscribe(this.controls);
 | |
|         }
 | |
|         this.removeListeners = this.controls.addListeners() || indexLegacy.noop;
 | |
|     }
 | |
|     unmount() {
 | |
|         this.removeGroupControls();
 | |
|         this.removeListeners();
 | |
|     }
 | |
| }
 | |
| 
 | |
| const asyncHandler = (handler) => (event, info) => {
 | |
|     if (handler) {
 | |
|         indexLegacy.frame.update(() => handler(event, info));
 | |
|     }
 | |
| };
 | |
| class PanGesture extends Feature {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.removePointerDownListener = indexLegacy.noop;
 | |
|     }
 | |
|     onPointerDown(pointerDownEvent) {
 | |
|         this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), {
 | |
|             transformPagePoint: this.node.getTransformPagePoint(),
 | |
|             contextWindow: getContextWindow(this.node),
 | |
|         });
 | |
|     }
 | |
|     createPanHandlers() {
 | |
|         const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps();
 | |
|         return {
 | |
|             onSessionStart: asyncHandler(onPanSessionStart),
 | |
|             onStart: asyncHandler(onPanStart),
 | |
|             onMove: onPan,
 | |
|             onEnd: (event, info) => {
 | |
|                 delete this.session;
 | |
|                 if (onPanEnd) {
 | |
|                     indexLegacy.frame.update(() => onPanEnd(event, info));
 | |
|                 }
 | |
|             },
 | |
|         };
 | |
|     }
 | |
|     mount() {
 | |
|         this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event));
 | |
|     }
 | |
|     update() {
 | |
|         this.session && this.session.updateHandlers(this.createPanHandlers());
 | |
|     }
 | |
|     unmount() {
 | |
|         this.removePointerDownListener();
 | |
|         this.session && this.session.end();
 | |
|     }
 | |
| }
 | |
| 
 | |
| const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
 | |
| const numBorders = borders.length;
 | |
| const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
 | |
| const isPx = (value) => typeof value === "number" || indexLegacy.px.test(value);
 | |
| function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
 | |
|     if (shouldCrossfadeOpacity) {
 | |
|         target.opacity = indexLegacy.mix(0, 
 | |
|         // TODO Reinstate this if only child
 | |
|         lead.opacity !== undefined ? lead.opacity : 1, easeCrossfadeIn(progress));
 | |
|         target.opacityExit = indexLegacy.mix(follow.opacity !== undefined ? follow.opacity : 1, 0, easeCrossfadeOut(progress));
 | |
|     }
 | |
|     else if (isOnlyMember) {
 | |
|         target.opacity = indexLegacy.mix(follow.opacity !== undefined ? follow.opacity : 1, lead.opacity !== undefined ? lead.opacity : 1, progress);
 | |
|     }
 | |
|     /**
 | |
|      * Mix border radius
 | |
|      */
 | |
|     for (let i = 0; i < numBorders; i++) {
 | |
|         const borderLabel = `border${borders[i]}Radius`;
 | |
|         let followRadius = getRadius(follow, borderLabel);
 | |
|         let leadRadius = getRadius(lead, borderLabel);
 | |
|         if (followRadius === undefined && leadRadius === undefined)
 | |
|             continue;
 | |
|         followRadius || (followRadius = 0);
 | |
|         leadRadius || (leadRadius = 0);
 | |
|         const canMix = followRadius === 0 ||
 | |
|             leadRadius === 0 ||
 | |
|             isPx(followRadius) === isPx(leadRadius);
 | |
|         if (canMix) {
 | |
|             target[borderLabel] = Math.max(indexLegacy.mix(asNumber(followRadius), asNumber(leadRadius), progress), 0);
 | |
|             if (indexLegacy.percent.test(leadRadius) || indexLegacy.percent.test(followRadius)) {
 | |
|                 target[borderLabel] += "%";
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             target[borderLabel] = leadRadius;
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Mix rotation
 | |
|      */
 | |
|     if (follow.rotate || lead.rotate) {
 | |
|         target.rotate = indexLegacy.mix(follow.rotate || 0, lead.rotate || 0, progress);
 | |
|     }
 | |
| }
 | |
| function getRadius(values, radiusName) {
 | |
|     return values[radiusName] !== undefined
 | |
|         ? values[radiusName]
 | |
|         : values.borderRadius;
 | |
| }
 | |
| // /**
 | |
| //  * We only want to mix the background color if there's a follow element
 | |
| //  * that we're not crossfading opacity between. For instance with switch
 | |
| //  * AnimateSharedLayout animations, this helps the illusion of a continuous
 | |
| //  * element being animated but also cuts down on the number of paints triggered
 | |
| //  * for elements where opacity is doing that work for us.
 | |
| //  */
 | |
| // if (
 | |
| //     !hasFollowElement &&
 | |
| //     latestLeadValues.backgroundColor &&
 | |
| //     latestFollowValues.backgroundColor
 | |
| // ) {
 | |
| //     /**
 | |
| //      * This isn't ideal performance-wise as mixColor is creating a new function every frame.
 | |
| //      * We could probably create a mixer that runs at the start of the animation but
 | |
| //      * the idea behind the crossfader is that it runs dynamically between two potentially
 | |
| //      * changing targets (ie opacity or borderRadius may be animating independently via variants)
 | |
| //      */
 | |
| //     leadState.backgroundColor = followState.backgroundColor = mixColor(
 | |
| //         latestFollowValues.backgroundColor as string,
 | |
| //         latestLeadValues.backgroundColor as string
 | |
| //     )(p)
 | |
| // }
 | |
| const easeCrossfadeIn = compress(0, 0.5, indexLegacy.circOut);
 | |
| const easeCrossfadeOut = compress(0.5, 0.95, indexLegacy.noop);
 | |
| function compress(min, max, easing) {
 | |
|     return (p) => {
 | |
|         // Could replace ifs with clamp
 | |
|         if (p < min)
 | |
|             return 0;
 | |
|         if (p > max)
 | |
|             return 1;
 | |
|         return easing(indexLegacy.progress(min, max, p));
 | |
|     };
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Reset an axis to the provided origin box.
 | |
|  *
 | |
|  * This is a mutative operation.
 | |
|  */
 | |
| function copyAxisInto(axis, originAxis) {
 | |
|     axis.min = originAxis.min;
 | |
|     axis.max = originAxis.max;
 | |
| }
 | |
| /**
 | |
|  * Reset a box to the provided origin box.
 | |
|  *
 | |
|  * This is a mutative operation.
 | |
|  */
 | |
| function copyBoxInto(box, originBox) {
 | |
|     copyAxisInto(box.x, originBox.x);
 | |
|     copyAxisInto(box.y, originBox.y);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse
 | |
|  */
 | |
| function removePointDelta(point, translate, scale, originPoint, boxScale) {
 | |
|     point -= translate;
 | |
|     point = indexLegacy.scalePoint(point, 1 / scale, originPoint);
 | |
|     if (boxScale !== undefined) {
 | |
|         point = indexLegacy.scalePoint(point, 1 / boxScale, originPoint);
 | |
|     }
 | |
|     return point;
 | |
| }
 | |
| /**
 | |
|  * Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse
 | |
|  */
 | |
| function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) {
 | |
|     if (indexLegacy.percent.test(translate)) {
 | |
|         translate = parseFloat(translate);
 | |
|         const relativeProgress = indexLegacy.mix(sourceAxis.min, sourceAxis.max, translate / 100);
 | |
|         translate = relativeProgress - sourceAxis.min;
 | |
|     }
 | |
|     if (typeof translate !== "number")
 | |
|         return;
 | |
|     let originPoint = indexLegacy.mix(originAxis.min, originAxis.max, origin);
 | |
|     if (axis === originAxis)
 | |
|         originPoint -= translate;
 | |
|     axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale);
 | |
|     axis.max = removePointDelta(axis.max, translate, scale, originPoint, boxScale);
 | |
| }
 | |
| /**
 | |
|  * Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse
 | |
|  * and acts as a bridge between motion values and removeAxisDelta
 | |
|  */
 | |
| function removeAxisTransforms(axis, transforms, [key, scaleKey, originKey], origin, sourceAxis) {
 | |
|     removeAxisDelta(axis, transforms[key], transforms[scaleKey], transforms[originKey], transforms.scale, origin, sourceAxis);
 | |
| }
 | |
| /**
 | |
|  * 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"];
 | |
| /**
 | |
|  * Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse
 | |
|  * and acts as a bridge between motion values and removeAxisDelta
 | |
|  */
 | |
| function removeBoxTransforms(box, transforms, originBox, sourceBox) {
 | |
|     removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined);
 | |
|     removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined);
 | |
| }
 | |
| 
 | |
| function isAxisDeltaZero(delta) {
 | |
|     return delta.translate === 0 && delta.scale === 1;
 | |
| }
 | |
| function isDeltaZero(delta) {
 | |
|     return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y);
 | |
| }
 | |
| function boxEquals(a, b) {
 | |
|     return (a.x.min === b.x.min &&
 | |
|         a.x.max === b.x.max &&
 | |
|         a.y.min === b.y.min &&
 | |
|         a.y.max === b.y.max);
 | |
| }
 | |
| function boxEqualsRounded(a, b) {
 | |
|     return (Math.round(a.x.min) === Math.round(b.x.min) &&
 | |
|         Math.round(a.x.max) === Math.round(b.x.max) &&
 | |
|         Math.round(a.y.min) === Math.round(b.y.min) &&
 | |
|         Math.round(a.y.max) === Math.round(b.y.max));
 | |
| }
 | |
| function aspectRatio(box) {
 | |
|     return calcLength(box.x) / calcLength(box.y);
 | |
| }
 | |
| 
 | |
| class NodeStack {
 | |
|     constructor() {
 | |
|         this.members = [];
 | |
|     }
 | |
|     add(node) {
 | |
|         indexLegacy.addUniqueItem(this.members, node);
 | |
|         node.scheduleRender();
 | |
|     }
 | |
|     remove(node) {
 | |
|         indexLegacy.removeItem(this.members, node);
 | |
|         if (node === this.prevLead) {
 | |
|             this.prevLead = undefined;
 | |
|         }
 | |
|         if (node === this.lead) {
 | |
|             const prevLead = this.members[this.members.length - 1];
 | |
|             if (prevLead) {
 | |
|                 this.promote(prevLead);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     relegate(node) {
 | |
|         const indexOfNode = this.members.findIndex((member) => node === member);
 | |
|         if (indexOfNode === 0)
 | |
|             return false;
 | |
|         /**
 | |
|          * Find the next projection node that is present
 | |
|          */
 | |
|         let prevLead;
 | |
|         for (let i = indexOfNode; i >= 0; i--) {
 | |
|             const member = this.members[i];
 | |
|             if (member.isPresent !== false) {
 | |
|                 prevLead = member;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         if (prevLead) {
 | |
|             this.promote(prevLead);
 | |
|             return true;
 | |
|         }
 | |
|         else {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
|     promote(node, preserveFollowOpacity) {
 | |
|         const prevLead = this.lead;
 | |
|         if (node === prevLead)
 | |
|             return;
 | |
|         this.prevLead = prevLead;
 | |
|         this.lead = node;
 | |
|         node.show();
 | |
|         if (prevLead) {
 | |
|             prevLead.instance && prevLead.scheduleRender();
 | |
|             node.scheduleRender();
 | |
|             node.resumeFrom = prevLead;
 | |
|             if (preserveFollowOpacity) {
 | |
|                 node.resumeFrom.preserveOpacity = true;
 | |
|             }
 | |
|             if (prevLead.snapshot) {
 | |
|                 node.snapshot = prevLead.snapshot;
 | |
|                 node.snapshot.latestValues =
 | |
|                     prevLead.animationValues || prevLead.latestValues;
 | |
|             }
 | |
|             if (node.root && node.root.isUpdating) {
 | |
|                 node.isLayoutDirty = true;
 | |
|             }
 | |
|             const { crossfade } = node.options;
 | |
|             if (crossfade === false) {
 | |
|                 prevLead.hide();
 | |
|             }
 | |
|             /**
 | |
|              * TODO:
 | |
|              *   - Test border radius when previous node was deleted
 | |
|              *   - boxShadow mixing
 | |
|              *   - Shared between element A in scrolled container and element B (scroll stays the same or changes)
 | |
|              *   - Shared between element A in transformed container and element B (transform stays the same or changes)
 | |
|              *   - Shared between element A in scrolled page and element B (scroll stays the same or changes)
 | |
|              * ---
 | |
|              *   - Crossfade opacity of root nodes
 | |
|              *   - layoutId changes after animation
 | |
|              *   - layoutId changes mid animation
 | |
|              */
 | |
|         }
 | |
|     }
 | |
|     exitAnimationComplete() {
 | |
|         this.members.forEach((node) => {
 | |
|             const { options, resumingFrom } = node;
 | |
|             options.onExitComplete && options.onExitComplete();
 | |
|             if (resumingFrom) {
 | |
|                 resumingFrom.options.onExitComplete &&
 | |
|                     resumingFrom.options.onExitComplete();
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     scheduleRender() {
 | |
|         this.members.forEach((node) => {
 | |
|             node.instance && node.scheduleRender(false);
 | |
|         });
 | |
|     }
 | |
|     /**
 | |
|      * Clear any leads that have been removed this render to prevent them from being
 | |
|      * used in future animations and to prevent memory leaks
 | |
|      */
 | |
|     removeLeadSnapshot() {
 | |
|         if (this.lead && this.lead.snapshot) {
 | |
|             this.lead.snapshot = undefined;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| function buildProjectionTransform(delta, treeScale, latestTransform) {
 | |
|     let transform = "";
 | |
|     /**
 | |
|      * The translations we use to calculate are always relative to the viewport coordinate space.
 | |
|      * But when we apply scales, we also scale the coordinate space of an element and its children.
 | |
|      * For instance if we have a treeScale (the culmination of all parent scales) of 0.5 and we need
 | |
|      * to move an element 100 pixels, we actually need to move it 200 in within that scaled space.
 | |
|      */
 | |
|     const xTranslate = delta.x.translate / treeScale.x;
 | |
|     const yTranslate = delta.y.translate / treeScale.y;
 | |
|     if (xTranslate || yTranslate) {
 | |
|         transform = `translate3d(${xTranslate}px, ${yTranslate}px, 0) `;
 | |
|     }
 | |
|     /**
 | |
|      * Apply scale correction for the tree transform.
 | |
|      * This will apply scale to the screen-orientated axes.
 | |
|      */
 | |
|     if (treeScale.x !== 1 || treeScale.y !== 1) {
 | |
|         transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
 | |
|     }
 | |
|     if (latestTransform) {
 | |
|         const { rotate, rotateX, rotateY } = latestTransform;
 | |
|         if (rotate)
 | |
|             transform += `rotate(${rotate}deg) `;
 | |
|         if (rotateX)
 | |
|             transform += `rotateX(${rotateX}deg) `;
 | |
|         if (rotateY)
 | |
|             transform += `rotateY(${rotateY}deg) `;
 | |
|     }
 | |
|     /**
 | |
|      * Apply scale to match the size of the element to the size we want it.
 | |
|      * This will apply scale to the element-orientated axes.
 | |
|      */
 | |
|     const elementScaleX = delta.x.scale * treeScale.x;
 | |
|     const elementScaleY = delta.y.scale * treeScale.y;
 | |
|     if (elementScaleX !== 1 || elementScaleY !== 1) {
 | |
|         transform += `scale(${elementScaleX}, ${elementScaleY})`;
 | |
|     }
 | |
|     return transform || "none";
 | |
| }
 | |
| 
 | |
| const compareByDepth = (a, b) => a.depth - b.depth;
 | |
| 
 | |
| class FlatTree {
 | |
|     constructor() {
 | |
|         this.children = [];
 | |
|         this.isDirty = false;
 | |
|     }
 | |
|     add(child) {
 | |
|         indexLegacy.addUniqueItem(this.children, child);
 | |
|         this.isDirty = true;
 | |
|     }
 | |
|     remove(child) {
 | |
|         indexLegacy.removeItem(this.children, child);
 | |
|         this.isDirty = true;
 | |
|     }
 | |
|     forEach(callback) {
 | |
|         this.isDirty && this.children.sort(compareByDepth);
 | |
|         this.isDirty = false;
 | |
|         this.children.forEach(callback);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This should only ever be modified on the client otherwise it'll
 | |
|  * persist through server requests. If we need instanced states we
 | |
|  * could lazy-init via root.
 | |
|  */
 | |
| const globalProjectionState = {
 | |
|     /**
 | |
|      * Global flag as to whether the tree has animated since the last time
 | |
|      * we resized the window
 | |
|      */
 | |
|     hasAnimatedSinceResize: true,
 | |
|     /**
 | |
|      * We set this to true once, on the first update. Any nodes added to the tree beyond that
 | |
|      * update will be given a `data-projection-id` attribute.
 | |
|      */
 | |
|     hasEverUpdated: false,
 | |
| };
 | |
| 
 | |
| function record(data) {
 | |
|     if (window.MotionDebug) {
 | |
|         window.MotionDebug.record(data);
 | |
|     }
 | |
| }
 | |
| 
 | |
| const transformAxes = ["", "X", "Y", "Z"];
 | |
| const hiddenVisibility = { visibility: "hidden" };
 | |
| /**
 | |
|  * We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1
 | |
|  * which has a noticeable difference in spring animations
 | |
|  */
 | |
| const animationTarget = 1000;
 | |
| let id$1 = 0;
 | |
| /**
 | |
|  * Use a mutable data object for debug data so as to not create a new
 | |
|  * object every frame.
 | |
|  */
 | |
| const projectionFrameData = {
 | |
|     type: "projectionFrame",
 | |
|     totalNodes: 0,
 | |
|     resolvedTargetDeltas: 0,
 | |
|     recalculatedProjection: 0,
 | |
| };
 | |
| function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
 | |
|     return class ProjectionNode {
 | |
|         constructor(latestValues = {}, parent = defaultParent === null || defaultParent === void 0 ? void 0 : defaultParent()) {
 | |
|             /**
 | |
|              * A unique ID generated for every projection node.
 | |
|              */
 | |
|             this.id = id$1++;
 | |
|             /**
 | |
|              * An id that represents a unique session instigated by startUpdate.
 | |
|              */
 | |
|             this.animationId = 0;
 | |
|             /**
 | |
|              * A Set containing all this component's children. This is used to iterate
 | |
|              * through the children.
 | |
|              *
 | |
|              * TODO: This could be faster to iterate as a flat array stored on the root node.
 | |
|              */
 | |
|             this.children = new Set();
 | |
|             /**
 | |
|              * Options for the node. We use this to configure what kind of layout animations
 | |
|              * we should perform (if any).
 | |
|              */
 | |
|             this.options = {};
 | |
|             /**
 | |
|              * We use this to detect when its safe to shut down part of a projection tree.
 | |
|              * We have to keep projecting children for scale correction and relative projection
 | |
|              * until all their parents stop performing layout animations.
 | |
|              */
 | |
|             this.isTreeAnimating = false;
 | |
|             this.isAnimationBlocked = false;
 | |
|             /**
 | |
|              * Flag to true if we think this layout has been changed. We can't always know this,
 | |
|              * currently we set it to true every time a component renders, or if it has a layoutDependency
 | |
|              * if that has changed between renders. Additionally, components can be grouped by LayoutGroup
 | |
|              * and if one node is dirtied, they all are.
 | |
|              */
 | |
|             this.isLayoutDirty = false;
 | |
|             /**
 | |
|              * Flag to true if we think the projection calculations for this node needs
 | |
|              * recalculating as a result of an updated transform or layout animation.
 | |
|              */
 | |
|             this.isProjectionDirty = false;
 | |
|             /**
 | |
|              * Flag to true if the layout *or* transform has changed. This then gets propagated
 | |
|              * throughout the projection tree, forcing any element below to recalculate on the next frame.
 | |
|              */
 | |
|             this.isSharedProjectionDirty = false;
 | |
|             /**
 | |
|              * Flag transform dirty. This gets propagated throughout the whole tree but is only
 | |
|              * respected by shared nodes.
 | |
|              */
 | |
|             this.isTransformDirty = false;
 | |
|             /**
 | |
|              * Block layout updates for instant layout transitions throughout the tree.
 | |
|              */
 | |
|             this.updateManuallyBlocked = false;
 | |
|             this.updateBlockedByResize = false;
 | |
|             /**
 | |
|              * Set to true between the start of the first `willUpdate` call and the end of the `didUpdate`
 | |
|              * call.
 | |
|              */
 | |
|             this.isUpdating = false;
 | |
|             /**
 | |
|              * If this is an SVG element we currently disable projection transforms
 | |
|              */
 | |
|             this.isSVG = false;
 | |
|             /**
 | |
|              * Flag to true (during promotion) if a node doing an instant layout transition needs to reset
 | |
|              * its projection styles.
 | |
|              */
 | |
|             this.needsReset = false;
 | |
|             /**
 | |
|              * Flags whether this node should have its transform reset prior to measuring.
 | |
|              */
 | |
|             this.shouldResetTransform = false;
 | |
|             /**
 | |
|              * An object representing the calculated contextual/accumulated/tree scale.
 | |
|              * This will be used to scale calculcated projection transforms, as these are
 | |
|              * calculated in screen-space but need to be scaled for elements to layoutly
 | |
|              * make it to their calculated destinations.
 | |
|              *
 | |
|              * TODO: Lazy-init
 | |
|              */
 | |
|             this.treeScale = { x: 1, y: 1 };
 | |
|             /**
 | |
|              *
 | |
|              */
 | |
|             this.eventHandlers = new Map();
 | |
|             this.hasTreeAnimated = false;
 | |
|             // Note: Currently only running on root node
 | |
|             this.updateScheduled = false;
 | |
|             this.projectionUpdateScheduled = false;
 | |
|             this.checkUpdateFailed = () => {
 | |
|                 if (this.isUpdating) {
 | |
|                     this.isUpdating = false;
 | |
|                     this.clearAllSnapshots();
 | |
|                 }
 | |
|             };
 | |
|             /**
 | |
|              * This is a multi-step process as shared nodes might be of different depths. Nodes
 | |
|              * are sorted by depth order, so we need to resolve the entire tree before moving to
 | |
|              * the next step.
 | |
|              */
 | |
|             this.updateProjection = () => {
 | |
|                 this.projectionUpdateScheduled = false;
 | |
|                 /**
 | |
|                  * Reset debug counts. Manually resetting rather than creating a new
 | |
|                  * object each frame.
 | |
|                  */
 | |
|                 projectionFrameData.totalNodes =
 | |
|                     projectionFrameData.resolvedTargetDeltas =
 | |
|                         projectionFrameData.recalculatedProjection =
 | |
|                             0;
 | |
|                 this.nodes.forEach(propagateDirtyNodes);
 | |
|                 this.nodes.forEach(resolveTargetDelta);
 | |
|                 this.nodes.forEach(calcProjection);
 | |
|                 this.nodes.forEach(cleanDirtyNodes);
 | |
|                 record(projectionFrameData);
 | |
|             };
 | |
|             this.hasProjected = false;
 | |
|             this.isVisible = true;
 | |
|             this.animationProgress = 0;
 | |
|             /**
 | |
|              * Shared layout
 | |
|              */
 | |
|             // TODO Only running on root node
 | |
|             this.sharedNodes = new Map();
 | |
|             this.latestValues = latestValues;
 | |
|             this.root = parent ? parent.root || parent : this;
 | |
|             this.path = parent ? [...parent.path, parent] : [];
 | |
|             this.parent = parent;
 | |
|             this.depth = parent ? parent.depth + 1 : 0;
 | |
|             for (let i = 0; i < this.path.length; i++) {
 | |
|                 this.path[i].shouldResetTransform = true;
 | |
|             }
 | |
|             if (this.root === this)
 | |
|                 this.nodes = new FlatTree();
 | |
|         }
 | |
|         addEventListener(name, handler) {
 | |
|             if (!this.eventHandlers.has(name)) {
 | |
|                 this.eventHandlers.set(name, new indexLegacy.SubscriptionManager());
 | |
|             }
 | |
|             return this.eventHandlers.get(name).add(handler);
 | |
|         }
 | |
|         notifyListeners(name, ...args) {
 | |
|             const subscriptionManager = this.eventHandlers.get(name);
 | |
|             subscriptionManager && subscriptionManager.notify(...args);
 | |
|         }
 | |
|         hasListeners(name) {
 | |
|             return this.eventHandlers.has(name);
 | |
|         }
 | |
|         /**
 | |
|          * Lifecycles
 | |
|          */
 | |
|         mount(instance, isLayoutDirty = this.root.hasTreeAnimated) {
 | |
|             if (this.instance)
 | |
|                 return;
 | |
|             this.isSVG = indexLegacy.isSVGElement(instance);
 | |
|             this.instance = instance;
 | |
|             const { layoutId, layout, visualElement } = this.options;
 | |
|             if (visualElement && !visualElement.current) {
 | |
|                 visualElement.mount(instance);
 | |
|             }
 | |
|             this.root.nodes.add(this);
 | |
|             this.parent && this.parent.children.add(this);
 | |
|             if (isLayoutDirty && (layout || layoutId)) {
 | |
|                 this.isLayoutDirty = true;
 | |
|             }
 | |
|             if (attachResizeListener) {
 | |
|                 let cancelDelay;
 | |
|                 const resizeUnblockUpdate = () => (this.root.updateBlockedByResize = false);
 | |
|                 attachResizeListener(instance, () => {
 | |
|                     this.root.updateBlockedByResize = true;
 | |
|                     cancelDelay && cancelDelay();
 | |
|                     cancelDelay = indexLegacy.delay(resizeUnblockUpdate, 250);
 | |
|                     if (globalProjectionState.hasAnimatedSinceResize) {
 | |
|                         globalProjectionState.hasAnimatedSinceResize = false;
 | |
|                         this.nodes.forEach(finishAnimation);
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|             if (layoutId) {
 | |
|                 this.root.registerSharedNode(layoutId, this);
 | |
|             }
 | |
|             // Only register the handler if it requires layout animation
 | |
|             if (this.options.animate !== false &&
 | |
|                 visualElement &&
 | |
|                 (layoutId || layout)) {
 | |
|                 this.addEventListener("didUpdate", ({ delta, hasLayoutChanged, hasRelativeTargetChanged, layout: newLayout, }) => {
 | |
|                     if (this.isTreeAnimationBlocked()) {
 | |
|                         this.target = undefined;
 | |
|                         this.relativeTarget = undefined;
 | |
|                         return;
 | |
|                     }
 | |
|                     // TODO: Check here if an animation exists
 | |
|                     const layoutTransition = this.options.transition ||
 | |
|                         visualElement.getDefaultTransition() ||
 | |
|                         defaultLayoutTransition;
 | |
|                     const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps();
 | |
|                     /**
 | |
|                      * The target layout of the element might stay the same,
 | |
|                      * but its position relative to its parent has changed.
 | |
|                      */
 | |
|                     const targetChanged = !this.targetLayout ||
 | |
|                         !boxEqualsRounded(this.targetLayout, newLayout) ||
 | |
|                         hasRelativeTargetChanged;
 | |
|                     /**
 | |
|                      * If the layout hasn't seemed to have changed, it might be that the
 | |
|                      * element is visually in the same place in the document but its position
 | |
|                      * relative to its parent has indeed changed. So here we check for that.
 | |
|                      */
 | |
|                     const hasOnlyRelativeTargetChanged = !hasLayoutChanged && hasRelativeTargetChanged;
 | |
|                     if (this.options.layoutRoot ||
 | |
|                         (this.resumeFrom && this.resumeFrom.instance) ||
 | |
|                         hasOnlyRelativeTargetChanged ||
 | |
|                         (hasLayoutChanged &&
 | |
|                             (targetChanged || !this.currentAnimation))) {
 | |
|                         if (this.resumeFrom) {
 | |
|                             this.resumingFrom = this.resumeFrom;
 | |
|                             this.resumingFrom.resumingFrom = undefined;
 | |
|                         }
 | |
|                         this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged);
 | |
|                         const animationOptions = {
 | |
|                             ...indexLegacy.getValueTransition(layoutTransition, "layout"),
 | |
|                             onPlay: onLayoutAnimationStart,
 | |
|                             onComplete: onLayoutAnimationComplete,
 | |
|                         };
 | |
|                         if (visualElement.shouldReduceMotion ||
 | |
|                             this.options.layoutRoot) {
 | |
|                             animationOptions.delay = 0;
 | |
|                             animationOptions.type = false;
 | |
|                         }
 | |
|                         this.startAnimation(animationOptions);
 | |
|                     }
 | |
|                     else {
 | |
|                         /**
 | |
|                          * If the layout hasn't changed and we have an animation that hasn't started yet,
 | |
|                          * finish it immediately. Otherwise it will be animating from a location
 | |
|                          * that was probably never commited to screen and look like a jumpy box.
 | |
|                          */
 | |
|                         if (!hasLayoutChanged) {
 | |
|                             finishAnimation(this);
 | |
|                         }
 | |
|                         if (this.isLead() && this.options.onExitComplete) {
 | |
|                             this.options.onExitComplete();
 | |
|                         }
 | |
|                     }
 | |
|                     this.targetLayout = newLayout;
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         unmount() {
 | |
|             this.options.layoutId && this.willUpdate();
 | |
|             this.root.nodes.remove(this);
 | |
|             const stack = this.getStack();
 | |
|             stack && stack.remove(this);
 | |
|             this.parent && this.parent.children.delete(this);
 | |
|             this.instance = undefined;
 | |
|             indexLegacy.cancelFrame(this.updateProjection);
 | |
|         }
 | |
|         // only on the root
 | |
|         blockUpdate() {
 | |
|             this.updateManuallyBlocked = true;
 | |
|         }
 | |
|         unblockUpdate() {
 | |
|             this.updateManuallyBlocked = false;
 | |
|         }
 | |
|         isUpdateBlocked() {
 | |
|             return this.updateManuallyBlocked || this.updateBlockedByResize;
 | |
|         }
 | |
|         isTreeAnimationBlocked() {
 | |
|             return (this.isAnimationBlocked ||
 | |
|                 (this.parent && this.parent.isTreeAnimationBlocked()) ||
 | |
|                 false);
 | |
|         }
 | |
|         // Note: currently only running on root node
 | |
|         startUpdate() {
 | |
|             if (this.isUpdateBlocked())
 | |
|                 return;
 | |
|             this.isUpdating = true;
 | |
|             this.nodes && this.nodes.forEach(resetRotation);
 | |
|             this.animationId++;
 | |
|         }
 | |
|         getTransformTemplate() {
 | |
|             const { visualElement } = this.options;
 | |
|             return visualElement && visualElement.getProps().transformTemplate;
 | |
|         }
 | |
|         willUpdate(shouldNotifyListeners = true) {
 | |
|             this.root.hasTreeAnimated = true;
 | |
|             if (this.root.isUpdateBlocked()) {
 | |
|                 this.options.onExitComplete && this.options.onExitComplete();
 | |
|                 return;
 | |
|             }
 | |
|             !this.root.isUpdating && this.root.startUpdate();
 | |
|             if (this.isLayoutDirty)
 | |
|                 return;
 | |
|             this.isLayoutDirty = true;
 | |
|             for (let i = 0; i < this.path.length; i++) {
 | |
|                 const node = this.path[i];
 | |
|                 node.shouldResetTransform = true;
 | |
|                 node.updateScroll("snapshot");
 | |
|                 if (node.options.layoutRoot) {
 | |
|                     node.willUpdate(false);
 | |
|                 }
 | |
|             }
 | |
|             const { layoutId, layout } = this.options;
 | |
|             if (layoutId === undefined && !layout)
 | |
|                 return;
 | |
|             const transformTemplate = this.getTransformTemplate();
 | |
|             this.prevTransformTemplateValue = transformTemplate
 | |
|                 ? transformTemplate(this.latestValues, "")
 | |
|                 : undefined;
 | |
|             this.updateSnapshot();
 | |
|             shouldNotifyListeners && this.notifyListeners("willUpdate");
 | |
|         }
 | |
|         update() {
 | |
|             this.updateScheduled = false;
 | |
|             const updateWasBlocked = this.isUpdateBlocked();
 | |
|             // When doing an instant transition, we skip the layout update,
 | |
|             // but should still clean up the measurements so that the next
 | |
|             // snapshot could be taken correctly.
 | |
|             if (updateWasBlocked) {
 | |
|                 this.unblockUpdate();
 | |
|                 this.clearAllSnapshots();
 | |
|                 this.nodes.forEach(clearMeasurements);
 | |
|                 return;
 | |
|             }
 | |
|             if (!this.isUpdating) {
 | |
|                 this.nodes.forEach(clearIsLayoutDirty);
 | |
|             }
 | |
|             this.isUpdating = false;
 | |
|             /**
 | |
|              * Write
 | |
|              */
 | |
|             this.nodes.forEach(resetTransformStyle);
 | |
|             /**
 | |
|              * Read ==================
 | |
|              */
 | |
|             // Update layout measurements of updated children
 | |
|             this.nodes.forEach(updateLayout);
 | |
|             /**
 | |
|              * Write
 | |
|              */
 | |
|             // Notify listeners that the layout is updated
 | |
|             this.nodes.forEach(notifyLayoutUpdate);
 | |
|             this.clearAllSnapshots();
 | |
|             /**
 | |
|              * Manually flush any pending updates. Ideally
 | |
|              * we could leave this to the following requestAnimationFrame but this seems
 | |
|              * to leave a flash of incorrectly styled content.
 | |
|              */
 | |
|             const now = performance.now();
 | |
|             indexLegacy.frameData.delta = indexLegacy.clamp(0, 1000 / 60, now - indexLegacy.frameData.timestamp);
 | |
|             indexLegacy.frameData.timestamp = now;
 | |
|             indexLegacy.frameData.isProcessing = true;
 | |
|             indexLegacy.steps.update.process(indexLegacy.frameData);
 | |
|             indexLegacy.steps.preRender.process(indexLegacy.frameData);
 | |
|             indexLegacy.steps.render.process(indexLegacy.frameData);
 | |
|             indexLegacy.frameData.isProcessing = false;
 | |
|         }
 | |
|         didUpdate() {
 | |
|             if (!this.updateScheduled) {
 | |
|                 this.updateScheduled = true;
 | |
|                 queueMicrotask(() => this.update());
 | |
|             }
 | |
|         }
 | |
|         clearAllSnapshots() {
 | |
|             this.nodes.forEach(clearSnapshot);
 | |
|             this.sharedNodes.forEach(removeLeadSnapshots);
 | |
|         }
 | |
|         scheduleUpdateProjection() {
 | |
|             if (!this.projectionUpdateScheduled) {
 | |
|                 this.projectionUpdateScheduled = true;
 | |
|                 indexLegacy.frame.preRender(this.updateProjection, false, true);
 | |
|             }
 | |
|         }
 | |
|         scheduleCheckAfterUnmount() {
 | |
|             /**
 | |
|              * If the unmounting node is in a layoutGroup and did trigger a willUpdate,
 | |
|              * we manually call didUpdate to give a chance to the siblings to animate.
 | |
|              * Otherwise, cleanup all snapshots to prevents future nodes from reusing them.
 | |
|              */
 | |
|             indexLegacy.frame.postRender(() => {
 | |
|                 if (this.isLayoutDirty) {
 | |
|                     this.root.didUpdate();
 | |
|                 }
 | |
|                 else {
 | |
|                     this.root.checkUpdateFailed();
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         /**
 | |
|          * Update measurements
 | |
|          */
 | |
|         updateSnapshot() {
 | |
|             if (this.snapshot || !this.instance)
 | |
|                 return;
 | |
|             this.snapshot = this.measure();
 | |
|         }
 | |
|         updateLayout() {
 | |
|             if (!this.instance)
 | |
|                 return;
 | |
|             // TODO: Incorporate into a forwarded scroll offset
 | |
|             this.updateScroll();
 | |
|             if (!(this.options.alwaysMeasureLayout && this.isLead()) &&
 | |
|                 !this.isLayoutDirty) {
 | |
|                 return;
 | |
|             }
 | |
|             /**
 | |
|              * When a node is mounted, it simply resumes from the prevLead's
 | |
|              * snapshot instead of taking a new one, but the ancestors scroll
 | |
|              * might have updated while the prevLead is unmounted. We need to
 | |
|              * update the scroll again to make sure the layout we measure is
 | |
|              * up to date.
 | |
|              */
 | |
|             if (this.resumeFrom && !this.resumeFrom.instance) {
 | |
|                 for (let i = 0; i < this.path.length; i++) {
 | |
|                     const node = this.path[i];
 | |
|                     node.updateScroll();
 | |
|                 }
 | |
|             }
 | |
|             const prevLayout = this.layout;
 | |
|             this.layout = this.measure(false);
 | |
|             this.layoutCorrected = indexLegacy.createBox();
 | |
|             this.isLayoutDirty = false;
 | |
|             this.projectionDelta = undefined;
 | |
|             this.notifyListeners("measure", this.layout.layoutBox);
 | |
|             const { visualElement } = this.options;
 | |
|             visualElement &&
 | |
|                 visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined);
 | |
|         }
 | |
|         updateScroll(phase = "measure") {
 | |
|             let needsMeasurement = Boolean(this.options.layoutScroll && this.instance);
 | |
|             if (this.scroll &&
 | |
|                 this.scroll.animationId === this.root.animationId &&
 | |
|                 this.scroll.phase === phase) {
 | |
|                 needsMeasurement = false;
 | |
|             }
 | |
|             if (needsMeasurement) {
 | |
|                 this.scroll = {
 | |
|                     animationId: this.root.animationId,
 | |
|                     phase,
 | |
|                     isRoot: checkIsScrollRoot(this.instance),
 | |
|                     offset: measureScroll(this.instance),
 | |
|                 };
 | |
|             }
 | |
|         }
 | |
|         resetTransform() {
 | |
|             if (!resetTransform)
 | |
|                 return;
 | |
|             const isResetRequested = this.isLayoutDirty || this.shouldResetTransform;
 | |
|             const hasProjection = this.projectionDelta && !isDeltaZero(this.projectionDelta);
 | |
|             const transformTemplate = this.getTransformTemplate();
 | |
|             const transformTemplateValue = transformTemplate
 | |
|                 ? transformTemplate(this.latestValues, "")
 | |
|                 : undefined;
 | |
|             const transformTemplateHasChanged = transformTemplateValue !== this.prevTransformTemplateValue;
 | |
|             if (isResetRequested &&
 | |
|                 (hasProjection ||
 | |
|                     indexLegacy.hasTransform(this.latestValues) ||
 | |
|                     transformTemplateHasChanged)) {
 | |
|                 resetTransform(this.instance, transformTemplateValue);
 | |
|                 this.shouldResetTransform = false;
 | |
|                 this.scheduleRender();
 | |
|             }
 | |
|         }
 | |
|         measure(removeTransform = true) {
 | |
|             const pageBox = this.measurePageBox();
 | |
|             let layoutBox = this.removeElementScroll(pageBox);
 | |
|             /**
 | |
|              * Measurements taken during the pre-render stage
 | |
|              * still have transforms applied so we remove them
 | |
|              * via calculation.
 | |
|              */
 | |
|             if (removeTransform) {
 | |
|                 layoutBox = this.removeTransform(layoutBox);
 | |
|             }
 | |
|             roundBox(layoutBox);
 | |
|             return {
 | |
|                 animationId: this.root.animationId,
 | |
|                 measuredBox: pageBox,
 | |
|                 layoutBox,
 | |
|                 latestValues: {},
 | |
|                 source: this.id,
 | |
|             };
 | |
|         }
 | |
|         measurePageBox() {
 | |
|             const { visualElement } = this.options;
 | |
|             if (!visualElement)
 | |
|                 return indexLegacy.createBox();
 | |
|             const box = visualElement.measureViewportBox();
 | |
|             // Remove viewport scroll to give page-relative coordinates
 | |
|             const { scroll } = this.root;
 | |
|             if (scroll) {
 | |
|                 indexLegacy.translateAxis(box.x, scroll.offset.x);
 | |
|                 indexLegacy.translateAxis(box.y, scroll.offset.y);
 | |
|             }
 | |
|             return box;
 | |
|         }
 | |
|         removeElementScroll(box) {
 | |
|             const boxWithoutScroll = indexLegacy.createBox();
 | |
|             copyBoxInto(boxWithoutScroll, box);
 | |
|             /**
 | |
|              * Performance TODO: Keep a cumulative scroll offset down the tree
 | |
|              * rather than loop back up the path.
 | |
|              */
 | |
|             for (let i = 0; i < this.path.length; i++) {
 | |
|                 const node = this.path[i];
 | |
|                 const { scroll, options } = node;
 | |
|                 if (node !== this.root && scroll && options.layoutScroll) {
 | |
|                     /**
 | |
|                      * If this is a new scroll root, we want to remove all previous scrolls
 | |
|                      * from the viewport box.
 | |
|                      */
 | |
|                     if (scroll.isRoot) {
 | |
|                         copyBoxInto(boxWithoutScroll, box);
 | |
|                         const { scroll: rootScroll } = this.root;
 | |
|                         /**
 | |
|                          * Undo the application of page scroll that was originally added
 | |
|                          * to the measured bounding box.
 | |
|                          */
 | |
|                         if (rootScroll) {
 | |
|                             indexLegacy.translateAxis(boxWithoutScroll.x, -rootScroll.offset.x);
 | |
|                             indexLegacy.translateAxis(boxWithoutScroll.y, -rootScroll.offset.y);
 | |
|                         }
 | |
|                     }
 | |
|                     indexLegacy.translateAxis(boxWithoutScroll.x, scroll.offset.x);
 | |
|                     indexLegacy.translateAxis(boxWithoutScroll.y, scroll.offset.y);
 | |
|                 }
 | |
|             }
 | |
|             return boxWithoutScroll;
 | |
|         }
 | |
|         applyTransform(box, transformOnly = false) {
 | |
|             const withTransforms = indexLegacy.createBox();
 | |
|             copyBoxInto(withTransforms, box);
 | |
|             for (let i = 0; i < this.path.length; i++) {
 | |
|                 const node = this.path[i];
 | |
|                 if (!transformOnly &&
 | |
|                     node.options.layoutScroll &&
 | |
|                     node.scroll &&
 | |
|                     node !== node.root) {
 | |
|                     indexLegacy.transformBox(withTransforms, {
 | |
|                         x: -node.scroll.offset.x,
 | |
|                         y: -node.scroll.offset.y,
 | |
|                     });
 | |
|                 }
 | |
|                 if (!indexLegacy.hasTransform(node.latestValues))
 | |
|                     continue;
 | |
|                 indexLegacy.transformBox(withTransforms, node.latestValues);
 | |
|             }
 | |
|             if (indexLegacy.hasTransform(this.latestValues)) {
 | |
|                 indexLegacy.transformBox(withTransforms, this.latestValues);
 | |
|             }
 | |
|             return withTransforms;
 | |
|         }
 | |
|         removeTransform(box) {
 | |
|             const boxWithoutTransform = indexLegacy.createBox();
 | |
|             copyBoxInto(boxWithoutTransform, box);
 | |
|             for (let i = 0; i < this.path.length; i++) {
 | |
|                 const node = this.path[i];
 | |
|                 if (!node.instance)
 | |
|                     continue;
 | |
|                 if (!indexLegacy.hasTransform(node.latestValues))
 | |
|                     continue;
 | |
|                 indexLegacy.hasScale(node.latestValues) && node.updateSnapshot();
 | |
|                 const sourceBox = indexLegacy.createBox();
 | |
|                 const nodeBox = node.measurePageBox();
 | |
|                 copyBoxInto(sourceBox, nodeBox);
 | |
|                 removeBoxTransforms(boxWithoutTransform, node.latestValues, node.snapshot ? node.snapshot.layoutBox : undefined, sourceBox);
 | |
|             }
 | |
|             if (indexLegacy.hasTransform(this.latestValues)) {
 | |
|                 removeBoxTransforms(boxWithoutTransform, this.latestValues);
 | |
|             }
 | |
|             return boxWithoutTransform;
 | |
|         }
 | |
|         setTargetDelta(delta) {
 | |
|             this.targetDelta = delta;
 | |
|             this.root.scheduleUpdateProjection();
 | |
|             this.isProjectionDirty = true;
 | |
|         }
 | |
|         setOptions(options) {
 | |
|             this.options = {
 | |
|                 ...this.options,
 | |
|                 ...options,
 | |
|                 crossfade: options.crossfade !== undefined ? options.crossfade : true,
 | |
|             };
 | |
|         }
 | |
|         clearMeasurements() {
 | |
|             this.scroll = undefined;
 | |
|             this.layout = undefined;
 | |
|             this.snapshot = undefined;
 | |
|             this.prevTransformTemplateValue = undefined;
 | |
|             this.targetDelta = undefined;
 | |
|             this.target = undefined;
 | |
|             this.isLayoutDirty = false;
 | |
|         }
 | |
|         forceRelativeParentToResolveTarget() {
 | |
|             if (!this.relativeParent)
 | |
|                 return;
 | |
|             /**
 | |
|              * If the parent target isn't up-to-date, force it to update.
 | |
|              * This is an unfortunate de-optimisation as it means any updating relative
 | |
|              * projection will cause all the relative parents to recalculate back
 | |
|              * up the tree.
 | |
|              */
 | |
|             if (this.relativeParent.resolvedRelativeTargetAt !==
 | |
|                 indexLegacy.frameData.timestamp) {
 | |
|                 this.relativeParent.resolveTargetDelta(true);
 | |
|             }
 | |
|         }
 | |
|         resolveTargetDelta(forceRecalculation = false) {
 | |
|             var _a;
 | |
|             /**
 | |
|              * Once the dirty status of nodes has been spread through the tree, we also
 | |
|              * need to check if we have a shared node of a different depth that has itself
 | |
|              * been dirtied.
 | |
|              */
 | |
|             const lead = this.getLead();
 | |
|             this.isProjectionDirty || (this.isProjectionDirty = lead.isProjectionDirty);
 | |
|             this.isTransformDirty || (this.isTransformDirty = lead.isTransformDirty);
 | |
|             this.isSharedProjectionDirty || (this.isSharedProjectionDirty = lead.isSharedProjectionDirty);
 | |
|             const isShared = Boolean(this.resumingFrom) || this !== lead;
 | |
|             /**
 | |
|              * We don't use transform for this step of processing so we don't
 | |
|              * need to check whether any nodes have changed transform.
 | |
|              */
 | |
|             const canSkip = !(forceRecalculation ||
 | |
|                 (isShared && this.isSharedProjectionDirty) ||
 | |
|                 this.isProjectionDirty ||
 | |
|                 ((_a = this.parent) === null || _a === void 0 ? void 0 : _a.isProjectionDirty) ||
 | |
|                 this.attemptToResolveRelativeTarget);
 | |
|             if (canSkip)
 | |
|                 return;
 | |
|             const { layout, layoutId } = this.options;
 | |
|             /**
 | |
|              * If we have no layout, we can't perform projection, so early return
 | |
|              */
 | |
|             if (!this.layout || !(layout || layoutId))
 | |
|                 return;
 | |
|             this.resolvedRelativeTargetAt = indexLegacy.frameData.timestamp;
 | |
|             /**
 | |
|              * If we don't have a targetDelta but do have a layout, we can attempt to resolve
 | |
|              * a relativeParent. This will allow a component to perform scale correction
 | |
|              * even if no animation has started.
 | |
|              */
 | |
|             // TODO If this is unsuccessful this currently happens every frame
 | |
|             if (!this.targetDelta && !this.relativeTarget) {
 | |
|                 // TODO: This is a semi-repetition of further down this function, make DRY
 | |
|                 const relativeParent = this.getClosestProjectingParent();
 | |
|                 if (relativeParent &&
 | |
|                     relativeParent.layout &&
 | |
|                     this.animationProgress !== 1) {
 | |
|                     this.relativeParent = relativeParent;
 | |
|                     this.forceRelativeParentToResolveTarget();
 | |
|                     this.relativeTarget = indexLegacy.createBox();
 | |
|                     this.relativeTargetOrigin = indexLegacy.createBox();
 | |
|                     calcRelativePosition(this.relativeTargetOrigin, this.layout.layoutBox, relativeParent.layout.layoutBox);
 | |
|                     copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
 | |
|                 }
 | |
|                 else {
 | |
|                     this.relativeParent = this.relativeTarget = undefined;
 | |
|                 }
 | |
|             }
 | |
|             /**
 | |
|              * If we have no relative target or no target delta our target isn't valid
 | |
|              * for this frame.
 | |
|              */
 | |
|             if (!this.relativeTarget && !this.targetDelta)
 | |
|                 return;
 | |
|             /**
 | |
|              * Lazy-init target data structure
 | |
|              */
 | |
|             if (!this.target) {
 | |
|                 this.target = indexLegacy.createBox();
 | |
|                 this.targetWithTransforms = indexLegacy.createBox();
 | |
|             }
 | |
|             /**
 | |
|              * If we've got a relative box for this component, resolve it into a target relative to the parent.
 | |
|              */
 | |
|             if (this.relativeTarget &&
 | |
|                 this.relativeTargetOrigin &&
 | |
|                 this.relativeParent &&
 | |
|                 this.relativeParent.target) {
 | |
|                 this.forceRelativeParentToResolveTarget();
 | |
|                 calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target);
 | |
|                 /**
 | |
|                  * If we've only got a targetDelta, resolve it into a target
 | |
|                  */
 | |
|             }
 | |
|             else if (this.targetDelta) {
 | |
|                 if (Boolean(this.resumingFrom)) {
 | |
|                     // TODO: This is creating a new object every frame
 | |
|                     this.target = this.applyTransform(this.layout.layoutBox);
 | |
|                 }
 | |
|                 else {
 | |
|                     copyBoxInto(this.target, this.layout.layoutBox);
 | |
|                 }
 | |
|                 indexLegacy.applyBoxDelta(this.target, this.targetDelta);
 | |
|             }
 | |
|             else {
 | |
|                 /**
 | |
|                  * If no target, use own layout as target
 | |
|                  */
 | |
|                 copyBoxInto(this.target, this.layout.layoutBox);
 | |
|             }
 | |
|             /**
 | |
|              * If we've been told to attempt to resolve a relative target, do so.
 | |
|              */
 | |
|             if (this.attemptToResolveRelativeTarget) {
 | |
|                 this.attemptToResolveRelativeTarget = false;
 | |
|                 const relativeParent = this.getClosestProjectingParent();
 | |
|                 if (relativeParent &&
 | |
|                     Boolean(relativeParent.resumingFrom) ===
 | |
|                         Boolean(this.resumingFrom) &&
 | |
|                     !relativeParent.options.layoutScroll &&
 | |
|                     relativeParent.target &&
 | |
|                     this.animationProgress !== 1) {
 | |
|                     this.relativeParent = relativeParent;
 | |
|                     this.forceRelativeParentToResolveTarget();
 | |
|                     this.relativeTarget = indexLegacy.createBox();
 | |
|                     this.relativeTargetOrigin = indexLegacy.createBox();
 | |
|                     calcRelativePosition(this.relativeTargetOrigin, this.target, relativeParent.target);
 | |
|                     copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
 | |
|                 }
 | |
|                 else {
 | |
|                     this.relativeParent = this.relativeTarget = undefined;
 | |
|                 }
 | |
|             }
 | |
|             /**
 | |
|              * Increase debug counter for resolved target deltas
 | |
|              */
 | |
|             projectionFrameData.resolvedTargetDeltas++;
 | |
|         }
 | |
|         getClosestProjectingParent() {
 | |
|             if (!this.parent ||
 | |
|                 indexLegacy.hasScale(this.parent.latestValues) ||
 | |
|                 indexLegacy.has2DTranslate(this.parent.latestValues)) {
 | |
|                 return undefined;
 | |
|             }
 | |
|             if (this.parent.isProjecting()) {
 | |
|                 return this.parent;
 | |
|             }
 | |
|             else {
 | |
|                 return this.parent.getClosestProjectingParent();
 | |
|             }
 | |
|         }
 | |
|         isProjecting() {
 | |
|             return Boolean((this.relativeTarget ||
 | |
|                 this.targetDelta ||
 | |
|                 this.options.layoutRoot) &&
 | |
|                 this.layout);
 | |
|         }
 | |
|         calcProjection() {
 | |
|             var _a;
 | |
|             const lead = this.getLead();
 | |
|             const isShared = Boolean(this.resumingFrom) || this !== lead;
 | |
|             let canSkip = true;
 | |
|             /**
 | |
|              * If this is a normal layout animation and neither this node nor its nearest projecting
 | |
|              * is dirty then we can't skip.
 | |
|              */
 | |
|             if (this.isProjectionDirty || ((_a = this.parent) === null || _a === void 0 ? void 0 : _a.isProjectionDirty)) {
 | |
|                 canSkip = false;
 | |
|             }
 | |
|             /**
 | |
|              * If this is a shared layout animation and this node's shared projection is dirty then
 | |
|              * we can't skip.
 | |
|              */
 | |
|             if (isShared &&
 | |
|                 (this.isSharedProjectionDirty || this.isTransformDirty)) {
 | |
|                 canSkip = false;
 | |
|             }
 | |
|             /**
 | |
|              * If we have resolved the target this frame we must recalculate the
 | |
|              * projection to ensure it visually represents the internal calculations.
 | |
|              */
 | |
|             if (this.resolvedRelativeTargetAt === indexLegacy.frameData.timestamp) {
 | |
|                 canSkip = false;
 | |
|             }
 | |
|             if (canSkip)
 | |
|                 return;
 | |
|             const { layout, layoutId } = this.options;
 | |
|             /**
 | |
|              * If this section of the tree isn't animating we can
 | |
|              * delete our target sources for the following frame.
 | |
|              */
 | |
|             this.isTreeAnimating = Boolean((this.parent && this.parent.isTreeAnimating) ||
 | |
|                 this.currentAnimation ||
 | |
|                 this.pendingAnimation);
 | |
|             if (!this.isTreeAnimating) {
 | |
|                 this.targetDelta = this.relativeTarget = undefined;
 | |
|             }
 | |
|             if (!this.layout || !(layout || layoutId))
 | |
|                 return;
 | |
|             /**
 | |
|              * Reset the corrected box with the latest values from box, as we're then going
 | |
|              * to perform mutative operations on it.
 | |
|              */
 | |
|             copyBoxInto(this.layoutCorrected, this.layout.layoutBox);
 | |
|             /**
 | |
|              * Record previous tree scales before updating.
 | |
|              */
 | |
|             const prevTreeScaleX = this.treeScale.x;
 | |
|             const prevTreeScaleY = this.treeScale.y;
 | |
|             /**
 | |
|              * Apply all the parent deltas to this box to produce the corrected box. This
 | |
|              * is the layout box, as it will appear on screen as a result of the transforms of its parents.
 | |
|              */
 | |
|             indexLegacy.applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared);
 | |
|             /**
 | |
|              * If this layer needs to perform scale correction but doesn't have a target,
 | |
|              * use the layout as the target.
 | |
|              */
 | |
|             if (lead.layout &&
 | |
|                 !lead.target &&
 | |
|                 (this.treeScale.x !== 1 || this.treeScale.y !== 1)) {
 | |
|                 lead.target = lead.layout.layoutBox;
 | |
|             }
 | |
|             const { target } = lead;
 | |
|             if (!target) {
 | |
|                 /**
 | |
|                  * If we don't have a target to project into, but we were previously
 | |
|                  * projecting, we want to remove the stored transform and schedule
 | |
|                  * a render to ensure the elements reflect the removed transform.
 | |
|                  */
 | |
|                 if (this.projectionTransform) {
 | |
|                     this.projectionDelta = indexLegacy.createDelta();
 | |
|                     this.projectionTransform = "none";
 | |
|                     this.scheduleRender();
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             if (!this.projectionDelta) {
 | |
|                 this.projectionDelta = indexLegacy.createDelta();
 | |
|                 this.projectionDeltaWithTransform = indexLegacy.createDelta();
 | |
|             }
 | |
|             const prevProjectionTransform = this.projectionTransform;
 | |
|             /**
 | |
|              * Update the delta between the corrected box and the target box before user-set transforms were applied.
 | |
|              * This will allow us to calculate the corrected borderRadius and boxShadow to compensate
 | |
|              * for our layout reprojection, but still allow them to be scaled correctly by the user.
 | |
|              * It might be that to simplify this we may want to accept that user-set scale is also corrected
 | |
|              * and we wouldn't have to keep and calc both deltas, OR we could support a user setting
 | |
|              * to allow people to choose whether these styles are corrected based on just the
 | |
|              * layout reprojection or the final bounding box.
 | |
|              */
 | |
|             calcBoxDelta(this.projectionDelta, this.layoutCorrected, target, this.latestValues);
 | |
|             this.projectionTransform = buildProjectionTransform(this.projectionDelta, this.treeScale);
 | |
|             if (this.projectionTransform !== prevProjectionTransform ||
 | |
|                 this.treeScale.x !== prevTreeScaleX ||
 | |
|                 this.treeScale.y !== prevTreeScaleY) {
 | |
|                 this.hasProjected = true;
 | |
|                 this.scheduleRender();
 | |
|                 this.notifyListeners("projectionUpdate", target);
 | |
|             }
 | |
|             /**
 | |
|              * Increase debug counter for recalculated projections
 | |
|              */
 | |
|             projectionFrameData.recalculatedProjection++;
 | |
|         }
 | |
|         hide() {
 | |
|             this.isVisible = false;
 | |
|             // TODO: Schedule render
 | |
|         }
 | |
|         show() {
 | |
|             this.isVisible = true;
 | |
|             // TODO: Schedule render
 | |
|         }
 | |
|         scheduleRender(notifyAll = true) {
 | |
|             this.options.scheduleRender && this.options.scheduleRender();
 | |
|             if (notifyAll) {
 | |
|                 const stack = this.getStack();
 | |
|                 stack && stack.scheduleRender();
 | |
|             }
 | |
|             if (this.resumingFrom && !this.resumingFrom.instance) {
 | |
|                 this.resumingFrom = undefined;
 | |
|             }
 | |
|         }
 | |
|         setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
 | |
|             const snapshot = this.snapshot;
 | |
|             const snapshotLatestValues = snapshot
 | |
|                 ? snapshot.latestValues
 | |
|                 : {};
 | |
|             const mixedValues = { ...this.latestValues };
 | |
|             const targetDelta = indexLegacy.createDelta();
 | |
|             if (!this.relativeParent ||
 | |
|                 !this.relativeParent.options.layoutRoot) {
 | |
|                 this.relativeTarget = this.relativeTargetOrigin = undefined;
 | |
|             }
 | |
|             this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
 | |
|             const relativeLayout = indexLegacy.createBox();
 | |
|             const snapshotSource = snapshot ? snapshot.source : undefined;
 | |
|             const layoutSource = this.layout ? this.layout.source : undefined;
 | |
|             const isSharedLayoutAnimation = snapshotSource !== layoutSource;
 | |
|             const stack = this.getStack();
 | |
|             const isOnlyMember = !stack || stack.members.length <= 1;
 | |
|             const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
 | |
|                 !isOnlyMember &&
 | |
|                 this.options.crossfade === true &&
 | |
|                 !this.path.some(hasOpacityCrossfade));
 | |
|             this.animationProgress = 0;
 | |
|             let prevRelativeTarget;
 | |
|             this.mixTargetDelta = (latest) => {
 | |
|                 const progress = latest / 1000;
 | |
|                 mixAxisDelta(targetDelta.x, delta.x, progress);
 | |
|                 mixAxisDelta(targetDelta.y, delta.y, progress);
 | |
|                 this.setTargetDelta(targetDelta);
 | |
|                 if (this.relativeTarget &&
 | |
|                     this.relativeTargetOrigin &&
 | |
|                     this.layout &&
 | |
|                     this.relativeParent &&
 | |
|                     this.relativeParent.layout) {
 | |
|                     calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox);
 | |
|                     mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress);
 | |
|                     /**
 | |
|                      * If this is an unchanged relative target we can consider the
 | |
|                      * projection not dirty.
 | |
|                      */
 | |
|                     if (prevRelativeTarget &&
 | |
|                         boxEquals(this.relativeTarget, prevRelativeTarget)) {
 | |
|                         this.isProjectionDirty = false;
 | |
|                     }
 | |
|                     if (!prevRelativeTarget)
 | |
|                         prevRelativeTarget = indexLegacy.createBox();
 | |
|                     copyBoxInto(prevRelativeTarget, this.relativeTarget);
 | |
|                 }
 | |
|                 if (isSharedLayoutAnimation) {
 | |
|                     this.animationValues = mixedValues;
 | |
|                     mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember);
 | |
|                 }
 | |
|                 this.root.scheduleUpdateProjection();
 | |
|                 this.scheduleRender();
 | |
|                 this.animationProgress = progress;
 | |
|             };
 | |
|             this.mixTargetDelta(this.options.layoutRoot ? 1000 : 0);
 | |
|         }
 | |
|         startAnimation(options) {
 | |
|             this.notifyListeners("animationStart");
 | |
|             this.currentAnimation && this.currentAnimation.stop();
 | |
|             if (this.resumingFrom && this.resumingFrom.currentAnimation) {
 | |
|                 this.resumingFrom.currentAnimation.stop();
 | |
|             }
 | |
|             if (this.pendingAnimation) {
 | |
|                 indexLegacy.cancelFrame(this.pendingAnimation);
 | |
|                 this.pendingAnimation = undefined;
 | |
|             }
 | |
|             /**
 | |
|              * Start the animation in the next frame to have a frame with progress 0,
 | |
|              * where the target is the same as when the animation started, so we can
 | |
|              * calculate the relative positions correctly for instant transitions.
 | |
|              */
 | |
|             this.pendingAnimation = indexLegacy.frame.update(() => {
 | |
|                 globalProjectionState.hasAnimatedSinceResize = true;
 | |
|                 this.currentAnimation = indexLegacy.animateSingleValue(0, animationTarget, {
 | |
|                     ...options,
 | |
|                     onUpdate: (latest) => {
 | |
|                         this.mixTargetDelta(latest);
 | |
|                         options.onUpdate && options.onUpdate(latest);
 | |
|                     },
 | |
|                     onComplete: () => {
 | |
|                         options.onComplete && options.onComplete();
 | |
|                         this.completeAnimation();
 | |
|                     },
 | |
|                 });
 | |
|                 if (this.resumingFrom) {
 | |
|                     this.resumingFrom.currentAnimation = this.currentAnimation;
 | |
|                 }
 | |
|                 this.pendingAnimation = undefined;
 | |
|             });
 | |
|         }
 | |
|         completeAnimation() {
 | |
|             if (this.resumingFrom) {
 | |
|                 this.resumingFrom.currentAnimation = undefined;
 | |
|                 this.resumingFrom.preserveOpacity = undefined;
 | |
|             }
 | |
|             const stack = this.getStack();
 | |
|             stack && stack.exitAnimationComplete();
 | |
|             this.resumingFrom =
 | |
|                 this.currentAnimation =
 | |
|                     this.animationValues =
 | |
|                         undefined;
 | |
|             this.notifyListeners("animationComplete");
 | |
|         }
 | |
|         finishAnimation() {
 | |
|             if (this.currentAnimation) {
 | |
|                 this.mixTargetDelta && this.mixTargetDelta(animationTarget);
 | |
|                 this.currentAnimation.stop();
 | |
|             }
 | |
|             this.completeAnimation();
 | |
|         }
 | |
|         applyTransformsToTarget() {
 | |
|             const lead = this.getLead();
 | |
|             let { targetWithTransforms, target, layout, latestValues } = lead;
 | |
|             if (!targetWithTransforms || !target || !layout)
 | |
|                 return;
 | |
|             /**
 | |
|              * If we're only animating position, and this element isn't the lead element,
 | |
|              * then instead of projecting into the lead box we instead want to calculate
 | |
|              * a new target that aligns the two boxes but maintains the layout shape.
 | |
|              */
 | |
|             if (this !== lead &&
 | |
|                 this.layout &&
 | |
|                 layout &&
 | |
|                 shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) {
 | |
|                 target = this.target || indexLegacy.createBox();
 | |
|                 const xLength = calcLength(this.layout.layoutBox.x);
 | |
|                 target.x.min = lead.target.x.min;
 | |
|                 target.x.max = target.x.min + xLength;
 | |
|                 const yLength = calcLength(this.layout.layoutBox.y);
 | |
|                 target.y.min = lead.target.y.min;
 | |
|                 target.y.max = target.y.min + yLength;
 | |
|             }
 | |
|             copyBoxInto(targetWithTransforms, target);
 | |
|             /**
 | |
|              * Apply the latest user-set transforms to the targetBox to produce the targetBoxFinal.
 | |
|              * This is the final box that we will then project into by calculating a transform delta and
 | |
|              * applying it to the corrected box.
 | |
|              */
 | |
|             indexLegacy.transformBox(targetWithTransforms, latestValues);
 | |
|             /**
 | |
|              * Update the delta between the corrected box and the final target box, after
 | |
|              * user-set transforms are applied to it. This will be used by the renderer to
 | |
|              * create a transform style that will reproject the element from its layout layout
 | |
|              * into the desired bounding box.
 | |
|              */
 | |
|             calcBoxDelta(this.projectionDeltaWithTransform, this.layoutCorrected, targetWithTransforms, latestValues);
 | |
|         }
 | |
|         registerSharedNode(layoutId, node) {
 | |
|             if (!this.sharedNodes.has(layoutId)) {
 | |
|                 this.sharedNodes.set(layoutId, new NodeStack());
 | |
|             }
 | |
|             const stack = this.sharedNodes.get(layoutId);
 | |
|             stack.add(node);
 | |
|             const config = node.options.initialPromotionConfig;
 | |
|             node.promote({
 | |
|                 transition: config ? config.transition : undefined,
 | |
|                 preserveFollowOpacity: config && config.shouldPreserveFollowOpacity
 | |
|                     ? config.shouldPreserveFollowOpacity(node)
 | |
|                     : undefined,
 | |
|             });
 | |
|         }
 | |
|         isLead() {
 | |
|             const stack = this.getStack();
 | |
|             return stack ? stack.lead === this : true;
 | |
|         }
 | |
|         getLead() {
 | |
|             var _a;
 | |
|             const { layoutId } = this.options;
 | |
|             return layoutId ? ((_a = this.getStack()) === null || _a === void 0 ? void 0 : _a.lead) || this : this;
 | |
|         }
 | |
|         getPrevLead() {
 | |
|             var _a;
 | |
|             const { layoutId } = this.options;
 | |
|             return layoutId ? (_a = this.getStack()) === null || _a === void 0 ? void 0 : _a.prevLead : undefined;
 | |
|         }
 | |
|         getStack() {
 | |
|             const { layoutId } = this.options;
 | |
|             if (layoutId)
 | |
|                 return this.root.sharedNodes.get(layoutId);
 | |
|         }
 | |
|         promote({ needsReset, transition, preserveFollowOpacity, } = {}) {
 | |
|             const stack = this.getStack();
 | |
|             if (stack)
 | |
|                 stack.promote(this, preserveFollowOpacity);
 | |
|             if (needsReset) {
 | |
|                 this.projectionDelta = undefined;
 | |
|                 this.needsReset = true;
 | |
|             }
 | |
|             if (transition)
 | |
|                 this.setOptions({ transition });
 | |
|         }
 | |
|         relegate() {
 | |
|             const stack = this.getStack();
 | |
|             if (stack) {
 | |
|                 return stack.relegate(this);
 | |
|             }
 | |
|             else {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         resetRotation() {
 | |
|             const { visualElement } = this.options;
 | |
|             if (!visualElement)
 | |
|                 return;
 | |
|             // If there's no detected rotation values, we can early return without a forced render.
 | |
|             let hasRotate = false;
 | |
|             /**
 | |
|              * An unrolled check for rotation values. Most elements don't have any rotation and
 | |
|              * skipping the nested loop and new object creation is 50% faster.
 | |
|              */
 | |
|             const { latestValues } = visualElement;
 | |
|             if (latestValues.rotate ||
 | |
|                 latestValues.rotateX ||
 | |
|                 latestValues.rotateY ||
 | |
|                 latestValues.rotateZ) {
 | |
|                 hasRotate = true;
 | |
|             }
 | |
|             // If there's no rotation values, we don't need to do any more.
 | |
|             if (!hasRotate)
 | |
|                 return;
 | |
|             const resetValues = {};
 | |
|             // Check the rotate value of all axes and reset to 0
 | |
|             for (let i = 0; i < transformAxes.length; i++) {
 | |
|                 const key = "rotate" + transformAxes[i];
 | |
|                 // Record the rotation and then temporarily set it to 0
 | |
|                 if (latestValues[key]) {
 | |
|                     resetValues[key] = latestValues[key];
 | |
|                     visualElement.setStaticValue(key, 0);
 | |
|                 }
 | |
|             }
 | |
|             // Force a render of this element to apply the transform with all rotations
 | |
|             // set to 0.
 | |
|             visualElement.render();
 | |
|             // Put back all the values we reset
 | |
|             for (const key in resetValues) {
 | |
|                 visualElement.setStaticValue(key, resetValues[key]);
 | |
|             }
 | |
|             // Schedule a render for the next frame. This ensures we won't visually
 | |
|             // see the element with the reset rotate value applied.
 | |
|             visualElement.scheduleRender();
 | |
|         }
 | |
|         getProjectionStyles(styleProp) {
 | |
|             var _a, _b;
 | |
|             if (!this.instance || this.isSVG)
 | |
|                 return undefined;
 | |
|             if (!this.isVisible) {
 | |
|                 return hiddenVisibility;
 | |
|             }
 | |
|             const styles = {
 | |
|                 visibility: "",
 | |
|             };
 | |
|             const transformTemplate = this.getTransformTemplate();
 | |
|             if (this.needsReset) {
 | |
|                 this.needsReset = false;
 | |
|                 styles.opacity = "";
 | |
|                 styles.pointerEvents =
 | |
|                     resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "";
 | |
|                 styles.transform = transformTemplate
 | |
|                     ? transformTemplate(this.latestValues, "")
 | |
|                     : "none";
 | |
|                 return styles;
 | |
|             }
 | |
|             const lead = this.getLead();
 | |
|             if (!this.projectionDelta || !this.layout || !lead.target) {
 | |
|                 const emptyStyles = {};
 | |
|                 if (this.options.layoutId) {
 | |
|                     emptyStyles.opacity =
 | |
|                         this.latestValues.opacity !== undefined
 | |
|                             ? this.latestValues.opacity
 | |
|                             : 1;
 | |
|                     emptyStyles.pointerEvents =
 | |
|                         resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || "";
 | |
|                 }
 | |
|                 if (this.hasProjected && !indexLegacy.hasTransform(this.latestValues)) {
 | |
|                     emptyStyles.transform = transformTemplate
 | |
|                         ? transformTemplate({}, "")
 | |
|                         : "none";
 | |
|                     this.hasProjected = false;
 | |
|                 }
 | |
|                 return emptyStyles;
 | |
|             }
 | |
|             const valuesToRender = lead.animationValues || lead.latestValues;
 | |
|             this.applyTransformsToTarget();
 | |
|             styles.transform = buildProjectionTransform(this.projectionDeltaWithTransform, this.treeScale, valuesToRender);
 | |
|             if (transformTemplate) {
 | |
|                 styles.transform = transformTemplate(valuesToRender, styles.transform);
 | |
|             }
 | |
|             const { x, y } = this.projectionDelta;
 | |
|             styles.transformOrigin = `${x.origin * 100}% ${y.origin * 100}% 0`;
 | |
|             if (lead.animationValues) {
 | |
|                 /**
 | |
|                  * If the lead component is animating, assign this either the entering/leaving
 | |
|                  * opacity
 | |
|                  */
 | |
|                 styles.opacity =
 | |
|                     lead === this
 | |
|                         ? (_b = (_a = valuesToRender.opacity) !== null && _a !== void 0 ? _a : this.latestValues.opacity) !== null && _b !== void 0 ? _b : 1
 | |
|                         : this.preserveOpacity
 | |
|                             ? this.latestValues.opacity
 | |
|                             : valuesToRender.opacityExit;
 | |
|             }
 | |
|             else {
 | |
|                 /**
 | |
|                  * Or we're not animating at all, set the lead component to its layout
 | |
|                  * opacity and other components to hidden.
 | |
|                  */
 | |
|                 styles.opacity =
 | |
|                     lead === this
 | |
|                         ? valuesToRender.opacity !== undefined
 | |
|                             ? valuesToRender.opacity
 | |
|                             : ""
 | |
|                         : valuesToRender.opacityExit !== undefined
 | |
|                             ? valuesToRender.opacityExit
 | |
|                             : 0;
 | |
|             }
 | |
|             /**
 | |
|              * Apply scale correction
 | |
|              */
 | |
|             for (const key in indexLegacy.scaleCorrectors) {
 | |
|                 if (valuesToRender[key] === undefined)
 | |
|                     continue;
 | |
|                 const { correct, applyTo } = indexLegacy.scaleCorrectors[key];
 | |
|                 /**
 | |
|                  * Only apply scale correction to the value if we have an
 | |
|                  * active projection transform. Otherwise these values become
 | |
|                  * vulnerable to distortion if the element changes size without
 | |
|                  * a corresponding layout animation.
 | |
|                  */
 | |
|                 const corrected = styles.transform === "none"
 | |
|                     ? valuesToRender[key]
 | |
|                     : correct(valuesToRender[key], lead);
 | |
|                 if (applyTo) {
 | |
|                     const num = applyTo.length;
 | |
|                     for (let i = 0; i < num; i++) {
 | |
|                         styles[applyTo[i]] = corrected;
 | |
|                     }
 | |
|                 }
 | |
|                 else {
 | |
|                     styles[key] = corrected;
 | |
|                 }
 | |
|             }
 | |
|             /**
 | |
|              * Disable pointer events on follow components. This is to ensure
 | |
|              * that if a follow component covers a lead component it doesn't block
 | |
|              * pointer events on the lead.
 | |
|              */
 | |
|             if (this.options.layoutId) {
 | |
|                 styles.pointerEvents =
 | |
|                     lead === this
 | |
|                         ? resolveMotionValue(styleProp === null || styleProp === void 0 ? void 0 : styleProp.pointerEvents) || ""
 | |
|                         : "none";
 | |
|             }
 | |
|             return styles;
 | |
|         }
 | |
|         clearSnapshot() {
 | |
|             this.resumeFrom = this.snapshot = undefined;
 | |
|         }
 | |
|         // Only run on root
 | |
|         resetTree() {
 | |
|             this.root.nodes.forEach((node) => { var _a; return (_a = node.currentAnimation) === null || _a === void 0 ? void 0 : _a.stop(); });
 | |
|             this.root.nodes.forEach(clearMeasurements);
 | |
|             this.root.sharedNodes.clear();
 | |
|         }
 | |
|     };
 | |
| }
 | |
| function updateLayout(node) {
 | |
|     node.updateLayout();
 | |
| }
 | |
| function notifyLayoutUpdate(node) {
 | |
|     var _a;
 | |
|     const snapshot = ((_a = node.resumeFrom) === null || _a === void 0 ? void 0 : _a.snapshot) || node.snapshot;
 | |
|     if (node.isLead() &&
 | |
|         node.layout &&
 | |
|         snapshot &&
 | |
|         node.hasListeners("didUpdate")) {
 | |
|         const { layoutBox: layout, measuredBox: measuredLayout } = node.layout;
 | |
|         const { animationType } = node.options;
 | |
|         const isShared = snapshot.source !== node.layout.source;
 | |
|         // TODO Maybe we want to also resize the layout snapshot so we don't trigger
 | |
|         // animations for instance if layout="size" and an element has only changed position
 | |
|         if (animationType === "size") {
 | |
|             eachAxis((axis) => {
 | |
|                 const axisSnapshot = isShared
 | |
|                     ? snapshot.measuredBox[axis]
 | |
|                     : snapshot.layoutBox[axis];
 | |
|                 const length = calcLength(axisSnapshot);
 | |
|                 axisSnapshot.min = layout[axis].min;
 | |
|                 axisSnapshot.max = axisSnapshot.min + length;
 | |
|             });
 | |
|         }
 | |
|         else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
 | |
|             eachAxis((axis) => {
 | |
|                 const axisSnapshot = isShared
 | |
|                     ? snapshot.measuredBox[axis]
 | |
|                     : snapshot.layoutBox[axis];
 | |
|                 const length = calcLength(layout[axis]);
 | |
|                 axisSnapshot.max = axisSnapshot.min + length;
 | |
|                 /**
 | |
|                  * Ensure relative target gets resized and rerendererd
 | |
|                  */
 | |
|                 if (node.relativeTarget && !node.currentAnimation) {
 | |
|                     node.isProjectionDirty = true;
 | |
|                     node.relativeTarget[axis].max =
 | |
|                         node.relativeTarget[axis].min + length;
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         const layoutDelta = indexLegacy.createDelta();
 | |
|         calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
 | |
|         const visualDelta = indexLegacy.createDelta();
 | |
|         if (isShared) {
 | |
|             calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
 | |
|         }
 | |
|         else {
 | |
|             calcBoxDelta(visualDelta, layout, snapshot.layoutBox);
 | |
|         }
 | |
|         const hasLayoutChanged = !isDeltaZero(layoutDelta);
 | |
|         let hasRelativeTargetChanged = false;
 | |
|         if (!node.resumeFrom) {
 | |
|             const relativeParent = node.getClosestProjectingParent();
 | |
|             /**
 | |
|              * If the relativeParent is itself resuming from a different element then
 | |
|              * the relative snapshot is not relavent
 | |
|              */
 | |
|             if (relativeParent && !relativeParent.resumeFrom) {
 | |
|                 const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent;
 | |
|                 if (parentSnapshot && parentLayout) {
 | |
|                     const relativeSnapshot = indexLegacy.createBox();
 | |
|                     calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox);
 | |
|                     const relativeLayout = indexLegacy.createBox();
 | |
|                     calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox);
 | |
|                     if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) {
 | |
|                         hasRelativeTargetChanged = true;
 | |
|                     }
 | |
|                     if (relativeParent.options.layoutRoot) {
 | |
|                         node.relativeTarget = relativeLayout;
 | |
|                         node.relativeTargetOrigin = relativeSnapshot;
 | |
|                         node.relativeParent = relativeParent;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         node.notifyListeners("didUpdate", {
 | |
|             layout,
 | |
|             snapshot,
 | |
|             delta: visualDelta,
 | |
|             layoutDelta,
 | |
|             hasLayoutChanged,
 | |
|             hasRelativeTargetChanged,
 | |
|         });
 | |
|     }
 | |
|     else if (node.isLead()) {
 | |
|         const { onExitComplete } = node.options;
 | |
|         onExitComplete && onExitComplete();
 | |
|     }
 | |
|     /**
 | |
|      * Clearing transition
 | |
|      * TODO: Investigate why this transition is being passed in as {type: false } from Framer
 | |
|      * and why we need it at all
 | |
|      */
 | |
|     node.options.transition = undefined;
 | |
| }
 | |
| function propagateDirtyNodes(node) {
 | |
|     /**
 | |
|      * Increase debug counter for nodes encountered this frame
 | |
|      */
 | |
|     projectionFrameData.totalNodes++;
 | |
|     if (!node.parent)
 | |
|         return;
 | |
|     /**
 | |
|      * If this node isn't projecting, propagate isProjectionDirty. It will have
 | |
|      * no performance impact but it will allow the next child that *is* projecting
 | |
|      * but *isn't* dirty to just check its parent to see if *any* ancestor needs
 | |
|      * correcting.
 | |
|      */
 | |
|     if (!node.isProjecting()) {
 | |
|         node.isProjectionDirty = node.parent.isProjectionDirty;
 | |
|     }
 | |
|     /**
 | |
|      * Propagate isSharedProjectionDirty and isTransformDirty
 | |
|      * throughout the whole tree. A future revision can take another look at
 | |
|      * this but for safety we still recalcualte shared nodes.
 | |
|      */
 | |
|     node.isSharedProjectionDirty || (node.isSharedProjectionDirty = Boolean(node.isProjectionDirty ||
 | |
|         node.parent.isProjectionDirty ||
 | |
|         node.parent.isSharedProjectionDirty));
 | |
|     node.isTransformDirty || (node.isTransformDirty = node.parent.isTransformDirty);
 | |
| }
 | |
| function cleanDirtyNodes(node) {
 | |
|     node.isProjectionDirty =
 | |
|         node.isSharedProjectionDirty =
 | |
|             node.isTransformDirty =
 | |
|                 false;
 | |
| }
 | |
| function clearSnapshot(node) {
 | |
|     node.clearSnapshot();
 | |
| }
 | |
| function clearMeasurements(node) {
 | |
|     node.clearMeasurements();
 | |
| }
 | |
| function clearIsLayoutDirty(node) {
 | |
|     node.isLayoutDirty = false;
 | |
| }
 | |
| function resetTransformStyle(node) {
 | |
|     const { visualElement } = node.options;
 | |
|     if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) {
 | |
|         visualElement.notify("BeforeLayoutMeasure");
 | |
|     }
 | |
|     node.resetTransform();
 | |
| }
 | |
| function finishAnimation(node) {
 | |
|     node.finishAnimation();
 | |
|     node.targetDelta = node.relativeTarget = node.target = undefined;
 | |
|     node.isProjectionDirty = true;
 | |
| }
 | |
| function resolveTargetDelta(node) {
 | |
|     node.resolveTargetDelta();
 | |
| }
 | |
| function calcProjection(node) {
 | |
|     node.calcProjection();
 | |
| }
 | |
| function resetRotation(node) {
 | |
|     node.resetRotation();
 | |
| }
 | |
| function removeLeadSnapshots(stack) {
 | |
|     stack.removeLeadSnapshot();
 | |
| }
 | |
| function mixAxisDelta(output, delta, p) {
 | |
|     output.translate = indexLegacy.mix(delta.translate, 0, p);
 | |
|     output.scale = indexLegacy.mix(delta.scale, 1, p);
 | |
|     output.origin = delta.origin;
 | |
|     output.originPoint = delta.originPoint;
 | |
| }
 | |
| function mixAxis(output, from, to, p) {
 | |
|     output.min = indexLegacy.mix(from.min, to.min, p);
 | |
|     output.max = indexLegacy.mix(from.max, to.max, p);
 | |
| }
 | |
| function mixBox(output, from, to, p) {
 | |
|     mixAxis(output.x, from.x, to.x, p);
 | |
|     mixAxis(output.y, from.y, to.y, p);
 | |
| }
 | |
| function hasOpacityCrossfade(node) {
 | |
|     return (node.animationValues && node.animationValues.opacityExit !== undefined);
 | |
| }
 | |
| const defaultLayoutTransition = {
 | |
|     duration: 0.45,
 | |
|     ease: [0.4, 0, 0.1, 1],
 | |
| };
 | |
| const userAgentContains = (string) => typeof navigator !== "undefined" &&
 | |
|     navigator.userAgent.toLowerCase().includes(string);
 | |
| /**
 | |
|  * Measured bounding boxes must be rounded in Safari and
 | |
|  * left untouched in Chrome, otherwise non-integer layouts within scaled-up elements
 | |
|  * can appear to jump.
 | |
|  */
 | |
| const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
 | |
|     ? Math.round
 | |
|     : indexLegacy.noop;
 | |
| function roundAxis(axis) {
 | |
|     // Round to the nearest .5 pixels to support subpixel layouts
 | |
|     axis.min = roundPoint(axis.min);
 | |
|     axis.max = roundPoint(axis.max);
 | |
| }
 | |
| function roundBox(box) {
 | |
|     roundAxis(box.x);
 | |
|     roundAxis(box.y);
 | |
| }
 | |
| function shouldAnimatePositionOnly(animationType, snapshot, layout) {
 | |
|     return (animationType === "position" ||
 | |
|         (animationType === "preserve-aspect" &&
 | |
|             !isNear(aspectRatio(snapshot), aspectRatio(layout), 0.2)));
 | |
| }
 | |
| 
 | |
| const DocumentProjectionNode = createProjectionNode({
 | |
|     attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
 | |
|     measureScroll: () => ({
 | |
|         x: document.documentElement.scrollLeft || document.body.scrollLeft,
 | |
|         y: document.documentElement.scrollTop || document.body.scrollTop,
 | |
|     }),
 | |
|     checkIsScrollRoot: () => true,
 | |
| });
 | |
| 
 | |
| const rootProjectionNode = {
 | |
|     current: undefined,
 | |
| };
 | |
| const HTMLProjectionNode = createProjectionNode({
 | |
|     measureScroll: (instance) => ({
 | |
|         x: instance.scrollLeft,
 | |
|         y: instance.scrollTop,
 | |
|     }),
 | |
|     defaultParent: () => {
 | |
|         if (!rootProjectionNode.current) {
 | |
|             const documentNode = new DocumentProjectionNode({});
 | |
|             documentNode.mount(window);
 | |
|             documentNode.setOptions({ layoutScroll: true });
 | |
|             rootProjectionNode.current = documentNode;
 | |
|         }
 | |
|         return rootProjectionNode.current;
 | |
|     },
 | |
|     resetTransform: (instance, value) => {
 | |
|         instance.style.transform = value !== undefined ? value : "none";
 | |
|     },
 | |
|     checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
 | |
| });
 | |
| 
 | |
| const notify = (node) => !node.isLayoutDirty && node.willUpdate(false);
 | |
| function nodeGroup() {
 | |
|     const nodes = new Set();
 | |
|     const subscriptions = new WeakMap();
 | |
|     const dirtyAll = () => nodes.forEach(notify);
 | |
|     return {
 | |
|         add: (node) => {
 | |
|             nodes.add(node);
 | |
|             subscriptions.set(node, node.addEventListener("willUpdate", dirtyAll));
 | |
|         },
 | |
|         remove: (node) => {
 | |
|             nodes.delete(node);
 | |
|             const unsubscribe = subscriptions.get(node);
 | |
|             if (unsubscribe) {
 | |
|                 unsubscribe();
 | |
|                 subscriptions.delete(node);
 | |
|             }
 | |
|             dirtyAll();
 | |
|         },
 | |
|         dirty: dirtyAll,
 | |
|     };
 | |
| }
 | |
| 
 | |
| function pixelsToPercent(pixels, axis) {
 | |
|     if (axis.max === axis.min)
 | |
|         return 0;
 | |
|     return (pixels / (axis.max - axis.min)) * 100;
 | |
| }
 | |
| /**
 | |
|  * We always correct borderRadius as a percentage rather than pixels to reduce paints.
 | |
|  * For example, if you are projecting a box that is 100px wide with a 10px borderRadius
 | |
|  * into a box that is 200px wide with a 20px borderRadius, that is actually a 10%
 | |
|  * borderRadius in both states. If we animate between the two in pixels that will trigger
 | |
|  * a paint each time. If we animate between the two in percentage we'll avoid a paint.
 | |
|  */
 | |
| const correctBorderRadius = {
 | |
|     correct: (latest, node) => {
 | |
|         if (!node.target)
 | |
|             return latest;
 | |
|         /**
 | |
|          * If latest is a string, if it's a percentage we can return immediately as it's
 | |
|          * going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number.
 | |
|          */
 | |
|         if (typeof latest === "string") {
 | |
|             if (indexLegacy.px.test(latest)) {
 | |
|                 latest = parseFloat(latest);
 | |
|             }
 | |
|             else {
 | |
|                 return latest;
 | |
|             }
 | |
|         }
 | |
|         /**
 | |
|          * If latest is a number, it's a pixel value. We use the current viewportBox to calculate that
 | |
|          * pixel value as a percentage of each axis
 | |
|          */
 | |
|         const x = pixelsToPercent(latest, node.target.x);
 | |
|         const y = pixelsToPercent(latest, node.target.y);
 | |
|         return `${x}% ${y}%`;
 | |
|     },
 | |
| };
 | |
| 
 | |
| const correctBoxShadow = {
 | |
|     correct: (latest, { treeScale, projectionDelta }) => {
 | |
|         const original = latest;
 | |
|         const shadow = indexLegacy.complex.parse(latest);
 | |
|         // TODO: Doesn't support multiple shadows
 | |
|         if (shadow.length > 5)
 | |
|             return original;
 | |
|         const template = indexLegacy.complex.createTransformer(latest);
 | |
|         const offset = typeof shadow[0] !== "number" ? 1 : 0;
 | |
|         // Calculate the overall context scale
 | |
|         const xScale = projectionDelta.x.scale * treeScale.x;
 | |
|         const yScale = projectionDelta.y.scale * treeScale.y;
 | |
|         shadow[0 + offset] /= xScale;
 | |
|         shadow[1 + offset] /= yScale;
 | |
|         /**
 | |
|          * Ideally we'd correct x and y scales individually, but because blur and
 | |
|          * spread apply to both we have to take a scale average and apply that instead.
 | |
|          * We could potentially improve the outcome of this by incorporating the ratio between
 | |
|          * the two scales.
 | |
|          */
 | |
|         const averageScale = indexLegacy.mix(xScale, yScale, 0.5);
 | |
|         // Blur
 | |
|         if (typeof shadow[2 + offset] === "number")
 | |
|             shadow[2 + offset] /= averageScale;
 | |
|         // Spread
 | |
|         if (typeof shadow[3 + offset] === "number")
 | |
|             shadow[3 + offset] /= averageScale;
 | |
|         return template(shadow);
 | |
|     },
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * When a component is the child of `AnimatePresence`, it can use `usePresence`
 | |
|  * to access information about whether it's still present in the React tree.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import { usePresence } from "framer-motion"
 | |
|  *
 | |
|  * export const Component = () => {
 | |
|  *   const [isPresent, safeToRemove] = usePresence()
 | |
|  *
 | |
|  *   useEffect(() => {
 | |
|  *     !isPresent && setTimeout(safeToRemove, 1000)
 | |
|  *   }, [isPresent])
 | |
|  *
 | |
|  *   return <div />
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * If `isPresent` is `false`, it means that a component has been removed the tree, but
 | |
|  * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function usePresence() {
 | |
|     const context = React.useContext(PresenceContext);
 | |
|     if (context === null)
 | |
|         return [true, null];
 | |
|     const { isPresent, onExitComplete, register } = context;
 | |
|     // It's safe to call the following hooks conditionally (after an early return) because the context will always
 | |
|     // either be null or non-null for the lifespan of the component.
 | |
|     const id = React.useId();
 | |
|     React.useEffect(() => register(id), []);
 | |
|     const safeToRemove = () => onExitComplete && onExitComplete(id);
 | |
|     return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
 | |
| }
 | |
| /**
 | |
|  * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
 | |
|  * There is no `safeToRemove` function.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import { useIsPresent } from "framer-motion"
 | |
|  *
 | |
|  * export const Component = () => {
 | |
|  *   const isPresent = useIsPresent()
 | |
|  *
 | |
|  *   useEffect(() => {
 | |
|  *     !isPresent && console.log("I've been removed!")
 | |
|  *   }, [isPresent])
 | |
|  *
 | |
|  *   return <div />
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useIsPresent() {
 | |
|     return isPresent(React.useContext(PresenceContext));
 | |
| }
 | |
| function isPresent(context) {
 | |
|     return context === null ? true : context.isPresent;
 | |
| }
 | |
| 
 | |
| class MeasureLayoutWithContext extends React__default["default"].Component {
 | |
|     /**
 | |
|      * This only mounts projection nodes for components that
 | |
|      * need measuring, we might want to do it for all components
 | |
|      * in order to incorporate transforms
 | |
|      */
 | |
|     componentDidMount() {
 | |
|         const { visualElement, layoutGroup, switchLayoutGroup, layoutId } = this.props;
 | |
|         const { projection } = visualElement;
 | |
|         indexLegacy.addScaleCorrector(defaultScaleCorrectors);
 | |
|         if (projection) {
 | |
|             if (layoutGroup.group)
 | |
|                 layoutGroup.group.add(projection);
 | |
|             if (switchLayoutGroup && switchLayoutGroup.register && layoutId) {
 | |
|                 switchLayoutGroup.register(projection);
 | |
|             }
 | |
|             projection.root.didUpdate();
 | |
|             projection.addEventListener("animationComplete", () => {
 | |
|                 this.safeToRemove();
 | |
|             });
 | |
|             projection.setOptions({
 | |
|                 ...projection.options,
 | |
|                 onExitComplete: () => this.safeToRemove(),
 | |
|             });
 | |
|         }
 | |
|         globalProjectionState.hasEverUpdated = true;
 | |
|     }
 | |
|     getSnapshotBeforeUpdate(prevProps) {
 | |
|         const { layoutDependency, visualElement, drag, isPresent } = this.props;
 | |
|         const projection = visualElement.projection;
 | |
|         if (!projection)
 | |
|             return null;
 | |
|         /**
 | |
|          * TODO: We use this data in relegate to determine whether to
 | |
|          * promote a previous element. There's no guarantee its presence data
 | |
|          * will have updated by this point - if a bug like this arises it will
 | |
|          * have to be that we markForRelegation and then find a new lead some other way,
 | |
|          * perhaps in didUpdate
 | |
|          */
 | |
|         projection.isPresent = isPresent;
 | |
|         if (drag ||
 | |
|             prevProps.layoutDependency !== layoutDependency ||
 | |
|             layoutDependency === undefined) {
 | |
|             projection.willUpdate();
 | |
|         }
 | |
|         else {
 | |
|             this.safeToRemove();
 | |
|         }
 | |
|         if (prevProps.isPresent !== isPresent) {
 | |
|             if (isPresent) {
 | |
|                 projection.promote();
 | |
|             }
 | |
|             else if (!projection.relegate()) {
 | |
|                 /**
 | |
|                  * If there's another stack member taking over from this one,
 | |
|                  * it's in charge of the exit animation and therefore should
 | |
|                  * be in charge of the safe to remove. Otherwise we call it here.
 | |
|                  */
 | |
|                 indexLegacy.frame.postRender(() => {
 | |
|                     const stack = projection.getStack();
 | |
|                     if (!stack || !stack.members.length) {
 | |
|                         this.safeToRemove();
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
|     componentDidUpdate() {
 | |
|         const { projection } = this.props.visualElement;
 | |
|         if (projection) {
 | |
|             projection.root.didUpdate();
 | |
|             queueMicrotask(() => {
 | |
|                 if (!projection.currentAnimation && projection.isLead()) {
 | |
|                     this.safeToRemove();
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|     }
 | |
|     componentWillUnmount() {
 | |
|         const { visualElement, layoutGroup, switchLayoutGroup: promoteContext, } = this.props;
 | |
|         const { projection } = visualElement;
 | |
|         if (projection) {
 | |
|             projection.scheduleCheckAfterUnmount();
 | |
|             if (layoutGroup && layoutGroup.group)
 | |
|                 layoutGroup.group.remove(projection);
 | |
|             if (promoteContext && promoteContext.deregister)
 | |
|                 promoteContext.deregister(projection);
 | |
|         }
 | |
|     }
 | |
|     safeToRemove() {
 | |
|         const { safeToRemove } = this.props;
 | |
|         safeToRemove && safeToRemove();
 | |
|     }
 | |
|     render() {
 | |
|         return null;
 | |
|     }
 | |
| }
 | |
| function MeasureLayout(props) {
 | |
|     const [isPresent, safeToRemove] = usePresence();
 | |
|     const layoutGroup = React.useContext(LayoutGroupContext);
 | |
|     return (React__default["default"].createElement(MeasureLayoutWithContext, { ...props, layoutGroup: layoutGroup, switchLayoutGroup: React.useContext(SwitchLayoutGroupContext), isPresent: isPresent, safeToRemove: safeToRemove }));
 | |
| }
 | |
| const defaultScaleCorrectors = {
 | |
|     borderRadius: {
 | |
|         ...correctBorderRadius,
 | |
|         applyTo: [
 | |
|             "borderTopLeftRadius",
 | |
|             "borderTopRightRadius",
 | |
|             "borderBottomLeftRadius",
 | |
|             "borderBottomRightRadius",
 | |
|         ],
 | |
|     },
 | |
|     borderTopLeftRadius: correctBorderRadius,
 | |
|     borderTopRightRadius: correctBorderRadius,
 | |
|     borderBottomLeftRadius: correctBorderRadius,
 | |
|     borderBottomRightRadius: correctBorderRadius,
 | |
|     boxShadow: correctBoxShadow,
 | |
| };
 | |
| 
 | |
| const drag = {
 | |
|     pan: {
 | |
|         Feature: PanGesture,
 | |
|     },
 | |
|     drag: {
 | |
|         Feature: DragGesture,
 | |
|         ProjectionNode: HTMLProjectionNode,
 | |
|         MeasureLayout,
 | |
|     },
 | |
| };
 | |
| 
 | |
| const createDomVisualElement = (Component, options) => {
 | |
|     return isSVGComponent(Component)
 | |
|         ? new indexLegacy.SVGVisualElement(options, { enableHardwareAcceleration: false })
 | |
|         : new indexLegacy.HTMLVisualElement(options, { enableHardwareAcceleration: true });
 | |
| };
 | |
| 
 | |
| const layout = {
 | |
|     layout: {
 | |
|         ProjectionNode: HTMLProjectionNode,
 | |
|         MeasureLayout,
 | |
|     },
 | |
| };
 | |
| 
 | |
| const preloadedFeatures = {
 | |
|     ...animations,
 | |
|     ...gestureAnimations,
 | |
|     ...drag,
 | |
|     ...layout,
 | |
| };
 | |
| /**
 | |
|  * HTML & SVG components, optimised for use with gestures and animation. These can be used as
 | |
|  * drop-in replacements for any HTML & SVG component, all CSS & SVG properties are supported.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| const motion = /*@__PURE__*/ createMotionProxy((Component, config) => createDomMotionConfig(Component, config, preloadedFeatures, createDomVisualElement));
 | |
| /**
 | |
|  * Create a DOM `motion` component with the provided string. This is primarily intended
 | |
|  * as a full alternative to `motion` for consumers who have to support environments that don't
 | |
|  * support `Proxy`.
 | |
|  *
 | |
|  * ```javascript
 | |
|  * import { createDomMotionComponent } from "framer-motion"
 | |
|  *
 | |
|  * const motion = {
 | |
|  *   div: createDomMotionComponent('div')
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function createDomMotionComponent(key) {
 | |
|     return createMotionComponent(createDomMotionConfig(key, { forwardMotionProps: false }, preloadedFeatures, createDomVisualElement));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @public
 | |
|  */
 | |
| const m = createMotionProxy(createDomMotionConfig);
 | |
| 
 | |
| function useIsMounted() {
 | |
|     const isMounted = React.useRef(false);
 | |
|     useIsomorphicLayoutEffect(() => {
 | |
|         isMounted.current = true;
 | |
|         return () => {
 | |
|             isMounted.current = false;
 | |
|         };
 | |
|     }, []);
 | |
|     return isMounted;
 | |
| }
 | |
| 
 | |
| function useForceUpdate() {
 | |
|     const isMounted = useIsMounted();
 | |
|     const [forcedRenderCount, setForcedRenderCount] = React.useState(0);
 | |
|     const forceRender = React.useCallback(() => {
 | |
|         isMounted.current && setForcedRenderCount(forcedRenderCount + 1);
 | |
|     }, [forcedRenderCount]);
 | |
|     /**
 | |
|      * Defer this to the end of the next animation frame in case there are multiple
 | |
|      * synchronous calls.
 | |
|      */
 | |
|     const deferredForceRender = React.useCallback(() => indexLegacy.frame.postRender(forceRender), [forceRender]);
 | |
|     return [deferredForceRender, forcedRenderCount];
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Measurement functionality has to be within a separate component
 | |
|  * to leverage snapshot lifecycle.
 | |
|  */
 | |
| class PopChildMeasure extends React__namespace.Component {
 | |
|     getSnapshotBeforeUpdate(prevProps) {
 | |
|         const element = this.props.childRef.current;
 | |
|         if (element && prevProps.isPresent && !this.props.isPresent) {
 | |
|             const size = this.props.sizeRef.current;
 | |
|             size.height = element.offsetHeight || 0;
 | |
|             size.width = element.offsetWidth || 0;
 | |
|             size.top = element.offsetTop;
 | |
|             size.left = element.offsetLeft;
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
|     /**
 | |
|      * Required with getSnapshotBeforeUpdate to stop React complaining.
 | |
|      */
 | |
|     componentDidUpdate() { }
 | |
|     render() {
 | |
|         return this.props.children;
 | |
|     }
 | |
| }
 | |
| function PopChild({ children, isPresent }) {
 | |
|     const id = React.useId();
 | |
|     const ref = React.useRef(null);
 | |
|     const size = React.useRef({
 | |
|         width: 0,
 | |
|         height: 0,
 | |
|         top: 0,
 | |
|         left: 0,
 | |
|     });
 | |
|     /**
 | |
|      * We create and inject a style block so we can apply this explicit
 | |
|      * sizing in a non-destructive manner by just deleting the style block.
 | |
|      *
 | |
|      * We can't apply size via render as the measurement happens
 | |
|      * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
 | |
|      * styles directly on the DOM node, we might be overwriting
 | |
|      * styles set via the style prop.
 | |
|      */
 | |
|     React.useInsertionEffect(() => {
 | |
|         const { width, height, top, left } = size.current;
 | |
|         if (isPresent || !ref.current || !width || !height)
 | |
|             return;
 | |
|         ref.current.dataset.motionPopId = id;
 | |
|         const style = document.createElement("style");
 | |
|         document.head.appendChild(style);
 | |
|         if (style.sheet) {
 | |
|             style.sheet.insertRule(`
 | |
|           [data-motion-pop-id="${id}"] {
 | |
|             position: absolute !important;
 | |
|             width: ${width}px !important;
 | |
|             height: ${height}px !important;
 | |
|             top: ${top}px !important;
 | |
|             left: ${left}px !important;
 | |
|           }
 | |
|         `);
 | |
|         }
 | |
|         return () => {
 | |
|             document.head.removeChild(style);
 | |
|         };
 | |
|     }, [isPresent]);
 | |
|     return (React__namespace.createElement(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size }, React__namespace.cloneElement(children, { ref })));
 | |
| }
 | |
| 
 | |
| const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, }) => {
 | |
|     const presenceChildren = useConstant(newChildrenMap);
 | |
|     const id = React.useId();
 | |
|     const context = React.useMemo(() => ({
 | |
|         id,
 | |
|         initial,
 | |
|         isPresent,
 | |
|         custom,
 | |
|         onExitComplete: (childId) => {
 | |
|             presenceChildren.set(childId, true);
 | |
|             for (const isComplete of presenceChildren.values()) {
 | |
|                 if (!isComplete)
 | |
|                     return; // can stop searching when any is incomplete
 | |
|             }
 | |
|             onExitComplete && onExitComplete();
 | |
|         },
 | |
|         register: (childId) => {
 | |
|             presenceChildren.set(childId, false);
 | |
|             return () => presenceChildren.delete(childId);
 | |
|         },
 | |
|     }), 
 | |
|     /**
 | |
|      * If the presence of a child affects the layout of the components around it,
 | |
|      * we want to make a new context value to ensure they get re-rendered
 | |
|      * so they can detect that layout change.
 | |
|      */
 | |
|     presenceAffectsLayout ? undefined : [isPresent]);
 | |
|     React.useMemo(() => {
 | |
|         presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
 | |
|     }, [isPresent]);
 | |
|     /**
 | |
|      * If there's no `motion` components to fire exit animations, we want to remove this
 | |
|      * component immediately.
 | |
|      */
 | |
|     React__namespace.useEffect(() => {
 | |
|         !isPresent &&
 | |
|             !presenceChildren.size &&
 | |
|             onExitComplete &&
 | |
|             onExitComplete();
 | |
|     }, [isPresent]);
 | |
|     if (mode === "popLayout") {
 | |
|         children = React__namespace.createElement(PopChild, { isPresent: isPresent }, children);
 | |
|     }
 | |
|     return (React__namespace.createElement(PresenceContext.Provider, { value: context }, children));
 | |
| };
 | |
| function newChildrenMap() {
 | |
|     return new Map();
 | |
| }
 | |
| 
 | |
| function useUnmountEffect(callback) {
 | |
|     return React.useEffect(() => () => callback(), []);
 | |
| }
 | |
| 
 | |
| const getChildKey = (child) => child.key || "";
 | |
| function updateChildLookup(children, allChildren) {
 | |
|     children.forEach((child) => {
 | |
|         const key = getChildKey(child);
 | |
|         allChildren.set(key, child);
 | |
|     });
 | |
| }
 | |
| function onlyElements(children) {
 | |
|     const filtered = [];
 | |
|     // We use forEach here instead of map as map mutates the component key by preprending `.$`
 | |
|     React.Children.forEach(children, (child) => {
 | |
|         if (React.isValidElement(child))
 | |
|             filtered.push(child);
 | |
|     });
 | |
|     return filtered;
 | |
| }
 | |
| /**
 | |
|  * `AnimatePresence` enables the animation of components that have been removed from the tree.
 | |
|  *
 | |
|  * When adding/removing more than a single child, every child **must** be given a unique `key` prop.
 | |
|  *
 | |
|  * Any `motion` components that have an `exit` property defined will animate out when removed from
 | |
|  * the tree.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import { motion, AnimatePresence } from 'framer-motion'
 | |
|  *
 | |
|  * export const Items = ({ items }) => (
 | |
|  *   <AnimatePresence>
 | |
|  *     {items.map(item => (
 | |
|  *       <motion.div
 | |
|  *         key={item.id}
 | |
|  *         initial={{ opacity: 0 }}
 | |
|  *         animate={{ opacity: 1 }}
 | |
|  *         exit={{ opacity: 0 }}
 | |
|  *       />
 | |
|  *     ))}
 | |
|  *   </AnimatePresence>
 | |
|  * )
 | |
|  * ```
 | |
|  *
 | |
|  * You can sequence exit animations throughout a tree using variants.
 | |
|  *
 | |
|  * If a child contains multiple `motion` components with `exit` props, it will only unmount the child
 | |
|  * once all `motion` components have finished animating out. Likewise, any components using
 | |
|  * `usePresence` all need to call `safeToRemove`.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| const AnimatePresence = ({ children, custom, initial = true, onExitComplete, exitBeforeEnter, presenceAffectsLayout = true, mode = "sync", }) => {
 | |
|     indexLegacy.invariant(!exitBeforeEnter, "Replace exitBeforeEnter with mode='wait'");
 | |
|     // We want to force a re-render once all exiting animations have finished. We
 | |
|     // either use a local forceRender function, or one from a parent context if it exists.
 | |
|     const forceRender = React.useContext(LayoutGroupContext).forceRender || useForceUpdate()[0];
 | |
|     const isMounted = useIsMounted();
 | |
|     // Filter out any children that aren't ReactElements. We can only track ReactElements with a props.key
 | |
|     const filteredChildren = onlyElements(children);
 | |
|     let childrenToRender = filteredChildren;
 | |
|     const exitingChildren = React.useRef(new Map()).current;
 | |
|     // Keep a living record of the children we're actually rendering so we
 | |
|     // can diff to figure out which are entering and exiting
 | |
|     const presentChildren = React.useRef(childrenToRender);
 | |
|     // A lookup table to quickly reference components by key
 | |
|     const allChildren = React.useRef(new Map()).current;
 | |
|     // If this is the initial component render, just deal with logic surrounding whether
 | |
|     // we play onMount animations or not.
 | |
|     const isInitialRender = React.useRef(true);
 | |
|     useIsomorphicLayoutEffect(() => {
 | |
|         isInitialRender.current = false;
 | |
|         updateChildLookup(filteredChildren, allChildren);
 | |
|         presentChildren.current = childrenToRender;
 | |
|     });
 | |
|     useUnmountEffect(() => {
 | |
|         isInitialRender.current = true;
 | |
|         allChildren.clear();
 | |
|         exitingChildren.clear();
 | |
|     });
 | |
|     if (isInitialRender.current) {
 | |
|         return (React__namespace.createElement(React__namespace.Fragment, null, childrenToRender.map((child) => (React__namespace.createElement(PresenceChild, { key: getChildKey(child), isPresent: true, initial: initial ? undefined : false, presenceAffectsLayout: presenceAffectsLayout, mode: mode }, child)))));
 | |
|     }
 | |
|     // If this is a subsequent render, deal with entering and exiting children
 | |
|     childrenToRender = [...childrenToRender];
 | |
|     // Diff the keys of the currently-present and target children to update our
 | |
|     // exiting list.
 | |
|     const presentKeys = presentChildren.current.map(getChildKey);
 | |
|     const targetKeys = filteredChildren.map(getChildKey);
 | |
|     // Diff the present children with our target children and mark those that are exiting
 | |
|     const numPresent = presentKeys.length;
 | |
|     for (let i = 0; i < numPresent; i++) {
 | |
|         const key = presentKeys[i];
 | |
|         if (targetKeys.indexOf(key) === -1 && !exitingChildren.has(key)) {
 | |
|             exitingChildren.set(key, undefined);
 | |
|         }
 | |
|     }
 | |
|     // If we currently have exiting children, and we're deferring rendering incoming children
 | |
|     // until after all current children have exiting, empty the childrenToRender array
 | |
|     if (mode === "wait" && exitingChildren.size) {
 | |
|         childrenToRender = [];
 | |
|     }
 | |
|     // Loop through all currently exiting components and clone them to overwrite `animate`
 | |
|     // with any `exit` prop they might have defined.
 | |
|     exitingChildren.forEach((component, key) => {
 | |
|         // If this component is actually entering again, early return
 | |
|         if (targetKeys.indexOf(key) !== -1)
 | |
|             return;
 | |
|         const child = allChildren.get(key);
 | |
|         if (!child)
 | |
|             return;
 | |
|         const insertionIndex = presentKeys.indexOf(key);
 | |
|         let exitingComponent = component;
 | |
|         if (!exitingComponent) {
 | |
|             const onExit = () => {
 | |
|                 // clean up the exiting children map
 | |
|                 exitingChildren.delete(key);
 | |
|                 // compute the keys of children that were rendered once but are no longer present
 | |
|                 // this could happen in case of too many fast consequent renderings
 | |
|                 // @link https://github.com/framer/motion/issues/2023
 | |
|                 const leftOverKeys = Array.from(allChildren.keys()).filter((childKey) => !targetKeys.includes(childKey));
 | |
|                 // clean up the all children map
 | |
|                 leftOverKeys.forEach((leftOverKey) => allChildren.delete(leftOverKey));
 | |
|                 // make sure to render only the children that are actually visible
 | |
|                 presentChildren.current = filteredChildren.filter((presentChild) => {
 | |
|                     const presentChildKey = getChildKey(presentChild);
 | |
|                     return (
 | |
|                     // filter out the node exiting
 | |
|                     presentChildKey === key ||
 | |
|                         // filter out the leftover children
 | |
|                         leftOverKeys.includes(presentChildKey));
 | |
|                 });
 | |
|                 // Defer re-rendering until all exiting children have indeed left
 | |
|                 if (!exitingChildren.size) {
 | |
|                     if (isMounted.current === false)
 | |
|                         return;
 | |
|                     forceRender();
 | |
|                     onExitComplete && onExitComplete();
 | |
|                 }
 | |
|             };
 | |
|             exitingComponent = (React__namespace.createElement(PresenceChild, { key: getChildKey(child), isPresent: false, onExitComplete: onExit, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode }, child));
 | |
|             exitingChildren.set(key, exitingComponent);
 | |
|         }
 | |
|         childrenToRender.splice(insertionIndex, 0, exitingComponent);
 | |
|     });
 | |
|     // Add `MotionContext` even to children that don't need it to ensure we're rendering
 | |
|     // the same tree between renders
 | |
|     childrenToRender = childrenToRender.map((child) => {
 | |
|         const key = child.key;
 | |
|         return exitingChildren.has(key) ? (child) : (React__namespace.createElement(PresenceChild, { key: getChildKey(child), isPresent: true, presenceAffectsLayout: presenceAffectsLayout, mode: mode }, child));
 | |
|     });
 | |
|     if (process.env.NODE_ENV !== "production" &&
 | |
|         mode === "wait" &&
 | |
|         childrenToRender.length > 1) {
 | |
|         console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`);
 | |
|     }
 | |
|     return (React__namespace.createElement(React__namespace.Fragment, null, exitingChildren.size
 | |
|         ? childrenToRender
 | |
|         : childrenToRender.map((child) => React.cloneElement(child))));
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * `MotionConfig` is used to set configuration options for all children `motion` components.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import { motion, MotionConfig } from "framer-motion"
 | |
|  *
 | |
|  * export function App() {
 | |
|  *   return (
 | |
|  *     <MotionConfig transition={{ type: "spring" }}>
 | |
|  *       <motion.div animate={{ x: 100 }} />
 | |
|  *     </MotionConfig>
 | |
|  *   )
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function MotionConfig({ children, isValidProp, ...config }) {
 | |
|     isValidProp && loadExternalIsValidProp(isValidProp);
 | |
|     /**
 | |
|      * Inherit props from any parent MotionConfig components
 | |
|      */
 | |
|     config = { ...React.useContext(MotionConfigContext), ...config };
 | |
|     /**
 | |
|      * Don't allow isStatic to change between renders as it affects how many hooks
 | |
|      * motion components fire.
 | |
|      */
 | |
|     config.isStatic = useConstant(() => config.isStatic);
 | |
|     /**
 | |
|      * Creating a new config context object will re-render every `motion` component
 | |
|      * every time it renders. So we only want to create a new one sparingly.
 | |
|      */
 | |
|     const context = React.useMemo(() => config, [JSON.stringify(config.transition), config.transformPagePoint, config.reducedMotion]);
 | |
|     return (React__namespace.createElement(MotionConfigContext.Provider, { value: context }, children));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Used in conjunction with the `m` component to reduce bundle size.
 | |
|  *
 | |
|  * `m` is a version of the `motion` component that only loads functionality
 | |
|  * critical for the initial render.
 | |
|  *
 | |
|  * `LazyMotion` can then be used to either synchronously or asynchronously
 | |
|  * load animation and gesture support.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * // Synchronous loading
 | |
|  * import { LazyMotion, m, domAnimation } from "framer-motion"
 | |
|  *
 | |
|  * function App() {
 | |
|  *   return (
 | |
|  *     <LazyMotion features={domAnimation}>
 | |
|  *       <m.div animate={{ scale: 2 }} />
 | |
|  *     </LazyMotion>
 | |
|  *   )
 | |
|  * }
 | |
|  *
 | |
|  * // Asynchronous loading
 | |
|  * import { LazyMotion, m } from "framer-motion"
 | |
|  *
 | |
|  * function App() {
 | |
|  *   return (
 | |
|  *     <LazyMotion features={() => import('./path/to/domAnimation')}>
 | |
|  *       <m.div animate={{ scale: 2 }} />
 | |
|  *     </LazyMotion>
 | |
|  *   )
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function LazyMotion({ children, features, strict = false }) {
 | |
|     const [, setIsLoaded] = React.useState(!isLazyBundle(features));
 | |
|     const loadedRenderer = React.useRef(undefined);
 | |
|     /**
 | |
|      * If this is a synchronous load, load features immediately
 | |
|      */
 | |
|     if (!isLazyBundle(features)) {
 | |
|         const { renderer, ...loadedFeatures } = features;
 | |
|         loadedRenderer.current = renderer;
 | |
|         loadFeatures(loadedFeatures);
 | |
|     }
 | |
|     React.useEffect(() => {
 | |
|         if (isLazyBundle(features)) {
 | |
|             features().then(({ renderer, ...loadedFeatures }) => {
 | |
|                 loadFeatures(loadedFeatures);
 | |
|                 loadedRenderer.current = renderer;
 | |
|                 setIsLoaded(true);
 | |
|             });
 | |
|         }
 | |
|     }, []);
 | |
|     return (React__namespace.createElement(LazyContext.Provider, { value: { renderer: loadedRenderer.current, strict } }, children));
 | |
| }
 | |
| function isLazyBundle(features) {
 | |
|     return typeof features === "function";
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Note: Still used by components generated by old versions of Framer
 | |
|  *
 | |
|  * @deprecated
 | |
|  */
 | |
| const DeprecatedLayoutGroupContext = React.createContext(null);
 | |
| 
 | |
| const shouldInheritGroup = (inherit) => inherit === true;
 | |
| const shouldInheritId = (inherit) => shouldInheritGroup(inherit === true) || inherit === "id";
 | |
| const LayoutGroup = ({ children, id, inherit = true }) => {
 | |
|     const layoutGroupContext = React.useContext(LayoutGroupContext);
 | |
|     const deprecatedLayoutGroupContext = React.useContext(DeprecatedLayoutGroupContext);
 | |
|     const [forceRender, key] = useForceUpdate();
 | |
|     const context = React.useRef(null);
 | |
|     const upstreamId = layoutGroupContext.id || deprecatedLayoutGroupContext;
 | |
|     if (context.current === null) {
 | |
|         if (shouldInheritId(inherit) && upstreamId) {
 | |
|             id = id ? upstreamId + "-" + id : upstreamId;
 | |
|         }
 | |
|         context.current = {
 | |
|             id,
 | |
|             group: shouldInheritGroup(inherit)
 | |
|                 ? layoutGroupContext.group || nodeGroup()
 | |
|                 : nodeGroup(),
 | |
|         };
 | |
|     }
 | |
|     const memoizedContext = React.useMemo(() => ({ ...context.current, forceRender }), [key]);
 | |
|     return (React__namespace.createElement(LayoutGroupContext.Provider, { value: memoizedContext }, children));
 | |
| };
 | |
| 
 | |
| const ReorderContext = React.createContext(null);
 | |
| 
 | |
| function checkReorder(order, value, offset, velocity) {
 | |
|     if (!velocity)
 | |
|         return order;
 | |
|     const index = order.findIndex((item) => item.value === value);
 | |
|     if (index === -1)
 | |
|         return order;
 | |
|     const nextOffset = velocity > 0 ? 1 : -1;
 | |
|     const nextItem = order[index + nextOffset];
 | |
|     if (!nextItem)
 | |
|         return order;
 | |
|     const item = order[index];
 | |
|     const nextLayout = nextItem.layout;
 | |
|     const nextItemCenter = indexLegacy.mix(nextLayout.min, nextLayout.max, 0.5);
 | |
|     if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) ||
 | |
|         (nextOffset === -1 && item.layout.min + offset < nextItemCenter)) {
 | |
|         return indexLegacy.moveItem(order, index, index + nextOffset);
 | |
|     }
 | |
|     return order;
 | |
| }
 | |
| 
 | |
| function ReorderGroup({ children, as = "ul", axis = "y", onReorder, values, ...props }, externalRef) {
 | |
|     const Component = useConstant(() => motion(as));
 | |
|     const order = [];
 | |
|     const isReordering = React.useRef(false);
 | |
|     indexLegacy.invariant(Boolean(values), "Reorder.Group must be provided a values prop");
 | |
|     const context = {
 | |
|         axis,
 | |
|         registerItem: (value, layout) => {
 | |
|             // If the entry was already added, update it rather than adding it again
 | |
|             const idx = order.findIndex((entry) => value === entry.value);
 | |
|             if (idx !== -1) {
 | |
|                 order[idx].layout = layout[axis];
 | |
|             }
 | |
|             else {
 | |
|                 order.push({ value: value, layout: layout[axis] });
 | |
|             }
 | |
|             order.sort(compareMin);
 | |
|         },
 | |
|         updateOrder: (item, offset, velocity) => {
 | |
|             if (isReordering.current)
 | |
|                 return;
 | |
|             const newOrder = checkReorder(order, item, offset, velocity);
 | |
|             if (order !== newOrder) {
 | |
|                 isReordering.current = true;
 | |
|                 onReorder(newOrder
 | |
|                     .map(getValue)
 | |
|                     .filter((value) => values.indexOf(value) !== -1));
 | |
|             }
 | |
|         },
 | |
|     };
 | |
|     React.useEffect(() => {
 | |
|         isReordering.current = false;
 | |
|     });
 | |
|     return (React__namespace.createElement(Component, { ...props, ref: externalRef, ignoreStrict: true },
 | |
|         React__namespace.createElement(ReorderContext.Provider, { value: context }, children)));
 | |
| }
 | |
| const Group = React.forwardRef(ReorderGroup);
 | |
| function getValue(item) {
 | |
|     return item.value;
 | |
| }
 | |
| function compareMin(a, b) {
 | |
|     return a.layout.min - b.layout.min;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates a `MotionValue` to track the state and velocity of a value.
 | |
|  *
 | |
|  * Usually, these are created automatically. For advanced use-cases, like use with `useTransform`, you can create `MotionValue`s externally and pass them into the animated component via the `style` prop.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * export const MyComponent = () => {
 | |
|  *   const scale = useMotionValue(1)
 | |
|  *
 | |
|  *   return <motion.div style={{ scale }} />
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @param initial - The initial state.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useMotionValue(initial) {
 | |
|     const value = useConstant(() => indexLegacy.motionValue(initial));
 | |
|     /**
 | |
|      * If this motion value is being used in static mode, like on
 | |
|      * the Framer canvas, force components to rerender when the motion
 | |
|      * value is updated.
 | |
|      */
 | |
|     const { isStatic } = React.useContext(MotionConfigContext);
 | |
|     if (isStatic) {
 | |
|         const [, setLatest] = React.useState(initial);
 | |
|         React.useEffect(() => value.on("change", setLatest), []);
 | |
|     }
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| function useCombineMotionValues(values, combineValues) {
 | |
|     /**
 | |
|      * Initialise the returned motion value. This remains the same between renders.
 | |
|      */
 | |
|     const value = useMotionValue(combineValues());
 | |
|     /**
 | |
|      * Create a function that will update the template motion value with the latest values.
 | |
|      * This is pre-bound so whenever a motion value updates it can schedule its
 | |
|      * execution in Framesync. If it's already been scheduled it won't be fired twice
 | |
|      * in a single frame.
 | |
|      */
 | |
|     const updateValue = () => value.set(combineValues());
 | |
|     /**
 | |
|      * Synchronously update the motion value with the latest values during the render.
 | |
|      * This ensures that within a React render, the styles applied to the DOM are up-to-date.
 | |
|      */
 | |
|     updateValue();
 | |
|     /**
 | |
|      * Subscribe to all motion values found within the template. Whenever any of them change,
 | |
|      * schedule an update.
 | |
|      */
 | |
|     useIsomorphicLayoutEffect(() => {
 | |
|         const scheduleUpdate = () => indexLegacy.frame.update(updateValue, false, true);
 | |
|         const subscriptions = values.map((v) => v.on("change", scheduleUpdate));
 | |
|         return () => {
 | |
|             subscriptions.forEach((unsubscribe) => unsubscribe());
 | |
|             indexLegacy.cancelFrame(updateValue);
 | |
|         };
 | |
|     });
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| function useComputed(compute) {
 | |
|     /**
 | |
|      * Open session of collectMotionValues. Any MotionValue that calls get()
 | |
|      * will be saved into this array.
 | |
|      */
 | |
|     indexLegacy.collectMotionValues.current = [];
 | |
|     compute();
 | |
|     const value = useCombineMotionValues(indexLegacy.collectMotionValues.current, compute);
 | |
|     /**
 | |
|      * Synchronously close session of collectMotionValues.
 | |
|      */
 | |
|     indexLegacy.collectMotionValues.current = undefined;
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| function useTransform(input, inputRangeOrTransformer, outputRange, options) {
 | |
|     if (typeof input === "function") {
 | |
|         return useComputed(input);
 | |
|     }
 | |
|     const transformer = typeof inputRangeOrTransformer === "function"
 | |
|         ? inputRangeOrTransformer
 | |
|         : indexLegacy.transform(inputRangeOrTransformer, outputRange, options);
 | |
|     return Array.isArray(input)
 | |
|         ? useListTransform(input, transformer)
 | |
|         : useListTransform([input], ([latest]) => transformer(latest));
 | |
| }
 | |
| function useListTransform(values, transformer) {
 | |
|     const latest = useConstant(() => []);
 | |
|     return useCombineMotionValues(values, () => {
 | |
|         latest.length = 0;
 | |
|         const numValues = values.length;
 | |
|         for (let i = 0; i < numValues; i++) {
 | |
|             latest[i] = values[i].get();
 | |
|         }
 | |
|         return transformer(latest);
 | |
|     });
 | |
| }
 | |
| 
 | |
| function useDefaultMotionValue(value, defaultValue = 0) {
 | |
|     return indexLegacy.isMotionValue(value) ? value : useMotionValue(defaultValue);
 | |
| }
 | |
| function ReorderItem({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) {
 | |
|     const Component = useConstant(() => motion(as));
 | |
|     const context = React.useContext(ReorderContext);
 | |
|     const point = {
 | |
|         x: useDefaultMotionValue(style.x),
 | |
|         y: useDefaultMotionValue(style.y),
 | |
|     };
 | |
|     const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset");
 | |
|     indexLegacy.invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group");
 | |
|     const { axis, registerItem, updateOrder } = context;
 | |
|     return (React__namespace.createElement(Component, { drag: axis, ...props, dragSnapToOrigin: true, style: { ...style, x: point.x, y: point.y, zIndex }, layout: layout, onDrag: (event, gesturePoint) => {
 | |
|             const { velocity } = gesturePoint;
 | |
|             velocity[axis] &&
 | |
|                 updateOrder(value, point[axis].get(), velocity[axis]);
 | |
|             onDrag && onDrag(event, gesturePoint);
 | |
|         }, onLayoutMeasure: (measured) => registerItem(value, measured), ref: externalRef, ignoreStrict: true }, children));
 | |
| }
 | |
| const Item = React.forwardRef(ReorderItem);
 | |
| 
 | |
| const Reorder = {
 | |
|     Group,
 | |
|     Item,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @public
 | |
|  */
 | |
| const domAnimation = {
 | |
|     renderer: createDomVisualElement,
 | |
|     ...animations,
 | |
|     ...gestureAnimations,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * @public
 | |
|  */
 | |
| const domMax = {
 | |
|     ...domAnimation,
 | |
|     ...drag,
 | |
|     ...layout,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Combine multiple motion values into a new one using a string template literal.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import {
 | |
|  *   motion,
 | |
|  *   useSpring,
 | |
|  *   useMotionValue,
 | |
|  *   useMotionTemplate
 | |
|  * } from "framer-motion"
 | |
|  *
 | |
|  * function Component() {
 | |
|  *   const shadowX = useSpring(0)
 | |
|  *   const shadowY = useMotionValue(0)
 | |
|  *   const shadow = useMotionTemplate`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))`
 | |
|  *
 | |
|  *   return <motion.div style={{ filter: shadow }} />
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useMotionTemplate(fragments, ...values) {
 | |
|     /**
 | |
|      * Create a function that will build a string from the latest motion values.
 | |
|      */
 | |
|     const numFragments = fragments.length;
 | |
|     function buildValue() {
 | |
|         let output = ``;
 | |
|         for (let i = 0; i < numFragments; i++) {
 | |
|             output += fragments[i];
 | |
|             const value = values[i];
 | |
|             if (value) {
 | |
|                 output += indexLegacy.isMotionValue(value) ? value.get() : value;
 | |
|             }
 | |
|         }
 | |
|         return output;
 | |
|     }
 | |
|     return useCombineMotionValues(values.filter(indexLegacy.isMotionValue), buildValue);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates a `MotionValue` that, when `set`, will use a spring animation to animate to its new state.
 | |
|  *
 | |
|  * It can either work as a stand-alone `MotionValue` by initialising it with a value, or as a subscriber
 | |
|  * to another `MotionValue`.
 | |
|  *
 | |
|  * @remarks
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const x = useSpring(0, { stiffness: 300 })
 | |
|  * const y = useSpring(x, { damping: 10 })
 | |
|  * ```
 | |
|  *
 | |
|  * @param inputValue - `MotionValue` or number. If provided a `MotionValue`, when the input `MotionValue` changes, the created `MotionValue` will spring towards that value.
 | |
|  * @param springConfig - Configuration options for the spring.
 | |
|  * @returns `MotionValue`
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useSpring(source, config = {}) {
 | |
|     const { isStatic } = React.useContext(MotionConfigContext);
 | |
|     const activeSpringAnimation = React.useRef(null);
 | |
|     const value = useMotionValue(indexLegacy.isMotionValue(source) ? source.get() : source);
 | |
|     const stopAnimation = () => {
 | |
|         if (activeSpringAnimation.current) {
 | |
|             activeSpringAnimation.current.stop();
 | |
|         }
 | |
|     };
 | |
|     React.useInsertionEffect(() => {
 | |
|         return value.attach((v, set) => {
 | |
|             /**
 | |
|              * A more hollistic approach to this might be to use isStatic to fix VisualElement animations
 | |
|              * at that level, but this will work for now
 | |
|              */
 | |
|             if (isStatic)
 | |
|                 return set(v);
 | |
|             stopAnimation();
 | |
|             activeSpringAnimation.current = indexLegacy.animateValue({
 | |
|                 keyframes: [value.get(), v],
 | |
|                 velocity: value.getVelocity(),
 | |
|                 type: "spring",
 | |
|                 restDelta: 0.001,
 | |
|                 restSpeed: 0.01,
 | |
|                 ...config,
 | |
|                 onUpdate: set,
 | |
|             });
 | |
|             /**
 | |
|              * If we're between frames, resync the animation to the frameloop.
 | |
|              */
 | |
|             if (!indexLegacy.frameData.isProcessing) {
 | |
|                 const delta = performance.now() - indexLegacy.frameData.timestamp;
 | |
|                 if (delta < 30) {
 | |
|                     activeSpringAnimation.current.time =
 | |
|                         indexLegacy.millisecondsToSeconds(delta);
 | |
|                 }
 | |
|             }
 | |
|             return value.get();
 | |
|         }, stopAnimation);
 | |
|     }, [JSON.stringify(config)]);
 | |
|     useIsomorphicLayoutEffect(() => {
 | |
|         if (indexLegacy.isMotionValue(source)) {
 | |
|             return source.on("change", (v) => value.set(parseFloat(v)));
 | |
|         }
 | |
|     }, [value]);
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| function useMotionValueEvent(value, event, callback) {
 | |
|     /**
 | |
|      * useInsertionEffect will create subscriptions before any other
 | |
|      * effects will run. Effects run upwards through the tree so it
 | |
|      * can be that binding a useLayoutEffect higher up the tree can
 | |
|      * miss changes from lower down the tree.
 | |
|      */
 | |
|     React.useInsertionEffect(() => value.on(event, callback), [value, event, callback]);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes.
 | |
|  *
 | |
|  * ```javascript
 | |
|  * const x = useMotionValue(0)
 | |
|  * const xVelocity = useVelocity(x)
 | |
|  * const xAcceleration = useVelocity(xVelocity)
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useVelocity(value) {
 | |
|     const velocity = useMotionValue(value.getVelocity());
 | |
|     useMotionValueEvent(value, "velocityChange", (newVelocity) => {
 | |
|         velocity.set(newVelocity);
 | |
|     });
 | |
|     return velocity;
 | |
| }
 | |
| 
 | |
| function refWarning(name, ref) {
 | |
|     indexLegacy.warning(Boolean(!ref || ref.current), `You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`);
 | |
| }
 | |
| const createScrollMotionValues = () => ({
 | |
|     scrollX: indexLegacy.motionValue(0),
 | |
|     scrollY: indexLegacy.motionValue(0),
 | |
|     scrollXProgress: indexLegacy.motionValue(0),
 | |
|     scrollYProgress: indexLegacy.motionValue(0),
 | |
| });
 | |
| function useScroll({ container, target, layoutEffect = true, ...options } = {}) {
 | |
|     const values = useConstant(createScrollMotionValues);
 | |
|     const useLifecycleEffect = layoutEffect
 | |
|         ? useIsomorphicLayoutEffect
 | |
|         : React.useEffect;
 | |
|     useLifecycleEffect(() => {
 | |
|         refWarning("target", target);
 | |
|         refWarning("container", container);
 | |
|         return indexLegacy.scrollInfo(({ x, y }) => {
 | |
|             values.scrollX.set(x.current);
 | |
|             values.scrollXProgress.set(x.progress);
 | |
|             values.scrollY.set(y.current);
 | |
|             values.scrollYProgress.set(y.progress);
 | |
|         }, {
 | |
|             ...options,
 | |
|             container: (container === null || container === void 0 ? void 0 : container.current) || undefined,
 | |
|             target: (target === null || target === void 0 ? void 0 : target.current) || undefined,
 | |
|         });
 | |
|     }, [container, target, JSON.stringify(options.offset)]);
 | |
|     return values;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @deprecated useElementScroll is deprecated. Convert to useScroll({ container: ref })
 | |
|  */
 | |
| function useElementScroll(ref) {
 | |
|     if (process.env.NODE_ENV === "development") {
 | |
|         indexLegacy.warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref }).");
 | |
|     }
 | |
|     return useScroll({ container: ref });
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @deprecated useViewportScroll is deprecated. Convert to useScroll()
 | |
|  */
 | |
| function useViewportScroll() {
 | |
|     if (process.env.NODE_ENV !== "production") {
 | |
|         indexLegacy.warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll().");
 | |
|     }
 | |
|     return useScroll();
 | |
| }
 | |
| 
 | |
| function useAnimationFrame(callback) {
 | |
|     const initialTimestamp = React.useRef(0);
 | |
|     const { isStatic } = React.useContext(MotionConfigContext);
 | |
|     React.useEffect(() => {
 | |
|         if (isStatic)
 | |
|             return;
 | |
|         const provideTimeSinceStart = ({ timestamp, delta }) => {
 | |
|             if (!initialTimestamp.current)
 | |
|                 initialTimestamp.current = timestamp;
 | |
|             callback(timestamp - initialTimestamp.current, delta);
 | |
|         };
 | |
|         indexLegacy.frame.update(provideTimeSinceStart, true);
 | |
|         return () => indexLegacy.cancelFrame(provideTimeSinceStart);
 | |
|     }, [callback]);
 | |
| }
 | |
| 
 | |
| function useTime() {
 | |
|     const time = useMotionValue(0);
 | |
|     useAnimationFrame((t) => time.set(t));
 | |
|     return time;
 | |
| }
 | |
| 
 | |
| class WillChangeMotionValue extends indexLegacy.MotionValue {
 | |
|     constructor() {
 | |
|         super(...arguments);
 | |
|         this.members = [];
 | |
|         this.transforms = new Set();
 | |
|     }
 | |
|     add(name) {
 | |
|         let memberName;
 | |
|         if (indexLegacy.transformProps.has(name)) {
 | |
|             this.transforms.add(name);
 | |
|             memberName = "transform";
 | |
|         }
 | |
|         else if (!name.startsWith("origin") &&
 | |
|             !indexLegacy.isCSSVariableName(name) &&
 | |
|             name !== "willChange") {
 | |
|             memberName = indexLegacy.camelToDash(name);
 | |
|         }
 | |
|         if (memberName) {
 | |
|             indexLegacy.addUniqueItem(this.members, memberName);
 | |
|             this.update();
 | |
|         }
 | |
|     }
 | |
|     remove(name) {
 | |
|         if (indexLegacy.transformProps.has(name)) {
 | |
|             this.transforms.delete(name);
 | |
|             if (!this.transforms.size) {
 | |
|                 indexLegacy.removeItem(this.members, "transform");
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             indexLegacy.removeItem(this.members, indexLegacy.camelToDash(name));
 | |
|         }
 | |
|         this.update();
 | |
|     }
 | |
|     update() {
 | |
|         this.set(this.members.length ? this.members.join(", ") : "auto");
 | |
|     }
 | |
| }
 | |
| function useWillChange() {
 | |
|     return useConstant(() => new WillChangeMotionValue("auto"));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
 | |
|  *
 | |
|  * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing
 | |
|  * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion.
 | |
|  *
 | |
|  * It will actively respond to changes and re-render your components with the latest setting.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * export function Sidebar({ isOpen }) {
 | |
|  *   const shouldReduceMotion = useReducedMotion()
 | |
|  *   const closedX = shouldReduceMotion ? 0 : "-100%"
 | |
|  *
 | |
|  *   return (
 | |
|  *     <motion.div animate={{
 | |
|  *       opacity: isOpen ? 1 : 0,
 | |
|  *       x: isOpen ? 0 : closedX
 | |
|  *     }} />
 | |
|  *   )
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @return boolean
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useReducedMotion() {
 | |
|     /**
 | |
|      * Lazy initialisation of prefersReducedMotion
 | |
|      */
 | |
|     !indexLegacy.hasReducedMotionListener.current && indexLegacy.initPrefersReducedMotion();
 | |
|     const [shouldReduceMotion] = React.useState(indexLegacy.prefersReducedMotion.current);
 | |
|     if (process.env.NODE_ENV !== "production") {
 | |
|         indexLegacy.warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
 | |
|     }
 | |
|     /**
 | |
|      * TODO See if people miss automatically updating shouldReduceMotion setting
 | |
|      */
 | |
|     return shouldReduceMotion;
 | |
| }
 | |
| 
 | |
| function useReducedMotionConfig() {
 | |
|     const reducedMotionPreference = useReducedMotion();
 | |
|     const { reducedMotion } = React.useContext(MotionConfigContext);
 | |
|     if (reducedMotion === "never") {
 | |
|         return false;
 | |
|     }
 | |
|     else if (reducedMotion === "always") {
 | |
|         return true;
 | |
|     }
 | |
|     else {
 | |
|         return reducedMotionPreference;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function stopAnimation(visualElement) {
 | |
|     visualElement.values.forEach((value) => value.stop());
 | |
| }
 | |
| /**
 | |
|  * @public
 | |
|  */
 | |
| function animationControls() {
 | |
|     /**
 | |
|      * Track whether the host component has mounted.
 | |
|      */
 | |
|     let hasMounted = false;
 | |
|     /**
 | |
|      * A collection of linked component animation controls.
 | |
|      */
 | |
|     const subscribers = new Set();
 | |
|     const controls = {
 | |
|         subscribe(visualElement) {
 | |
|             subscribers.add(visualElement);
 | |
|             return () => void subscribers.delete(visualElement);
 | |
|         },
 | |
|         start(definition, transitionOverride) {
 | |
|             indexLegacy.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
 | |
|             const animations = [];
 | |
|             subscribers.forEach((visualElement) => {
 | |
|                 animations.push(animateVisualElement(visualElement, definition, {
 | |
|                     transitionOverride,
 | |
|                 }));
 | |
|             });
 | |
|             return Promise.all(animations);
 | |
|         },
 | |
|         set(definition) {
 | |
|             indexLegacy.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
 | |
|             return subscribers.forEach((visualElement) => {
 | |
|                 indexLegacy.setValues(visualElement, definition);
 | |
|             });
 | |
|         },
 | |
|         stop() {
 | |
|             subscribers.forEach((visualElement) => {
 | |
|                 stopAnimation(visualElement);
 | |
|             });
 | |
|         },
 | |
|         mount() {
 | |
|             hasMounted = true;
 | |
|             return () => {
 | |
|                 hasMounted = false;
 | |
|                 controls.stop();
 | |
|             };
 | |
|         },
 | |
|     };
 | |
|     return controls;
 | |
| }
 | |
| 
 | |
| function useAnimate() {
 | |
|     const scope = useConstant(() => ({
 | |
|         current: null,
 | |
|         animations: [],
 | |
|     }));
 | |
|     const animate = useConstant(() => indexLegacy.createScopedAnimate(scope));
 | |
|     useUnmountEffect(() => {
 | |
|         scope.animations.forEach((animation) => animation.stop());
 | |
|     });
 | |
|     return [scope, animate];
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates `AnimationControls`, which can be used to manually start, stop
 | |
|  * and sequence animations on one or more components.
 | |
|  *
 | |
|  * The returned `AnimationControls` should be passed to the `animate` property
 | |
|  * of the components you want to animate.
 | |
|  *
 | |
|  * These components can then be animated with the `start` method.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import * as React from 'react'
 | |
|  * import { motion, useAnimation } from 'framer-motion'
 | |
|  *
 | |
|  * export function MyComponent(props) {
 | |
|  *    const controls = useAnimation()
 | |
|  *
 | |
|  *    controls.start({
 | |
|  *        x: 100,
 | |
|  *        transition: { duration: 0.5 },
 | |
|  *    })
 | |
|  *
 | |
|  *    return <motion.div animate={controls} />
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @returns Animation controller with `start` and `stop` methods
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useAnimationControls() {
 | |
|     const controls = useConstant(animationControls);
 | |
|     useIsomorphicLayoutEffect(controls.mount, []);
 | |
|     return controls;
 | |
| }
 | |
| const useAnimation = useAnimationControls;
 | |
| 
 | |
| /**
 | |
|  * Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments.
 | |
|  *
 | |
|  * An index value can be passed to the returned `cycle` function to cycle to a specific index.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * import * as React from "react"
 | |
|  * import { motion, useCycle } from "framer-motion"
 | |
|  *
 | |
|  * export const MyComponent = () => {
 | |
|  *   const [x, cycleX] = useCycle(0, 50, 100)
 | |
|  *
 | |
|  *   return (
 | |
|  *     <motion.div
 | |
|  *       animate={{ x: x }}
 | |
|  *       onTap={() => cycleX()}
 | |
|  *      />
 | |
|  *    )
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @param items - items to cycle through
 | |
|  * @returns [currentState, cycleState]
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useCycle(...items) {
 | |
|     const index = React.useRef(0);
 | |
|     const [item, setItem] = React.useState(items[index.current]);
 | |
|     const runCycle = React.useCallback((next) => {
 | |
|         index.current =
 | |
|             typeof next !== "number"
 | |
|                 ? indexLegacy.wrap(0, items.length, index.current + 1)
 | |
|                 : next;
 | |
|         setItem(items[index.current]);
 | |
|     }, 
 | |
|     // The array will change on each call, but by putting items.length at
 | |
|     // the front of this array, we guarantee the dependency comparison will match up
 | |
|     // eslint-disable-next-line react-hooks/exhaustive-deps
 | |
|     [items.length, ...items]);
 | |
|     return [item, runCycle];
 | |
| }
 | |
| 
 | |
| function useInView(ref, { root, margin, amount, once = false } = {}) {
 | |
|     const [isInView, setInView] = React.useState(false);
 | |
|     React.useEffect(() => {
 | |
|         if (!ref.current || (once && isInView))
 | |
|             return;
 | |
|         const onEnter = () => {
 | |
|             setInView(true);
 | |
|             return once ? undefined : () => setInView(false);
 | |
|         };
 | |
|         const options = {
 | |
|             root: (root && root.current) || undefined,
 | |
|             margin,
 | |
|             amount,
 | |
|         };
 | |
|         return indexLegacy.inView(ref.current, onEnter, options);
 | |
|     }, [root, ref, margin, once, amount]);
 | |
|     return isInView;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const dragControls = useDragControls()
 | |
|  *
 | |
|  * function startDrag(event) {
 | |
|  *   dragControls.start(event, { snapToCursor: true })
 | |
|  * }
 | |
|  *
 | |
|  * return (
 | |
|  *   <>
 | |
|  *     <div onPointerDown={startDrag} />
 | |
|  *     <motion.div drag="x" dragControls={dragControls} />
 | |
|  *   </>
 | |
|  * )
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| class DragControls {
 | |
|     constructor() {
 | |
|         this.componentControls = new Set();
 | |
|     }
 | |
|     /**
 | |
|      * Subscribe a component's internal `VisualElementDragControls` to the user-facing API.
 | |
|      *
 | |
|      * @internal
 | |
|      */
 | |
|     subscribe(controls) {
 | |
|         this.componentControls.add(controls);
 | |
|         return () => this.componentControls.delete(controls);
 | |
|     }
 | |
|     /**
 | |
|      * Start a drag gesture on every `motion` component that has this set of drag controls
 | |
|      * passed into it via the `dragControls` prop.
 | |
|      *
 | |
|      * ```jsx
 | |
|      * dragControls.start(e, {
 | |
|      *   snapToCursor: true
 | |
|      * })
 | |
|      * ```
 | |
|      *
 | |
|      * @param event - PointerEvent
 | |
|      * @param options - Options
 | |
|      *
 | |
|      * @public
 | |
|      */
 | |
|     start(event, options) {
 | |
|         this.componentControls.forEach((controls) => {
 | |
|             controls.start(event.nativeEvent || event, options);
 | |
|         });
 | |
|     }
 | |
| }
 | |
| const createDragControls = () => new DragControls();
 | |
| /**
 | |
|  * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop
 | |
|  * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we
 | |
|  * might want to initiate that dragging from a different component than the draggable one.
 | |
|  *
 | |
|  * By creating a `dragControls` using the `useDragControls` hook, we can pass this into
 | |
|  * the draggable component's `dragControls` prop. It exposes a `start` method
 | |
|  * that can start dragging from pointer events on other components.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const dragControls = useDragControls()
 | |
|  *
 | |
|  * function startDrag(event) {
 | |
|  *   dragControls.start(event, { snapToCursor: true })
 | |
|  * }
 | |
|  *
 | |
|  * return (
 | |
|  *   <>
 | |
|  *     <div onPointerDown={startDrag} />
 | |
|  *     <motion.div drag="x" dragControls={dragControls} />
 | |
|  *   </>
 | |
|  * )
 | |
|  * ```
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useDragControls() {
 | |
|     return useConstant(createDragControls);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Attaches an event listener directly to the provided DOM element.
 | |
|  *
 | |
|  * Bypassing React's event system can be desirable, for instance when attaching non-passive
 | |
|  * event handlers.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const ref = useRef(null)
 | |
|  *
 | |
|  * useDomEvent(ref, 'wheel', onWheel, { passive: false })
 | |
|  *
 | |
|  * return <div ref={ref} />
 | |
|  * ```
 | |
|  *
 | |
|  * @param ref - React.RefObject that's been provided to the element you want to bind the listener to.
 | |
|  * @param eventName - Name of the event you want listen for.
 | |
|  * @param handler - Function to fire when receiving the event.
 | |
|  * @param options - Options to pass to `Event.addEventListener`.
 | |
|  *
 | |
|  * @public
 | |
|  */
 | |
| function useDomEvent(ref, eventName, handler, options) {
 | |
|     React.useEffect(() => {
 | |
|         const element = ref.current;
 | |
|         if (handler && element) {
 | |
|             return addDomEvent(element, eventName, handler, options);
 | |
|         }
 | |
|     }, [ref, eventName, handler, options]);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks if a component is a `motion` component.
 | |
|  */
 | |
| function isMotionComponent(component) {
 | |
|     return (component !== null &&
 | |
|         typeof component === "object" &&
 | |
|         motionComponentSymbol in component);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Unwraps a `motion` component and returns either a string for `motion.div` or
 | |
|  * the React component for `motion(Component)`.
 | |
|  *
 | |
|  * If the component is not a `motion` component it returns undefined.
 | |
|  */
 | |
| function unwrapMotionComponent(component) {
 | |
|     if (isMotionComponent(component)) {
 | |
|         return component[motionComponentSymbol];
 | |
|     }
 | |
|     return undefined;
 | |
| }
 | |
| 
 | |
| function useInstantLayoutTransition() {
 | |
|     return startTransition;
 | |
| }
 | |
| function startTransition(callback) {
 | |
|     if (!rootProjectionNode.current)
 | |
|         return;
 | |
|     rootProjectionNode.current.isUpdating = false;
 | |
|     rootProjectionNode.current.blockUpdate();
 | |
|     callback && callback();
 | |
| }
 | |
| 
 | |
| function useInstantTransition() {
 | |
|     const [forceUpdate, forcedRenderCount] = useForceUpdate();
 | |
|     const startInstantLayoutTransition = useInstantLayoutTransition();
 | |
|     const unlockOnFrameRef = React.useRef();
 | |
|     React.useEffect(() => {
 | |
|         /**
 | |
|          * Unblock after two animation frames, otherwise this will unblock too soon.
 | |
|          */
 | |
|         indexLegacy.frame.postRender(() => indexLegacy.frame.postRender(() => {
 | |
|             /**
 | |
|              * If the callback has been called again after the effect
 | |
|              * triggered this 2 frame delay, don't unblock animations. This
 | |
|              * prevents the previous effect from unblocking the current
 | |
|              * instant transition too soon. This becomes more likely when
 | |
|              * used in conjunction with React.startTransition().
 | |
|              */
 | |
|             if (forcedRenderCount !== unlockOnFrameRef.current)
 | |
|                 return;
 | |
|             indexLegacy.instantAnimationState.current = false;
 | |
|         }));
 | |
|     }, [forcedRenderCount]);
 | |
|     return (callback) => {
 | |
|         startInstantLayoutTransition(() => {
 | |
|             indexLegacy.instantAnimationState.current = true;
 | |
|             forceUpdate();
 | |
|             callback();
 | |
|             unlockOnFrameRef.current = forcedRenderCount + 1;
 | |
|         });
 | |
|     };
 | |
| }
 | |
| function disableInstantTransitions() {
 | |
|     indexLegacy.instantAnimationState.current = false;
 | |
| }
 | |
| 
 | |
| function useResetProjection() {
 | |
|     const reset = React__namespace.useCallback(() => {
 | |
|         const root = rootProjectionNode.current;
 | |
|         if (!root)
 | |
|             return;
 | |
|         root.resetTree();
 | |
|     }, []);
 | |
|     return reset;
 | |
| }
 | |
| 
 | |
| const appearStoreId = (id, value) => `${id}: ${value}`;
 | |
| 
 | |
| const appearAnimationStore = new Map();
 | |
| 
 | |
| let handoffFrameTime;
 | |
| function handoffOptimizedAppearAnimation(elementId, valueName, 
 | |
| /**
 | |
|  * Legacy arguments. This function is inlined as part of SSG so it can be there's
 | |
|  * a version mismatch between the main included Motion and the inlined script.
 | |
|  *
 | |
|  * Remove in early 2024.
 | |
|  */
 | |
| _value, _frame) {
 | |
|     const optimisedValueName = indexLegacy.transformProps.has(valueName)
 | |
|         ? "transform"
 | |
|         : valueName;
 | |
|     const storeId = appearStoreId(elementId, optimisedValueName);
 | |
|     const optimisedAnimation = appearAnimationStore.get(storeId);
 | |
|     if (!optimisedAnimation) {
 | |
|         return null;
 | |
|     }
 | |
|     const { animation, startTime } = optimisedAnimation;
 | |
|     const cancelAnimation = () => {
 | |
|         appearAnimationStore.delete(storeId);
 | |
|         try {
 | |
|             animation.cancel();
 | |
|         }
 | |
|         catch (error) { }
 | |
|     };
 | |
|     /**
 | |
|      * If the startTime is null, this animation is the Paint Ready detection animation
 | |
|      * and we can cancel it immediately without handoff.
 | |
|      *
 | |
|      * Or if we've already handed off the animation then we're now interrupting it.
 | |
|      * In which case we need to cancel it.
 | |
|      */
 | |
|     if (startTime === null || window.HandoffComplete) {
 | |
|         cancelAnimation();
 | |
|         return null;
 | |
|     }
 | |
|     else {
 | |
|         /**
 | |
|          * Otherwise we're handing off this animation to the main thread.
 | |
|          *
 | |
|          * Record the time of the first handoff. We call performance.now() once
 | |
|          * here and once in startOptimisedAnimation to ensure we're getting
 | |
|          * close to a frame-locked time. This keeps all animations in sync.
 | |
|          */
 | |
|         if (handoffFrameTime === undefined) {
 | |
|             handoffFrameTime = performance.now();
 | |
|         }
 | |
|         /**
 | |
|          * We use main thread timings vs those returned by Animation.currentTime as it
 | |
|          * can be the case, particularly in Firefox, that currentTime doesn't return
 | |
|          * an updated value for several frames, even as the animation plays smoothly via
 | |
|          * the GPU.
 | |
|          */
 | |
|         return handoffFrameTime - startTime || 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A single time to use across all animations to manually set startTime
 | |
|  * and ensure they're all in sync.
 | |
|  */
 | |
| let startFrameTime;
 | |
| /**
 | |
|  * A dummy animation to detect when Chrome is ready to start
 | |
|  * painting the page and hold off from triggering the real animation
 | |
|  * until then. We only need one animation to detect paint ready.
 | |
|  *
 | |
|  * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850
 | |
|  */
 | |
| let readyAnimation;
 | |
| function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) {
 | |
|     // Prevent optimised appear animations if Motion has already started animating.
 | |
|     if (window.HandoffComplete) {
 | |
|         window.HandoffAppearAnimations = undefined;
 | |
|         return;
 | |
|     }
 | |
|     const id = element.dataset[indexLegacy.optimizedAppearDataId];
 | |
|     if (!id)
 | |
|         return;
 | |
|     window.HandoffAppearAnimations = handoffOptimizedAppearAnimation;
 | |
|     const storeId = appearStoreId(id, name);
 | |
|     if (!readyAnimation) {
 | |
|         readyAnimation = indexLegacy.animateStyle(element, name, [keyframes[0], keyframes[0]], 
 | |
|         /**
 | |
|          * 10 secs is basically just a super-safe duration to give Chrome
 | |
|          * long enough to get the animation ready.
 | |
|          */
 | |
|         { duration: 10000, ease: "linear" });
 | |
|         appearAnimationStore.set(storeId, {
 | |
|             animation: readyAnimation,
 | |
|             startTime: null,
 | |
|         });
 | |
|     }
 | |
|     const startAnimation = () => {
 | |
|         readyAnimation.cancel();
 | |
|         const appearAnimation = indexLegacy.animateStyle(element, name, keyframes, options);
 | |
|         /**
 | |
|          * Record the time of the first started animation. We call performance.now() once
 | |
|          * here and once in handoff to ensure we're getting
 | |
|          * close to a frame-locked time. This keeps all animations in sync.
 | |
|          */
 | |
|         if (startFrameTime === undefined) {
 | |
|             startFrameTime = performance.now();
 | |
|         }
 | |
|         appearAnimation.startTime = startFrameTime;
 | |
|         appearAnimationStore.set(storeId, {
 | |
|             animation: appearAnimation,
 | |
|             startTime: startFrameTime,
 | |
|         });
 | |
|         if (onReady)
 | |
|             onReady(appearAnimation);
 | |
|     };
 | |
|     if (readyAnimation.ready) {
 | |
|         readyAnimation.ready.then(startAnimation).catch(indexLegacy.noop);
 | |
|     }
 | |
|     else {
 | |
|         startAnimation();
 | |
|     }
 | |
| }
 | |
| 
 | |
| const createObject = () => ({});
 | |
| class StateVisualElement extends indexLegacy.VisualElement {
 | |
|     build() { }
 | |
|     measureInstanceViewportBox() {
 | |
|         return indexLegacy.createBox();
 | |
|     }
 | |
|     resetTransform() { }
 | |
|     restoreTransform() { }
 | |
|     removeValueFromRenderState() { }
 | |
|     renderInstance() { }
 | |
|     scrapeMotionValuesFromProps() {
 | |
|         return createObject();
 | |
|     }
 | |
|     getBaseTargetFromProps() {
 | |
|         return undefined;
 | |
|     }
 | |
|     readValueFromInstance(_state, key, options) {
 | |
|         return options.initialState[key] || 0;
 | |
|     }
 | |
|     sortInstanceNodePosition() {
 | |
|         return 0;
 | |
|     }
 | |
|     makeTargetAnimatableFromInstance({ transition, transitionEnd, ...target }) {
 | |
|         const origin = indexLegacy.getOrigin(target, transition || {}, this);
 | |
|         indexLegacy.checkTargetForNewValues(this, target, origin);
 | |
|         return { transition, transitionEnd, ...target };
 | |
|     }
 | |
| }
 | |
| const useVisualState = makeUseVisualState({
 | |
|     scrapeMotionValuesFromProps: createObject,
 | |
|     createRenderState: createObject,
 | |
| });
 | |
| /**
 | |
|  * This is not an officially supported API and may be removed
 | |
|  * on any version.
 | |
|  */
 | |
| function useAnimatedState(initialState) {
 | |
|     const [animationState, setAnimationState] = React.useState(initialState);
 | |
|     const visualState = useVisualState({}, false);
 | |
|     const element = useConstant(() => {
 | |
|         return new StateVisualElement({ props: {}, visualState, presenceContext: null }, { initialState });
 | |
|     });
 | |
|     React.useEffect(() => {
 | |
|         element.mount({});
 | |
|         return () => element.unmount();
 | |
|     }, [element]);
 | |
|     React.useEffect(() => {
 | |
|         element.update({
 | |
|             onUpdate: (v) => {
 | |
|                 setAnimationState({ ...v });
 | |
|             },
 | |
|         }, null);
 | |
|     }, [setAnimationState, element]);
 | |
|     const startAnimation = useConstant(() => (animationDefinition) => {
 | |
|         return animateVisualElement(element, animationDefinition);
 | |
|     });
 | |
|     return [animationState, startAnimation];
 | |
| }
 | |
| 
 | |
| // Keep things reasonable and avoid scale: Infinity. In practise we might need
 | |
| // to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1]
 | |
| // to simply hide content at unreasonable scales.
 | |
| const maxScale = 100000;
 | |
| const invertScale = (scale) => scale > 0.001 ? 1 / scale : maxScale;
 | |
| let hasWarned = false;
 | |
| /**
 | |
|  * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse
 | |
|  * of their respective parent scales.
 | |
|  *
 | |
|  * This is useful for undoing the distortion of content when scaling a parent component.
 | |
|  *
 | |
|  * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent.
 | |
|  * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output
 | |
|  * of those instead.
 | |
|  *
 | |
|  * ```jsx
 | |
|  * const MyComponent = () => {
 | |
|  *   const { scaleX, scaleY } = useInvertedScale()
 | |
|  *   return <motion.div style={{ scaleX, scaleY }} />
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @deprecated
 | |
|  */
 | |
| function useInvertedScale(scale) {
 | |
|     let parentScaleX = useMotionValue(1);
 | |
|     let parentScaleY = useMotionValue(1);
 | |
|     const { visualElement } = React.useContext(MotionContext);
 | |
|     indexLegacy.invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component.");
 | |
|     indexLegacy.warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead.");
 | |
|     hasWarned = true;
 | |
|     if (scale) {
 | |
|         parentScaleX = scale.scaleX || parentScaleX;
 | |
|         parentScaleY = scale.scaleY || parentScaleY;
 | |
|     }
 | |
|     else if (visualElement) {
 | |
|         parentScaleX = visualElement.getValue("scaleX", 1);
 | |
|         parentScaleY = visualElement.getValue("scaleY", 1);
 | |
|     }
 | |
|     const scaleX = useTransform(parentScaleX, invertScale);
 | |
|     const scaleY = useTransform(parentScaleY, invertScale);
 | |
|     return { scaleX, scaleY };
 | |
| }
 | |
| 
 | |
| let id = 0;
 | |
| const AnimateSharedLayout = ({ children }) => {
 | |
|     React__namespace.useEffect(() => {
 | |
|         indexLegacy.invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations");
 | |
|     }, []);
 | |
|     return (React__namespace.createElement(LayoutGroup, { id: useConstant(() => `asl-${id++}`) }, children));
 | |
| };
 | |
| 
 | |
| exports.MotionGlobalConfig = indexLegacy.MotionGlobalConfig;
 | |
| exports.MotionValue = indexLegacy.MotionValue;
 | |
| exports.VisualElement = indexLegacy.VisualElement;
 | |
| exports.addScaleCorrector = indexLegacy.addScaleCorrector;
 | |
| exports.animate = indexLegacy.animate;
 | |
| exports.animateValue = indexLegacy.animateValue;
 | |
| exports.anticipate = indexLegacy.anticipate;
 | |
| exports.backIn = indexLegacy.backIn;
 | |
| exports.backInOut = indexLegacy.backInOut;
 | |
| exports.backOut = indexLegacy.backOut;
 | |
| exports.buildTransform = indexLegacy.buildTransform;
 | |
| exports.cancelFrame = indexLegacy.cancelFrame;
 | |
| exports.cancelSync = indexLegacy.cancelSync;
 | |
| exports.checkTargetForNewValues = indexLegacy.checkTargetForNewValues;
 | |
| exports.circIn = indexLegacy.circIn;
 | |
| exports.circInOut = indexLegacy.circInOut;
 | |
| exports.circOut = indexLegacy.circOut;
 | |
| exports.clamp = indexLegacy.clamp;
 | |
| exports.color = indexLegacy.color;
 | |
| exports.complex = indexLegacy.complex;
 | |
| exports.createBox = indexLegacy.createBox;
 | |
| exports.createScopedAnimate = indexLegacy.createScopedAnimate;
 | |
| exports.cubicBezier = indexLegacy.cubicBezier;
 | |
| exports.delay = indexLegacy.delay;
 | |
| exports.distance = indexLegacy.distance;
 | |
| exports.distance2D = indexLegacy.distance2D;
 | |
| exports.easeIn = indexLegacy.easeIn;
 | |
| exports.easeInOut = indexLegacy.easeInOut;
 | |
| exports.easeOut = indexLegacy.easeOut;
 | |
| exports.frame = indexLegacy.frame;
 | |
| exports.frameData = indexLegacy.frameData;
 | |
| exports.inView = indexLegacy.inView;
 | |
| exports.interpolate = indexLegacy.interpolate;
 | |
| Object.defineProperty(exports, 'invariant', {
 | |
|     enumerable: true,
 | |
|     get: function () { return indexLegacy.invariant; }
 | |
| });
 | |
| exports.isBrowser = indexLegacy.isBrowser;
 | |
| exports.isMotionValue = indexLegacy.isMotionValue;
 | |
| exports.mirrorEasing = indexLegacy.mirrorEasing;
 | |
| exports.mix = indexLegacy.mix;
 | |
| exports.motionValue = indexLegacy.motionValue;
 | |
| exports.optimizedAppearDataAttribute = indexLegacy.optimizedAppearDataAttribute;
 | |
| exports.pipe = indexLegacy.pipe;
 | |
| exports.progress = indexLegacy.progress;
 | |
| exports.px = indexLegacy.px;
 | |
| exports.reverseEasing = indexLegacy.reverseEasing;
 | |
| exports.scroll = indexLegacy.scroll;
 | |
| exports.scrollInfo = indexLegacy.scrollInfo;
 | |
| exports.spring = indexLegacy.spring;
 | |
| exports.stagger = indexLegacy.stagger;
 | |
| exports.steps = indexLegacy.steps;
 | |
| exports.sync = indexLegacy.sync;
 | |
| exports.transform = indexLegacy.transform;
 | |
| exports.visualElementStore = indexLegacy.visualElementStore;
 | |
| Object.defineProperty(exports, 'warning', {
 | |
|     enumerable: true,
 | |
|     get: function () { return indexLegacy.warning; }
 | |
| });
 | |
| exports.wrap = indexLegacy.wrap;
 | |
| exports.AnimatePresence = AnimatePresence;
 | |
| exports.AnimateSharedLayout = AnimateSharedLayout;
 | |
| exports.DeprecatedLayoutGroupContext = DeprecatedLayoutGroupContext;
 | |
| exports.DragControls = DragControls;
 | |
| exports.FlatTree = FlatTree;
 | |
| exports.LayoutGroup = LayoutGroup;
 | |
| exports.LayoutGroupContext = LayoutGroupContext;
 | |
| exports.LazyMotion = LazyMotion;
 | |
| exports.MotionConfig = MotionConfig;
 | |
| exports.MotionConfigContext = MotionConfigContext;
 | |
| exports.MotionContext = MotionContext;
 | |
| exports.PresenceContext = PresenceContext;
 | |
| exports.Reorder = Reorder;
 | |
| exports.SwitchLayoutGroupContext = SwitchLayoutGroupContext;
 | |
| exports.addPointerEvent = addPointerEvent;
 | |
| exports.addPointerInfo = addPointerInfo;
 | |
| exports.animateVisualElement = animateVisualElement;
 | |
| exports.animationControls = animationControls;
 | |
| exports.animations = animations;
 | |
| exports.calcLength = calcLength;
 | |
| exports.createDomMotionComponent = createDomMotionComponent;
 | |
| exports.createMotionComponent = createMotionComponent;
 | |
| exports.disableInstantTransitions = disableInstantTransitions;
 | |
| exports.domAnimation = domAnimation;
 | |
| exports.domMax = domMax;
 | |
| exports.filterProps = filterProps;
 | |
| exports.isDragActive = isDragActive;
 | |
| exports.isMotionComponent = isMotionComponent;
 | |
| exports.isValidMotionProp = isValidMotionProp;
 | |
| exports.m = m;
 | |
| exports.makeUseVisualState = makeUseVisualState;
 | |
| exports.motion = motion;
 | |
| exports.resolveMotionValue = resolveMotionValue;
 | |
| exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
 | |
| exports.unwrapMotionComponent = unwrapMotionComponent;
 | |
| exports.useAnimate = useAnimate;
 | |
| exports.useAnimation = useAnimation;
 | |
| exports.useAnimationControls = useAnimationControls;
 | |
| exports.useAnimationFrame = useAnimationFrame;
 | |
| exports.useCycle = useCycle;
 | |
| exports.useDeprecatedAnimatedState = useAnimatedState;
 | |
| exports.useDeprecatedInvertedScale = useInvertedScale;
 | |
| exports.useDomEvent = useDomEvent;
 | |
| exports.useDragControls = useDragControls;
 | |
| exports.useElementScroll = useElementScroll;
 | |
| exports.useForceUpdate = useForceUpdate;
 | |
| exports.useInView = useInView;
 | |
| exports.useInstantLayoutTransition = useInstantLayoutTransition;
 | |
| exports.useInstantTransition = useInstantTransition;
 | |
| exports.useIsPresent = useIsPresent;
 | |
| exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
 | |
| exports.useMotionTemplate = useMotionTemplate;
 | |
| exports.useMotionValue = useMotionValue;
 | |
| exports.useMotionValueEvent = useMotionValueEvent;
 | |
| exports.usePresence = usePresence;
 | |
| exports.useReducedMotion = useReducedMotion;
 | |
| exports.useReducedMotionConfig = useReducedMotionConfig;
 | |
| exports.useResetProjection = useResetProjection;
 | |
| exports.useScroll = useScroll;
 | |
| exports.useSpring = useSpring;
 | |
| exports.useTime = useTime;
 | |
| exports.useTransform = useTransform;
 | |
| exports.useUnmountEffect = useUnmountEffect;
 | |
| exports.useVelocity = useVelocity;
 | |
| exports.useViewportScroll = useViewportScroll;
 | |
| exports.useWillChange = useWillChange;
 |