UNPKG

191 kBJavaScriptView Raw
1import { __assign, __spreadArrays, __rest, __extends } from 'tslib';
2import { useRef, memo, useEffect, createContext, useMemo, useContext, forwardRef, createElement, Fragment, useCallback, useState, Component, cloneElement, Children, isValidElement } from 'react';
3import sync, { getFrameData, cancelSync } from 'framesync';
4import { velocityPerSecond, distance, mix, interpolate, wrap } from '@popmotion/popcorn';
5import styler, { createStylerFactory, buildStyleProperty, isTransformProp, transformProps, buildSVGAttrs } from 'stylefire';
6import { invariant, warning } from 'hey-listen';
7import { color, complex, number, px, percent, degrees, vw, vh } from 'style-value-types';
8import { action, delay, tween, spring, keyframes as keyframes$1, inertia } from 'popmotion';
9import * as easingLookup from '@popmotion/easing';
10import { cubicBezier, linear } from '@popmotion/easing';
11
12var isFloat = function (value) {
13 return !isNaN(parseFloat(value));
14};
15/**
16 * `MotionValue` is used to track the state and velocity of motion values.
17 *
18 * @public
19 */
20var MotionValue = /** @class */ (function () {
21 /**
22 * @param init - The initiating value
23 * @param config - Optional configuration options
24 *
25 * - `transformer`: A function to transform incoming values with.
26 *
27 * @internal
28 */
29 function MotionValue(init, _a) {
30 var _this = this;
31 var _b = _a === void 0 ? {} : _a, transformer = _b.transformer, parent = _b.parent;
32 /**
33 * Duration, in milliseconds, since last updating frame.
34 *
35 * @internal
36 */
37 this.timeDelta = 0;
38 /**
39 * Timestamp of the last time this `MotionValue` was updated.
40 *
41 * @internal
42 */
43 this.lastUpdated = 0;
44 /**
45 * Tracks whether this value can output a velocity. Currently this is only true
46 * if the value is numerical, but we might be able to widen the scope here and support
47 * other value types.
48 *
49 * @internal
50 */
51 this.canTrackVelocity = false;
52 this.updateAndNotify = function (v, render) {
53 if (render === void 0) { render = true; }
54 _this.prev = _this.current;
55 _this.current = _this.transformer ? _this.transformer(v) : v;
56 if (_this.updateSubscribers && _this.prev !== _this.current) {
57 _this.updateSubscribers.forEach(_this.notifySubscriber);
58 }
59 if (_this.children) {
60 _this.children.forEach(_this.setChild);
61 }
62 if (render && _this.renderSubscribers) {
63 _this.renderSubscribers.forEach(_this.notifySubscriber);
64 }
65 // Update timestamp
66 var _a = getFrameData(), delta = _a.delta, timestamp = _a.timestamp;
67 if (_this.lastUpdated !== timestamp) {
68 _this.timeDelta = delta;
69 _this.lastUpdated = timestamp;
70 sync.postRender(_this.scheduleVelocityCheck);
71 }
72 };
73 /**
74 * Notify a subscriber with the latest value.
75 *
76 * This is an instanced and bound function to prevent generating a new
77 * function once per frame.
78 *
79 * @param subscriber - The subscriber to notify.
80 *
81 * @internal
82 */
83 this.notifySubscriber = function (subscriber) {
84 subscriber(_this.current);
85 };
86 /**
87 * Schedule a velocity check for the next frame.
88 *
89 * This is an instanced and bound function to prevent generating a new
90 * function once per frame.
91 *
92 * @internal
93 */
94 this.scheduleVelocityCheck = function () { return sync.postRender(_this.velocityCheck); };
95 /**
96 * Updates `prev` with `current` if the value hasn't been updated this frame.
97 * This ensures velocity calculations return `0`.
98 *
99 * This is an instanced and bound function to prevent generating a new
100 * function once per frame.
101 *
102 * @internal
103 */
104 this.velocityCheck = function (_a) {
105 var timestamp = _a.timestamp;
106 if (timestamp !== _this.lastUpdated) {
107 _this.prev = _this.current;
108 }
109 };
110 /**
111 * Updates child `MotionValue`.
112 *
113 * @param child - Child `MotionValue`.
114 *
115 * @internal
116 */
117 this.setChild = function (child) { return child.set(_this.current); };
118 this.parent = parent;
119 this.transformer = transformer;
120 this.set(init, false);
121 this.canTrackVelocity = isFloat(this.current);
122 }
123 /**
124 * Creates a new `MotionValue` that's subscribed to the output of this one.
125 *
126 * @param config - Optional configuration options
127 *
128 * - `transformer`: A function to transform incoming values with.
129 *
130 * @internal
131 */
132 MotionValue.prototype.addChild = function (config) {
133 if (config === void 0) { config = {}; }
134 var child = new MotionValue(this.current, __assign({ parent: this }, config));
135 if (!this.children)
136 this.children = new Set();
137 this.children.add(child);
138 return child;
139 };
140 /**
141 * Stops a `MotionValue` from being subscribed to this one.
142 *
143 * @param child - The subscribed `MotionValue`
144 *
145 * @internal
146 */
147 MotionValue.prototype.removeChild = function (child) {
148 if (!this.children) {
149 return;
150 }
151 this.children.delete(child);
152 };
153 /**
154 * Subscribes a subscriber function to a subscription list.
155 *
156 * @param subscriptions - A `Set` of subscribers.
157 * @param subscription - A subscriber function.
158 */
159 MotionValue.prototype.subscribeTo = function (subscriptions, subscription) {
160 var _this = this;
161 var updateSubscriber = function () { return subscription(_this.current); };
162 subscriptions.add(updateSubscriber);
163 return function () { return subscriptions.delete(updateSubscriber); };
164 };
165 /**
166 * Adds a function that will be notified when the `MotionValue` is updated.
167 *
168 * It returns a function that, when called, will cancel the subscription.
169 *
170 * When calling `onChange` inside a React component, it should be wrapped with the
171 * `useEffect` hook. As it returns an unsubscribe function, this should be returned
172 * from the `useEffect` function to ensure you don't add duplicate subscribers..
173 *
174 * @library
175 *
176 * ```jsx
177 * function MyComponent() {
178 * const x = useMotionValue(0)
179 * const y = useMotionValue(0)
180 * const opacity = useMotionValue(1)
181 *
182 * useEffect(() => {
183 * function updateOpacity() {
184 * const maxXY = Math.max(x.get(), y.get())
185 * const newOpacity = transform(maxXY, [0, 100], [1, 0])
186 * opacity.set(newOpacity)
187 * }
188 *
189 * const unsubscribeX = x.onChange(updateOpacity)
190 * const unsubscribeY = y.onChange(updateOpacity)
191 *
192 * return () => {
193 * unsubscribeX()
194 * unsubscribeY()
195 * }
196 * }, [])
197 *
198 * return <Frame x={x} />
199 * }
200 * ```
201 *
202 * @motion
203 *
204 * ```jsx
205 * export const MyComponent = () => {
206 * const x = useMotionValue(0)
207 * const y = useMotionValue(0)
208 * const opacity = useMotionValue(1)
209 *
210 * useEffect(() => {
211 * function updateOpacity() {
212 * const maxXY = Math.max(x.get(), y.get())
213 * const newOpacity = transform(maxXY, [0, 100], [1, 0])
214 * opacity.set(newOpacity)
215 * }
216 *
217 * const unsubscribeX = x.onChange(updateOpacity)
218 * const unsubscribeY = y.onChange(updateOpacity)
219 *
220 * return () => {
221 * unsubscribeX()
222 * unsubscribeY()
223 * }
224 * }, [])
225 *
226 * return <motion.div style={{ x }} />
227 * }
228 * ```
229 *
230 * @internalremarks
231 *
232 * We could look into a `useOnChange` hook if the above lifecycle management proves confusing.
233 *
234 * ```jsx
235 * useOnChange(x, () => {})
236 * ```
237 *
238 * @param subscriber - A function that receives the latest value.
239 * @returns A function that, when called, will cancel this subscription.
240 *
241 * @public
242 */
243 MotionValue.prototype.onChange = function (subscription) {
244 if (!this.updateSubscribers)
245 this.updateSubscribers = new Set();
246 return this.subscribeTo(this.updateSubscribers, subscription);
247 };
248 /**
249 * Adds a function that will be notified when the `MotionValue` requests a render.
250 *
251 * @param subscriber - A function that's provided the latest value.
252 * @returns A function that, when called, will cancel this subscription.
253 *
254 * @internal
255 */
256 MotionValue.prototype.onRenderRequest = function (subscription) {
257 if (!this.renderSubscribers)
258 this.renderSubscribers = new Set();
259 // Render immediately
260 this.notifySubscriber(subscription);
261 return this.subscribeTo(this.renderSubscribers, subscription);
262 };
263 /**
264 * Attaches a passive effect to the `MotionValue`.
265 *
266 * @internal
267 */
268 MotionValue.prototype.attach = function (passiveEffect) {
269 this.passiveEffect = passiveEffect;
270 };
271 /**
272 * Sets the state of the `MotionValue`.
273 *
274 * @remarks
275 *
276 * ```jsx
277 * const x = useMotionValue(0)
278 * x.set(10)
279 * ```
280 *
281 * @param latest - Latest value to set.
282 * @param render - Whether to notify render subscribers. Defaults to `true`
283 *
284 * @public
285 */
286 MotionValue.prototype.set = function (v, render) {
287 if (render === void 0) { render = true; }
288 if (!render || !this.passiveEffect) {
289 this.updateAndNotify(v, render);
290 }
291 else {
292 this.passiveEffect(v, this.updateAndNotify);
293 }
294 };
295 /**
296 * Returns the latest state of `MotionValue`
297 *
298 * @returns - The latest state of `MotionValue`
299 *
300 * @public
301 */
302 MotionValue.prototype.get = function () {
303 return this.current;
304 };
305 /**
306 * Returns the latest velocity of `MotionValue`
307 *
308 * @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
309 *
310 * @public
311 */
312 MotionValue.prototype.getVelocity = function () {
313 // This could be isFloat(this.prev) && isFloat(this.current), but that would be wasteful
314 return this.canTrackVelocity
315 ? // These casts could be avoided if parseFloat would be typed better
316 velocityPerSecond(parseFloat(this.current) -
317 parseFloat(this.prev), this.timeDelta)
318 : 0;
319 };
320 /**
321 * Registers a new animation to control this `MotionValue`. Only one
322 * animation can drive a `MotionValue` at one time.
323 *
324 * ```jsx
325 * value.start()
326 * ```
327 *
328 * @param animation - A function that starts the provided animation
329 *
330 * @internal
331 */
332 MotionValue.prototype.start = function (animation) {
333 var _this = this;
334 this.stop();
335 return new Promise(function (resolve) {
336 _this.stopAnimation = animation(resolve);
337 }).then(function () { return _this.clearAnimation(); });
338 };
339 /**
340 * Stop the currently active animation.
341 *
342 * @public
343 */
344 MotionValue.prototype.stop = function () {
345 if (this.stopAnimation)
346 this.stopAnimation();
347 this.clearAnimation();
348 };
349 /**
350 * Returns `true` if this value is currently animating.
351 *
352 * @public
353 */
354 MotionValue.prototype.isAnimating = function () {
355 return !!this.stopAnimation;
356 };
357 MotionValue.prototype.clearAnimation = function () {
358 this.stopAnimation = null;
359 };
360 /**
361 * Destroy and clean up subscribers to this `MotionValue`.
362 *
363 * The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
364 * handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
365 * created a `MotionValue` via the `motionValue` function.
366 *
367 * @public
368 */
369 MotionValue.prototype.destroy = function () {
370 this.updateSubscribers && this.updateSubscribers.clear();
371 this.renderSubscribers && this.renderSubscribers.clear();
372 this.parent && this.parent.removeChild(this);
373 this.stop();
374 };
375 return MotionValue;
376}());
377/**
378 * @internal
379 */
380function motionValue(init, opts) {
381 return new MotionValue(init, opts);
382}
383
384/**
385 * Creates a constant value over the lifecycle of a component.
386 *
387 * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
388 * a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
389 * you can ensure that initialisers don't execute twice or more.
390 */
391function useConstant(init) {
392 var ref = useRef(null);
393 if (ref.current === null) {
394 ref.current = init();
395 }
396 return ref.current;
397}
398
399var isMotionValue = function (value) {
400 return value instanceof MotionValue;
401};
402
403// Creating a styler factory for the `onUpdate` prop allows all values
404// to fire and the `onUpdate` prop will only fire once per frame
405var updateStyler = createStylerFactory({
406 onRead: function () { return null; },
407 onRender: function (state, _a) {
408 var onUpdate = _a.onUpdate;
409 return onUpdate(state);
410 },
411});
412var MotionValuesMap = /** @class */ (function () {
413 function MotionValuesMap() {
414 this.hasMounted = false;
415 this.values = new Map();
416 this.unsubscribers = new Map();
417 }
418 MotionValuesMap.prototype.has = function (key) {
419 return this.values.has(key);
420 };
421 MotionValuesMap.prototype.set = function (key, value) {
422 this.values.set(key, value);
423 if (this.hasMounted) {
424 this.bindValueToOutput(key, value);
425 }
426 };
427 MotionValuesMap.prototype.get = function (key, defaultValue) {
428 var value = this.values.get(key);
429 if (value === undefined && defaultValue !== undefined) {
430 value = new MotionValue(defaultValue);
431 this.set(key, value);
432 }
433 return value;
434 };
435 MotionValuesMap.prototype.forEach = function (callback) {
436 return this.values.forEach(callback);
437 };
438 MotionValuesMap.prototype.bindValueToOutput = function (key, value) {
439 var _this = this;
440 var onRender = function (v) { return _this.output && _this.output(key, v); };
441 var unsubscribeOnRender = value.onRenderRequest(onRender);
442 var onChange = function (v) {
443 _this.onUpdate && _this.onUpdate.set(key, v);
444 };
445 var unsubscribeOnChange = value.onChange(onChange);
446 if (this.unsubscribers.has(key)) {
447 this.unsubscribers.get(key)();
448 }
449 this.unsubscribers.set(key, function () {
450 unsubscribeOnRender();
451 unsubscribeOnChange();
452 });
453 };
454 MotionValuesMap.prototype.setOnUpdate = function (onUpdate) {
455 this.onUpdate = undefined;
456 if (onUpdate) {
457 this.onUpdate = updateStyler({ onUpdate: onUpdate });
458 }
459 };
460 MotionValuesMap.prototype.setTransformTemplate = function (transformTemplate) {
461 if (this.transformTemplate !== transformTemplate) {
462 this.transformTemplate = transformTemplate;
463 this.updateTransformTemplate();
464 }
465 };
466 MotionValuesMap.prototype.getTransformTemplate = function () {
467 return this.transformTemplate;
468 };
469 MotionValuesMap.prototype.updateTransformTemplate = function () {
470 if (this.output) {
471 this.output("transform", this.transformTemplate);
472 }
473 };
474 MotionValuesMap.prototype.mount = function (output) {
475 var _this = this;
476 this.hasMounted = true;
477 if (output)
478 this.output = output;
479 this.values.forEach(function (value, key) { return _this.bindValueToOutput(key, value); });
480 this.updateTransformTemplate();
481 };
482 MotionValuesMap.prototype.unmount = function () {
483 var _this = this;
484 this.values.forEach(function (_value, key) {
485 var unsubscribe = _this.unsubscribers.get(key);
486 unsubscribe && unsubscribe();
487 });
488 };
489 return MotionValuesMap;
490}());
491var specialMotionValueProps = new Set(["dragOriginX", "dragOriginY"]);
492var useMotionValues = function (props) {
493 var motionValues = useConstant(function () {
494 var map = new MotionValuesMap();
495 /**
496 * Loop through every prop and add any detected `MotionValue`s. This is SVG-specific
497 * code that should be extracted, perhaps considered hollistically with `useMotionStyles`.
498 *
499 * <motion.circle cx={motionValue(0)} />
500 */
501 for (var key in props) {
502 if (isMotionValue(props[key]) &&
503 !specialMotionValueProps.has(key)) {
504 map.set(key, props[key]);
505 }
506 }
507 return map;
508 });
509 motionValues.setOnUpdate(props.onUpdate);
510 motionValues.setTransformTemplate(props.transformTemplate);
511 return motionValues;
512};
513
514var session = null;
515var syncRenderSession = {
516 isOpen: function () { return session !== null; },
517 open: function () {
518 invariant(!session, "Sync render session already open");
519 session = [];
520 },
521 flush: function () {
522 invariant(session !== null, "No sync render session found");
523 session && session.forEach(function (styler) { return styler.render(); });
524 session = null;
525 },
526 push: function (styler) {
527 invariant(session !== null, "No sync render session found");
528 session && session.push(styler);
529 },
530};
531
532/**
533 * `useEffect` gets resolved bottom-up. We defer some optional functionality to child
534 * components, so to ensure everything runs correctly we export the ref-binding logic
535 * to a new component rather than in `useMotionValues`.
536 */
537var MountComponent = function (_a) {
538 var ref = _a.innerRef, values = _a.values, isStatic = _a.isStatic;
539 useEffect(function () {
540 invariant(ref.current instanceof Element, "No `ref` found. Ensure components created with `motion.custom` forward refs using `React.forwardRef`");
541 var domStyler = styler(ref.current, {
542 preparseOutput: false,
543 enableHardwareAcceleration: !isStatic,
544 });
545 values.mount(function (key, value) {
546 domStyler.set(key, value);
547 if (syncRenderSession.isOpen()) {
548 syncRenderSession.push(domStyler);
549 }
550 });
551 return function () { return values.unmount(); };
552 }, []);
553 return null;
554};
555var Mount = memo(MountComponent);
556
557var createValueResolver = function (resolver) { return function (values) {
558 var resolvedValues = {};
559 values.forEach(function (value, key) { return (resolvedValues[key] = resolver(value)); });
560 return resolvedValues;
561}; };
562var resolveCurrent = createValueResolver(function (value) { return value.get(); });
563
564var transformOriginProps = new Set(["originX", "originY", "originZ"]);
565var isTransformOriginProp = function (key) { return transformOriginProps.has(key); };
566var buildStyleAttr = function (values, styleProp, isStatic) {
567 var motionValueStyles = resolveCurrent(values);
568 var transformTemplate = values.getTransformTemplate();
569 if (transformTemplate) {
570 // If `transform` has been manually set as a string, pass that through the template
571 // otherwise pass it forward to Stylefire's style property builder
572 motionValueStyles.transform = styleProp.transform
573 ? transformTemplate({}, styleProp.transform)
574 : transformTemplate;
575 }
576 return buildStyleProperty(__assign(__assign({}, styleProp), motionValueStyles), !isStatic);
577};
578var useMotionStyles = function (values, styleProp, isStatic, transformValues) {
579 if (styleProp === void 0) { styleProp = {}; }
580 var style = {};
581 var prevMotionStyles = useRef({}).current;
582 for (var key in styleProp) {
583 var thisStyle = styleProp[key];
584 if (isMotionValue(thisStyle)) {
585 // If this is a motion value, add it to our MotionValuesMap
586 values.set(key, thisStyle);
587 }
588 else if (!isStatic &&
589 (isTransformProp(key) || isTransformOriginProp(key))) {
590 // Or if it's a transform prop, create a motion value (or update an existing one)
591 // to ensure Stylefire can reconcile all the transform values together.
592 // A further iteration on this would be to create a single styler per component that gets
593 // used in the DOM renderer's buildStyleAttr *and* animations, then we would only
594 // have to convert animating values to `MotionValues` (we could probably remove this entire function).
595 // The only architectural consideration is to allow Stylefire to have elements mounted after
596 // a styler is created.
597 if (!values.has(key)) {
598 // If it doesn't exist as a motion value, create it
599 values.set(key, motionValue(thisStyle));
600 }
601 else {
602 // Otherwise only update it if it's changed from a previous render
603 if (thisStyle !== prevMotionStyles[key]) {
604 var value = values.get(key);
605 value.set(thisStyle);
606 }
607 }
608 prevMotionStyles[key] = thisStyle;
609 }
610 else {
611 style[key] = thisStyle;
612 }
613 }
614 return transformValues ? transformValues(style) : style;
615};
616
617var isKeyframesTarget = function (v) {
618 return Array.isArray(v);
619};
620
621var isCustomValue = function (v) {
622 return Boolean(v && typeof v === "object" && v.mix && v.toValue);
623};
624var resolveFinalValueInKeyframes = function (v) {
625 // TODO maybe throw if v.length - 1 is placeholder token?
626 return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
627};
628
629var auto = {
630 test: function (v) { return v === "auto"; },
631 parse: function (v) { return v; },
632};
633var dimensionTypes = [number, px, percent, degrees, vw, vh, auto];
634var valueTypes = __spreadArrays(dimensionTypes, [color, complex]);
635var testValueType = function (v) { return function (type) { return type.test(v); }; };
636var getDimensionValueType = function (v) {
637 return dimensionTypes.find(testValueType(v));
638};
639var getValueType = function (v) { return valueTypes.find(testValueType(v)); };
640
641var underDampedSpring = function () { return ({
642 type: "spring",
643 stiffness: 500,
644 damping: 25,
645 restDelta: 0.5,
646 restSpeed: 10,
647}); };
648var overDampedSpring = function (to) { return ({
649 type: "spring",
650 stiffness: 700,
651 damping: to === 0 ? 100 : 35,
652}); };
653var linearTween = function () { return ({
654 ease: "linear",
655 duration: 0.3,
656}); };
657var keyframes = function (values) { return ({
658 type: "keyframes",
659 duration: 0.8,
660 values: values,
661}); };
662var defaultTransitions = {
663 x: underDampedSpring,
664 y: underDampedSpring,
665 z: underDampedSpring,
666 rotate: underDampedSpring,
667 rotateX: underDampedSpring,
668 rotateY: underDampedSpring,
669 rotateZ: underDampedSpring,
670 scaleX: overDampedSpring,
671 scaleY: overDampedSpring,
672 scale: overDampedSpring,
673 opacity: linearTween,
674 backgroundColor: linearTween,
675 color: linearTween,
676 default: overDampedSpring,
677};
678var getDefaultTransition = function (valueKey, to) {
679 var transitionFactory;
680 if (isKeyframesTarget(to)) {
681 transitionFactory = keyframes;
682 }
683 else {
684 transitionFactory =
685 defaultTransitions[valueKey] || defaultTransitions.default;
686 }
687 return __assign({ to: to }, transitionFactory(to));
688};
689
690/**
691 * A Popmotion action that accepts a single `to` prop. When it starts, it immediately
692 * updates with `to` and then completes. By using this we can compose instant transitions
693 * in with the same logic that applies `delay` or returns a `Promise` etc.
694 *
695 * Accepting `duration` is a little bit of a hack that simply defers the completetion of
696 * the animation until after the duration finishes. This is for situations when you're **only**
697 * animating non-animatable values and then setting something on `transitionEnd`. Really
698 * you want this to fire after the "animation" finishes, rather than instantly.
699 *
700 * ```
701 * animate={{
702 * display: 'block',
703 * transitionEnd: { display: 'none' }
704 * }}
705 * ```
706 */
707var just = function (_a) {
708 var to = _a.to, duration = _a.duration;
709 return action(function (_a) {
710 var update = _a.update, complete = _a.complete;
711 update(to);
712 duration ? delay(duration).start({ complete: complete }) : complete();
713 });
714};
715
716var easingDefinitionToFunction = function (definition) {
717 if (Array.isArray(definition)) {
718 // If cubic bezier definition, create bezier curve
719 invariant(definition.length === 4, "Cubic bezier arrays must contain four numerical values.");
720 var x1 = definition[0], y1 = definition[1], x2 = definition[2], y2 = definition[3];
721 return cubicBezier(x1, y1, x2, y2);
722 }
723 else if (typeof definition === "string") {
724 // Else lookup from table
725 invariant(easingLookup[definition] !== undefined, "Invalid easing type '" + definition + "'");
726 return easingLookup[definition];
727 }
728 return definition;
729};
730var isEasingArray = function (ease) {
731 return Array.isArray(ease) && typeof ease[0] !== "number";
732};
733
734var isDurationAnimation = function (v) {
735 return v.hasOwnProperty("duration") || v.hasOwnProperty("repeatDelay");
736};
737
738/**
739 * Check if a value is animatable. Examples:
740 *
741 * ✅: 100, "100px", "#fff"
742 * ❌: "block", "url(2.jpg)"
743 * @param value
744 *
745 * @internal
746 */
747var isAnimatable = function (key, value) {
748 // If the list of keys tat might be non-animatable grows, replace with Set
749 if (key === "zIndex")
750 return false;
751 // If it's a number or a keyframes array, we can animate it. We might at some point
752 // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
753 // but for now lets leave it like this for performance reasons
754 if (typeof value === "number" || Array.isArray(value))
755 return true;
756 if (typeof value === "string" && // It's animatable if we have a string
757 complex.test(value) && // And it contains numbers and/or colors
758 !value.startsWith("url(") // Unless it starts with "url("
759 ) {
760 return true;
761 }
762 return false;
763};
764
765/**
766 * Converts seconds to milliseconds
767 *
768 * @param seconds - Time in seconds.
769 * @return milliseconds - Converted time in milliseconds.
770 */
771var secondsToMilliseconds = function (seconds) { return seconds * 1000; };
772
773var transitions = { tween: tween, spring: spring, keyframes: keyframes$1, inertia: inertia, just: just };
774var transitionOptionParser = {
775 tween: function (opts) {
776 if (opts.ease) {
777 var ease = isEasingArray(opts.ease) ? opts.ease[0] : opts.ease;
778 opts.ease = easingDefinitionToFunction(ease);
779 }
780 return opts;
781 },
782 keyframes: function (_a) {
783 var from = _a.from, to = _a.to, velocity = _a.velocity, opts = __rest(_a, ["from", "to", "velocity"]);
784 if (opts.values && opts.values[0] === null) {
785 var values = __spreadArrays(opts.values);
786 values[0] = from;
787 opts.values = values;
788 }
789 if (opts.ease) {
790 opts.easings = isEasingArray(opts.ease)
791 ? opts.ease.map(easingDefinitionToFunction)
792 : easingDefinitionToFunction(opts.ease);
793 }
794 opts.ease = linear;
795 return opts;
796 },
797};
798var isTransitionDefined = function (_a) {
799 var when = _a.when, delay = _a.delay, delayChildren = _a.delayChildren, staggerChildren = _a.staggerChildren, staggerDirection = _a.staggerDirection, transition = __rest(_a, ["when", "delay", "delayChildren", "staggerChildren", "staggerDirection"]);
800 return Object.keys(transition).length;
801};
802var getTransitionDefinition = function (key, to, transitionDefinition) {
803 var delay = transitionDefinition ? transitionDefinition.delay : 0;
804 // If no object, return default transition
805 // A better way to handle this would be to deconstruct out all the shared Orchestration props
806 // and see if there's any props remaining
807 if (transitionDefinition === undefined ||
808 !isTransitionDefined(transitionDefinition)) {
809 return __assign({ delay: delay }, getDefaultTransition(key, to));
810 }
811 var valueTransitionDefinition = transitionDefinition[key] ||
812 transitionDefinition.default ||
813 transitionDefinition;
814 if (valueTransitionDefinition.type === false) {
815 return {
816 delay: valueTransitionDefinition.hasOwnProperty("delay")
817 ? valueTransitionDefinition.delay
818 : delay,
819 to: isKeyframesTarget(to)
820 ? to[to.length - 1]
821 : to,
822 type: "just",
823 };
824 }
825 else if (isKeyframesTarget(to)) {
826 return __assign(__assign({ values: to, duration: 0.8, delay: delay, ease: "linear" }, valueTransitionDefinition), {
827 // This animation must be keyframes if we're animating through an array
828 type: "keyframes" });
829 }
830 else {
831 return __assign({ type: "tween", to: to,
832 delay: delay }, valueTransitionDefinition);
833 }
834};
835var preprocessOptions = function (type, opts) {
836 return transitionOptionParser[type]
837 ? transitionOptionParser[type](opts)
838 : opts;
839};
840var getAnimation = function (key, value, target, transition) {
841 var origin = value.get();
842 var isOriginAnimatable = isAnimatable(key, origin);
843 var isTargetAnimatable = isAnimatable(key, target);
844 // TODO we could probably improve this check to ensure both values are of the same type -
845 // for instance 100 to #fff. This might live better in Popmotion.
846 warning(isOriginAnimatable === isTargetAnimatable, "You are trying to animate " + key + " from \"" + origin + "\" to " + target + ". \"" + origin + "\" is not an animatable value - to enable this animation set " + origin + " to a value animatable to " + target + " via the `style` property.");
847 // Parse the `transition` prop and return options for the Popmotion animation
848 var _a = getTransitionDefinition(key, target, transition), _b = _a.type, type = _b === void 0 ? "tween" : _b, transitionDefinition = __rest(_a, ["type"]);
849 // If this is an animatable pair of values, return an animation, otherwise use `just`
850 var actionFactory = isOriginAnimatable && isTargetAnimatable
851 ? transitions[type]
852 : just;
853 var opts = preprocessOptions(type, __assign({ from: origin, velocity: value.getVelocity() }, transitionDefinition));
854 // Convert duration from Framer Motion's seconds into Popmotion's milliseconds
855 if (isDurationAnimation(opts)) {
856 if (opts.duration) {
857 opts.duration = secondsToMilliseconds(opts.duration);
858 }
859 if (opts.repeatDelay) {
860 opts.repeatDelay = secondsToMilliseconds(opts.repeatDelay);
861 }
862 }
863 return [actionFactory, opts];
864};
865/**
866 * Start animation on a value. This function completely encapsulates Popmotion-specific logic.
867 *
868 * @internal
869 */
870function startAnimation(key, value, target, _a) {
871 var _b = _a.delay, delay$1 = _b === void 0 ? 0 : _b, transition = __rest(_a, ["delay"]);
872 return value.start(function (complete) {
873 var activeAnimation;
874 var _a = getAnimation(key, value, target, transition), animationFactory = _a[0], _b = _a[1], valueDelay = _b.delay, options = __rest(_b, ["delay"]);
875 if (valueDelay !== undefined) {
876 delay$1 = valueDelay;
877 }
878 var animate = function () {
879 var animation = animationFactory(options);
880 // Bind animation opts to animation
881 activeAnimation = animation.start({
882 update: function (v) { return value.set(v); },
883 complete: complete,
884 });
885 };
886 // If we're delaying this animation, only resolve it **after** the delay to
887 // ensure the value's resolve velocity is up-to-date.
888 if (delay$1) {
889 activeAnimation = delay(secondsToMilliseconds(delay$1)).start({
890 complete: animate,
891 });
892 }
893 else {
894 animate();
895 }
896 return function () {
897 if (activeAnimation)
898 activeAnimation.stop();
899 };
900 });
901}
902
903/**
904 * Get the current value of every `MotionValue`
905 * @param values -
906 */
907var getCurrent = function (values) {
908 var current = {};
909 values.forEach(function (value, key) { return (current[key] = value.get()); });
910 return current;
911};
912/**
913 * Get the current velocity of every `MotionValue`
914 * @param values -
915 */
916var getVelocity = function (values) {
917 var velocity = {};
918 values.forEach(function (value, key) { return (velocity[key] = value.getVelocity()); });
919 return velocity;
920};
921/**
922 * Check if value is a function that returns a `Target`. A generic typeof === 'function'
923 * check, just helps with typing.
924 * @param p -
925 */
926var isTargetResolver = function (p) {
927 return typeof p === "function";
928};
929/**
930 * Check if value is a list of variant labels
931 * @param v -
932 */
933var isVariantLabels = function (v) { return Array.isArray(v); };
934/**
935 * Check if value is a numerical string, ie "100" or "100px"
936 */
937var isNumericalString = function (v) { return /^\d*\.?\d+$/.test(v); };
938/**
939 * Control animations for a single component
940 *
941 * @internal
942 */
943var ValueAnimationControls = /** @class */ (function () {
944 function ValueAnimationControls(_a) {
945 var _this = this;
946 var values = _a.values, readValueFromSource = _a.readValueFromSource, makeTargetAnimatable = _a.makeTargetAnimatable;
947 /**
948 * A reference to the component's latest props. We could probably ditch this in
949 * favour to a reference to the `custom` prop now we don't send all props through
950 * to target resolvers.
951 */
952 this.props = {};
953 /**
954 * The component's variants, as provided by `variants`
955 */
956 this.variants = {};
957 /**
958 * A set of values that we animate back to when a value is cleared of all overrides.
959 */
960 this.baseTarget = {};
961 /**
962 * A series of target overrides that we can animate to/from when overrides are set/cleared.
963 */
964 this.overrides = [];
965 /**
966 * A series of target overrides as they were originally resolved.
967 */
968 this.resolvedOverrides = [];
969 /**
970 * A Set of currently active override indexes
971 */
972 this.activeOverrides = new Set();
973 /**
974 * A Set of value keys that are currently animating.
975 */
976 this.isAnimating = new Set();
977 /**
978 * Check if the associated `MotionValueMap` has a key with the provided string.
979 * Pre-bound to the class so we can provide directly to the `filter` in `checkForNewValues`.
980 */
981 this.hasValue = function (key) { return !_this.values.has(key); };
982 this.values = values;
983 this.readValueFromSource = readValueFromSource;
984 this.makeTargetAnimatable = makeTargetAnimatable;
985 this.values.forEach(function (value, key) { return (_this.baseTarget[key] = value.get()); });
986 }
987 /**
988 * Set the reference to the component's props.
989 * @param props -
990 */
991 ValueAnimationControls.prototype.setProps = function (props) {
992 this.props = props;
993 };
994 /**
995 * Set the reference to the component's variants
996 * @param variants -
997 */
998 ValueAnimationControls.prototype.setVariants = function (variants) {
999 if (variants)
1000 this.variants = variants;
1001 };
1002 /**
1003 * Set the component's default transition
1004 * @param transition -
1005 */
1006 ValueAnimationControls.prototype.setDefaultTransition = function (transition) {
1007 if (transition)
1008 this.defaultTransition = transition;
1009 };
1010 /**
1011 * Set motion values without animation.
1012 *
1013 * @param definition -
1014 * @param isActive -
1015 */
1016 ValueAnimationControls.prototype.setValues = function (definition, _a) {
1017 var _this = this;
1018 var _b = _a === void 0 ? {} : _a, _c = _b.isActive, isActive = _c === void 0 ? new Set() : _c, priority = _b.priority;
1019 var _d = this.resolveVariant(definition), target = _d.target, transitionEnd = _d.transitionEnd;
1020 target = this.transformValues(__assign(__assign({}, target), transitionEnd));
1021 return Object.keys(target).forEach(function (key) {
1022 if (isActive.has(key))
1023 return;
1024 isActive.add(key);
1025 if (target) {
1026 var targetValue = resolveFinalValueInKeyframes(target[key]);
1027 if (_this.values.has(key)) {
1028 var value = _this.values.get(key);
1029 value && value.set(targetValue);
1030 }
1031 else {
1032 _this.values.set(key, motionValue(targetValue));
1033 }
1034 if (!priority)
1035 _this.baseTarget[key] = targetValue;
1036 }
1037 });
1038 };
1039 /**
1040 * Allows `transformValues` to be set by a component that allows us to
1041 * transform the values in a given `Target`. This allows Framer Library
1042 * to extend Framer Motion to animate `Color` variables etc. Currently we have
1043 * to manually support these extended types here in Framer Motion.
1044 *
1045 * @param values -
1046 */
1047 ValueAnimationControls.prototype.transformValues = function (values) {
1048 var transformValues = this.props.transformValues;
1049 return transformValues ? transformValues(values) : values;
1050 };
1051 /**
1052 * Check a `Target` for new values we haven't animated yet, and add them
1053 * to the `MotionValueMap`.
1054 *
1055 * Currently there's functionality here that is DOM-specific, we should allow
1056 * this functionality to be injected by the factory that creates DOM-specific
1057 * components.
1058 *
1059 * @param target -
1060 */
1061 ValueAnimationControls.prototype.checkForNewValues = function (target) {
1062 var newValueKeys = Object.keys(target).filter(this.hasValue);
1063 var numNewValues = newValueKeys.length;
1064 if (!numNewValues)
1065 return;
1066 for (var i = 0; i < numNewValues; i++) {
1067 var key = newValueKeys[i];
1068 var targetValue = target[key];
1069 var value = null;
1070 // If this is a keyframes value, we can attempt to use the first value in the
1071 // array as that's going to be the first value of the animation anyway
1072 if (Array.isArray(targetValue)) {
1073 value = targetValue[0];
1074 }
1075 // If it isn't a keyframes or the first keyframes value was set as `null`, read the
1076 // value from the DOM. It might be worth investigating whether to check props (for SVG)
1077 // or props.style (for HTML) if the value exists there before attempting to read.
1078 if (value === null) {
1079 value = this.readValueFromSource(key);
1080 invariant(value !== null, "No initial value for \"" + key + "\" can be inferred. Ensure an initial value for \"" + key + "\" is defined on the component.");
1081 }
1082 if (typeof value === "string" && isNumericalString(value)) {
1083 // If this is a number read as a string, ie "0" or "200", convert it to a number
1084 value = parseFloat(value);
1085 }
1086 else if (!getValueType(value) && complex.test(targetValue)) {
1087 // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
1088 value = complex.getAnimatableNone(targetValue);
1089 }
1090 this.values.set(key, motionValue(value));
1091 this.baseTarget[key] = value;
1092 }
1093 };
1094 /**
1095 * Resolve a variant from its label or resolver into an actual `Target` we can animate to.
1096 * @param variant -
1097 */
1098 ValueAnimationControls.prototype.resolveVariant = function (variant) {
1099 if (!variant) {
1100 return {
1101 target: undefined,
1102 transition: undefined,
1103 transitionEnd: undefined,
1104 };
1105 }
1106 if (isTargetResolver(variant)) {
1107 // resolve current and velocity
1108 variant = variant(this.props.custom, getCurrent(this.values), getVelocity(this.values));
1109 }
1110 var _a = variant.transition, transition = _a === void 0 ? this.defaultTransition : _a, transitionEnd = variant.transitionEnd, target = __rest(variant, ["transition", "transitionEnd"]);
1111 return { transition: transition, transitionEnd: transitionEnd, target: target };
1112 };
1113 /**
1114 * Get the highest active override priority index
1115 */
1116 ValueAnimationControls.prototype.getHighestPriority = function () {
1117 if (!this.activeOverrides.size)
1118 return 0;
1119 return Math.max.apply(Math, Array.from(this.activeOverrides));
1120 };
1121 /**
1122 * Set an override. We add this layer of indirection so if, for instance, a tap gesture
1123 * starts and overrides a hover gesture, when we clear the tap gesture and fallback to the
1124 * hover gesture, if that hover gesture has changed in the meantime we can go to that rather
1125 * than the one that was resolved when the hover gesture animation started.
1126 *
1127 * @param definition -
1128 * @param overrideIndex -
1129 */
1130 ValueAnimationControls.prototype.setOverride = function (definition, overrideIndex) {
1131 this.overrides[overrideIndex] = definition;
1132 if (this.children) {
1133 this.children.forEach(function (child) {
1134 return child.setOverride(definition, overrideIndex);
1135 });
1136 }
1137 };
1138 /**
1139 * Start an override animation.
1140 * @param overrideIndex -
1141 */
1142 ValueAnimationControls.prototype.startOverride = function (overrideIndex) {
1143 var override = this.overrides[overrideIndex];
1144 if (override) {
1145 return this.start(override, { priority: overrideIndex });
1146 }
1147 };
1148 /**
1149 * Clear an override. We check every value we animated to in this override to see if
1150 * its present on any lower-priority overrides. If not, we animate it back to its base target.
1151 * @param overrideIndex -
1152 */
1153 ValueAnimationControls.prototype.clearOverride = function (overrideIndex) {
1154 var _this = this;
1155 if (this.children) {
1156 this.children.forEach(function (child) { return child.clearOverride(overrideIndex); });
1157 }
1158 var override = this.overrides[overrideIndex];
1159 if (!override)
1160 return;
1161 this.activeOverrides.delete(overrideIndex);
1162 var highest = this.getHighestPriority();
1163 this.resetIsAnimating();
1164 if (highest) {
1165 var highestOverride = this.overrides[highest];
1166 highestOverride && this.startOverride(highest);
1167 }
1168 // Figure out which remaining values were affected by the override and animate those
1169 var overrideTarget = this.resolvedOverrides[overrideIndex];
1170 if (!overrideTarget)
1171 return;
1172 var remainingValues = {};
1173 for (var key in this.baseTarget) {
1174 if (overrideTarget[key] !== undefined) {
1175 remainingValues[key] = this.baseTarget[key];
1176 }
1177 }
1178 this.onStart();
1179 this.animate(remainingValues).then(function () { return _this.onComplete(); });
1180 };
1181 /**
1182 * Apply a target/variant without any animation
1183 */
1184 ValueAnimationControls.prototype.apply = function (definition) {
1185 if (Array.isArray(definition)) {
1186 return this.applyVariantLabels(definition);
1187 }
1188 else if (typeof definition === "string") {
1189 return this.applyVariantLabels([definition]);
1190 }
1191 else {
1192 this.setValues(definition);
1193 }
1194 };
1195 /**
1196 * Apply variant labels without animation
1197 */
1198 ValueAnimationControls.prototype.applyVariantLabels = function (variantLabelList) {
1199 var _this = this;
1200 var isActive = new Set();
1201 var reversedList = __spreadArrays(variantLabelList).reverse();
1202 reversedList.forEach(function (key) {
1203 var _a = _this.resolveVariant(_this.variants[key]), target = _a.target, transitionEnd = _a.transitionEnd;
1204 if (transitionEnd) {
1205 _this.setValues(transitionEnd, { isActive: isActive });
1206 }
1207 if (target) {
1208 _this.setValues(target, { isActive: isActive });
1209 }
1210 if (_this.children && _this.children.size) {
1211 _this.children.forEach(function (child) {
1212 return child.applyVariantLabels(variantLabelList);
1213 });
1214 }
1215 });
1216 };
1217 ValueAnimationControls.prototype.start = function (definition, opts) {
1218 var _this = this;
1219 if (opts === void 0) { opts = {}; }
1220 if (opts.priority) {
1221 this.activeOverrides.add(opts.priority);
1222 }
1223 this.resetIsAnimating(opts.priority);
1224 var animation;
1225 if (isVariantLabels(definition)) {
1226 animation = this.animateVariantLabels(definition, opts);
1227 }
1228 else if (typeof definition === "string") {
1229 animation = this.animateVariant(definition, opts);
1230 }
1231 else {
1232 animation = this.animate(definition, opts);
1233 }
1234 this.onStart();
1235 return animation.then(function () { return _this.onComplete(); });
1236 };
1237 ValueAnimationControls.prototype.animate = function (animationDefinition, _a) {
1238 var _this = this;
1239 var _b = _a === void 0 ? {} : _a, _c = _b.delay, delay = _c === void 0 ? 0 : _c, _d = _b.priority, priority = _d === void 0 ? 0 : _d, transitionOverride = _b.transitionOverride;
1240 var _e = this.resolveVariant(animationDefinition), target = _e.target, transition = _e.transition, transitionEnd = _e.transitionEnd;
1241 if (transitionOverride) {
1242 transition = transitionOverride;
1243 }
1244 if (!target)
1245 return Promise.resolve();
1246 target = this.transformValues(target);
1247 if (transitionEnd) {
1248 transitionEnd = this.transformValues(transitionEnd);
1249 }
1250 this.checkForNewValues(target);
1251 if (this.makeTargetAnimatable) {
1252 var animatable = this.makeTargetAnimatable(target, transitionEnd);
1253 target = animatable.target;
1254 transitionEnd = animatable.transitionEnd;
1255 }
1256 if (priority) {
1257 this.resolvedOverrides[priority] = target;
1258 }
1259 this.checkForNewValues(target);
1260 var animations = [];
1261 for (var key in target) {
1262 var value = this.values.get(key);
1263 if (!value || !target || target[key] === undefined)
1264 continue;
1265 var valueTarget = target[key];
1266 if (!priority) {
1267 this.baseTarget[key] = resolveFinalValueInKeyframes(valueTarget);
1268 }
1269 if (this.isAnimating.has(key))
1270 continue;
1271 this.isAnimating.add(key);
1272 animations.push(startAnimation(key, value, valueTarget, __assign({ delay: delay }, transition)));
1273 }
1274 var allAnimations = Promise.all(animations);
1275 return transitionEnd
1276 ? allAnimations.then(function () {
1277 _this.setValues(transitionEnd, { priority: priority });
1278 })
1279 : allAnimations;
1280 };
1281 ValueAnimationControls.prototype.animateVariantLabels = function (variantLabels, opts) {
1282 var _this = this;
1283 var animations = __spreadArrays(variantLabels).reverse()
1284 .map(function (label) { return _this.animateVariant(label, opts); });
1285 return Promise.all(animations);
1286 };
1287 ValueAnimationControls.prototype.animateVariant = function (variantLabel, opts) {
1288 var _this = this;
1289 var when = false;
1290 var delayChildren = 0;
1291 var staggerChildren = 0;
1292 var staggerDirection = 1;
1293 var priority = (opts && opts.priority) || 0;
1294 var variant = this.variants[variantLabel];
1295 var getAnimations = variant
1296 ? function () { return _this.animate(variant, opts); }
1297 : function () { return Promise.resolve(); };
1298 var getChildrenAnimations = this.children
1299 ? function () {
1300 return _this.animateChildren(variantLabel, delayChildren, staggerChildren, staggerDirection, priority);
1301 }
1302 : function () { return Promise.resolve(); };
1303 if (variant && this.children) {
1304 var transition = this.resolveVariant(variant).transition;
1305 if (transition) {
1306 when = transition.when || when;
1307 delayChildren = transition.delayChildren || delayChildren;
1308 staggerChildren = transition.staggerChildren || staggerChildren;
1309 staggerDirection =
1310 transition.staggerDirection || staggerDirection;
1311 }
1312 }
1313 if (when) {
1314 var _a = when === "beforeChildren"
1315 ? [getAnimations, getChildrenAnimations]
1316 : [getChildrenAnimations, getAnimations], first = _a[0], last = _a[1];
1317 return first().then(last);
1318 }
1319 else {
1320 return Promise.all([getAnimations(), getChildrenAnimations()]);
1321 }
1322 };
1323 ValueAnimationControls.prototype.animateChildren = function (variantLabel, delayChildren, staggerChildren, staggerDirection, priority) {
1324 if (delayChildren === void 0) { delayChildren = 0; }
1325 if (staggerChildren === void 0) { staggerChildren = 0; }
1326 if (staggerDirection === void 0) { staggerDirection = 1; }
1327 if (priority === void 0) { priority = 0; }
1328 if (!this.children) {
1329 return Promise.resolve();
1330 }
1331 var animations = [];
1332 var maxStaggerDuration = (this.children.size - 1) * staggerChildren;
1333 var generateStaggerDuration = staggerDirection === 1
1334 ? function (i) { return i * staggerChildren; }
1335 : function (i) { return maxStaggerDuration - i * staggerChildren; };
1336 Array.from(this.children).forEach(function (childControls, i) {
1337 var animation = childControls.animateVariant(variantLabel, {
1338 priority: priority,
1339 delay: delayChildren + generateStaggerDuration(i),
1340 });
1341 animations.push(animation);
1342 });
1343 return Promise.all(animations);
1344 };
1345 ValueAnimationControls.prototype.onStart = function () {
1346 var onAnimationStart = this.props.onAnimationStart;
1347 onAnimationStart && onAnimationStart();
1348 };
1349 ValueAnimationControls.prototype.onComplete = function () {
1350 var onAnimationComplete = this.props.onAnimationComplete;
1351 onAnimationComplete && onAnimationComplete();
1352 };
1353 ValueAnimationControls.prototype.checkOverrideIsAnimating = function (priority) {
1354 var numOverrides = this.overrides.length;
1355 for (var i = priority + 1; i < numOverrides; i++) {
1356 var resolvedOverride = this.resolvedOverrides[i];
1357 if (resolvedOverride) {
1358 for (var key in resolvedOverride) {
1359 this.isAnimating.add(key);
1360 }
1361 }
1362 }
1363 };
1364 ValueAnimationControls.prototype.resetIsAnimating = function (priority) {
1365 if (priority === void 0) { priority = 0; }
1366 this.isAnimating.clear();
1367 // If this isn't the highest priority gesture, block the animation
1368 // of anything that's currently being animated
1369 if (priority < this.getHighestPriority()) {
1370 this.checkOverrideIsAnimating(priority);
1371 }
1372 if (this.children) {
1373 this.children.forEach(function (child) { return child.resetIsAnimating(priority); });
1374 }
1375 };
1376 ValueAnimationControls.prototype.stop = function () {
1377 this.values.forEach(function (value) { return value.stop(); });
1378 };
1379 /**
1380 * Add the controls of a child component.
1381 * @param controls -
1382 */
1383 ValueAnimationControls.prototype.addChild = function (controls) {
1384 if (!this.children) {
1385 this.children = new Set();
1386 }
1387 this.children.add(controls);
1388 // We set child overrides when `setOverride` is called, but also have to do it here
1389 // as the first time `setOverride` is called all the children might not have been added yet.
1390 this.overrides.forEach(function (override, i) {
1391 override && controls.setOverride(override, i);
1392 });
1393 };
1394 ValueAnimationControls.prototype.removeChild = function (controls) {
1395 if (!this.children) {
1396 return;
1397 }
1398 this.children.delete(controls);
1399 };
1400 ValueAnimationControls.prototype.resetChildren = function () {
1401 if (this.children)
1402 this.children.clear();
1403 };
1404 return ValueAnimationControls;
1405}());
1406
1407/**
1408 * Use callback either only on the initial render or on all renders. In concurrent mode
1409 * the "initial" render might run multiple times
1410 *
1411 * @param callback - Callback to run
1412 * @param isInitialOnly - Set to `true` to only run on initial render, or `false` for all renders. Defaults to `false`.
1413 *
1414 * @public
1415 */
1416function useInitialOrEveryRender(callback, isInitialOnly) {
1417 if (isInitialOnly === void 0) { isInitialOnly = false; }
1418 var isInitialRender = useRef(true);
1419 if (!isInitialOnly || (isInitialOnly && isInitialRender.current)) {
1420 callback();
1421 }
1422 isInitialRender.current = false;
1423}
1424
1425/**
1426 * Control animations on one or more components.
1427 *
1428 * @public
1429 */
1430var AnimationControls = /** @class */ (function () {
1431 function AnimationControls() {
1432 /**
1433 * Track whether the host component has mounted.
1434 *
1435 * @internal
1436 */
1437 this.hasMounted = false;
1438 /**
1439 * Pending animations that are started before a component is mounted.
1440 *
1441 * @internal
1442 */
1443 this.pendingAnimations = [];
1444 /**
1445 * A collection of linked component animation controls.
1446 *
1447 * @internal
1448 */
1449 this.componentControls = new Set();
1450 }
1451 /**
1452 * Set variants on this and all child components.
1453 *
1454 * @param variants - The variants to set
1455 *
1456 * @internal
1457 */
1458 AnimationControls.prototype.setVariants = function (variants) {
1459 this.variants = variants;
1460 this.componentControls.forEach(function (controls) {
1461 return controls.setVariants(variants);
1462 });
1463 };
1464 /**
1465 * Set a default transition on this and all child components
1466 *
1467 * @param transition - The default transition to set
1468 *
1469 * @internal
1470 */
1471 AnimationControls.prototype.setDefaultTransition = function (transition) {
1472 this.defaultTransition = transition;
1473 this.componentControls.forEach(function (controls) {
1474 return controls.setDefaultTransition(transition);
1475 });
1476 };
1477 /**
1478 * Subscribes a component's animation controls to this.
1479 *
1480 * @param controls - The controls to subscribe
1481 * @returns An unsubscribe function.
1482 *
1483 * @internal
1484 */
1485 AnimationControls.prototype.subscribe = function (controls) {
1486 var _this = this;
1487 this.componentControls.add(controls);
1488 if (this.variants)
1489 controls.setVariants(this.variants);
1490 if (this.defaultTransition)
1491 controls.setDefaultTransition(this.defaultTransition);
1492 return function () { return _this.componentControls.delete(controls); };
1493 };
1494 /**
1495 * Starts an animation on all linked components.
1496 *
1497 * @remarks
1498 *
1499 * ```jsx
1500 * controls.start("variantLabel")
1501 * controls.start({
1502 * x: 0,
1503 * transition: { duration: 1 }
1504 * })
1505 * ```
1506 *
1507 * @param definition - Properties or variant label to animate to
1508 * @param transition - Optional `transtion` to apply to a variant
1509 * @returns - A `Promise` that resolves when all animations have completed.
1510 *
1511 * @public
1512 */
1513 AnimationControls.prototype.start = function (definition, transitionOverride) {
1514 var _this = this;
1515 if (this.hasMounted) {
1516 var animations_1 = [];
1517 this.componentControls.forEach(function (controls) {
1518 var animation = controls.start(definition, {
1519 transitionOverride: transitionOverride,
1520 });
1521 animations_1.push(animation);
1522 });
1523 return Promise.all(animations_1);
1524 }
1525 else {
1526 return new Promise(function (resolve) {
1527 _this.pendingAnimations.push({
1528 animation: [definition, transitionOverride],
1529 resolve: resolve,
1530 });
1531 });
1532 }
1533 };
1534 /**
1535 * Instantly set to a set of properties or a variant.
1536 *
1537 * ```jsx
1538 * // With properties
1539 * controls.set({ opacity: 0 })
1540 *
1541 * // With variants
1542 * controls.set("hidden")
1543 * ```
1544 *
1545 * @internalremarks
1546 * We could perform a similar trick to `.start` where this can be called before mount
1547 * and we maintain a list of of pending actions that get applied on mount. But the
1548 * expectation of `set` is that it happens synchronously and this would be difficult
1549 * to do before any children have even attached themselves. It's also poor practise
1550 * and we should discourage render-synchronous `.start` calls rather than lean into this.
1551 *
1552 * @public
1553 */
1554 AnimationControls.prototype.set = function (definition) {
1555 invariant(this.hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
1556 return this.componentControls.forEach(function (controls) {
1557 return controls.apply(definition);
1558 });
1559 };
1560 /**
1561 * Stops animations on all linked components.
1562 *
1563 * ```jsx
1564 * controls.stop()
1565 * ```
1566 *
1567 * @public
1568 */
1569 AnimationControls.prototype.stop = function () {
1570 this.componentControls.forEach(function (controls) { return controls.stop(); });
1571 };
1572 /**
1573 * Initialises the animation controls.
1574 *
1575 * @internal
1576 */
1577 AnimationControls.prototype.mount = function () {
1578 var _this = this;
1579 this.hasMounted = true;
1580 this.pendingAnimations.forEach(function (_a) {
1581 var animation = _a.animation, resolve = _a.resolve;
1582 return _this.start.apply(_this, animation).then(resolve);
1583 });
1584 };
1585 /**
1586 * Stops all child animations when the host component unmounts.
1587 *
1588 * @internal
1589 */
1590 AnimationControls.prototype.unmount = function () {
1591 this.hasMounted = false;
1592 this.stop();
1593 };
1594 return AnimationControls;
1595}());
1596/**
1597 * @internal
1598 */
1599var animationControls = function () { return new AnimationControls(); };
1600
1601/**
1602 * @internal
1603 */
1604var MotionContext = createContext({
1605 static: false,
1606});
1607var isVariantLabel = function (v) {
1608 return typeof v === "string" || Array.isArray(v);
1609};
1610var isAnimationControls = function (v) {
1611 return v instanceof AnimationControls;
1612};
1613/**
1614 * Set up the context for children motion components.
1615 *
1616 * We also use this opportunity to apply `initial` values
1617 */
1618var useMotionContext = function (parentContext, controls, values, isStatic, _a) {
1619 if (isStatic === void 0) { isStatic = false; }
1620 var initial = _a.initial, animate = _a.animate, variants = _a.variants, whileTap = _a.whileTap, whileHover = _a.whileHover;
1621 // Override initial with that from a parent context, if defined
1622 if (parentContext.exitProps &&
1623 parentContext.exitProps.initial !== undefined) {
1624 initial = parentContext.exitProps.initial;
1625 }
1626 var initialState;
1627 if (initial === false && !isAnimationControls(animate)) {
1628 initialState = animate;
1629 }
1630 else if (typeof initial !== "boolean") {
1631 initialState = initial;
1632 }
1633 // Track mounted status so children can detect whether they were present during their
1634 // parent's first render
1635 var hasMounted = useRef(false);
1636 // We propagate this component's ValueAnimationControls *if* we're being provided variants,
1637 // if we're being used to control variants, or if we're being passed animation controls.
1638 // Otherwise this component should be "invisible" to variant propagation. This is a slight concession
1639 // to Framer X where every `Frame` is a `motion` component and it might be if we change that in the future
1640 // that this restriction is removed.
1641 var shouldPropagateControls = variants ||
1642 isVariantLabel(animate) ||
1643 isVariantLabel(whileTap) ||
1644 isVariantLabel(whileHover) ||
1645 isAnimationControls(animate);
1646 // If this component's `initial` prop is a variant label, propagate it. Otherwise pass the parent's.
1647 var targetInitial = isVariantLabel(initialState)
1648 ? initialState
1649 : parentContext.initial;
1650 // If this is a variant tree we need to propagate the `animate` prop in case new children are added after
1651 // the tree initially animates.
1652 var targetAnimate = isVariantLabel(animate)
1653 ? animate
1654 : parentContext.animate;
1655 // Only allow `initial` to trigger context re-renders if this is a `static` component (ie we're on the Framer canvas)
1656 // or in another non-animation/interaction environment.
1657 var initialDependency = isStatic ? targetInitial : null;
1658 // Only allow `animate` to trigger context re-renders if it's a variant label. If this is an array of
1659 // variant labels there's probably an optimisation to deep-compare but it might be an over-optimisation.
1660 // We want to do this as we rely on React's component rendering order each render cycle to determine
1661 // the new order of any child components for the `staggerChildren` functionality.
1662 var animateDependency = shouldPropagateControls && isVariantLabel(targetAnimate)
1663 ? targetAnimate
1664 : null;
1665 // The context to provide to the child. We `useMemo` because although `controls` and `initial` are
1666 // unlikely to change, by making the context an object it'll be considered a new value every render.
1667 // So all child motion components will re-render as a result.
1668 var context = useMemo(function () { return ({
1669 controls: shouldPropagateControls
1670 ? controls
1671 : parentContext.controls,
1672 initial: targetInitial,
1673 animate: targetAnimate,
1674 values: values,
1675 hasMounted: hasMounted,
1676 isReducedMotion: parentContext.isReducedMotion,
1677 }); }, [initialDependency, animateDependency, parentContext.isReducedMotion]);
1678 // Update the `static` property every render. This is unlikely to change but also essentially free.
1679 context.static = isStatic;
1680 // Set initial state. If this is a static component (ie in Framer canvas), respond to updates
1681 // in `initial`.
1682 useInitialOrEveryRender(function () {
1683 var initialToApply = initialState || parentContext.initial;
1684 initialToApply && controls.apply(initialToApply);
1685 }, !isStatic);
1686 useEffect(function () {
1687 hasMounted.current = true;
1688 }, []);
1689 return context;
1690};
1691
1692/**
1693 * Creates an imperative set of controls to trigger animations.
1694 *
1695 * This allows a consolidated, uniform API for animations, to be triggered by other APIs like the `animate` prop, or the gesture handlers.
1696 *
1697 * @param values
1698 * @param props
1699 * @param ref
1700 * @param subscribeToParentControls
1701 *
1702 * @internal
1703 */
1704function useValueAnimationControls(config, props, subscribeToParentControls, parentContext) {
1705 var variants = props.variants, transition = props.transition;
1706 var parentControls = useContext(MotionContext).controls;
1707 var controls = useConstant(function () { return new ValueAnimationControls(config); });
1708 // Reset and resubscribe children every render to ensure stagger order is correct
1709 if (!parentContext ||
1710 !parentContext.exitProps ||
1711 !parentContext.exitProps.isExiting) {
1712 controls.resetChildren();
1713 controls.setProps(props);
1714 controls.setVariants(variants);
1715 controls.setDefaultTransition(transition);
1716 }
1717 // We have to subscribe to the parent controls within a useEffect rather than during render,
1718 // as
1719 useEffect(function () {
1720 if (subscribeToParentControls && parentControls) {
1721 parentControls.addChild(controls);
1722 }
1723 });
1724 useEffect(function () {
1725 return function () {
1726 // Remove reference to onAnimationComplete from controls. All the MotionValues
1727 // are unsubscribed from this component separately. We let animations run out
1728 // as they might be animating other components.
1729 var onAnimationComplete = props.onAnimationComplete, unmountProps = __rest(props, ["onAnimationComplete"]);
1730 controls.setProps(unmountProps);
1731 parentControls && parentControls.removeChild(controls);
1732 };
1733 }, []);
1734 return controls;
1735}
1736
1737var checkShouldInheritVariant = function (_a) {
1738 var animate = _a.animate, variants = _a.variants, _b = _a.inherit, inherit = _b === void 0 ? true : _b;
1739 return (inherit &&
1740 !!variants &&
1741 (!animate || animate instanceof AnimationControls));
1742};
1743
1744/**
1745 * Uses the ref that is passed in, or creates a new one
1746 * @param external - External ref
1747 * @internal
1748 */
1749function useExternalRef(externalRef) {
1750 // We're conditionally calling `useRef` here which is sort of naughty as hooks
1751 // shouldn't be called conditionally. However, Framer Motion will break if this
1752 // condition changes anyway. It might be possible to use an invariant here to
1753 // make it explicit, but I expect changing `ref` is not normal behaviour.
1754 var ref = !externalRef || typeof externalRef === "function"
1755 ? useRef(null)
1756 : externalRef;
1757 // Handle `ref` functions. Again, calling the hook conditionally is kind of naughty
1758 // but `ref` types changing between renders would break Motion anyway. If we receive
1759 // bug reports about this, we should track the provided ref and throw an invariant
1760 // rather than move the conditional to inside the useEffect as this will be fired
1761 // for every Frame component within Framer.
1762 if (externalRef && typeof externalRef === "function") {
1763 useEffect(function () {
1764 externalRef(ref.current);
1765 return function () { return externalRef(null); };
1766 }, []);
1767 }
1768 return ref;
1769}
1770
1771/**
1772 * @internal
1773 */
1774var createMotionComponent = function (_a) {
1775 var getValueControlsConfig = _a.getValueControlsConfig, loadFunctionalityComponents = _a.loadFunctionalityComponents, renderComponent = _a.renderComponent;
1776 function MotionComponent(props, externalRef) {
1777 var ref = useExternalRef(externalRef);
1778 var parentContext = useContext(MotionContext);
1779 var isStatic = parentContext.static || props.static || false;
1780 var values = useMotionValues(props);
1781 var style = useMotionStyles(values, props.style, isStatic, props.transformValues);
1782 var shouldInheritVariant = checkShouldInheritVariant(props);
1783 var controlsConfig = useConstant(function () {
1784 return getValueControlsConfig(ref, values);
1785 });
1786 var controls = useValueAnimationControls(controlsConfig, props, shouldInheritVariant, parentContext);
1787 var context = useMotionContext(parentContext, controls, values, isStatic, props);
1788 var functionality = isStatic
1789 ? null
1790 : loadFunctionalityComponents(ref, values, props, parentContext, controls, shouldInheritVariant);
1791 var renderedComponent = renderComponent(ref, style, values, props, isStatic);
1792 return (createElement(Fragment, null,
1793 createElement(MotionContext.Provider, { value: context }, renderedComponent),
1794 createElement(Fragment, null,
1795 createElement(Mount, { innerRef: ref, values: values, isStatic: isStatic }),
1796 functionality)));
1797 }
1798 return forwardRef(MotionComponent);
1799};
1800
1801/**
1802 * @internal
1803 */
1804var htmlElements = [
1805 "a",
1806 "abbr",
1807 "address",
1808 "area",
1809 "article",
1810 "aside",
1811 "audio",
1812 "b",
1813 "base",
1814 "bdi",
1815 "bdo",
1816 "big",
1817 "blockquote",
1818 "body",
1819 "br",
1820 "button",
1821 "canvas",
1822 "caption",
1823 "cite",
1824 "code",
1825 "col",
1826 "colgroup",
1827 "data",
1828 "datalist",
1829 "dd",
1830 "del",
1831 "details",
1832 "dfn",
1833 "dialog",
1834 "div",
1835 "dl",
1836 "dt",
1837 "em",
1838 "embed",
1839 "fieldset",
1840 "figcaption",
1841 "figure",
1842 "footer",
1843 "form",
1844 "h1",
1845 "h2",
1846 "h3",
1847 "h4",
1848 "h5",
1849 "h6",
1850 "head",
1851 "header",
1852 "hgroup",
1853 "hr",
1854 "html",
1855 "i",
1856 "iframe",
1857 "img",
1858 "input",
1859 "ins",
1860 "kbd",
1861 "keygen",
1862 "label",
1863 "legend",
1864 "li",
1865 "link",
1866 "main",
1867 "map",
1868 "mark",
1869 "menu",
1870 "menuitem",
1871 "meta",
1872 "meter",
1873 "nav",
1874 "noscript",
1875 "object",
1876 "ol",
1877 "optgroup",
1878 "option",
1879 "output",
1880 "p",
1881 "param",
1882 "picture",
1883 "pre",
1884 "progress",
1885 "q",
1886 "rp",
1887 "rt",
1888 "ruby",
1889 "s",
1890 "samp",
1891 "script",
1892 "section",
1893 "select",
1894 "small",
1895 "source",
1896 "span",
1897 "strong",
1898 "style",
1899 "sub",
1900 "summary",
1901 "sup",
1902 "table",
1903 "tbody",
1904 "td",
1905 "textarea",
1906 "tfoot",
1907 "th",
1908 "thead",
1909 "time",
1910 "title",
1911 "tr",
1912 "track",
1913 "u",
1914 "ul",
1915 "var",
1916 "video",
1917 "wbr",
1918 "webview",
1919];
1920/**
1921 * @internal
1922 */
1923var svgElements = [
1924 "animate",
1925 "circle",
1926 "clipPath",
1927 "defs",
1928 "desc",
1929 "ellipse",
1930 "feBlend",
1931 "feColorMatrix",
1932 "feComponentTransfer",
1933 "feComposite",
1934 "feConvolveMatrix",
1935 "feDiffuseLighting",
1936 "feDisplacementMap",
1937 "feDistantLight",
1938 "feDropShadow",
1939 "feFlood",
1940 "feFuncA",
1941 "feFuncB",
1942 "feFuncG",
1943 "feFuncR",
1944 "feGaussianBlur",
1945 "feImage",
1946 "feMerge",
1947 "feMergeNode",
1948 "feMorphology",
1949 "feOffset",
1950 "fePointLight",
1951 "feSpecularLighting",
1952 "feSpotLight",
1953 "feTile",
1954 "feTurbulence",
1955 "filter",
1956 "foreignObject",
1957 "g",
1958 "image",
1959 "line",
1960 "linearGradient",
1961 "marker",
1962 "mask",
1963 "metadata",
1964 "path",
1965 "pattern",
1966 "polygon",
1967 "polyline",
1968 "radialGradient",
1969 "rect",
1970 "stop",
1971 "svg",
1972 "switch",
1973 "symbol",
1974 "text",
1975 "textPath",
1976 "tspan",
1977 "use",
1978 "view",
1979];
1980
1981/**
1982 * @internal
1983 */
1984var MotionPluginContext = createContext({
1985 transformPagePoint: function (p) { return p; },
1986});
1987/**
1988 * @remarks For now I think this should remain a private API for our own use
1989 * until we can figure out a nicer way of allowing people to add these
1990 *
1991 * @internal
1992 */
1993function MotionPlugins(_a) {
1994 var children = _a.children, props = __rest(_a, ["children"]);
1995 var pluginContext = useContext(MotionPluginContext);
1996 var value = useRef(__assign({}, pluginContext)).current;
1997 // Mutative to prevent triggering rerenders in all listening
1998 // components every time this component renders
1999 for (var key in props) {
2000 value[key] = props[key];
2001 }
2002 return (createElement(MotionPluginContext.Provider, { value: value }, children));
2003}
2004
2005function useUnmountEffect(callback) {
2006 return useEffect(function () { return function () { return callback(); }; }, []);
2007}
2008
2009function addDomEvent(target, eventName, handler, options) {
2010 if (!handler)
2011 return;
2012 target.addEventListener(eventName, handler, options);
2013 return function () { return target.removeEventListener(eventName, handler, options); };
2014}
2015/**
2016 * Attaches an event listener directly to the provided DOM element.
2017 *
2018 * Bypassing React's event system can be desirable, for instance when attaching non-passive
2019 * event handlers.
2020 *
2021 * ```jsx
2022 * const ref = useRef(null)
2023 *
2024 * useDomEvent(ref, 'wheel', onWheel, { passive: false })
2025 *
2026 * return <div ref={ref} />
2027 * ```
2028 *
2029 * @param ref - React.RefObject that's been provided to the element you want to bind the listener to.
2030 * @param eventName - Name of the event you want listen for.
2031 * @param handler - Function to fire when receiving the event.
2032 * @param options - Options to pass to `Event.addEventListener`.
2033 *
2034 * @public
2035 */
2036function useDomEvent(ref, eventName, handler, options) {
2037 useEffect(function () {
2038 var element = ref.current;
2039 if (handler && element) {
2040 return addDomEvent(element, eventName, handler, options);
2041 }
2042 }, [ref, eventName, handler, options]);
2043}
2044
2045function isMouseEvent(event) {
2046 // PointerEvent inherits from MouseEvent so we can't use a straight instanceof check.
2047 if (typeof PointerEvent !== "undefined" && event instanceof PointerEvent) {
2048 return !!(event.pointerType === "mouse");
2049 }
2050 return event instanceof MouseEvent;
2051}
2052function isTouchEvent(event) {
2053 var hasTouches = !!event.touches;
2054 return hasTouches;
2055}
2056
2057/**
2058 * Filters out events not attached to the primary pointer (currently left mouse button)
2059 * @param eventHandler
2060 */
2061function filterPrimaryPointer(eventHandler) {
2062 if (!eventHandler)
2063 return undefined;
2064 return function (event) {
2065 var isMouseEvent = event instanceof MouseEvent;
2066 var isPrimaryPointer = !isMouseEvent ||
2067 (isMouseEvent && event.button === 0);
2068 if (isPrimaryPointer) {
2069 eventHandler(event);
2070 }
2071 };
2072}
2073var defaultPagePoint = { pageX: 0, pageY: 0 };
2074function pointFromTouch(e) {
2075 var primaryTouch = e.touches[0] || e.changedTouches[0];
2076 var _a = primaryTouch || defaultPagePoint, pageX = _a.pageX, pageY = _a.pageY;
2077 return { x: pageX, y: pageY };
2078}
2079function pointFromMouse(_a) {
2080 var _b = _a.pageX, pageX = _b === void 0 ? 0 : _b, _c = _a.pageY, pageY = _c === void 0 ? 0 : _c;
2081 return { x: pageX, y: pageY };
2082}
2083function extractEventInfo(event) {
2084 return {
2085 point: isTouchEvent(event)
2086 ? pointFromTouch(event)
2087 : pointFromMouse(event),
2088 };
2089}
2090var wrapHandler = function (handler, shouldFilterPrimaryPointer) {
2091 if (shouldFilterPrimaryPointer === void 0) { shouldFilterPrimaryPointer = false; }
2092 if (!handler)
2093 return;
2094 var listener = function (event) { return handler(event, extractEventInfo(event)); };
2095 return shouldFilterPrimaryPointer
2096 ? filterPrimaryPointer(listener)
2097 : listener;
2098};
2099
2100var isBrowser = typeof window !== "undefined";
2101// We check for event support via functions in case they've been mocked by a testing suite.
2102var supportsPointerEvents = function () {
2103 return isBrowser && window.onpointerdown === null;
2104};
2105var supportsTouchEvents = function () {
2106 return isBrowser && window.ontouchstart === null;
2107};
2108var supportsMouseEvents = function () {
2109 return isBrowser && window.onmousedown === null;
2110};
2111
2112var mouseEventNames = {
2113 pointerdown: "mousedown",
2114 pointermove: "mousemove",
2115 pointerup: "mouseup",
2116 pointercancel: "mousecancel",
2117 pointerover: "mouseover",
2118 pointerout: "mouseout",
2119 pointerenter: "mouseenter",
2120 pointerleave: "mouseleave",
2121};
2122var touchEventNames = {
2123 pointerdown: "touchstart",
2124 pointermove: "touchmove",
2125 pointerup: "touchend",
2126 pointercancel: "touchcancel",
2127};
2128function getPointerEventName(name) {
2129 if (supportsPointerEvents()) {
2130 return name;
2131 }
2132 else if (supportsTouchEvents()) {
2133 return touchEventNames[name];
2134 }
2135 else if (supportsMouseEvents()) {
2136 return mouseEventNames[name];
2137 }
2138 return name;
2139}
2140function addPointerEvent(target, eventName, handler, options) {
2141 return addDomEvent(target, getPointerEventName(eventName), wrapHandler(handler, eventName === "pointerdown"), options);
2142}
2143function usePointerEvent(ref, eventName, handler, options) {
2144 return useDomEvent(ref, getPointerEventName(eventName), wrapHandler(handler, eventName === "pointerdown"), options);
2145}
2146
2147/** @public */
2148var Point;
2149(function (Point) {
2150 /** @beta */
2151 Point.subtract = function (a, b) {
2152 return { x: a.x - b.x, y: a.y - b.y };
2153 };
2154 /** @beta */
2155 Point.relativeTo = function (idOrElem) {
2156 var elem;
2157 var getElem = function () {
2158 // Caching element here could be leaky because of React lifecycle
2159 if (elem !== undefined)
2160 return elem;
2161 if (typeof idOrElem === "string") {
2162 elem = document.getElementById(idOrElem);
2163 }
2164 else {
2165 elem = idOrElem;
2166 }
2167 return elem;
2168 };
2169 return function (_a) {
2170 var x = _a.x, y = _a.y;
2171 var localElem = getElem();
2172 if (!localElem)
2173 return undefined;
2174 var rect = localElem.getBoundingClientRect();
2175 return {
2176 x: x - rect.left - window.scrollX,
2177 y: y - rect.top - window.scrollY,
2178 };
2179 };
2180 };
2181})(Point || (Point = {}));
2182
2183var isViewportScrollBlocked = false;
2184var isBrowser$1 = typeof window !== "undefined";
2185if (isBrowser$1) {
2186 document.addEventListener("touchmove", function (event) {
2187 if (isViewportScrollBlocked) {
2188 event.preventDefault();
2189 }
2190 }, { passive: false });
2191}
2192var blockViewportScroll = function () { return (isViewportScrollBlocked = true); };
2193var unblockViewportScroll = function () { return (isViewportScrollBlocked = false); };
2194
2195/**
2196 * @internal
2197 */
2198var PanSession = /** @class */ (function () {
2199 function PanSession(event, handlers, _a) {
2200 var _this = this;
2201 var transformPagePoint = (_a === void 0 ? {} : _a).transformPagePoint;
2202 /**
2203 * @internal
2204 */
2205 this.startEvent = null;
2206 /**
2207 * @internal
2208 */
2209 this.lastMoveEvent = null;
2210 /**
2211 * @internal
2212 */
2213 this.lastMoveEventInfo = null;
2214 /**
2215 * @internal
2216 */
2217 this.handlers = {};
2218 this.updatePoint = function () {
2219 if (!(_this.lastMoveEvent && _this.lastMoveEventInfo))
2220 return;
2221 var info = getPanInfo(_this.lastMoveEventInfo, _this.history);
2222 var isPanStarted = _this.startEvent !== null;
2223 // Only start panning if the offset is larger than 3 pixels. If we make it
2224 // any larger than this we'll want to reset the pointer history
2225 // on the first update to avoid visual snapping to the cursoe.
2226 var isDistancePastThreshold = distance(info.offset, { x: 0, y: 0 }) >= 3;
2227 if (!isPanStarted && !isDistancePastThreshold)
2228 return;
2229 var point = info.point;
2230 var timestamp = getFrameData().timestamp;
2231 _this.history.push(__assign(__assign({}, point), { timestamp: timestamp }));
2232 var _a = _this.handlers, onStart = _a.onStart, onMove = _a.onMove;
2233 if (!isPanStarted) {
2234 onStart && onStart(_this.lastMoveEvent, info);
2235 _this.startEvent = _this.lastMoveEvent;
2236 }
2237 onMove && onMove(_this.lastMoveEvent, info);
2238 };
2239 // If we have more than one touch, don't start detecting this gesture
2240 if (isTouchEvent(event) && event.touches.length > 1)
2241 return;
2242 this.handlers = handlers;
2243 this.transformPagePoint = transformPagePoint;
2244 var info = extractEventInfo(event);
2245 var initialInfo = transformPoint(info, this.transformPagePoint);
2246 var point = initialInfo.point;
2247 var timestamp = getFrameData().timestamp;
2248 this.history = [__assign(__assign({}, point), { timestamp: timestamp })];
2249 var onSessionStart = handlers.onSessionStart;
2250 onSessionStart &&
2251 onSessionStart(event, getPanInfo(initialInfo, this.history));
2252 var removeOnPointerMove = addPointerEvent(window, "pointermove", function (event, info) { return _this.handlePointerMove(event, info); });
2253 var removeOnPointerUp = addPointerEvent(window, "pointerup", function (event, info) { return _this.handlePointerUp(event, info); });
2254 this.removeListeners = function () {
2255 removeOnPointerMove && removeOnPointerMove();
2256 removeOnPointerUp && removeOnPointerUp();
2257 };
2258 }
2259 PanSession.prototype.handlePointerMove = function (event, info) {
2260 this.lastMoveEvent = event;
2261 this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
2262 // Because Safari doesn't trigger mouseup events when it's above a `<select>`
2263 if (isMouseEvent(event) && event.buttons === 0) {
2264 this.handlePointerUp(event, info);
2265 return;
2266 }
2267 // Throttle mouse move event to once per frame
2268 sync.update(this.updatePoint, true);
2269 };
2270 PanSession.prototype.handlePointerUp = function (event, info) {
2271 this.end();
2272 var onEnd = this.handlers.onEnd;
2273 if (!onEnd)
2274 return;
2275 var panInfo = getPanInfo(transformPoint(info, this.transformPagePoint), this.history);
2276 onEnd && onEnd(event, panInfo);
2277 };
2278 PanSession.prototype.updateHandlers = function (handlers) {
2279 this.handlers = handlers;
2280 };
2281 PanSession.prototype.end = function () {
2282 this.removeListeners && this.removeListeners();
2283 cancelSync.update(this.updatePoint);
2284 unblockViewportScroll();
2285 };
2286 return PanSession;
2287}());
2288function transformPoint(info, transformPagePoint) {
2289 return transformPagePoint ? { point: transformPagePoint(info.point) } : info;
2290}
2291function getPanInfo(_a, history) {
2292 var point = _a.point;
2293 return {
2294 point: point,
2295 delta: Point.subtract(point, lastDevicePoint(history)),
2296 offset: Point.subtract(point, startDevicePoint(history)),
2297 velocity: getVelocity$1(history, 0.1),
2298 };
2299}
2300function startDevicePoint(history) {
2301 return history[0];
2302}
2303function lastDevicePoint(history) {
2304 return history[history.length - 1];
2305}
2306function getVelocity$1(history, timeDelta) {
2307 if (history.length < 2) {
2308 return { x: 0, y: 0 };
2309 }
2310 var i = history.length - 1;
2311 var timestampedPoint = null;
2312 var lastPoint = lastDevicePoint(history);
2313 while (i >= 0) {
2314 timestampedPoint = history[i];
2315 if (lastPoint.timestamp - timestampedPoint.timestamp >
2316 secondsToMilliseconds(timeDelta)) {
2317 break;
2318 }
2319 i--;
2320 }
2321 if (!timestampedPoint) {
2322 return { x: 0, y: 0 };
2323 }
2324 var time = (lastPoint.timestamp - timestampedPoint.timestamp) / 1000;
2325 if (time === 0) {
2326 return { x: 0, y: 0 };
2327 }
2328 var currentVelocity = {
2329 x: (lastPoint.x - timestampedPoint.x) / time,
2330 y: (lastPoint.y - timestampedPoint.y) / time,
2331 };
2332 if (currentVelocity.x === Infinity) {
2333 currentVelocity.x = 0;
2334 }
2335 if (currentVelocity.y === Infinity) {
2336 currentVelocity.y = 0;
2337 }
2338 return currentVelocity;
2339}
2340
2341/**
2342 *
2343 * @param handlers -
2344 * @param ref -
2345 *
2346 * @internalremarks
2347 * Currently this sets new pan gesture functions every render. The memo route has been explored
2348 * in the past but ultimately we're still creating new functions every render. An optimisation
2349 * to explore is creating the pan gestures and loading them into a `ref`.
2350 *
2351 * @internal
2352 */
2353function usePanGesture(_a, ref) {
2354 var onPan = _a.onPan, onPanStart = _a.onPanStart, onPanEnd = _a.onPanEnd, onPanSessionStart = _a.onPanSessionStart;
2355 var hasPanEvents = onPan || onPanStart || onPanEnd || onPanSessionStart;
2356 var panSession = useRef(null);
2357 var transformPagePoint = useContext(MotionPluginContext).transformPagePoint;
2358 var handlers = {
2359 onSessionStart: onPanSessionStart,
2360 onStart: onPanStart,
2361 onMove: onPan,
2362 onEnd: function (event, info) {
2363 panSession.current = null;
2364 onPanEnd && onPanEnd(event, info);
2365 },
2366 };
2367 if (panSession.current !== null) {
2368 panSession.current.updateHandlers(handlers);
2369 }
2370 function onPointerDown(event) {
2371 panSession.current = new PanSession(event, handlers, {
2372 transformPagePoint: transformPagePoint,
2373 });
2374 }
2375 usePointerEvent(ref, "pointerdown", hasPanEvents && onPointerDown);
2376 useUnmountEffect(function () { return panSession.current && panSession.current.end(); });
2377}
2378
2379/**
2380 * Recursively traverse up the tree to check whether the provided child node
2381 * is the parent or a descendant of it.
2382 *
2383 * @param parent - Element to find
2384 * @param child - Element to test against parent
2385 */
2386var isNodeOrChild = function (parent, child) {
2387 if (!child) {
2388 return false;
2389 }
2390 else if (parent === child) {
2391 return true;
2392 }
2393 else {
2394 return isNodeOrChild(parent, child.parentElement);
2395 }
2396};
2397
2398var order = ["whileHover", "whileTap", "whileDrag"];
2399var getGesturePriority = function (gesture) {
2400 return order.indexOf(gesture) + 1;
2401};
2402
2403function createLock(name) {
2404 var lock = null;
2405 return function () {
2406 var openLock = function () {
2407 lock = null;
2408 };
2409 if (lock === null) {
2410 lock = name;
2411 return openLock;
2412 }
2413 return false;
2414 };
2415}
2416var globalHorizontalLock = createLock("dragHorizontal");
2417var globalVerticalLock = createLock("dragVertical");
2418function getGlobalLock(drag) {
2419 var lock = false;
2420 if (drag === "y") {
2421 lock = globalVerticalLock();
2422 }
2423 else if (drag === "x") {
2424 lock = globalHorizontalLock();
2425 }
2426 else {
2427 var openHorizontal_1 = globalHorizontalLock();
2428 var openVertical_1 = globalVerticalLock();
2429 if (openHorizontal_1 && openVertical_1) {
2430 lock = function () {
2431 openHorizontal_1();
2432 openVertical_1();
2433 };
2434 }
2435 else {
2436 // Release the locks because we don't use them
2437 if (openHorizontal_1)
2438 openHorizontal_1();
2439 if (openVertical_1)
2440 openVertical_1();
2441 }
2442 }
2443 return lock;
2444}
2445
2446var tapGesturePriority = getGesturePriority("whileTap");
2447/**
2448 * @param handlers -
2449 * @internal
2450 */
2451function useTapGesture(_a, ref) {
2452 var onTap = _a.onTap, onTapStart = _a.onTapStart, onTapCancel = _a.onTapCancel, whileTap = _a.whileTap, controls = _a.controls;
2453 var hasTapListeners = onTap || onTapStart || onTapCancel || whileTap;
2454 var isTapping = useRef(false);
2455 var cancelPointerEventListener = useRef(null);
2456 function removePointerUp() {
2457 cancelPointerEventListener.current &&
2458 cancelPointerEventListener.current();
2459 cancelPointerEventListener.current = null;
2460 }
2461 if (whileTap && controls) {
2462 controls.setOverride(whileTap, tapGesturePriority);
2463 }
2464 // We load this event handler into a ref so we can later refer to
2465 // onPointerUp.current which will always have reference to the latest props
2466 var onPointerUp = useRef(null);
2467 onPointerUp.current = function (event, info) {
2468 var element = ref.current;
2469 removePointerUp();
2470 if (!isTapping.current || !element)
2471 return;
2472 isTapping.current = false;
2473 if (controls && whileTap) {
2474 controls.clearOverride(tapGesturePriority);
2475 }
2476 // Check the gesture lock - if we get it, it means no drag gesture is active
2477 // and we can safely fire the tap gesture.
2478 var openGestureLock = getGlobalLock(true);
2479 if (!openGestureLock)
2480 return;
2481 openGestureLock();
2482 if (!isNodeOrChild(element, event.target)) {
2483 onTapCancel && onTapCancel(event, info);
2484 }
2485 else {
2486 onTap && onTap(event, info);
2487 }
2488 };
2489 function onPointerDown(event, info) {
2490 removePointerUp();
2491 cancelPointerEventListener.current = addPointerEvent(window, "pointerup", function (event, info) { return onPointerUp.current(event, info); });
2492 var element = ref.current;
2493 if (!element || isTapping.current)
2494 return;
2495 isTapping.current = true;
2496 onTapStart && onTapStart(event, info);
2497 if (controls && whileTap) {
2498 controls.startOverride(tapGesturePriority);
2499 }
2500 }
2501 usePointerEvent(ref, "pointerdown", hasTapListeners ? onPointerDown : undefined);
2502 useUnmountEffect(removePointerUp);
2503}
2504
2505var hoverPriority = getGesturePriority("whileHover");
2506var filterTouch = function (listener) { return function (event, info) {
2507 if (isMouseEvent(event))
2508 listener(event, info);
2509}; };
2510/**
2511 *
2512 * @param props
2513 * @param ref
2514 * @internal
2515 */
2516function useHoverGesture(_a, ref) {
2517 var whileHover = _a.whileHover, onHoverStart = _a.onHoverStart, onHoverEnd = _a.onHoverEnd, controls = _a.controls;
2518 if (whileHover && controls) {
2519 controls.setOverride(whileHover, hoverPriority);
2520 }
2521 usePointerEvent(ref, "pointerenter", filterTouch(function (event, info) {
2522 if (onHoverStart)
2523 onHoverStart(event, info);
2524 if (whileHover && controls) {
2525 controls.startOverride(hoverPriority);
2526 }
2527 }));
2528 usePointerEvent(ref, "pointerleave", filterTouch(function (event, info) {
2529 if (onHoverEnd)
2530 onHoverEnd(event, info);
2531 if (whileHover && controls) {
2532 controls.clearOverride(hoverPriority);
2533 }
2534 }));
2535}
2536
2537/**
2538 * Add pan and tap gesture recognition to an element.
2539 *
2540 * @param props - Gesture event handlers
2541 * @param ref - React `ref` containing a DOM `Element`
2542 * @public
2543 */
2544function useGestures(props, ref) {
2545 usePanGesture(props, ref);
2546 useTapGesture(props, ref);
2547 useHoverGesture(props, ref);
2548}
2549
2550var makeRenderlessComponent = function (hook) { return function (props) {
2551 hook(props);
2552 return null;
2553}; };
2554
2555var gestureProps = [
2556 "onPan",
2557 "onPanStart",
2558 "onPanEnd",
2559 "onPanSessionStart",
2560 "onTap",
2561 "onTapStart",
2562 "onTapCancel",
2563 "whileTap",
2564 "whileHover",
2565 "onHoverStart",
2566 "onHoverEnd",
2567];
2568var Gestures = {
2569 key: "gestures",
2570 shouldRender: function (props) {
2571 return gestureProps.some(function (key) { return props.hasOwnProperty(key); });
2572 },
2573 Component: makeRenderlessComponent(function (_a) {
2574 var innerRef = _a.innerRef, props = __rest(_a, ["innerRef"]);
2575 useGestures(props, innerRef);
2576 }),
2577};
2578
2579var isRefObject = function (ref) {
2580 return typeof ref === "object" && ref.hasOwnProperty("current");
2581};
2582
2583var noop = function (v) { return v; };
2584/**
2585 * Don't block the default pointerdown behaviour of these elements.
2586 */
2587var allowDefaultPointerDown = new Set(["INPUT", "TEXTAREA", "SELECT"]);
2588var ComponentDragControls = /** @class */ (function () {
2589 function ComponentDragControls(_a) {
2590 var ref = _a.ref, values = _a.values, controls = _a.controls;
2591 /**
2592 * Track whether we're currently dragging.
2593 *
2594 * @internal
2595 */
2596 this.isDragging = false;
2597 /**
2598 * The current direction of drag, or `null` if both.
2599 *
2600 * @internal
2601 */
2602 this.currentDirection = null;
2603 /**
2604 * The permitted t/r/b/l boundaries of travel, in pixels.
2605 *
2606 * @internal
2607 */
2608 this.constraints = false;
2609 /**
2610 * A reference to the host component's latest props.
2611 *
2612 * @internal
2613 */
2614 this.props = {
2615 transformPagePoint: noop,
2616 };
2617 /**
2618 * References to the MotionValues used for tracking the current dragged point.
2619 *
2620 * @internal
2621 */
2622 this.point = {};
2623 /**
2624 * The origin point for the current drag gesture.
2625 *
2626 * @internal
2627 */
2628 this.origin = {
2629 x: motionValue(0),
2630 y: motionValue(0),
2631 };
2632 // This is a reference to the global drag gesture lock, ensuring only one component
2633 // can "capture" the drag of one or both axes.
2634 // TODO: Look into moving this into pansession?
2635 this.openGlobalLock = null;
2636 /**
2637 * @internal
2638 */
2639 this.panSession = null;
2640 /**
2641 * A reference to the previous constraints bounding box
2642 *
2643 * @internal
2644 */
2645 this.prevConstraintsBox = {
2646 width: 0,
2647 height: 0,
2648 x: 0,
2649 y: 0,
2650 };
2651 this.ref = ref;
2652 this.values = values;
2653 this.controls = controls;
2654 }
2655 /**
2656 * Start dragging the host component.
2657 *
2658 * @param event - The originating pointer event.
2659 * @param options -
2660 *
2661 * @public
2662 */
2663 ComponentDragControls.prototype.start = function (originEvent, _a) {
2664 var _this = this;
2665 var _b = (_a === void 0 ? {} : _a).snapToCursor, snapToCursor = _b === void 0 ? false : _b;
2666 snapToCursor && this.snapToCursor(originEvent);
2667 var onSessionStart = function (event) {
2668 // Prevent browser-specific behaviours like text selection or Chrome's image dragging.
2669 if (event.target &&
2670 !allowDefaultPointerDown.has(event.target.tagName)) {
2671 // On iOS it's important to not `preventDefault` the `touchstart`
2672 // event, as otherwise clicks won't fire inside the draggable element.
2673 if (!supportsTouchEvents()) {
2674 event.preventDefault();
2675 // Make sure input elements loose focus when we prevent the default.
2676 if (document.activeElement instanceof HTMLElement) {
2677 document.activeElement.blur();
2678 }
2679 }
2680 }
2681 // Initiate viewport scroll blocking on touch start. This is a very aggressive approach
2682 // which has come out of the difficulty in us being able to do this once a scroll gesture
2683 // has initiated in mobile browsers. This means if there's a horizontally-scrolling carousel
2684 // on a page we can't let a user scroll the page itself from it. Ideally what we'd do is
2685 // trigger this once we've got a scroll direction determined. This approach sort-of worked
2686 // but if the component was dragged too far in a single frame page scrolling would initiate.
2687 blockViewportScroll();
2688 // Stop any animations on both axis values immediately. This allows the user to throw and catch
2689 // the component.
2690 bothAxis(function (axis) {
2691 var axisPoint = _this.point[axis];
2692 axisPoint && axisPoint.stop();
2693 });
2694 };
2695 var onStart = function (event, info) {
2696 // If constraints are an element, resolve them again in case they've updated.
2697 if (_this.constraintsNeedResolution) {
2698 var _a = _this.props, dragConstraints = _a.dragConstraints, transformPagePoint_1 = _a.transformPagePoint;
2699 _this.constraints = calculateConstraintsFromDom(dragConstraints, _this.ref, _this.point, transformPagePoint_1);
2700 _this.applyConstraintsToPoint();
2701 }
2702 // Set point origin and stop any existing animations.
2703 bothAxis(function (axis) {
2704 var axisPoint = _this.point[axis];
2705 if (!axisPoint)
2706 return;
2707 _this.origin[axis].set(axisPoint.get());
2708 });
2709 // Attempt to grab the global drag gesture lock - maybe make this part of PanSession
2710 var _b = _this.props, drag = _b.drag, dragPropagation = _b.dragPropagation;
2711 if (drag && !dragPropagation) {
2712 if (_this.openGlobalLock)
2713 _this.openGlobalLock();
2714 _this.openGlobalLock = getGlobalLock(drag);
2715 if (!_this.openGlobalLock)
2716 return;
2717 }
2718 _this.isDragging = true;
2719 _this.currentDirection = null;
2720 var onDragStart = _this.props.onDragStart;
2721 onDragStart &&
2722 onDragStart(event, convertPanToDrag(info, _this.point));
2723 };
2724 var onMove = function (event, info) {
2725 var _a = _this.props, dragPropagation = _a.dragPropagation, dragDirectionLock = _a.dragDirectionLock;
2726 // If we didn't successfully receive the gesture lock, early return.
2727 if (!dragPropagation && !_this.openGlobalLock)
2728 return;
2729 var offset = info.offset;
2730 // Attempt to detect drag direction if directionLock is true
2731 if (dragDirectionLock && _this.currentDirection === null) {
2732 _this.currentDirection = getCurrentDirection(offset);
2733 // If we've successfully set a direction, notify listener
2734 if (_this.currentDirection !== null) {
2735 var onDirectionLock = _this.props.onDirectionLock;
2736 onDirectionLock && onDirectionLock(_this.currentDirection);
2737 }
2738 return;
2739 }
2740 _this.updatePoint("x", offset);
2741 _this.updatePoint("y", offset);
2742 var onDrag = _this.props.onDrag;
2743 onDrag && onDrag(event, convertPanToDrag(info, _this.point));
2744 };
2745 var onEnd = function (event, info) {
2746 _this.stop(event, info);
2747 };
2748 var transformPagePoint = this.props.transformPagePoint;
2749 this.panSession = new PanSession(originEvent, {
2750 onSessionStart: onSessionStart,
2751 onStart: onStart,
2752 onMove: onMove,
2753 onEnd: onEnd,
2754 }, { transformPagePoint: transformPagePoint });
2755 };
2756 ComponentDragControls.prototype.cancelDrag = function () {
2757 unblockViewportScroll();
2758 this.isDragging = false;
2759 this.panSession && this.panSession.end();
2760 this.panSession = null;
2761 if (!this.props.dragPropagation && this.openGlobalLock) {
2762 this.openGlobalLock();
2763 this.openGlobalLock = null;
2764 }
2765 };
2766 ComponentDragControls.prototype.stop = function (event, info) {
2767 var _a;
2768 (_a = this.panSession) === null || _a === void 0 ? void 0 : _a.end();
2769 this.panSession = null;
2770 var isDragging = this.isDragging;
2771 this.cancelDrag();
2772 if (!isDragging)
2773 return;
2774 var _b = this.props, dragMomentum = _b.dragMomentum, dragElastic = _b.dragElastic, onDragEnd = _b.onDragEnd;
2775 if (dragMomentum || dragElastic) {
2776 var velocity = info.velocity;
2777 this.animateDragEnd(velocity);
2778 }
2779 else {
2780 this.recordBoxInfo(this.constraints);
2781 }
2782 onDragEnd && onDragEnd(event, convertPanToDrag(info, this.point));
2783 };
2784 ComponentDragControls.prototype.recordBoxInfo = function (constraints) {
2785 if (constraints) {
2786 var right = constraints.right, left = constraints.left, bottom = constraints.bottom, top_1 = constraints.top;
2787 this.prevConstraintsBox.width = (right || 0) - (left || 0);
2788 this.prevConstraintsBox.height = (bottom || 0) - (top_1 || 0);
2789 }
2790 if (this.point.x)
2791 this.prevConstraintsBox.x = this.point.x.get();
2792 if (this.point.y)
2793 this.prevConstraintsBox.y = this.point.y.get();
2794 };
2795 ComponentDragControls.prototype.snapToCursor = function (event) {
2796 var _this = this;
2797 var transformPagePoint = this.props.transformPagePoint;
2798 var point = extractEventInfo(event).point;
2799 var boundingBox = getBoundingBox(this.ref, transformPagePoint);
2800 var center = {
2801 x: boundingBox.width / 2 + boundingBox.left + window.scrollX,
2802 y: boundingBox.height / 2 + boundingBox.top + window.scrollY,
2803 };
2804 var offset = {
2805 x: point.x - center.x,
2806 y: point.y - center.y,
2807 };
2808 bothAxis(function (axis) {
2809 var point = _this.point[axis];
2810 if (!point)
2811 return;
2812 _this.origin[axis].set(point.get());
2813 });
2814 this.updatePoint("x", offset);
2815 this.updatePoint("y", offset);
2816 };
2817 ComponentDragControls.prototype.setPoint = function (axis, value) {
2818 this.point[axis] = value;
2819 };
2820 ComponentDragControls.prototype.updatePoint = function (axis, offset) {
2821 var _a = this.props, drag = _a.drag, dragElastic = _a.dragElastic;
2822 var axisPoint = this.point[axis];
2823 // If we're not dragging this axis, do an early return.
2824 if (!shouldDrag(axis, drag, this.currentDirection) || !axisPoint)
2825 return;
2826 var current = applyConstraints(axis, this.origin[axis].get() + offset[axis], this.constraints, dragElastic);
2827 axisPoint.set(current);
2828 };
2829 ComponentDragControls.prototype.updateProps = function (_a) {
2830 var _this = this;
2831 var _b = _a.drag, drag = _b === void 0 ? false : _b, _c = _a.dragDirectionLock, dragDirectionLock = _c === void 0 ? false : _c, _d = _a.dragPropagation, dragPropagation = _d === void 0 ? false : _d, _e = _a.dragConstraints, dragConstraints = _e === void 0 ? false : _e, _f = _a.dragElastic, dragElastic = _f === void 0 ? true : _f, _g = _a.dragMomentum, dragMomentum = _g === void 0 ? true : _g, remainingProps = __rest(_a, ["drag", "dragDirectionLock", "dragPropagation", "dragConstraints", "dragElastic", "dragMomentum"]);
2832 this.props = __assign({ drag: drag,
2833 dragDirectionLock: dragDirectionLock,
2834 dragPropagation: dragPropagation,
2835 dragConstraints: dragConstraints,
2836 dragElastic: dragElastic,
2837 dragMomentum: dragMomentum }, remainingProps);
2838 var _dragValueX = remainingProps._dragValueX, _dragValueY = remainingProps._dragValueY, dragOriginX = remainingProps.dragOriginX, dragOriginY = remainingProps.dragOriginY;
2839 if (dragOriginX)
2840 this.origin.x = dragOriginX;
2841 if (dragOriginY)
2842 this.origin.y = dragOriginY;
2843 // Get the `MotionValue` for both draggable axes, or create them if they don't already
2844 // exist on this component.
2845 bothAxis(function (axis) {
2846 if (!shouldDrag(axis, drag, _this.currentDirection))
2847 return;
2848 var defaultValue = axis === "x" ? _dragValueX : _dragValueY;
2849 _this.setPoint(axis, defaultValue || _this.values.get(axis, 0));
2850 });
2851 // If `dragConstraints` is a React `ref`, we should resolve the constraints once the
2852 // component has rendered.
2853 this.constraintsNeedResolution = isRefObject(dragConstraints);
2854 this.constraints = this.constraintsNeedResolution
2855 ? this.constraints || false
2856 : dragConstraints;
2857 };
2858 ComponentDragControls.prototype.applyConstraintsToPoint = function (constraints) {
2859 var _this = this;
2860 if (constraints === void 0) { constraints = this.constraints; }
2861 return bothAxis(function (axis) {
2862 var axisPoint = _this.point[axis];
2863 axisPoint &&
2864 !axisPoint.isAnimating() &&
2865 applyConstraints(axis, axisPoint, constraints, 0);
2866 });
2867 };
2868 ComponentDragControls.prototype.animateDragEnd = function (velocity) {
2869 var _this = this;
2870 var _a = this.props, drag = _a.drag, dragMomentum = _a.dragMomentum, dragElastic = _a.dragElastic, dragTransition = _a.dragTransition, _dragValueX = _a._dragValueX, _dragValueY = _a._dragValueY, _dragTransitionControls = _a._dragTransitionControls;
2871 var momentumAnimations = bothAxis(function (axis) {
2872 var _a;
2873 if (!shouldDrag(axis, drag, _this.currentDirection)) {
2874 return;
2875 }
2876 var transition = _this.constraints
2877 ? getConstraints(axis, _this.constraints)
2878 : {};
2879 /**
2880 * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame
2881 * of spring animations so we should look into adding a disable spring option to `inertia`.
2882 * We could do something here where we affect the `bounceStiffness` and `bounceDamping`
2883 * using the value of `dragElastic`.
2884 */
2885 var bounceStiffness = dragElastic ? 200 : 1000000;
2886 var bounceDamping = dragElastic ? 40 : 10000000;
2887 var animationControls = _dragTransitionControls || _this.controls;
2888 var inertia = __assign(__assign({ type: "inertia", velocity: dragMomentum ? velocity[axis] : 0, bounceStiffness: bounceStiffness,
2889 bounceDamping: bounceDamping, timeConstant: 750, restDelta: 1 }, dragTransition), transition);
2890 var externalAxisMotionValue = axis === "x" ? _dragValueX : _dragValueY;
2891 // If we're not animating on an externally-provided `MotionValue` we can use the
2892 // component's animation controls which will handle interactions with whileHover (etc),
2893 // otherwise we just have to animate the `MotionValue` itself.
2894 return externalAxisMotionValue
2895 ? startAnimation(axis, externalAxisMotionValue, 0, inertia)
2896 : animationControls.start((_a = {},
2897 _a[axis] = 0,
2898 _a.transition = inertia,
2899 _a));
2900 });
2901 // Run all animations and then resolve the new drag constraints.
2902 return Promise.all(momentumAnimations).then(function () {
2903 _this.recordBoxInfo(_this.constraints);
2904 _this.scalePoint();
2905 var onDragTransitionEnd = _this.props.onDragTransitionEnd;
2906 onDragTransitionEnd && onDragTransitionEnd();
2907 });
2908 };
2909 ComponentDragControls.prototype.scalePoint = function () {
2910 var _this = this;
2911 var _a = this.props, dragConstraints = _a.dragConstraints, transformPagePoint = _a.transformPagePoint;
2912 if (!isRefObject(dragConstraints))
2913 return;
2914 var constraintsBox = getBoundingBox(dragConstraints, transformPagePoint);
2915 var draggableBox = getBoundingBox(this.ref, transformPagePoint);
2916 // Scale a point relative to the transformation of a constraints-providing element.
2917 var scaleAxisPoint = function (axis, dimension) {
2918 var pointToScale = _this.point[axis];
2919 if (!pointToScale)
2920 return;
2921 // Stop any current animations as they bug out if you resize during one
2922 if (pointToScale.isAnimating()) {
2923 pointToScale.stop();
2924 _this.recordBoxInfo();
2925 return;
2926 }
2927 // If the previous dimension was `0` (default), set `scale` to `1` to prevent
2928 // divide by zero errors.
2929 var scale = _this.prevConstraintsBox[dimension]
2930 ? (constraintsBox[dimension] - draggableBox[dimension]) /
2931 _this.prevConstraintsBox[dimension]
2932 : 1;
2933 pointToScale.set(_this.prevConstraintsBox[axis] * scale);
2934 };
2935 scaleAxisPoint("x", "width");
2936 scaleAxisPoint("y", "height");
2937 };
2938 ComponentDragControls.prototype.mount = function (element) {
2939 var _this = this;
2940 var stopPointerListener = addPointerEvent(element, "pointerdown", function (event) {
2941 var _a = _this.props, drag = _a.drag, _b = _a.dragListener, dragListener = _b === void 0 ? true : _b;
2942 drag && dragListener && _this.start(event);
2943 });
2944 var stopResizeListener = addDomEvent(window, "resize", function () {
2945 return _this.scalePoint();
2946 });
2947 if (this.constraintsNeedResolution) {
2948 var _a = this.props, dragConstraints = _a.dragConstraints, transformPagePoint = _a.transformPagePoint;
2949 var constraints = calculateConstraintsFromDom(dragConstraints, this.ref, this.point, transformPagePoint);
2950 this.applyConstraintsToPoint(constraints);
2951 this.recordBoxInfo(constraints);
2952 }
2953 else if (!this.isDragging && this.constraints) {
2954 this.applyConstraintsToPoint();
2955 }
2956 return function () {
2957 stopPointerListener && stopPointerListener();
2958 stopResizeListener && stopResizeListener();
2959 _this.cancelDrag();
2960 };
2961 };
2962 return ComponentDragControls;
2963}());
2964// Call a handler once for each axis
2965function bothAxis(handler) {
2966 return [handler("x"), handler("y")];
2967}
2968function convertPanToDrag(info, point) {
2969 return __assign(__assign({}, info), { point: {
2970 x: point.x ? point.x.get() : 0,
2971 y: point.y ? point.y.get() : 0,
2972 } });
2973}
2974function getConstraints(axis, _a) {
2975 var top = _a.top, right = _a.right, bottom = _a.bottom, left = _a.left;
2976 if (axis === "x") {
2977 return { min: left, max: right };
2978 }
2979 else {
2980 return { min: top, max: bottom };
2981 }
2982}
2983function shouldDrag(direction, drag, currentDirection) {
2984 return ((drag === true || drag === direction) &&
2985 (currentDirection === null || currentDirection === direction));
2986}
2987/**
2988 * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower
2989 * than the provided threshold, return `null`.
2990 *
2991 * @param offset - The x/y offset from origin.
2992 * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction.
2993 */
2994function getCurrentDirection(offset, lockThreshold) {
2995 if (lockThreshold === void 0) { lockThreshold = 10; }
2996 var direction = null;
2997 if (Math.abs(offset.y) > lockThreshold) {
2998 direction = "y";
2999 }
3000 else if (Math.abs(offset.x) > lockThreshold) {
3001 direction = "x";
3002 }
3003 return direction;
3004}
3005/**
3006 * Takes a parent Element and a draggable Element and returns pixel-based drag constraints.
3007 *
3008 * @param constraintsRef
3009 * @param draggableRef
3010 */
3011function calculateConstraintsFromDom(constraintsRef, draggableRef, point, transformPagePoint) {
3012 invariant(constraintsRef.current !== null && draggableRef.current !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop.");
3013 var parentBoundingBox = getBoundingBox(constraintsRef, transformPagePoint);
3014 var draggableBoundingBox = getBoundingBox(draggableRef, transformPagePoint);
3015 var left = parentBoundingBox.left -
3016 draggableBoundingBox.left +
3017 getCurrentOffset(point.x);
3018 var top = parentBoundingBox.top -
3019 draggableBoundingBox.top +
3020 getCurrentOffset(point.y);
3021 var right = parentBoundingBox.width - draggableBoundingBox.width + left;
3022 var bottom = parentBoundingBox.height - draggableBoundingBox.height + top;
3023 return { top: top, left: left, right: right, bottom: bottom };
3024}
3025function getBoundingBox(ref, transformPagePoint) {
3026 var rect = ref.current.getBoundingClientRect();
3027 var _a = transformPagePoint({
3028 x: rect.left,
3029 y: rect.top,
3030 }), left = _a.x, top = _a.y;
3031 var _b = transformPagePoint({
3032 x: rect.width,
3033 y: rect.height,
3034 }), width = _b.x, height = _b.y;
3035 return { left: left, top: top, width: width, height: height };
3036}
3037function getCurrentOffset(point) {
3038 return point ? point.get() : 0;
3039}
3040function applyConstraints(axis, value, constraints, dragElastic) {
3041 var constrainedValue = value instanceof MotionValue ? value.get() : value;
3042 if (!constraints) {
3043 return constrainedValue;
3044 }
3045 var _a = getConstraints(axis, constraints), min = _a.min, max = _a.max;
3046 if (min !== undefined && constrainedValue < min) {
3047 constrainedValue = dragElastic
3048 ? applyOverdrag(min, constrainedValue, dragElastic)
3049 : Math.max(min, constrainedValue);
3050 }
3051 else if (max !== undefined && constrainedValue > max) {
3052 constrainedValue = dragElastic
3053 ? applyOverdrag(max, constrainedValue, dragElastic)
3054 : Math.min(max, constrainedValue);
3055 }
3056 if (value instanceof MotionValue) {
3057 value.set(constrainedValue);
3058 }
3059 return constrainedValue;
3060}
3061function applyOverdrag(origin, current, dragElastic) {
3062 var dragFactor = typeof dragElastic === "number" ? dragElastic : 0.35;
3063 return mix(origin, current, dragFactor);
3064}
3065
3066/**
3067 * A hook that allows an element to be dragged.
3068 *
3069 * @param param
3070 * @param ref
3071 * @param values
3072 * @param controls
3073 *
3074 * @internal
3075 */
3076function useDrag(props, ref, values, controls) {
3077 var groupDragControls = props.dragControls;
3078 var transformPagePoint = useContext(MotionPluginContext).transformPagePoint;
3079 var dragControls = useConstant(function () { return new ComponentDragControls({ ref: ref, values: values, controls: controls }); });
3080 dragControls.updateProps(__assign(__assign({}, props), { transformPagePoint: transformPagePoint }));
3081 useEffect(function () { return groupDragControls && groupDragControls.subscribe(dragControls); }, [dragControls]);
3082 useEffect(function () { return dragControls.mount(ref.current); }, []);
3083}
3084
3085var Drag = {
3086 key: "drag",
3087 shouldRender: function (props) { return !!props.drag; },
3088 Component: makeRenderlessComponent(function (_a) {
3089 var innerRef = _a.innerRef, values = _a.values, controls = _a.controls, props = __rest(_a, ["innerRef", "values", "controls"]);
3090 return useDrag(props, innerRef, values, controls);
3091 }),
3092};
3093
3094function isCSSVariable(value) {
3095 return typeof value === "string" && value.startsWith("var(--");
3096}
3097/**
3098 * Parse Framer's special CSS variable format into a CSS token and a fallback.
3099 *
3100 * ```
3101 * `var(--foo, #fff)` => [`--foo`, '#fff']
3102 * ```
3103 *
3104 * @param current
3105 */
3106var cssVariableRegex = /var\((--[a-zA-Z0-9-_]+),? ?([a-zA-Z0-9 ()%#.,-]+)?\)/;
3107function parseCSSVariable(current) {
3108 var match = cssVariableRegex.exec(current);
3109 if (!match)
3110 return [,];
3111 var token = match[1], fallback = match[2];
3112 return [token, fallback];
3113}
3114var maxDepth = 4;
3115function getVariableValue(current, element, depth) {
3116 if (depth === void 0) { depth = 1; }
3117 invariant(depth <= maxDepth, "Max CSS variable fallback depth detected in property \"" + current + "\". This may indicate a circular fallback dependency.");
3118 var _a = parseCSSVariable(current), token = _a[0], fallback = _a[1];
3119 // No CSS variable detected
3120 if (!token)
3121 return;
3122 // Attempt to read this CSS variable off the element
3123 var resolved = window.getComputedStyle(element).getPropertyValue(token);
3124 if (resolved) {
3125 return resolved;
3126 }
3127 else if (isCSSVariable(fallback)) {
3128 // The fallback might itself be a CSS variable, in which case we attempt to resolve it too.
3129 return getVariableValue(fallback, element, depth + 1);
3130 }
3131 else {
3132 return fallback;
3133 }
3134}
3135/**
3136 * Resolve CSS variables from
3137 *
3138 * @internal
3139 */
3140function resolveCSSVariables(values, ref, _a, transitionEnd) {
3141 var target = __rest(_a, []);
3142 var element = ref.current;
3143 if (!(element instanceof HTMLElement))
3144 return { target: target, transitionEnd: transitionEnd };
3145 // If `transitionEnd` isn't `undefined`, clone it. We could clone `target` and `transitionEnd`
3146 // only if they change but I think this reads clearer and this isn't a performance-critical path.
3147 if (transitionEnd) {
3148 transitionEnd = __assign({}, transitionEnd);
3149 }
3150 // Go through existing `MotionValue`s and ensure any existing CSS variables are resolved
3151 values.forEach(function (value) {
3152 var current = value.get();
3153 if (!isCSSVariable(current))
3154 return;
3155 var resolved = getVariableValue(current, element);
3156 if (resolved)
3157 value.set(resolved);
3158 });
3159 // Cycle through every target property and resolve CSS variables. Currently
3160 // we only read single-var properties like `var(--foo)`, not `calc(var(--foo) + 20px)`
3161 for (var key in target) {
3162 var current = target[key];
3163 if (!isCSSVariable(current))
3164 continue;
3165 var resolved = getVariableValue(current, element);
3166 if (!resolved)
3167 continue;
3168 // Clone target if it hasn't already been
3169 target[key] = resolved;
3170 // If the user hasn't already set this key on `transitionEnd`, set it to the unresolved
3171 // CSS variable. This will ensure that after the animation the component will reflect
3172 // changes in the value of the CSS variable.
3173 if (transitionEnd && transitionEnd[key] === undefined) {
3174 transitionEnd[key] = current;
3175 }
3176 }
3177 return { target: target, transitionEnd: transitionEnd };
3178}
3179
3180var positionalKeys = new Set([
3181 "width",
3182 "height",
3183 "top",
3184 "left",
3185 "right",
3186 "bottom",
3187 "x",
3188 "y",
3189]);
3190var isPositionalKey = function (key) { return positionalKeys.has(key); };
3191var hasPositionalKey = function (target) {
3192 return Object.keys(target).some(isPositionalKey);
3193};
3194var setAndResetVelocity = function (value, to) {
3195 // Looks odd but setting it twice doesn't render, it'll just
3196 // set both prev and current to the latest value
3197 value.set(to, false);
3198 value.set(to);
3199};
3200var isNumOrPxType = function (v) {
3201 return v === number || v === px;
3202};
3203var BoundingBoxDimension;
3204(function (BoundingBoxDimension) {
3205 BoundingBoxDimension["width"] = "width";
3206 BoundingBoxDimension["height"] = "height";
3207 BoundingBoxDimension["left"] = "left";
3208 BoundingBoxDimension["right"] = "right";
3209 BoundingBoxDimension["top"] = "top";
3210 BoundingBoxDimension["bottom"] = "bottom";
3211})(BoundingBoxDimension || (BoundingBoxDimension = {}));
3212var getPosFromMatrix = function (matrix, pos) {
3213 return parseFloat(matrix.split(", ")[pos]);
3214};
3215var getTranslateFromMatrix = function (pos2, pos3) { return function (_bbox, _a) {
3216 var transform = _a.transform;
3217 if (transform === "none" || !transform)
3218 return 0;
3219 var matrix3d = transform.match(/^matrix3d\((.+)\)$/);
3220 if (matrix3d) {
3221 return getPosFromMatrix(matrix3d[1], pos3);
3222 }
3223 else {
3224 var matrix = transform.match(/^matrix\((.+)\)$/);
3225 return getPosFromMatrix(matrix[1], pos2);
3226 }
3227}; };
3228var transformKeys = new Set(["x", "y", "z"]);
3229var nonTranslationalTransformKeys = transformProps.filter(function (key) { return !transformKeys.has(key); });
3230function removeNonTranslationalTransform(values, elementStyler) {
3231 var removedTransforms = [];
3232 nonTranslationalTransformKeys.forEach(function (key) {
3233 var value = values.get(key);
3234 if (value !== undefined) {
3235 removedTransforms.push([key, value.get()]);
3236 value.set(key.startsWith("scale") ? 1 : 0);
3237 }
3238 });
3239 // Apply changes to element before measurement
3240 if (removedTransforms.length)
3241 elementStyler.render();
3242 return removedTransforms;
3243}
3244var positionalValues = {
3245 // Dimensions
3246 width: function (_a) {
3247 var width = _a.width;
3248 return width;
3249 },
3250 height: function (_a) {
3251 var height = _a.height;
3252 return height;
3253 },
3254 top: function (_bbox, _a) {
3255 var top = _a.top;
3256 return parseFloat(top);
3257 },
3258 left: function (_bbox, _a) {
3259 var left = _a.left;
3260 return parseFloat(left);
3261 },
3262 bottom: function (_a, _b) {
3263 var height = _a.height;
3264 var top = _b.top;
3265 return parseFloat(top) + height;
3266 },
3267 right: function (_a, _b) {
3268 var width = _a.width;
3269 var left = _b.left;
3270 return parseFloat(left) + width;
3271 },
3272 // Transform
3273 x: getTranslateFromMatrix(4, 13),
3274 y: getTranslateFromMatrix(5, 14),
3275};
3276var convertChangedValueTypes = function (target, values, element, elementStyler, changedKeys) {
3277 var originBbox = element.getBoundingClientRect();
3278 var elementComputedStyle = getComputedStyle(element);
3279 var display = elementComputedStyle.display, top = elementComputedStyle.top, left = elementComputedStyle.left, bottom = elementComputedStyle.bottom, right = elementComputedStyle.right, transform = elementComputedStyle.transform;
3280 var originComputedStyle = { top: top, left: left, bottom: bottom, right: right, transform: transform };
3281 // If the element is currently set to display: "none", make it visible before
3282 // measuring the target bounding box
3283 if (display === "none") {
3284 elementStyler.set("display", target.display || "block");
3285 }
3286 // Apply the latest values (as set in checkAndConvertChangedValueTypes)
3287 elementStyler.render();
3288 var targetBbox = element.getBoundingClientRect();
3289 changedKeys.forEach(function (key) {
3290 // Restore styles to their **calculated computed style**, not their actual
3291 // originally set style. This allows us to animate between equivalent pixel units.
3292 var value = values.get(key);
3293 setAndResetVelocity(value, positionalValues[key](originBbox, originComputedStyle));
3294 target[key] = positionalValues[key](targetBbox, elementComputedStyle);
3295 });
3296 return target;
3297};
3298var checkAndConvertChangedValueTypes = function (values, ref, target, transitionEnd) {
3299 if (transitionEnd === void 0) { transitionEnd = {}; }
3300 target = __assign({}, target);
3301 transitionEnd = __assign({}, transitionEnd);
3302 var element = ref.current;
3303 var elementStyler = styler(element);
3304 var targetPositionalKeys = Object.keys(target).filter(isPositionalKey);
3305 // We want to remove any transform values that could affect the element's bounding box before
3306 // it's measured. We'll reapply these later.
3307 var removedTransformValues = [];
3308 var hasAttemptedToRemoveTransformValues = false;
3309 var changedValueTypeKeys = targetPositionalKeys.reduce(function (acc, key) {
3310 var value = values.get(key);
3311 if (!value)
3312 return acc;
3313 var from = value.get();
3314 var to = target[key];
3315 var fromType = getDimensionValueType(from);
3316 var toType;
3317 // TODO: The current implementation of this basically throws an error
3318 // if you try and do value conversion via keyframes. There's probably
3319 // a way of doing this but the performance implications would need greater scrutiny,
3320 // as it'd be doing multiple resize-remeasure operations.
3321 if (isKeyframesTarget(to)) {
3322 var numKeyframes = to.length;
3323 for (var i = to[0] === null ? 1 : 0; i < numKeyframes; i++) {
3324 if (!toType) {
3325 toType = getDimensionValueType(to[i]);
3326 invariant(toType === fromType ||
3327 (isNumOrPxType(fromType) &&
3328 isNumOrPxType(toType)), "Keyframes must be of the same dimension as the current value");
3329 }
3330 else {
3331 invariant(getDimensionValueType(to[i]) === toType, "All keyframes must be of the same type");
3332 }
3333 }
3334 }
3335 else {
3336 toType = getDimensionValueType(to);
3337 }
3338 if (fromType !== toType) {
3339 // If they're both just number or px, convert them both to numbers rather than
3340 // relying on resize/remeasure to convert (which is wasteful in this situation)
3341 if (isNumOrPxType(fromType) && isNumOrPxType(toType)) {
3342 var current = value.get();
3343 if (typeof current === "string") {
3344 value.set(parseFloat(current));
3345 }
3346 if (typeof to === "string") {
3347 target[key] = parseFloat(to);
3348 }
3349 else if (Array.isArray(to) && toType === px) {
3350 target[key] = to.map(parseFloat);
3351 }
3352 }
3353 else {
3354 // If we're going to do value conversion via DOM measurements, we first
3355 // need to remove non-positional transform values that could affect the bbox measurements.
3356 if (!hasAttemptedToRemoveTransformValues) {
3357 removedTransformValues = removeNonTranslationalTransform(values, elementStyler);
3358 hasAttemptedToRemoveTransformValues = true;
3359 }
3360 acc.push(key);
3361 transitionEnd[key] =
3362 transitionEnd[key] !== undefined
3363 ? transitionEnd[key]
3364 : target[key];
3365 setAndResetVelocity(value, to);
3366 }
3367 }
3368 return acc;
3369 }, []);
3370 if (changedValueTypeKeys.length) {
3371 var convertedTarget = convertChangedValueTypes(target, values, element, elementStyler, changedValueTypeKeys);
3372 // If we removed transform values, reapply them before the next render
3373 if (removedTransformValues.length) {
3374 removedTransformValues.forEach(function (_a) {
3375 var key = _a[0], value = _a[1];
3376 values.get(key).set(value);
3377 });
3378 }
3379 // Reapply original values
3380 elementStyler.render();
3381 return { target: convertedTarget, transitionEnd: transitionEnd };
3382 }
3383 else {
3384 return { target: target, transitionEnd: transitionEnd };
3385 }
3386};
3387/**
3388 * Convert value types for x/y/width/height/top/left/bottom/right
3389 *
3390 * Allows animation between `'auto'` -> `'100%'` or `0` -> `'calc(50% - 10vw)'`
3391 *
3392 * @param values
3393 * @param ref
3394 * @param target
3395 * @param transitionEnd
3396 * @internal
3397 */
3398function unitConversion(values, ref, target, transitionEnd) {
3399 return hasPositionalKey(target)
3400 ? checkAndConvertChangedValueTypes(values, ref, target, transitionEnd)
3401 : { target: target, transitionEnd: transitionEnd };
3402}
3403
3404var parseDomVariant = function (values, ref) {
3405 return function (target, transitionEnd) {
3406 var resolved = resolveCSSVariables(values, ref, target, transitionEnd);
3407 target = resolved.target;
3408 transitionEnd = resolved.transitionEnd;
3409 return unitConversion(values, ref, target, transitionEnd);
3410 };
3411};
3412
3413function useForceUpdate() {
3414 var _a = useState(0), forcedRenderCount = _a[0], setForcedRenderCount = _a[1];
3415 return useCallback(function () { return setForcedRenderCount(forcedRenderCount + 1); }, [
3416 forcedRenderCount,
3417 ]);
3418}
3419
3420var SyncLayoutContext = createContext(null);
3421/**
3422 * When layout changes happen asynchronously to their instigating render (ie when exiting
3423 * children of `AnimatePresence` are removed), `SyncLayout` can wrap parent and sibling
3424 * components that need to animate as a result of this layout change.
3425 *
3426 * @motion
3427 *
3428 * ```jsx
3429 * const MyComponent = ({ isVisible }) => {
3430 * return (
3431 * <SyncLayout>
3432 * <AnimatePresence>
3433 * {isVisible && (
3434 * <motion.div exit={{ opacity: 0 }} />
3435 * )}
3436 * </AnimatePresence>
3437 * <motion.div positionTransition />
3438 * </SyncLayout>
3439 * )
3440 * }
3441 * ```
3442 *
3443 * @internalremarks
3444 *
3445 * The way this component works is by memoising a function and passing it down via context.
3446 * The function, when called, updates the local state, which is used to invalidate the
3447 * memoisation cache. A new function is called, performing a synced re-render of components
3448 * that are using the SyncLayoutContext.
3449 *
3450 * @internal
3451 */
3452var UnstableSyncLayout = function (_a) {
3453 var children = _a.children;
3454 var forceUpdate = useForceUpdate();
3455 return (createElement(SyncLayoutContext.Provider, { value: forceUpdate }, children));
3456};
3457
3458var _a;
3459var StepName;
3460(function (StepName) {
3461 StepName["Prepare"] = "prepare";
3462 StepName["Read"] = "read";
3463 StepName["Render"] = "render";
3464})(StepName || (StepName = {}));
3465var stepOrder = [StepName.Prepare, StepName.Read, StepName.Render];
3466var jobs = stepOrder.reduce(function (acc, key) {
3467 acc[key] = [];
3468 return acc;
3469}, {});
3470var jobsNeedProcessing = false;
3471function flushCallbackList(list) {
3472 var numJobs = list.length;
3473 for (var i = 0; i < numJobs; i++) {
3474 list[i]();
3475 }
3476 list.length = 0;
3477}
3478function flushAllJobs() {
3479 if (!jobsNeedProcessing)
3480 return;
3481 flushCallbackList(jobs.prepare);
3482 flushCallbackList(jobs.read);
3483 flushCallbackList(jobs.render);
3484 jobsNeedProcessing = false;
3485}
3486// Note: The approach of schedulng jobs during the render step is incompatible with concurrent mode
3487// where multiple renders might happen without a DOM update. This would result in unneccessary batched
3488// jobs. But this was already a problem with our previous approach to positionTransition.
3489// Hopefully the React team offer a getSnapshotBeforeUpdate-esque hook and we can move to that.
3490var createUseSyncEffect = function (stepName) { return function (callback) {
3491 if (!callback)
3492 return;
3493 jobsNeedProcessing = true;
3494 jobs[stepName].push(callback);
3495}; };
3496var layoutSync = (_a = {},
3497 _a[StepName.Prepare] = createUseSyncEffect(StepName.Prepare),
3498 _a[StepName.Read] = createUseSyncEffect(StepName.Read),
3499 _a[StepName.Render] = createUseSyncEffect(StepName.Render),
3500 _a.flush = flushAllJobs,
3501 _a);
3502
3503function isHTMLElement(element) {
3504 return element instanceof HTMLElement;
3505}
3506
3507var defaultLayoutTransition = {
3508 duration: 0.8,
3509 ease: [0.45, 0.05, 0.19, 1.0],
3510};
3511var defaultPositionTransition = underDampedSpring();
3512function getDefaultLayoutTransition(isPositionOnly) {
3513 return isPositionOnly ? defaultPositionTransition : defaultLayoutTransition;
3514}
3515function isResolver(transition) {
3516 return typeof transition === "function";
3517}
3518var axisLabels = {
3519 x: {
3520 id: "x",
3521 size: "width",
3522 min: "left",
3523 max: "right",
3524 origin: "originX",
3525 },
3526 y: {
3527 id: "y",
3528 size: "height",
3529 min: "top",
3530 max: "bottom",
3531 origin: "originY",
3532 },
3533};
3534function centerOf(min, max) {
3535 return (min + max) / 2;
3536}
3537function calcAxisDelta(prev, next, names) {
3538 var _a;
3539 var sizeDelta = prev[names.size] - next[names.size];
3540 var origin = 0.5;
3541 // If the element has changed size we want to check whether either side is in
3542 // the same position before/after the layout transition. If so, we can anchor
3543 // the element to that position and only animate its size.
3544 if (sizeDelta) {
3545 if (prev[names.min] === next[names.min]) {
3546 origin = 0;
3547 }
3548 else if (prev[names.max] === next[names.max]) {
3549 origin = 1;
3550 }
3551 }
3552 var delta = (_a = {},
3553 _a[names.size] = sizeDelta,
3554 _a[names.origin] = origin,
3555 _a[names.id] =
3556 // Only measure a position delta if we haven't anchored to one side
3557 origin === 0.5
3558 ? centerOf(prev[names.min], prev[names.max]) -
3559 centerOf(next[names.min], next[names.max])
3560 : 0,
3561 _a);
3562 return delta;
3563}
3564function calcDelta(prev, next) {
3565 var delta = __assign(__assign({}, calcAxisDelta(prev, next, axisLabels.x)), calcAxisDelta(prev, next, axisLabels.y));
3566 return delta;
3567}
3568var offset = {
3569 getLayout: function (_a) {
3570 var offset = _a.offset;
3571 return offset;
3572 },
3573 measure: function (element) {
3574 var offsetLeft = element.offsetLeft, offsetTop = element.offsetTop, offsetWidth = element.offsetWidth, offsetHeight = element.offsetHeight;
3575 return {
3576 left: offsetLeft,
3577 top: offsetTop,
3578 right: offsetLeft + offsetWidth,
3579 bottom: offsetTop + offsetHeight,
3580 width: offsetWidth,
3581 height: offsetHeight,
3582 };
3583 },
3584};
3585var boundingBox = {
3586 getLayout: function (_a) {
3587 var boundingBox = _a.boundingBox;
3588 return boundingBox;
3589 },
3590 measure: function (element) {
3591 var _a = element.getBoundingClientRect(), left = _a.left, top = _a.top, width = _a.width, height = _a.height, right = _a.right, bottom = _a.bottom;
3592 return { left: left, top: top, width: width, height: height, right: right, bottom: bottom };
3593 },
3594};
3595function readPositionStyle(element) {
3596 return window.getComputedStyle(element).position;
3597}
3598function getLayoutType(prev, next, isPositionOnly) {
3599 return isPositionOnly && prev === next ? offset : boundingBox;
3600}
3601function isSizeKey(key) {
3602 return key === "width" || key === "height";
3603}
3604function getTransition(_a) {
3605 var layoutTransition = _a.layoutTransition, positionTransition = _a.positionTransition;
3606 return layoutTransition || positionTransition;
3607}
3608var LayoutAnimation = /** @class */ (function (_super) {
3609 __extends(LayoutAnimation, _super);
3610 function LayoutAnimation() {
3611 return _super !== null && _super.apply(this, arguments) || this;
3612 }
3613 // Measure the current state of the DOM before it's updated, and schedule checks to see
3614 // if it's changed as a result of a React render.
3615 LayoutAnimation.prototype.getSnapshotBeforeUpdate = function () {
3616 var _a = this.props, innerRef = _a.innerRef, positionTransition = _a.positionTransition, values = _a.values, controls = _a.controls;
3617 var element = innerRef.current;
3618 if (!isHTMLElement(element))
3619 return;
3620 var layoutTransition = getTransition(this.props);
3621 var isPositionOnly = !!positionTransition;
3622 var positionStyle = readPositionStyle(element);
3623 var prev = {
3624 offset: offset.measure(element),
3625 boundingBox: boundingBox.measure(element),
3626 };
3627 var transform;
3628 var next;
3629 var compare;
3630 // We split the unsetting, read and reapplication of the `transform` style prop into
3631 // different steps via useSyncEffect. Multiple components might all be doing the same
3632 // thing and by splitting these jobs and flushing them in batches we prevent layout thrashing.
3633 layoutSync.prepare(function () {
3634 // Unset the transform of all layoutTransition components so we can accurately measure
3635 // the target bounding box
3636 transform = element.style.transform;
3637 element.style.transform = "";
3638 });
3639 layoutSync.read(function () {
3640 // Read the target VisualInfo of all layoutTransition components
3641 next = {
3642 offset: offset.measure(element),
3643 boundingBox: boundingBox.measure(element),
3644 };
3645 var nextPosition = readPositionStyle(element);
3646 compare = getLayoutType(positionStyle, nextPosition, isPositionOnly);
3647 });
3648 layoutSync.render(function () {
3649 // Reverse the layout delta of all newly laid-out layoutTransition components into their
3650 // prev visual state and then animate them into their new one using transforms.
3651 var prevLayout = compare.getLayout(prev);
3652 var nextLayout = compare.getLayout(next);
3653 var delta = calcDelta(prevLayout, nextLayout);
3654 var hasAnyChanged = delta.x || delta.y || delta.width || delta.height;
3655 if (!hasAnyChanged) {
3656 // If layout hasn't changed, reapply the transform and get out of here.
3657 transform && (element.style.transform = transform);
3658 return;
3659 }
3660 styler(element).set({
3661 originX: delta.originX,
3662 originY: delta.originY,
3663 });
3664 syncRenderSession.open();
3665 var target = {};
3666 var transition = {};
3667 var transitionDefinition = isResolver(layoutTransition)
3668 ? layoutTransition({ delta: delta })
3669 : layoutTransition;
3670 function makeTransition(layoutKey, transformKey, targetValue, visualOrigin) {
3671 // If this dimension hasn't changed, early return
3672 var deltaKey = isSizeKey(layoutKey) ? layoutKey : transformKey;
3673 if (!delta[deltaKey])
3674 return;
3675 var baseTransition = typeof transitionDefinition === "boolean"
3676 ? __assign({}, getDefaultLayoutTransition(isPositionOnly)) : transitionDefinition;
3677 var value = values.get(transformKey, targetValue);
3678 var velocity = value.getVelocity();
3679 transition[transformKey] = baseTransition[transformKey]
3680 ? __assign({}, baseTransition[transformKey]) : __assign({}, baseTransition);
3681 if (transition[transformKey].velocity === undefined) {
3682 transition[transformKey].velocity = velocity || 0;
3683 }
3684 // The target value of all transforms is the default value of that prop (ie x = 0, scaleX = 1)
3685 // This is because we're inverting the layout change with `transform` and then animating to `transform: none`
3686 target[transformKey] = targetValue;
3687 var offsetToApply = !isSizeKey(layoutKey) && compare === offset
3688 ? value.get()
3689 : 0;
3690 value.set(visualOrigin + offsetToApply);
3691 }
3692 makeTransition("left", "x", 0, delta.x);
3693 makeTransition("top", "y", 0, delta.y);
3694 if (!isPositionOnly) {
3695 makeTransition("width", "scaleX", 1, prev.boundingBox.width / next.boundingBox.width);
3696 makeTransition("height", "scaleY", 1, prev.boundingBox.height / next.boundingBox.height);
3697 }
3698 target.transition = transition;
3699 // Only start the transition if `transitionDefinition` isn't `false`. Otherwise we want
3700 // to leave the values in their newly-inverted state and let the user cope with the rest.
3701 transitionDefinition && controls.start(target);
3702 // Force a render to ensure there's no visual flickering
3703 syncRenderSession.flush();
3704 });
3705 return null;
3706 };
3707 LayoutAnimation.prototype.componentDidUpdate = function () {
3708 layoutSync.flush();
3709 };
3710 LayoutAnimation.prototype.render = function () {
3711 return null;
3712 };
3713 LayoutAnimation.contextType = SyncLayoutContext;
3714 return LayoutAnimation;
3715}(Component));
3716var Layout = {
3717 key: "layout",
3718 shouldRender: function (_a) {
3719 var positionTransition = _a.positionTransition, layoutTransition = _a.layoutTransition;
3720 invariant(!(positionTransition && layoutTransition), "Don't set both positionTransition and layoutTransition on the same component");
3721 return (typeof window !== "undefined" &&
3722 !!(positionTransition || layoutTransition));
3723 },
3724 Component: LayoutAnimation,
3725};
3726
3727/**
3728 * A list of all valid MotionProps
3729 *
3730 * @internalremarks
3731 * This doesn't throw if a `MotionProp` name is missing - it should.
3732 */
3733var validMotionProps = new Set([
3734 "initial",
3735 "animate",
3736 "exit",
3737 "style",
3738 "variants",
3739 "transition",
3740 "transformTemplate",
3741 "transformValues",
3742 "custom",
3743 "inherit",
3744 "static",
3745 "positionTransition",
3746 "layoutTransition",
3747 "onAnimationStart",
3748 "onAnimationComplete",
3749 "onUpdate",
3750 "onDragStart",
3751 "onDrag",
3752 "onDragEnd",
3753 "onDirectionLock",
3754 "onDragTransitionEnd",
3755 "drag",
3756 "dragControls",
3757 "dragListener",
3758 "dragConstraints",
3759 "dragDirectionLock",
3760 "dragElastic",
3761 "dragMomentum",
3762 "dragPropagation",
3763 "dragTransition",
3764 "_dragValueX",
3765 "_dragValueY",
3766 "_dragTransitionControls",
3767 "dragOriginX",
3768 "dragOriginY",
3769 "onPan",
3770 "onPanStart",
3771 "onPanEnd",
3772 "onPanSessionStart",
3773 "onTap",
3774 "onTapStart",
3775 "onTapCancel",
3776 "whileHover",
3777 "whileTap",
3778 "onHoverEnd",
3779 "onHoverStart",
3780]);
3781/**
3782 * Check whether a prop name is a valid `MotionProp` key.
3783 *
3784 * @param key - Name of the property to check
3785 * @returns `true` is key is a valid `MotionProp`.
3786 *
3787 * @public
3788 */
3789function isValidMotionProp(key) {
3790 return validMotionProps.has(key);
3791}
3792
3793var AnimatePropType;
3794(function (AnimatePropType) {
3795 AnimatePropType["Target"] = "Target";
3796 AnimatePropType["VariantLabel"] = "VariantLabel";
3797 AnimatePropType["AnimationSubscription"] = "AnimationSubscription";
3798})(AnimatePropType || (AnimatePropType = {}));
3799
3800function shallowCompare(next, prev) {
3801 if (prev === null)
3802 return false;
3803 var prevLength = prev.length;
3804 if (prevLength !== next.length)
3805 return false;
3806 for (var i = 0; i < prevLength; i++) {
3807 if (prev[i] !== next[i])
3808 return false;
3809 }
3810 return true;
3811}
3812
3813var hasUpdated = function (prev, next) {
3814 return (next !== undefined &&
3815 (Array.isArray(prev) && Array.isArray(next)
3816 ? !shallowCompare(next, prev)
3817 : prev !== next));
3818};
3819function targetWithoutTransition(_a, mergeTransitionEnd) {
3820 if (mergeTransitionEnd === void 0) { mergeTransitionEnd = false; }
3821 var transition = _a.transition, transitionEnd = _a.transitionEnd, target = __rest(_a, ["transition", "transitionEnd"]);
3822 return mergeTransitionEnd
3823 ? __assign(__assign({}, target), transitionEnd)
3824 : target;
3825}
3826/**
3827 * Handle the `animate` prop when its an object of values, ie:
3828 *
3829 * ```jsx
3830 * <motion.div animate={{ opacity: 1 }} />
3831 * ```
3832 *
3833 * @internalremarks
3834 * It might be worth consolidating this with `use-variants`
3835 *
3836 * ```jsx
3837 * <motion.div animate="visible" />
3838 * ```
3839 *
3840 * @param target
3841 * @param controls
3842 * @param values
3843 * @param transition
3844 *
3845 * @internal
3846 */
3847function useAnimateProp(targetAndTransition, controls, values, defaultTransition) {
3848 var isInitialRender = useRef(true);
3849 var prevValues = useRef(null);
3850 if (!prevValues.current) {
3851 prevValues.current = targetWithoutTransition(targetAndTransition, true);
3852 }
3853 useEffect(function () {
3854 var targetToAnimate = {};
3855 // These are the values we're actually animating
3856 var animatingTarget = targetWithoutTransition(targetAndTransition);
3857 // This is the target as it'll be once transitionEnd values are applied
3858 var finalTarget = targetWithoutTransition(targetAndTransition, true);
3859 // Detect which values have changed between renders
3860 for (var key in animatingTarget) {
3861 // This value should animate on mount if this value doesn't already exist (wasn't
3862 // defined in `style` or `initial`) or if it does exist and it's already changed.
3863 var shouldAnimateOnMount = isInitialRender.current &&
3864 (!values.has(key) ||
3865 values.get(key).get() !== finalTarget[key]);
3866 // If this value has updated between renders or it's we're animating this value on mount,
3867 // add it to the animate target.
3868 var isValidValue = finalTarget[key] !== null;
3869 var valueHasUpdated = hasUpdated(prevValues.current[key], finalTarget[key]);
3870 if (isValidValue && (valueHasUpdated || shouldAnimateOnMount)) {
3871 targetToAnimate[key] = animatingTarget[key];
3872 }
3873 }
3874 isInitialRender.current = false;
3875 prevValues.current = __assign(__assign({}, prevValues.current), finalTarget);
3876 if (Object.keys(targetToAnimate).length) {
3877 controls.start(__assign(__assign({}, targetToAnimate), { transition: targetAndTransition.transition || defaultTransition, transitionEnd: targetAndTransition.transitionEnd }));
3878 }
3879 }, [targetAndTransition]);
3880}
3881
3882var labelsToArray = function (label) {
3883 if (!label) {
3884 return [];
3885 }
3886 if (Array.isArray(label)) {
3887 return label;
3888 }
3889 return [label];
3890};
3891var resolveVariantLabels = function (variant) {
3892 var unresolvedVariant = variant instanceof MotionValue ? variant.get() : variant;
3893 return Array.from(new Set(labelsToArray(unresolvedVariant)));
3894};
3895/**
3896 * Hooks in React sometimes accept a dependency array as their final argument. (ie useEffect/useMemo)
3897 * When values in this array change, React re-runs the dependency. However if the array
3898 * contains a variable number of items, React throws an error.
3899 */
3900var asDependencyList = function (list) { return [
3901 list.join(","),
3902]; };
3903
3904var hasVariantChanged = function (oldVariant, newVariant) {
3905 return oldVariant.join(",") !== newVariant.join(",");
3906};
3907/**
3908 * Handle variants and the `animate` prop when its set as variant labels.
3909 *
3910 * @param initial - Initial variant(s)
3911 * @param animate - Variant(s) to animate to
3912 * @param inherit - `true` is inheriting animations from parent
3913 * @param controls - Animation controls
3914 *
3915 * @internal
3916 */
3917function useVariants(initial, animate, inherit, controls) {
3918 var targetVariants = resolveVariantLabels(animate);
3919 var context = useContext(MotionContext);
3920 var parentAlreadyMounted = context.hasMounted && context.hasMounted.current;
3921 var hasMounted = useRef(false);
3922 useEffect(function () {
3923 var shouldAnimate = false;
3924 if (inherit) {
3925 // If we're inheriting variant changes and the parent has already
3926 // mounted when this component loads, we need to manually trigger
3927 // this animation.
3928 shouldAnimate = !!parentAlreadyMounted;
3929 targetVariants = resolveVariantLabels(context.animate);
3930 }
3931 else {
3932 shouldAnimate =
3933 hasMounted.current ||
3934 hasVariantChanged(resolveVariantLabels(initial), targetVariants);
3935 }
3936 shouldAnimate && controls.start(targetVariants);
3937 hasMounted.current = true;
3938 }, asDependencyList(targetVariants));
3939}
3940
3941/**
3942 * `useAnimationGroupSubscription` allows a component to subscribe to an
3943 * externally-created `AnimationControls`, created by the `useAnimation` hook.
3944 *
3945 * @param animation
3946 * @param controls
3947 *
3948 * @internal
3949 */
3950function useAnimationGroupSubscription(animation, controls) {
3951 var unsubscribe = useMemo(function () { return animation.subscribe(controls); }, [
3952 animation,
3953 ]);
3954 useEffect(function () { return function () {
3955 unsubscribe && unsubscribe();
3956 }; }, [unsubscribe]);
3957}
3958
3959var _a$1, _b;
3960var AnimatePropComponents = (_a$1 = {},
3961 _a$1[AnimatePropType.Target] = makeRenderlessComponent(function (_a) {
3962 var animate = _a.animate, controls = _a.controls, values = _a.values, transition = _a.transition;
3963 return useAnimateProp(animate, controls, values, transition);
3964 }),
3965 _a$1[AnimatePropType.VariantLabel] = makeRenderlessComponent(function (_a) {
3966 var animate = _a.animate, _b = _a.inherit, inherit = _b === void 0 ? true : _b, controls = _a.controls, initial = _a.initial;
3967 return useVariants(initial, animate, inherit, controls);
3968 }),
3969 _a$1[AnimatePropType.AnimationSubscription] = makeRenderlessComponent(function (_a) {
3970 var animate = _a.animate, controls = _a.controls;
3971 return useAnimationGroupSubscription(animate, controls);
3972 }),
3973 _a$1);
3974var isVariantLabel$1 = function (prop) {
3975 return Array.isArray(prop) || typeof prop === "string";
3976};
3977var isAnimationSubscription = function (_a) {
3978 var animate = _a.animate;
3979 return animate instanceof AnimationControls;
3980};
3981var animationProps = ["initial", "animate", "whileTap", "whileHover"];
3982var animatePropTypeTests = (_b = {},
3983 _b[AnimatePropType.Target] = function (props) {
3984 return (props.animate !== undefined &&
3985 !isVariantLabel$1(props.animate) &&
3986 !isAnimationSubscription(props));
3987 },
3988 _b[AnimatePropType.VariantLabel] = function (props) {
3989 return (props.variants !== undefined ||
3990 animationProps.some(function (key) { return typeof props[key] === "string"; }));
3991 },
3992 _b[AnimatePropType.AnimationSubscription] = isAnimationSubscription,
3993 _b);
3994var getAnimationComponent = function (props) {
3995 var animatePropType = undefined;
3996 for (var key in AnimatePropType) {
3997 if (animatePropTypeTests[key](props)) {
3998 animatePropType = key;
3999 }
4000 }
4001 return animatePropType ? AnimatePropComponents[animatePropType] : undefined;
4002};
4003
4004var Exit = {
4005 key: "exit",
4006 shouldRender: function (_a, _b) {
4007 var exit = _a.exit;
4008 var exitProps = _b.exitProps;
4009 var hasExitProps = !!exitProps;
4010 var hasExitAnimation = !!exit;
4011 invariant(!hasExitProps || (hasExitProps && hasExitAnimation), "No exit prop defined on a child of AnimatePresence.");
4012 return hasExitProps && hasExitAnimation;
4013 },
4014 Component: makeRenderlessComponent(function (props) {
4015 var animate = props.animate, controls = props.controls, parentContext = props.parentContext, exit = props.exit;
4016 var exitProps = parentContext.exitProps;
4017 var isPlayingExitAnimation = useRef(false);
4018 // This early return is more for types - it won't actually run because of the `shouldRender` above.
4019 if (!exitProps || !exit)
4020 return;
4021 var isExiting = exitProps.isExiting, custom = exitProps.custom, onExitComplete = exitProps.onExitComplete;
4022 useEffect(function () {
4023 if (isExiting) {
4024 if (!isPlayingExitAnimation.current && exit) {
4025 controls.setProps(__assign(__assign({}, props), { custom: custom !== undefined ? custom : props.custom }));
4026 controls.start(exit).then(onExitComplete);
4027 }
4028 isPlayingExitAnimation.current = true;
4029 }
4030 else if (isPlayingExitAnimation.current &&
4031 animate &&
4032 !(animate instanceof AnimationControls)) {
4033 controls.start(animate);
4034 }
4035 if (!isExiting) {
4036 isPlayingExitAnimation.current = false;
4037 }
4038 }, [isExiting]);
4039 }),
4040};
4041
4042var isPropValid = function (key) { return !isValidMotionProp(key); };
4043/**
4044 * Emotion and Styled Components both allow users to pass through arbitrary props to their components
4045 * to dynamically generate CSS. They both use the `@emotion/is-prop-valid` package to determine which
4046 * of these should be passed to the underlying DOM node.
4047 *
4048 * However, when styling a Motion component `styled(motion.div)`, both packages pass through *all* props
4049 * as it's seen as an arbitrary component rather than a DOM node. Motion only allows arbitrary props
4050 * passed through the `custom` prop so it doesn't *need* the payload or computational overhead of
4051 * `@emotion/is-prop-valid`, however to fix this problem we need to use it.
4052 *
4053 * By making it an optionalDependency we can offer this functionality only in the situations where it's
4054 * actually required.
4055 */
4056try {
4057 var emotionIsPropValid_1 = require("@emotion/is-prop-valid").default;
4058 isPropValid = function (key) {
4059 // Handle events explicitly as Emotion validates them all as true
4060 if (key.startsWith("on")) {
4061 return !isValidMotionProp(key);
4062 }
4063 else {
4064 return emotionIsPropValid_1(key);
4065 }
4066 };
4067}
4068catch (_a) {
4069 // We don't need to actually do anything here - the fallback is the existing `isPropValid`.
4070}
4071function filterValidProps(props) {
4072 var domProps = {};
4073 for (var key in props) {
4074 if (isPropValid(key)) {
4075 domProps[key] = props[key];
4076 }
4077 }
4078 return domProps;
4079}
4080var buildSVGProps = function (values, style) {
4081 var motionValueStyles = resolveCurrent(values);
4082 var props = buildSVGAttrs(motionValueStyles, undefined, undefined, undefined, undefined, false);
4083 props.style = __assign(__assign({}, style), props.style);
4084 return props;
4085};
4086var functionalityComponents = [Layout, Drag, Gestures, Exit];
4087var numFunctionalityComponents = functionalityComponents.length;
4088/**
4089 * Create a configuration for `motion` components that provides DOM-specific functionality.
4090 *
4091 * @internal
4092 */
4093function createDomMotionConfig(Component) {
4094 var isDOM = typeof Component === "string";
4095 var isSVG = isDOM && svgElements.indexOf(Component) !== -1;
4096 return {
4097 renderComponent: function (ref, style, values, props, isStatic) {
4098 var forwardedProps = isDOM ? filterValidProps(props) : props;
4099 var staticVisualStyles = isSVG
4100 ? buildSVGProps(values, style)
4101 : { style: buildStyleAttr(values, style, isStatic) };
4102 return createElement(Component, __assign(__assign(__assign({}, forwardedProps), { ref: ref }), staticVisualStyles));
4103 },
4104 /**
4105 * loadFunctionalityComponents gets used by the `motion` component
4106 *
4107 * Each functionality component gets provided the `ref`, animation controls and the `MotionValuesMap`
4108 * generated for that component, as well as all the `props` passed to it by the user.
4109 *
4110 * The pattern used to determine whether to load and use each piece of functionality is
4111 * consistent (should render? Then push component) and could be used to extend functionality.
4112 *
4113 * By exposing a mutable piece of memory via an API like `extendMotionComponent` we could
4114 * allow users to add `FunctionalComponentDefinition`s. This would allow us to offer file size
4115 * reductions by shipping an entry point that doesn't load gesture and drag functionality, and
4116 * also offer a way for users to develop plugins/other functionality. Because these functionalities
4117 * are loaded as components, we can look into using Suspense for this purpose.
4118 *
4119 * For user-defined functionality we'd need to allow
4120 * 1) User-defined prop typing (extending `P`)
4121 * 2) User-defined "clean props" function that removes their plugin's props before being passed to the DOM.
4122 */
4123 loadFunctionalityComponents: function (ref, values, props, context, controls, inherit) {
4124 var activeComponents = [];
4125 // TODO: Consolidate Animation functionality loading strategy with other functionality components
4126 var Animation = getAnimationComponent(props);
4127 if (Animation) {
4128 activeComponents.push(createElement(Animation, { key: "animation", initial: props.initial, animate: props.animate, variants: props.variants, transition: props.transition, controls: controls, inherit: inherit, values: values }));
4129 }
4130 for (var i = 0; i < numFunctionalityComponents; i++) {
4131 var _a = functionalityComponents[i], shouldRender = _a.shouldRender, key = _a.key, Component_1 = _a.Component;
4132 if (shouldRender(props, context)) {
4133 activeComponents.push(createElement(Component_1, __assign({ key: key }, props, { parentContext: context, values: values, controls: controls, innerRef: ref })));
4134 }
4135 }
4136 return activeComponents;
4137 },
4138 getValueControlsConfig: function (ref, values) {
4139 return {
4140 values: values,
4141 readValueFromSource: function (key) {
4142 return styler(ref.current).get(key);
4143 },
4144 // TODO: This is a good second source of plugins. This function contains the CSS variable
4145 // and unit conversion support. These functions share a common signature. We could make another
4146 // API for adding these.
4147 makeTargetAnimatable: parseDomVariant(values, ref),
4148 };
4149 },
4150 };
4151}
4152
4153var htmlMotionComponents = htmlElements.reduce(function (acc, Component) {
4154 var config = createDomMotionConfig(Component);
4155 // Suppress "Expression produces a union type that is too complex to represent" error
4156 // @ts-ignore
4157 acc[Component] = createMotionComponent(config);
4158 return acc;
4159}, {});
4160var svgMotionComponents = svgElements.reduce(function (acc, Component) {
4161 // Suppress "Expression produces a union type that is too complex to represent" error
4162 // @ts-ignore
4163 acc[Component] = createMotionComponent(createDomMotionConfig(Component));
4164 return acc;
4165}, {});
4166/**
4167 * HTML & SVG components, optimised for use with gestures and animation. These can be used as
4168 * drop-in replacements for any HTML & SVG component, all CSS & SVG properties are supported.
4169 *
4170 * @internalremarks
4171 *
4172 * I'd like to make it possible for these to be loaded "on demand" - to reduce bundle size by only
4173 * including HTML/SVG stylers, animation and/or gesture support when necessary.
4174 *
4175 * ```jsx
4176 * <motion.div animate={{ x: 100 }} />
4177 *
4178 * <motion.p animate={{ height: 200 }} />
4179 *
4180 * <svg><motion.circle r={10} animate={{ r: 20 }} /></svg>
4181 * ```
4182 *
4183 * @public
4184 */
4185var motion = __assign(__assign({
4186 /**
4187 * Convert a custom React component into a `motion` component.
4188 *
4189 * It can also accept a string, to create [custom DOM elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements).
4190 *
4191 * ```jsx
4192 * const Component = React.forwardRef((props: Props, ref) => {
4193 * return <div ref={ref} />
4194 * })
4195 *
4196 * const MotionComponent = motion.custom<Props>(Component)
4197 * ```
4198 *
4199 * @param Component -
4200 */
4201 custom: function custom(Component) {
4202 return createMotionComponent(createDomMotionConfig(Component));
4203 } }, htmlMotionComponents), svgMotionComponents);
4204
4205/**
4206 * Creates a `MotionValue` to track the state and velocity of a value.
4207 *
4208 * 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.
4209 *
4210 * @library
4211 *
4212 * ```jsx
4213 * export function MyComponent() {
4214 * const scale = useMotionValue(1)
4215 *
4216 * return <Frame scale={scale} />
4217 * }
4218 * ```
4219 *
4220 * @motion
4221 *
4222 * ```jsx
4223 * export const MyComponent = () => {
4224 * const scale = useMotionValue(1)
4225 *
4226 * return <motion.div style={{ scale }} />
4227 * }
4228 * ```
4229 *
4230 * @param initial - The initial state.
4231 *
4232 * @public
4233 */
4234function useMotionValue(initial) {
4235 return useConstant(function () { return motionValue(initial); });
4236}
4237
4238/**
4239 * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
4240 *
4241 * TODO: Remove and move to library
4242 *
4243 * @internal
4244 */
4245function unwrapMotionValue(value) {
4246 var unwrappedValue = value instanceof MotionValue ? value.get() : value;
4247 return isCustomValue(unwrappedValue)
4248 ? unwrappedValue.toValue()
4249 : unwrappedValue;
4250}
4251
4252var isCustomValueType = function (v) {
4253 return typeof v === "object" && v.mix;
4254};
4255var getMixer = function (v) { return (isCustomValueType(v) ? v.mix : undefined); };
4256function transform() {
4257 var args = [];
4258 for (var _i = 0; _i < arguments.length; _i++) {
4259 args[_i] = arguments[_i];
4260 }
4261 var useImmediate = !Array.isArray(args[0]);
4262 var argOffset = useImmediate ? 0 : -1;
4263 var inputValue = args[0 + argOffset];
4264 var inputRange = args[1 + argOffset];
4265 var outputRange = args[2 + argOffset];
4266 var options = args[3 + argOffset];
4267 var interpolator = interpolate(inputRange, outputRange, __assign({ mixer: getMixer(outputRange[0]) }, options));
4268 return useImmediate ? interpolator(inputValue) : interpolator;
4269}
4270
4271var isTransformer = function (v) {
4272 return typeof v === "function";
4273};
4274var noop$1 = function (v) { return v; };
4275function useTransform(parent, customTransform, to, options) {
4276 var value = useRef(null);
4277 var comparitor = [parent];
4278 var transformer = noop$1;
4279 if (isTransformer(customTransform)) {
4280 transformer = customTransform;
4281 }
4282 else if (Array.isArray(to)) {
4283 var from = customTransform;
4284 transformer = transform(from, to, options);
4285 comparitor = [parent, from.join(","), to.join(",")];
4286 }
4287 return useMemo(function () {
4288 if (value.current)
4289 value.current.destroy();
4290 value.current = parent.addChild({ transformer: transformer });
4291 return value.current;
4292 }, comparitor);
4293}
4294
4295// Keep things reasonable and avoid scale: Infinity. In practise we might need
4296// to add another value, opacity, that could interpolate scaleX/Y [0,0.01] => [0,1]
4297// to simply hide content at unreasonable scales.
4298var maxScale = 100000;
4299var invertScale = function (scale) { return (scale > 0.001 ? 1 / scale : maxScale); };
4300/**
4301 * Returns a `MotionValue` each for `scaleX` and `scaleY` that update with the inverse
4302 * of their respective parent scales.
4303 *
4304 * This is useful for undoing the distortion of content when scaling a parent component.
4305 *
4306 * By default, `useInvertedScale` will automatically fetch `scaleX` and `scaleY` from the nearest parent.
4307 * By passing other `MotionValue`s in as `useInvertedScale({ scaleX, scaleY })`, it will invert the output
4308 * of those instead.
4309 *
4310 * @motion
4311 *
4312 * ```jsx
4313 * const MyComponent = () => {
4314 * const { scaleX, scaleY } = useInvertedScale()
4315 * return <motion.div style={{ scaleX, scaleY }} />
4316 * }
4317 * ```
4318 *
4319 * @library
4320 *
4321 * ```jsx
4322 * function MyComponent() {
4323 * const { scaleX, scaleY } = useInvertedScale()
4324 * return <Frame scaleX={scaleX} scaleY={scaleY} />
4325 * }
4326 * ```
4327 *
4328 * @public
4329 */
4330function useInvertedScale(scale) {
4331 var parentScaleX = useMotionValue(1);
4332 var parentScaleY = useMotionValue(1);
4333 var values = useContext(MotionContext).values;
4334 invariant(!!(scale || values), "If no scale values are provided, useInvertedScale must be used within a child of another motion component.");
4335 if (scale) {
4336 parentScaleX = scale.scaleX || parentScaleX;
4337 parentScaleY = scale.scaleY || parentScaleY;
4338 }
4339 else if (values) {
4340 parentScaleX = values.get("scaleX", 1);
4341 parentScaleY = values.get("scaleY", 1);
4342 }
4343 var scaleX = useTransform(parentScaleX, invertScale);
4344 var scaleY = useTransform(parentScaleY, invertScale);
4345 return { scaleX: scaleX, scaleY: scaleY };
4346}
4347
4348function useOnChange(value, callback) {
4349 useEffect(function () { return (isMotionValue(value) ? value.onChange(callback) : undefined); }, [value]);
4350}
4351
4352/**
4353 * Creates a `MotionValue` that, when `set`, will use a spring animation to animate to its new state.
4354 *
4355 * It can either work as a stand-alone `MotionValue` by initialising it with a value, or as a subscriber
4356 * to another `MotionValue`.
4357 *
4358 * @remarks
4359 *
4360 * ```jsx
4361 * const x = useSpring(0, { stiffness: 300 })
4362 * const y = useSpring(x, { damping: 10 })
4363 * ```
4364 *
4365 * @param inputValue - `MotionValue` or number. If provided a `MotionValue`, when the input `MotionValue` changes, the created `MotionValue` will spring towards that value.
4366 * @param springConfig - Configuration options for the spring.
4367 * @returns `MotionValue`
4368 *
4369 * @public
4370 */
4371function useSpring(source, config) {
4372 if (config === void 0) { config = {}; }
4373 var activeSpringAnimation = useRef(null);
4374 var value = useMotionValue(isMotionValue(source) ? source.get() : source);
4375 useMemo(function () {
4376 return value.attach(function (v, set) {
4377 if (activeSpringAnimation.current) {
4378 activeSpringAnimation.current.stop();
4379 }
4380 activeSpringAnimation.current = spring(__assign({ from: value.get(), to: v, velocity: value.getVelocity() }, config)).start(set);
4381 return value.get();
4382 });
4383 }, Object.values(config));
4384 useOnChange(source, function (v) { return value.set(parseFloat(v)); });
4385 return value;
4386}
4387
4388var scrollX = motionValue(0);
4389var scrollY = motionValue(0);
4390var scrollXProgress = motionValue(0);
4391var scrollYProgress = motionValue(0);
4392var setProgress = function (offset, maxOffset, value) {
4393 value.set(!maxOffset || !offset ? 0 : offset / maxOffset);
4394};
4395var hasEventListener = false;
4396var addScrollListener = function () {
4397 hasEventListener = true;
4398 if (typeof window === "undefined")
4399 return;
4400 var updateScrollValues = function () {
4401 var xOffset = window.pageXOffset;
4402 var yOffset = window.pageYOffset;
4403 // Set absolute positions
4404 scrollX.set(xOffset);
4405 scrollY.set(yOffset);
4406 // Set 0-1 progress
4407 setProgress(xOffset, document.body.clientWidth - window.innerWidth, scrollXProgress);
4408 setProgress(yOffset, document.body.clientHeight - window.innerHeight, scrollYProgress);
4409 };
4410 updateScrollValues();
4411 window.addEventListener("resize", updateScrollValues);
4412 window.addEventListener("scroll", updateScrollValues, { passive: true });
4413};
4414var viewportMotionValues = {
4415 scrollX: scrollX,
4416 scrollY: scrollY,
4417 scrollXProgress: scrollXProgress,
4418 scrollYProgress: scrollYProgress,
4419};
4420/**
4421 * Provides `MotionValue`s that update when the viewport scrolls:
4422 *
4423 * - `scrollX` — Horizontal scroll distance in pixels.
4424 * - `scrollY` — Vertical scroll distance in pixels.
4425 * - `scrollXProgress` — Horizontal scroll progress between `0` and `1`.
4426 * - `scrollYProgress` — Vertical scroll progress between `0` and `1`.
4427 *
4428 * **Note:** If the returned scroll `MotionValue`s don't seem to be updating,
4429 * double check if the `body` tag styles are set to `width: 100%; height: 100%` or
4430 * similar, as this can break accurate measurement of viewport scroll.
4431 *
4432 * @library
4433 *
4434 * ```jsx
4435 * import * as React from "react"
4436 * import {
4437 * Frame,
4438 * useViewportScroll,
4439 * useTransform
4440 * } from "framer"
4441 *
4442 * export function MyComponent() {
4443 * const { scrollYProgress } = useViewportScroll()
4444 * return <Frame scaleX={scrollYProgress} />
4445 * }
4446 * ```
4447 *
4448 * @motion
4449 *
4450 * ```jsx
4451 * export const MyComponent = () => {
4452 * const { scrollYProgress } = useViewportScroll()
4453 * return <motion.div style={{ scaleX: scrollYProgress }} />
4454 * }
4455 * ```
4456 *
4457 * @internalremarks
4458 * This isn't technically a hook yet, but in the future it might be nice
4459 * to accept refs to elements and add scroll listeners to those, which
4460 * may involve the use of lifecycle.
4461 *
4462 * @public
4463 */
4464function useViewportScroll() {
4465 if (!hasEventListener) {
4466 addScrollListener();
4467 }
4468 return viewportMotionValues;
4469}
4470
4471/**
4472 * Creates `AnimationControls`, which can be used to manually start, stop
4473 * and sequence animations on one or more components.
4474 *
4475 * The returned `AnimationControls` should be passed to the `animate` property
4476 * of the components you want to animate.
4477 *
4478 * These components can then be animated with the `start` method.
4479 *
4480 * @library
4481 *
4482 * ```jsx
4483 * import * as React from 'react'
4484 * import { Frame, useAnimation } from 'framer'
4485 *
4486 * export function MyComponent(props) {
4487 * const controls = useAnimation()
4488 *
4489 * controls.start({
4490 * x: 100,
4491 * transition: { duration: 0.5 },
4492 * })
4493 *
4494 * return <Frame animate={controls} />
4495 * }
4496 * ```
4497 *
4498 * @motion
4499 *
4500 * ```jsx
4501 * import * as React from 'react'
4502 * import { motion, useAnimation } from 'framer-motion'
4503 *
4504 * export function MyComponent(props) {
4505 * const controls = useAnimation()
4506 *
4507 * controls.start({
4508 * x: 100,
4509 * transition: { duration: 0.5 },
4510 * })
4511 *
4512 * return <motion.div animate={controls} />
4513 * }
4514 * ```
4515 *
4516 * @returns Animation controller with `start` and `stop` methods
4517 *
4518 * @public
4519 */
4520function useAnimation() {
4521 var animationControls = useConstant(function () { return new AnimationControls(); });
4522 useEffect(function () {
4523 animationControls.mount();
4524 return function () { return animationControls.unmount(); };
4525 }, []);
4526 return animationControls;
4527}
4528
4529/**
4530 * Experimental API.
4531 *
4532 * Makes an animated version of `useState`.
4533 *
4534 * @remarks
4535 *
4536 * When the returned state setter is called, values will be animated to their new target.
4537 *
4538 * This allows the animation of arbitrary React components.
4539 *
4540 * **Note:** When animating DOM components, it's always preferable to use the `animate` prop, as Framer
4541 * will bypass React's rendering cycle with one optimised for 60fps motion. This Hook is specifically
4542 * for animating props on arbitrary React components, or for animating text content.
4543 *
4544 * ```jsx
4545 * const [state, setState] = useAnimatedState({ percentage: 0 })
4546 *
4547 * return (
4548 * <Graph
4549 * percentage={state.percentage}
4550 * onTap={() => setState({ percentage: 50 })}
4551 * />
4552 * )
4553 * ```
4554 *
4555 * @internalremarks
4556 *
4557 * TODO:
4558 * - Make hook accept a typed version of Target that accepts any value (not just DOM values)
4559 * - Allow hook to accept single values. ie useAnimatedState(0)
4560 * - Allow providing MotionValues via initialState.
4561 *
4562 * @beta
4563 */
4564function useAnimatedState(initialState) {
4565 var _a = useState(initialState), animationState = _a[0], onUpdate = _a[1];
4566 var config = useConstant(function () { return ({ onUpdate: onUpdate }); });
4567 var values = useMotionValues(config);
4568 var controls = useValueAnimationControls({
4569 values: values,
4570 readValueFromSource: function (key) { return animationState[key]; },
4571 }, {}, false);
4572 var startAnimation = useConstant(function () { return function (animationDefinition) {
4573 return controls.start(animationDefinition);
4574 }; });
4575 useEffect(function () {
4576 values.mount();
4577 return function () { return values.unmount(); };
4578 }, []);
4579 return [animationState, startAnimation];
4580}
4581
4582/**
4583 * 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.
4584 *
4585 * @library
4586 *
4587 * ```jsx
4588 * import * as React from "react"
4589 * import { Frame, useCycle } from "framer"
4590 *
4591 * export function MyComponent() {
4592 * const [x, cycleX] = useCycle(0, 50, 100)
4593 *
4594 * return (
4595 * <Frame
4596 * animate={{ x: x }}
4597 * onTap={() => cycleX()}
4598 * />
4599 * )
4600 * }
4601 * ```
4602 *
4603 * @motion
4604 *
4605 * An index value can be passed to the returned `cycle` function to cycle to a specific index.
4606 *
4607 * ```jsx
4608 * import * as React from "react"
4609 * import { motion, useCycle } from "framer-motion"
4610 *
4611 * export const MyComponent = () => {
4612 * const [x, cycleX] = useCycle(0, 50, 100)
4613 *
4614 * return (
4615 * <motion.div
4616 * animate={{ x: x }}
4617 * onTap={() => cycleX()}
4618 * />
4619 * )
4620 * }
4621 * ```
4622 *
4623 * @param items - items to cycle through
4624 * @returns [currentState, cycleState]
4625 *
4626 * @public
4627 */
4628function useCycle() {
4629 var items = [];
4630 for (var _i = 0; _i < arguments.length; _i++) {
4631 items[_i] = arguments[_i];
4632 }
4633 // TODO: After Framer X beta, remove this warning
4634 warning(items.length > 1, "useCycle syntax has changed. `useCycle([0, 1, 2])` becomes `useCycle(0, 1, 2)`");
4635 var index = useRef(0);
4636 var _a = useState(items[index.current]), item = _a[0], setItem = _a[1];
4637 return [
4638 item,
4639 function (next) {
4640 index.current =
4641 typeof next !== "number"
4642 ? wrap(0, items.length, index.current + 1)
4643 : next;
4644 setItem(items[index.current]);
4645 },
4646 ];
4647}
4648
4649/**
4650 * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components.
4651 *
4652 * @library
4653 *
4654 * ```jsx
4655 * const dragControls = useDragControls()
4656 *
4657 * function startDrag(event) {
4658 * dragControls.start(event, { snapToCursor: true })
4659 * }
4660 *
4661 * return (
4662 * <>
4663 * <Frame onTapStart={startDrag} />
4664 * <Frame drag="x" dragControls={dragControls} />
4665 * </>
4666 * )
4667 * ```
4668 *
4669 * @motion
4670 *
4671 * ```jsx
4672 * const dragControls = useDragControls()
4673 *
4674 * function startDrag(event) {
4675 * dragControls.start(event, { snapToCursor: true })
4676 * }
4677 *
4678 * return (
4679 * <>
4680 * <div onMouseDown={startDrag} />
4681 * <motion.div drag="x" dragControls={dragControls} />
4682 * </>
4683 * )
4684 * ```
4685 *
4686 * @public
4687 */
4688var DragControls = /** @class */ (function () {
4689 function DragControls() {
4690 this.componentControls = new Set();
4691 }
4692 /**
4693 * Subscribe a component's internal `ComponentDragControls` to the user-facing API.
4694 *
4695 * @internal
4696 */
4697 DragControls.prototype.subscribe = function (controls) {
4698 var _this = this;
4699 this.componentControls.add(controls);
4700 return function () { return _this.componentControls.delete(controls); };
4701 };
4702 /**
4703 * Start a drag gesture on every `motion` component that has this set of drag controls
4704 * passed into it via the `dragControls` prop.
4705 *
4706 * ```jsx
4707 * dragControls.start(e, {
4708 * snapToCursor: true
4709 * })
4710 * ```
4711 *
4712 * @param event - A mouse/touch/pointer event.
4713 * @param options - Options
4714 *
4715 * @public
4716 */
4717 DragControls.prototype.start = function (event, options) {
4718 this.componentControls.forEach(function (controls) {
4719 controls.start(event.nativeEvent || event, options);
4720 });
4721 };
4722 return DragControls;
4723}());
4724var createDragControls = function () { return new DragControls(); };
4725/**
4726 * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop
4727 * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we
4728 * might want to initiate that dragging from a different component than the draggable one.
4729 *
4730 * By creating a `dragControls` using the `useDragControls` hook, we can pass this into
4731 * the draggable component's `dragControls` prop. It exposes a `start` method
4732 * that can start dragging from pointer events on other components.
4733 *
4734 * @library
4735 *
4736 * ```jsx
4737 * const dragControls = useDragControls()
4738 *
4739 * function startDrag(event) {
4740 * dragControls.start(event, { snapToCursor: true })
4741 * }
4742 *
4743 * return (
4744 * <>
4745 * <Frame onTapStart={startDrag} />
4746 * <Frame drag="x" dragControls={dragControls} />
4747 * </>
4748 * )
4749 * ```
4750 *
4751 * @motion
4752 *
4753 * ```jsx
4754 * const dragControls = useDragControls()
4755 *
4756 * function startDrag(event) {
4757 * dragControls.start(event, { snapToCursor: true })
4758 * }
4759 *
4760 * return (
4761 * <>
4762 * <div onMouseDown={startDrag} />
4763 * <motion.div drag="x" dragControls={dragControls} />
4764 * </>
4765 * )
4766 * ```
4767 *
4768 * @public
4769 */
4770function useDragControls() {
4771 return useConstant(createDragControls);
4772}
4773
4774var PresenceChild = function (_a) {
4775 var children = _a.children, exitProps = _a.exitProps;
4776 var context = useContext(MotionContext);
4777 // Create a new `value` in all instances to ensure `motion` children re-render
4778 // and detect any layout changes that might have occurred.
4779 context = __assign(__assign({}, context), { exitProps: exitProps || {} });
4780 return (createElement(MotionContext.Provider, { value: context }, children));
4781};
4782function getChildKey(child) {
4783 return child.key || "";
4784}
4785function updateChildLookup(children, allChildren) {
4786 var seenChildren = process.env.NODE_ENV !== "production" ? new Set() : null;
4787 children.forEach(function (child) {
4788 var key = getChildKey(child);
4789 if (process.env.NODE_ENV !== "production" && seenChildren) {
4790 if (seenChildren.has(key)) {
4791 console.warn("Children of AnimatePresence require unique keys. \"" + key + "\" is a duplicate.");
4792 }
4793 seenChildren.add(key);
4794 }
4795 allChildren.set(key, child);
4796 });
4797}
4798function onlyElements(children) {
4799 var filtered = [];
4800 // We use forEach here instead of map as map mutates the component key by preprending `.$`
4801 Children.forEach(children, function (child) {
4802 if (isValidElement(child))
4803 filtered.push(child);
4804 });
4805 return filtered;
4806}
4807/**
4808 * The `AnimatePresence` component enables the use of the `exit` prop to animate components
4809 * when they're removed from the component tree.
4810 *
4811 * When adding/removing more than a single child component, every component
4812 * **must** be given a unique `key` prop.
4813 *
4814 * You can propagate exit animations throughout a tree by using variants.
4815 *
4816 * @library
4817 *
4818 * You can use any component(s) within `AnimatePresence`, but the first `Frame` in each should
4819 * have an `exit` property defined.
4820 *
4821 * ```jsx
4822 * import { Frame, AnimatePresence } from 'framer'
4823 *
4824 * // As items are added and removed from `items`
4825 * export function Items({ items }) {
4826 * return (
4827 * <AnimatePresence>
4828 * {items.map(item => (
4829 * <Frame
4830 * key={item.id}
4831 * initial={{ opacity: 0 }}
4832 * animate={{ opacity: 1 }}
4833 * exit={{ opacity: 0 }}
4834 * />
4835 * ))}
4836 * </AnimatePresence>
4837 * )
4838 * }
4839 * ```
4840 *
4841 * @motion
4842 *
4843 * You can use any component(s) within `AnimatePresence`, but the first `motion` component in each should
4844 * have an `exit` property defined.
4845 *
4846 * ```jsx
4847 * import { motion, AnimatePresence } from 'framer-motion'
4848 *
4849 * export const Items = ({ items }) => (
4850 * <AnimatePresence>
4851 * {items.map(item => (
4852 * <motion.div
4853 * key={item.id}
4854 * initial={{ opacity: 0 }}
4855 * animate={{ opacity: 1 }}
4856 * exit={{ opacity: 0 }}
4857 * />
4858 * ))}
4859 * </AnimatePresence>
4860 * )
4861 * ```
4862 *
4863 * @public
4864 */
4865var AnimatePresence = function (_a) {
4866 var children = _a.children, custom = _a.custom, _b = _a.initial, initial = _b === void 0 ? true : _b, onExitComplete = _a.onExitComplete, exitBeforeEnter = _a.exitBeforeEnter;
4867 // We want to force a re-render once all exiting animations have finished. We
4868 // either use a local forceUpdate function, or one from a parent context if it exists.
4869 var localForceUpdate = useForceUpdate();
4870 var contextForceUpdate = useContext(SyncLayoutContext);
4871 var forceUpdate = contextForceUpdate || localForceUpdate;
4872 var isInitialRender = useRef(true);
4873 // Filter out any children that aren't ReactElements. We can only track ReactElements with a props.key
4874 var filteredChildren = onlyElements(children);
4875 // Keep a living record of the children we're actually rendering so we
4876 // can diff to figure out which are entering and exiting
4877 var presentChildren = useRef(filteredChildren);
4878 // A lookup table to quickly reference components by key
4879 var allChildren = useRef(new Map())
4880 .current;
4881 // A living record of all currently exiting components.
4882 var exiting = useRef(new Set()).current;
4883 updateChildLookup(filteredChildren, allChildren);
4884 // If this is the initial component render, just deal with logic surrounding whether
4885 // we play onMount animations or not.
4886 if (isInitialRender.current) {
4887 isInitialRender.current = false;
4888 return (createElement(Fragment, null, filteredChildren.map(function (child) { return (createElement(PresenceChild, { key: getChildKey(child), exitProps: initial ? undefined : { initial: false } }, child)); })));
4889 }
4890 // If this is a subsequent render, deal with entering and exiting children
4891 var childrenToRender = __spreadArrays(filteredChildren);
4892 // Diff the keys of the currently-present and target children to update our
4893 // exiting list.
4894 var presentKeys = presentChildren.current.map(getChildKey);
4895 var targetKeys = filteredChildren.map(getChildKey);
4896 // Diff the present children with our target children and mark those that are exiting
4897 var numPresent = presentKeys.length;
4898 for (var i = 0; i < numPresent; i++) {
4899 var key = presentKeys[i];
4900 if (targetKeys.indexOf(key) === -1) {
4901 exiting.add(key);
4902 }
4903 else {
4904 // In case this key has re-entered, remove from the exiting list
4905 exiting.delete(key);
4906 }
4907 }
4908 // If we currently have exiting children, and we're deferring rendering incoming children
4909 // until after all current children have exiting, empty the childrenToRender array
4910 if (exitBeforeEnter && exiting.size) {
4911 childrenToRender = [];
4912 }
4913 // Loop through all currently exiting components and clone them to overwrite `animate`
4914 // with any `exit` prop they might have defined.
4915 exiting.forEach(function (key) {
4916 // If this component is actually entering again, early return
4917 if (targetKeys.indexOf(key) !== -1)
4918 return;
4919 var child = allChildren.get(key);
4920 if (!child)
4921 return;
4922 var insertionIndex = presentKeys.indexOf(key);
4923 var onExit = function () {
4924 exiting.delete(key);
4925 // Remove this child from the present children
4926 var removeIndex = presentChildren.current.findIndex(function (child) { return child.key === key; });
4927 presentChildren.current.splice(removeIndex, 1);
4928 // Defer re-rendering until all exiting children have indeed left
4929 if (!exiting.size) {
4930 presentChildren.current = filteredChildren;
4931 forceUpdate();
4932 onExitComplete && onExitComplete();
4933 }
4934 };
4935 var exitProps = {
4936 custom: custom,
4937 isExiting: true,
4938 onExitComplete: onExit,
4939 };
4940 childrenToRender.splice(insertionIndex, 0, createElement(PresenceChild, { key: getChildKey(child), exitProps: exitProps }, child));
4941 });
4942 // Add `MotionContext` even to children that don't need it to ensure we're rendering
4943 // the same tree between renders
4944 childrenToRender = childrenToRender.map(function (child) {
4945 var key = child.key;
4946 return exiting.has(key) ? (child) : (createElement(PresenceChild, { key: getChildKey(child) }, child));
4947 });
4948 presentChildren.current = childrenToRender;
4949 if (process.env.NODE_ENV !== "production" &&
4950 exitBeforeEnter &&
4951 childrenToRender.length > 1) {
4952 console.warn("You're attempting to animate multiple children within AnimatePresence, but its exitBeforeEnter prop is set to true. This will lead to odd visual behaviour.");
4953 }
4954 return (createElement(Fragment, null, exiting.size
4955 ? childrenToRender
4956 : childrenToRender.map(function (child) { return cloneElement(child); })));
4957};
4958
4959/**
4960 * When a component is the child of an `AnimatePresence` component, it has access to
4961 * information about whether it's still present the React tree. `usePresence` can be
4962 * used to access that data and perform operations before the component can be considered
4963 * safe to remove.
4964 *
4965 * It returns two values. `isPresent` is a boolean that is `true` when the component
4966 * is present within the React tree. It is `false` when it's been removed, but still visible.
4967 *
4968 * When `isPresent` is `false`, the `safeToRemove` callback can be used to tell `AnimatePresence`
4969 * that it's safe to remove the component from the DOM, for instance after a animation has completed.
4970 *
4971 * ```jsx
4972 * const [isPresent, safeToRemove] = usePresence()
4973 *
4974 * useEffect(() => {
4975 * !isPresent setTimeout(safeToRemove, 1000)
4976 * }, [isPresent])
4977 * ```
4978 *
4979 * @public
4980 */
4981function usePresence() {
4982 var exitProps = useContext(MotionContext).exitProps;
4983 if (!exitProps)
4984 return [true];
4985 var isExiting = exitProps.isExiting, onExitComplete = exitProps.onExitComplete;
4986 return isExiting && onExitComplete ? [false, onExitComplete] : [true];
4987}
4988
4989// Does this device prefer reduced motion? Returns `null` server-side.
4990var prefersReducedMotion = motionValue(null);
4991if (typeof window !== "undefined") {
4992 if (window.matchMedia) {
4993 var motionMediaQuery_1 = window.matchMedia("(prefers-reduced-motion)");
4994 var setReducedMotionPreferences = function () {
4995 return prefersReducedMotion.set(motionMediaQuery_1.matches);
4996 };
4997 motionMediaQuery_1.addListener(setReducedMotionPreferences);
4998 setReducedMotionPreferences();
4999 }
5000 else {
5001 prefersReducedMotion.set(false);
5002 }
5003}
5004function determineShouldReduceMotion(prefersReduced, isReducedMotion) {
5005 return typeof isReducedMotion === "boolean"
5006 ? isReducedMotion
5007 : Boolean(prefersReduced);
5008}
5009
5010/**
5011 * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
5012 *
5013 * This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing motion-sickness inducing
5014 * `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion.
5015 *
5016 * It will actively respond to changes and re-render your components with the latest setting.
5017 *
5018 * ```jsx
5019 * export function Sidebar({ isOpem }) {
5020 * const shouldReduceMotion = useReducedMotion()
5021 * const closedX = shouldReduceMotion ? 0 : "-100%"
5022 *
5023 * return (
5024 * <motion.div animate={{
5025 * opacity: isOpen ? 1 : 0,
5026 * x: isOpen ? 0 : closedX
5027 * }} />
5028 * )
5029 * }
5030 * ```
5031 *
5032 * @return boolean
5033 *
5034 * @public
5035 */
5036function useReducedMotion() {
5037 var isReducedMotion = useContext(MotionContext).isReducedMotion;
5038 var _a = useState(determineShouldReduceMotion(prefersReducedMotion.get(), isReducedMotion)), shouldReduceMotion = _a[0], setShouldReduceMotion = _a[1];
5039 useEffect(function () {
5040 return prefersReducedMotion.onChange(function (v) {
5041 setShouldReduceMotion(determineShouldReduceMotion(v, isReducedMotion));
5042 });
5043 }, [setShouldReduceMotion, isReducedMotion]);
5044 return shouldReduceMotion;
5045}
5046
5047/**
5048 * Define accessibility options for a tree. Can be used to force the tree into Reduced Motion mode,
5049 * or disable device detection.
5050 *
5051 * @internal
5052 */
5053function ReducedMotion(_a) {
5054 var children = _a.children, enabled = _a.enabled;
5055 var context = useContext(MotionContext);
5056 context = useMemo(function () { return (__assign(__assign({}, context), { isReducedMotion: enabled })); }, [enabled]);
5057 return (createElement(MotionContext.Provider, { value: context }, children));
5058}
5059
5060export { AnimatePresence, AnimationControls, DragControls, MotionContext, MotionPluginContext, MotionPlugins, MotionValue, Point, ReducedMotion, UnstableSyncLayout, animationControls, createMotionComponent, isValidMotionProp, motion, motionValue, transform, unwrapMotionValue, useAnimatedState, useAnimation, useCycle, useDomEvent, useDragControls, useExternalRef, useGestures, useInvertedScale, useMotionValue, usePanGesture, usePresence, useReducedMotion, useSpring, useTapGesture, useTransform, useViewportScroll };