framer-motion 9.0.2 → 9.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +3344 -3260
- package/dist/es/animation/hooks/use-animated-state.mjs +3 -3
- package/dist/es/animation/index.mjs +3 -5
- package/dist/es/animation/legacy-popmotion/index.mjs +6 -2
- package/dist/es/animation/legacy-popmotion/inertia.mjs +4 -5
- package/dist/es/components/AnimatePresence/use-presence.mjs +0 -1
- package/dist/es/events/add-dom-event.mjs +6 -0
- package/dist/es/events/add-pointer-event.mjs +8 -0
- package/dist/es/events/use-dom-event.mjs +2 -5
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +31 -30
- package/dist/es/gestures/drag/index.mjs +27 -0
- package/dist/es/gestures/focus.mjs +42 -0
- package/dist/es/gestures/hover.mjs +32 -0
- package/dist/es/gestures/{PanSession.mjs → pan/PanSession.mjs} +8 -8
- package/dist/es/gestures/pan/index.mjs +38 -0
- package/dist/es/gestures/press.mjs +109 -0
- package/dist/es/index.mjs +1 -1
- package/dist/es/motion/features/Feature.mjs +9 -0
- package/dist/es/motion/features/animation/exit.mjs +32 -0
- package/dist/es/motion/features/animation/index.mjs +38 -0
- package/dist/es/motion/features/animations.mjs +8 -37
- package/dist/es/motion/features/definitions.mjs +17 -23
- package/dist/es/motion/features/drag.mjs +12 -5
- package/dist/es/motion/features/gestures.mjs +16 -9
- package/dist/es/motion/features/layout/MeasureLayout.mjs +5 -5
- package/dist/es/motion/features/layout.mjs +11 -0
- package/dist/es/motion/features/load-features.mjs +4 -6
- package/dist/es/motion/features/viewport/index.mjs +96 -0
- package/dist/es/motion/index.mjs +12 -13
- package/dist/es/motion/utils/use-visual-element.mjs +8 -2
- package/dist/es/projection/geometry/delta-apply.mjs +9 -2
- package/dist/es/projection/geometry/delta-calc.mjs +2 -2
- package/dist/es/projection/geometry/delta-remove.mjs +2 -2
- package/dist/es/projection/node/DocumentProjectionNode.mjs +1 -1
- package/dist/es/projection/node/create-projection-node.mjs +65 -57
- package/dist/es/projection/node/group.mjs +5 -3
- package/dist/es/projection/shared/stack.mjs +7 -5
- package/dist/es/render/VisualElement.mjs +68 -47
- package/dist/es/render/dom/DOMVisualElement.mjs +1 -2
- package/dist/es/render/dom/features-max.mjs +2 -4
- package/dist/es/render/dom/motion.mjs +5 -6
- package/dist/es/render/dom/utils/create-config.mjs +1 -2
- package/dist/es/render/dom/value-types/animatable-none.mjs +3 -2
- package/dist/es/render/svg/SVGVisualElement.mjs +2 -2
- package/dist/es/render/utils/animation-state.mjs +6 -2
- package/dist/es/render/utils/animation.mjs +4 -4
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/render/utils/setters.mjs +14 -9
- package/dist/es/value/index.mjs +1 -1
- package/dist/es/value/utils/is-motion-value.mjs +1 -1
- package/dist/framer-motion.dev.js +3341 -3257
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +85 -77
- package/dist/projection.dev.js +217 -177
- package/dist/size-rollup-dom-animation-assets.js +1 -1
- package/dist/size-rollup-dom-animation-m.js +1 -1
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max-assets.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-m.js +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-webpack-dom-animation.js +1 -1
- package/dist/size-webpack-dom-max.js +1 -1
- package/dist/size-webpack-m.js +1 -1
- package/dist/three-entry.d.ts +74 -38
- package/package.json +8 -8
- package/dist/es/events/use-pointer-event.mjs +0 -11
- package/dist/es/gestures/drag/use-drag.mjs +0 -20
- package/dist/es/gestures/use-focus-gesture.mjs +0 -37
- package/dist/es/gestures/use-hover-gesture.mjs +0 -32
- package/dist/es/gestures/use-pan-gesture.mjs +0 -46
- package/dist/es/gestures/use-tap-gesture.mjs +0 -115
- package/dist/es/motion/features/layout/index.mjs +0 -7
- package/dist/es/motion/features/viewport/use-viewport.mjs +0 -97
- package/dist/es/motion/utils/VisualElementHandler.mjs +0 -19
- package/dist/es/motion/utils/make-renderless-component.mjs +0 -6
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isAnimationControls } from '../../../animation/utils/is-animation-controls.mjs';
|
|
2
|
+
import { createAnimationState } from '../../../render/utils/animation-state.mjs';
|
|
3
|
+
import { Feature } from '../Feature.mjs';
|
|
4
|
+
|
|
5
|
+
class AnimationFeature extends Feature {
|
|
6
|
+
/**
|
|
7
|
+
* We dynamically generate the AnimationState manager as it contains a reference
|
|
8
|
+
* to the underlying animation library. We only want to load that if we load this,
|
|
9
|
+
* so people can optionally code split it out using the `m` component.
|
|
10
|
+
*/
|
|
11
|
+
constructor(node) {
|
|
12
|
+
super(node);
|
|
13
|
+
node.animationState || (node.animationState = createAnimationState(node));
|
|
14
|
+
}
|
|
15
|
+
updateAnimationControlsSubscription() {
|
|
16
|
+
const { animate } = this.node.getProps();
|
|
17
|
+
this.unmount();
|
|
18
|
+
if (isAnimationControls(animate)) {
|
|
19
|
+
this.unmount = animate.subscribe(this.node);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe any provided AnimationControls to the component's VisualElement
|
|
24
|
+
*/
|
|
25
|
+
mount() {
|
|
26
|
+
this.updateAnimationControlsSubscription();
|
|
27
|
+
}
|
|
28
|
+
update() {
|
|
29
|
+
const { animate } = this.node.getProps();
|
|
30
|
+
const { animate: prevAnimate } = this.node.prevProps || {};
|
|
31
|
+
if (animate !== prevAnimate) {
|
|
32
|
+
this.updateAnimationControlsSubscription();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
unmount() { }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { AnimationFeature };
|
|
@@ -1,42 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { usePresence } from '../../components/AnimatePresence/use-presence.mjs';
|
|
4
|
-
import { PresenceContext } from '../../context/PresenceContext.mjs';
|
|
5
|
-
import { createAnimationState } from '../../render/utils/animation-state.mjs';
|
|
6
|
-
import { AnimationType } from '../../render/utils/types.mjs';
|
|
7
|
-
import { makeRenderlessComponent } from '../utils/make-renderless-component.mjs';
|
|
1
|
+
import { AnimationFeature } from './animation/index.mjs';
|
|
2
|
+
import { ExitAnimationFeature } from './animation/exit.mjs';
|
|
8
3
|
|
|
9
4
|
const animations = {
|
|
10
|
-
animation:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
visualElement.animationState || (visualElement.animationState = createAnimationState(visualElement));
|
|
17
|
-
/**
|
|
18
|
-
* Subscribe any provided AnimationControls to the component's VisualElement
|
|
19
|
-
*/
|
|
20
|
-
if (isAnimationControls(animate)) {
|
|
21
|
-
useEffect(() => animate.subscribe(visualElement), [animate]);
|
|
22
|
-
}
|
|
23
|
-
}),
|
|
24
|
-
exit: makeRenderlessComponent((props) => {
|
|
25
|
-
const { custom, visualElement } = props;
|
|
26
|
-
const [isPresent, safeToRemove] = usePresence();
|
|
27
|
-
const presenceContext = useContext(PresenceContext);
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
visualElement.isPresent = isPresent;
|
|
30
|
-
const animation = visualElement.animationState &&
|
|
31
|
-
visualElement.animationState.setActive(AnimationType.Exit, !isPresent, {
|
|
32
|
-
custom: (presenceContext && presenceContext.custom) ||
|
|
33
|
-
custom,
|
|
34
|
-
});
|
|
35
|
-
if (animation && !isPresent) {
|
|
36
|
-
animation.then(safeToRemove);
|
|
37
|
-
}
|
|
38
|
-
}, [isPresent]);
|
|
39
|
-
}),
|
|
5
|
+
animation: {
|
|
6
|
+
Feature: AnimationFeature,
|
|
7
|
+
},
|
|
8
|
+
exit: {
|
|
9
|
+
Feature: ExitAnimationFeature,
|
|
10
|
+
},
|
|
40
11
|
};
|
|
41
12
|
|
|
42
13
|
export { animations };
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
});
|
|
4
|
-
const featureDefinitions = {
|
|
5
|
-
measureLayout: createDefinition(["layout", "layoutId", "drag"]),
|
|
6
|
-
animation: createDefinition([
|
|
1
|
+
const featureProps = {
|
|
2
|
+
animation: [
|
|
7
3
|
"animate",
|
|
8
4
|
"exit",
|
|
9
5
|
"variants",
|
|
@@ -12,23 +8,21 @@ const featureDefinitions = {
|
|
|
12
8
|
"whileFocus",
|
|
13
9
|
"whileDrag",
|
|
14
10
|
"whileInView",
|
|
15
|
-
]
|
|
16
|
-
exit:
|
|
17
|
-
drag:
|
|
18
|
-
focus:
|
|
19
|
-
hover:
|
|
20
|
-
tap:
|
|
21
|
-
pan:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"onPanSessionStart",
|
|
25
|
-
"onPanEnd",
|
|
26
|
-
]),
|
|
27
|
-
inView: createDefinition([
|
|
28
|
-
"whileInView",
|
|
29
|
-
"onViewportEnter",
|
|
30
|
-
"onViewportLeave",
|
|
31
|
-
]),
|
|
11
|
+
],
|
|
12
|
+
exit: ["exit"],
|
|
13
|
+
drag: ["drag", "dragControls"],
|
|
14
|
+
focus: ["whileFocus"],
|
|
15
|
+
hover: ["whileHover", "onHoverStart", "onHoverEnd"],
|
|
16
|
+
tap: ["whileTap", "onTap", "onTapStart", "onTapCancel"],
|
|
17
|
+
pan: ["onPan", "onPanStart", "onPanSessionStart", "onPanEnd"],
|
|
18
|
+
inView: ["whileInView", "onViewportEnter", "onViewportLeave"],
|
|
19
|
+
layout: ["layout", "layoutId"],
|
|
32
20
|
};
|
|
21
|
+
const featureDefinitions = {};
|
|
22
|
+
for (const key in featureProps) {
|
|
23
|
+
featureDefinitions[key] = {
|
|
24
|
+
isEnabled: (props) => featureProps[key].some((name) => !!props[name]),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
33
27
|
|
|
34
28
|
export { featureDefinitions };
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { DragGesture } from '../../gestures/drag/index.mjs';
|
|
2
|
+
import { PanGesture } from '../../gestures/pan/index.mjs';
|
|
3
|
+
import { MeasureLayout } from './layout/MeasureLayout.mjs';
|
|
4
|
+
import { HTMLProjectionNode } from '../../projection/node/HTMLProjectionNode.mjs';
|
|
4
5
|
|
|
5
6
|
const drag = {
|
|
6
|
-
pan:
|
|
7
|
-
|
|
7
|
+
pan: {
|
|
8
|
+
Feature: PanGesture,
|
|
9
|
+
},
|
|
10
|
+
drag: {
|
|
11
|
+
Feature: DragGesture,
|
|
12
|
+
ProjectionNode: HTMLProjectionNode,
|
|
13
|
+
MeasureLayout,
|
|
14
|
+
},
|
|
8
15
|
};
|
|
9
16
|
|
|
10
17
|
export { drag };
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { makeRenderlessComponent } from '../utils/make-renderless-component.mjs';
|
|
1
|
+
import { HoverGesture } from '../../gestures/hover.mjs';
|
|
2
|
+
import { FocusGesture } from '../../gestures/focus.mjs';
|
|
3
|
+
import { PressGesture } from '../../gestures/press.mjs';
|
|
4
|
+
import { InViewFeature } from './viewport/index.mjs';
|
|
6
5
|
|
|
7
6
|
const gestureAnimations = {
|
|
8
|
-
inView:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
inView: {
|
|
8
|
+
Feature: InViewFeature,
|
|
9
|
+
},
|
|
10
|
+
tap: {
|
|
11
|
+
Feature: PressGesture,
|
|
12
|
+
},
|
|
13
|
+
focus: {
|
|
14
|
+
Feature: FocusGesture,
|
|
15
|
+
},
|
|
16
|
+
hover: {
|
|
17
|
+
Feature: HoverGesture,
|
|
18
|
+
},
|
|
12
19
|
};
|
|
13
20
|
|
|
14
21
|
export { gestureAnimations };
|
|
@@ -67,8 +67,8 @@ class MeasureLayoutWithContext extends React__default.Component {
|
|
|
67
67
|
* be in charge of the safe to remove. Otherwise we call it here.
|
|
68
68
|
*/
|
|
69
69
|
sync.postRender(() => {
|
|
70
|
-
|
|
71
|
-
if (!
|
|
70
|
+
const stack = projection.getStack();
|
|
71
|
+
if (!stack || !stack.members.length) {
|
|
72
72
|
this.safeToRemove();
|
|
73
73
|
}
|
|
74
74
|
});
|
|
@@ -90,15 +90,15 @@ class MeasureLayoutWithContext extends React__default.Component {
|
|
|
90
90
|
const { projection } = visualElement;
|
|
91
91
|
if (projection) {
|
|
92
92
|
projection.scheduleCheckAfterUnmount();
|
|
93
|
-
if (layoutGroup
|
|
93
|
+
if (layoutGroup && layoutGroup.group)
|
|
94
94
|
layoutGroup.group.remove(projection);
|
|
95
|
-
if (promoteContext
|
|
95
|
+
if (promoteContext && promoteContext.deregister)
|
|
96
96
|
promoteContext.deregister(projection);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
safeToRemove() {
|
|
100
100
|
const { safeToRemove } = this.props;
|
|
101
|
-
safeToRemove
|
|
101
|
+
safeToRemove && safeToRemove();
|
|
102
102
|
}
|
|
103
103
|
render() {
|
|
104
104
|
return null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HTMLProjectionNode } from '../../projection/node/HTMLProjectionNode.mjs';
|
|
2
|
+
import { MeasureLayout } from './layout/MeasureLayout.mjs';
|
|
3
|
+
|
|
4
|
+
const layout = {
|
|
5
|
+
layout: {
|
|
6
|
+
ProjectionNode: HTMLProjectionNode,
|
|
7
|
+
MeasureLayout,
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { layout };
|
|
@@ -2,12 +2,10 @@ import { featureDefinitions } from './definitions.mjs';
|
|
|
2
2
|
|
|
3
3
|
function loadFeatures(features) {
|
|
4
4
|
for (const key in features) {
|
|
5
|
-
|
|
6
|
-
featureDefinitions
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
featureDefinitions[key].Component = features[key];
|
|
10
|
-
}
|
|
5
|
+
featureDefinitions[key] = {
|
|
6
|
+
...featureDefinitions[key],
|
|
7
|
+
...features[key],
|
|
8
|
+
};
|
|
11
9
|
}
|
|
12
10
|
}
|
|
13
11
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AnimationType } from '../../../render/utils/types.mjs';
|
|
2
|
+
import { Feature } from '../Feature.mjs';
|
|
3
|
+
import { observeIntersection } from './observers.mjs';
|
|
4
|
+
|
|
5
|
+
const thresholdNames = {
|
|
6
|
+
some: 0,
|
|
7
|
+
all: 1,
|
|
8
|
+
};
|
|
9
|
+
class InViewFeature extends Feature {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.hasEnteredView = false;
|
|
13
|
+
this.isInView = false;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* TODO: Remove this in 10.0
|
|
17
|
+
*/
|
|
18
|
+
viewportFallback() {
|
|
19
|
+
/**
|
|
20
|
+
* Fire this in an rAF because, at this point, the animation state
|
|
21
|
+
* won't have flushed for the first time and there's certain logic in
|
|
22
|
+
* there that behaves differently on the initial animation.
|
|
23
|
+
*/
|
|
24
|
+
requestAnimationFrame(() => {
|
|
25
|
+
this.hasEnteredView = true;
|
|
26
|
+
const { onViewportEnter } = this.node.getProps();
|
|
27
|
+
onViewportEnter && onViewportEnter(null);
|
|
28
|
+
if (this.node.animationState) {
|
|
29
|
+
this.node.animationState.setActive(AnimationType.InView, true);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
startObserver() {
|
|
34
|
+
this.unmount();
|
|
35
|
+
const { viewport = {} } = this.node.getProps();
|
|
36
|
+
const { root, margin: rootMargin, amount = "some", once, fallback = true, } = viewport;
|
|
37
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
38
|
+
if (fallback)
|
|
39
|
+
this.viewportFallback();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const options = {
|
|
43
|
+
root: root ? root.current : undefined,
|
|
44
|
+
rootMargin,
|
|
45
|
+
threshold: typeof amount === "number" ? amount : thresholdNames[amount],
|
|
46
|
+
};
|
|
47
|
+
const onIntersectionUpdate = (entry) => {
|
|
48
|
+
const { isIntersecting } = entry;
|
|
49
|
+
/**
|
|
50
|
+
* If there's been no change in the viewport state, early return.
|
|
51
|
+
*/
|
|
52
|
+
if (this.isInView === isIntersecting)
|
|
53
|
+
return;
|
|
54
|
+
this.isInView = isIntersecting;
|
|
55
|
+
/**
|
|
56
|
+
* Handle hasEnteredView. If this is only meant to run once, and
|
|
57
|
+
* element isn't visible, early return. Otherwise set hasEnteredView to true.
|
|
58
|
+
*/
|
|
59
|
+
if (once && !isIntersecting && this.hasEnteredView) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
else if (isIntersecting) {
|
|
63
|
+
this.hasEnteredView = true;
|
|
64
|
+
}
|
|
65
|
+
if (this.node.animationState) {
|
|
66
|
+
this.node.animationState.setActive(AnimationType.InView, isIntersecting);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Use the latest committed props rather than the ones in scope
|
|
70
|
+
* when this observer is created
|
|
71
|
+
*/
|
|
72
|
+
const { onViewportEnter, onViewportLeave } = this.node.getProps();
|
|
73
|
+
const callback = isIntersecting ? onViewportEnter : onViewportLeave;
|
|
74
|
+
callback && callback(entry);
|
|
75
|
+
};
|
|
76
|
+
return observeIntersection(this.node.current, options, onIntersectionUpdate);
|
|
77
|
+
}
|
|
78
|
+
mount() {
|
|
79
|
+
this.startObserver();
|
|
80
|
+
}
|
|
81
|
+
update() {
|
|
82
|
+
if (typeof IntersectionObserver === "undefined")
|
|
83
|
+
return;
|
|
84
|
+
const { props, prevProps } = this.node;
|
|
85
|
+
const hasOptionsChanged = ["amount", "margin", "root"].some(hasViewportOptionChanged(props, prevProps));
|
|
86
|
+
if (hasOptionsChanged) {
|
|
87
|
+
this.startObserver();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
unmount() { }
|
|
91
|
+
}
|
|
92
|
+
function hasViewportOptionChanged({ viewport = {} }, { viewport: prevViewport = {} } = {}) {
|
|
93
|
+
return (name) => viewport[name] !== prevViewport[name];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { InViewFeature };
|
package/dist/es/motion/index.mjs
CHANGED
|
@@ -5,12 +5,10 @@ import { MotionContext } from '../context/MotionContext/index.mjs';
|
|
|
5
5
|
import { useVisualElement } from './utils/use-visual-element.mjs';
|
|
6
6
|
import { useMotionRef } from './utils/use-motion-ref.mjs';
|
|
7
7
|
import { useCreateMotionContext } from '../context/MotionContext/create.mjs';
|
|
8
|
-
import { featureDefinitions } from './features/definitions.mjs';
|
|
9
8
|
import { loadFeatures } from './features/load-features.mjs';
|
|
10
9
|
import { isBrowser } from '../utils/is-browser.mjs';
|
|
11
10
|
import { useProjectionId } from '../projection/node/id.mjs';
|
|
12
11
|
import { LayoutGroupContext } from '../context/LayoutGroupContext.mjs';
|
|
13
|
-
import { VisualElementHandler } from './utils/VisualElementHandler.mjs';
|
|
14
12
|
import { LazyContext } from '../context/LazyContext.mjs';
|
|
15
13
|
import { SwitchLayoutGroupContext } from '../context/SwitchLayoutGroupContext.mjs';
|
|
16
14
|
import { motionComponentSymbol } from './utils/symbol.mjs';
|
|
@@ -24,16 +22,20 @@ import { motionComponentSymbol } from './utils/symbol.mjs';
|
|
|
24
22
|
* Alongside this is a config option which provides a way of rendering the provided
|
|
25
23
|
* component "offline", or outside the React render cycle.
|
|
26
24
|
*/
|
|
27
|
-
function createMotionComponent({ preloadedFeatures, createVisualElement,
|
|
25
|
+
function createMotionComponent({ preloadedFeatures, createVisualElement, useRender, useVisualState, Component, }) {
|
|
28
26
|
preloadedFeatures && loadFeatures(preloadedFeatures);
|
|
29
27
|
function MotionComponent(props, externalRef) {
|
|
28
|
+
/**
|
|
29
|
+
* If we need to measure the element we load this functionality in a
|
|
30
|
+
* separate class component in order to gain access to getSnapshotBeforeUpdate.
|
|
31
|
+
*/
|
|
32
|
+
let MeasureLayout;
|
|
30
33
|
const configAndProps = {
|
|
31
34
|
...useContext(MotionConfigContext),
|
|
32
35
|
...props,
|
|
33
36
|
layoutId: useLayoutId(props),
|
|
34
37
|
};
|
|
35
38
|
const { isStatic } = configAndProps;
|
|
36
|
-
let features = null;
|
|
37
39
|
const context = useCreateMotionContext(props);
|
|
38
40
|
/**
|
|
39
41
|
* Create a unique projection ID for this component. If a new component is added
|
|
@@ -47,9 +49,6 @@ function createMotionComponent({ preloadedFeatures, createVisualElement, project
|
|
|
47
49
|
* that gets forceRendered and layout animations are triggered on its layout effect.
|
|
48
50
|
*/
|
|
49
51
|
const projectionId = isStatic ? undefined : useProjectionId();
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
*/
|
|
53
52
|
const visualState = useVisualState(props, isStatic);
|
|
54
53
|
if (!isStatic && isBrowser) {
|
|
55
54
|
/**
|
|
@@ -64,20 +63,20 @@ function createMotionComponent({ preloadedFeatures, createVisualElement, project
|
|
|
64
63
|
* components so each feature can optionally make use of React lifecycle methods.
|
|
65
64
|
*/
|
|
66
65
|
const initialLayoutGroupConfig = useContext(SwitchLayoutGroupContext);
|
|
66
|
+
const isStrict = useContext(LazyContext).strict;
|
|
67
67
|
if (context.visualElement) {
|
|
68
|
-
|
|
68
|
+
MeasureLayout = context.visualElement.loadFeatures(
|
|
69
69
|
// Note: Pass the full new combined props to correctly re-render dynamic feature components.
|
|
70
|
-
configAndProps,
|
|
71
|
-
featureDefinitions.projectionNodeConstructor, initialLayoutGroupConfig);
|
|
70
|
+
configAndProps, isStrict, preloadedFeatures, projectionId, initialLayoutGroupConfig);
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
/**
|
|
75
74
|
* The mount order and hierarchy is specific to ensure our element ref
|
|
76
75
|
* is hydrated by the time features fire their effects.
|
|
77
76
|
*/
|
|
78
|
-
return (React.createElement(
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
return (React.createElement(MotionContext.Provider, { value: context },
|
|
78
|
+
MeasureLayout && context.visualElement ? (React.createElement(MeasureLayout, { visualElement: context.visualElement, ...configAndProps })) : null,
|
|
79
|
+
useRender(Component, props, projectionId, useMotionRef(visualState, context.visualElement, externalRef), visualState, isStatic, context.visualElement)));
|
|
81
80
|
}
|
|
82
81
|
const ForwardRefComponent = forwardRef(MotionComponent);
|
|
83
82
|
ForwardRefComponent[motionComponentSymbol] = Component;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext, useRef, useEffect } from 'react';
|
|
1
|
+
import { useContext, useRef, useInsertionEffect, useEffect } from 'react';
|
|
2
2
|
import { PresenceContext } from '../../context/PresenceContext.mjs';
|
|
3
3
|
import { useVisualElementContext } from '../../context/MotionContext/index.mjs';
|
|
4
4
|
import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs';
|
|
@@ -20,7 +20,7 @@ function useVisualElement(Component, visualState, props, createVisualElement) {
|
|
|
20
20
|
visualState,
|
|
21
21
|
parent,
|
|
22
22
|
props,
|
|
23
|
-
|
|
23
|
+
presenceContext,
|
|
24
24
|
blockInitialAnimation: presenceContext
|
|
25
25
|
? presenceContext.initial === false
|
|
26
26
|
: false,
|
|
@@ -28,9 +28,15 @@ function useVisualElement(Component, visualState, props, createVisualElement) {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
const visualElement = visualElementRef.current;
|
|
31
|
+
useInsertionEffect(() => {
|
|
32
|
+
visualElement && visualElement.update(props, presenceContext);
|
|
33
|
+
});
|
|
31
34
|
useIsomorphicLayoutEffect(() => {
|
|
32
35
|
visualElement && visualElement.render();
|
|
33
36
|
});
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
visualElement && visualElement.updateFeatures();
|
|
39
|
+
});
|
|
34
40
|
/**
|
|
35
41
|
* Ideally this function would always run in a useEffect.
|
|
36
42
|
*
|
|
@@ -39,7 +39,6 @@ function applyBoxDelta(box, { x, y }) {
|
|
|
39
39
|
* This is the final nested loop within updateLayoutDelta for future refactoring
|
|
40
40
|
*/
|
|
41
41
|
function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
|
|
42
|
-
var _a, _b;
|
|
43
42
|
const treeLength = treePath.length;
|
|
44
43
|
if (!treeLength)
|
|
45
44
|
return;
|
|
@@ -50,8 +49,16 @@ function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
|
|
|
50
49
|
for (let i = 0; i < treeLength; i++) {
|
|
51
50
|
node = treePath[i];
|
|
52
51
|
delta = node.projectionDelta;
|
|
53
|
-
|
|
52
|
+
/**
|
|
53
|
+
* TODO: Prefer to remove this, but currently we have motion components with
|
|
54
|
+
* display: contents in Framer.
|
|
55
|
+
*/
|
|
56
|
+
const instance = node.instance;
|
|
57
|
+
if (instance &&
|
|
58
|
+
instance.style &&
|
|
59
|
+
instance.style.display === "contents") {
|
|
54
60
|
continue;
|
|
61
|
+
}
|
|
55
62
|
if (isSharedTransition &&
|
|
56
63
|
node.options.layoutScroll &&
|
|
57
64
|
node.scroll &&
|
|
@@ -18,8 +18,8 @@ function calcAxisDelta(delta, source, target, origin = 0.5) {
|
|
|
18
18
|
delta.translate = 0;
|
|
19
19
|
}
|
|
20
20
|
function calcBoxDelta(delta, source, target, origin) {
|
|
21
|
-
calcAxisDelta(delta.x, source.x, target.x, origin
|
|
22
|
-
calcAxisDelta(delta.y, source.y, target.y, origin
|
|
21
|
+
calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
|
|
22
|
+
calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
|
|
23
23
|
}
|
|
24
24
|
function calcRelativeAxis(target, relative, parent) {
|
|
25
25
|
target.min = parent.min + relative.min;
|
|
@@ -47,8 +47,8 @@ const yKeys = ["y", "scaleY", "originY"];
|
|
|
47
47
|
* and acts as a bridge between motion values and removeAxisDelta
|
|
48
48
|
*/
|
|
49
49
|
function removeBoxTransforms(box, transforms, originBox, sourceBox) {
|
|
50
|
-
removeAxisTransforms(box.x, transforms, xKeys, originBox
|
|
51
|
-
removeAxisTransforms(box.y, transforms, yKeys, originBox
|
|
50
|
+
removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined);
|
|
51
|
+
removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export { removeAxisDelta, removeAxisTransforms, removeBoxTransforms, removePointDelta };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createProjectionNode } from './create-projection-node.mjs';
|
|
2
|
-
import { addDomEvent } from '../../events/
|
|
2
|
+
import { addDomEvent } from '../../events/add-dom-event.mjs';
|
|
3
3
|
|
|
4
4
|
const DocumentProjectionNode = createProjectionNode({
|
|
5
5
|
attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
|