bc518174
王天杨
提交两个项目文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
"use client";
import { jsx, Fragment } from 'react/jsx-runtime';
import { useMemo, useRef, useState, useContext } from 'react';
import { LayoutGroupContext } from '../../context/LayoutGroupContext.mjs';
import { useConstant } from '../../utils/use-constant.mjs';
import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs';
import { PresenceChild } from './PresenceChild.mjs';
import { usePresence } from './use-presence.mjs';
import { onlyElements, getChildKey } from './utils.mjs';
/**
* `AnimatePresence` enables the animation of components that have been removed from the tree.
*
* When adding/removing more than a single child, every child **must** be given a unique `key` prop.
*
* Any `motion` components that have an `exit` property defined will animate out when removed from
* the tree.
*
* ```jsx
* import { motion, AnimatePresence } from 'framer-motion'
*
* export const Items = ({ items }) => (
* <AnimatePresence>
* {items.map(item => (
* <motion.div
* key={item.id}
* initial={{ opacity: 0 }}
* animate={{ opacity: 1 }}
* exit={{ opacity: 0 }}
* />
* ))}
* </AnimatePresence>
* )
* ```
*
* You can sequence exit animations throughout a tree using variants.
*
* If a child contains multiple `motion` components with `exit` props, it will only unmount the child
* once all `motion` components have finished animating out. Likewise, any components using
* `usePresence` all need to call `safeToRemove`.
*
* @public
*/
const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", anchorY = "top", root }) => {
const [isParentPresent, safeToRemove] = usePresence(propagate);
/**
* Filter any children that aren't ReactElements. We can only track components
* between renders with a props.key.
*/
const presentChildren = useMemo(() => onlyElements(children), [children]);
/**
* Track the keys of the currently rendered children. This is used to
* determine which children are exiting.
*/
const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
/**
* If `initial={false}` we only want to pass this to components in the first render.
*/
const isInitialRender = useRef(true);
/**
* A ref containing the currently present children. When all exit animations
* are complete, we use this to re-render the component with the latest children
* *committed* rather than the latest children *rendered*.
*/
const pendingPresentChildren = useRef(presentChildren);
/**
* Track which exiting children have finished animating out.
*/
const exitComplete = useConstant(() => new Map());
/**
* Track which components are currently processing exit to prevent duplicate processing.
*/
const exitingComponents = useRef(new Set());
/**
* Save children to render as React state. To ensure this component is concurrent-safe,
* we check for exiting children via an effect.
*/
const [diffedChildren, setDiffedChildren] = useState(presentChildren);
const [renderedChildren, setRenderedChildren] = useState(presentChildren);
useIsomorphicLayoutEffect(() => {
isInitialRender.current = false;
pendingPresentChildren.current = presentChildren;
/**
* Update complete status of exiting children.
*/
for (let i = 0; i < renderedChildren.length; i++) {
const key = getChildKey(renderedChildren[i]);
if (!presentKeys.includes(key)) {
if (exitComplete.get(key) !== true) {
exitComplete.set(key, false);
}
}
else {
exitComplete.delete(key);
exitingComponents.current.delete(key);
}
}
}, [renderedChildren, presentKeys.length, presentKeys.join("-")]);
const exitingChildren = [];
if (presentChildren !== diffedChildren) {
let nextChildren = [...presentChildren];
/**
* Loop through all the currently rendered components and decide which
* are exiting.
*/
for (let i = 0; i < renderedChildren.length; i++) {
const child = renderedChildren[i];
const key = getChildKey(child);
if (!presentKeys.includes(key)) {
nextChildren.splice(i, 0, child);
exitingChildren.push(child);
}
}
/**
* If we're in "wait" mode, and we have exiting children, we want to
* only render these until they've all exited.
*/
if (mode === "wait" && exitingChildren.length) {
nextChildren = exitingChildren;
}
setRenderedChildren(onlyElements(nextChildren));
setDiffedChildren(presentChildren);
/**
* Early return to ensure once we've set state with the latest diffed
* children, we can immediately re-render.
*/
return null;
}
if (process.env.NODE_ENV !== "production" &&
mode === "wait" &&
renderedChildren.length > 1) {
console.warn(`You're attempting to animate multiple children within AnimatePresence, but its mode is set to "wait". This will lead to odd visual behaviour.`);
}
/**
* If we've been provided a forceRender function by the LayoutGroupContext,
* we can use it to force a re-render amongst all surrounding components once
* all components have finished animating out.
*/
const { forceRender } = useContext(LayoutGroupContext);
return (jsx(Fragment, { children: renderedChildren.map((child) => {
const key = getChildKey(child);
const isPresent = propagate && !isParentPresent
? false
: presentChildren === renderedChildren ||
presentKeys.includes(key);
const onExit = () => {
if (exitingComponents.current.has(key)) {
return;
}
if (exitComplete.has(key)) {
exitingComponents.current.add(key);
exitComplete.set(key, true);
}
else {
return;
}
let isEveryExitComplete = true;
exitComplete.forEach((isExitComplete) => {
if (!isExitComplete)
isEveryExitComplete = false;
});
if (isEveryExitComplete) {
forceRender?.();
setRenderedChildren(pendingPresentChildren.current);
propagate && safeToRemove?.();
onExitComplete && onExitComplete();
}
};
return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial
? undefined
: false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, root: root, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, anchorY: anchorY, children: child }, key));
}) }));
};
export { AnimatePresence };
//# sourceMappingURL=index.mjs.map
|