framer-motion 12.11.4 → 12.12.1

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.
Files changed (46) hide show
  1. package/dist/cjs/client.js +1 -1
  2. package/dist/cjs/{create-t9legXtK.js → create-DCF2FFGK.js} +76 -84
  3. package/dist/cjs/dom-mini.js +1 -3
  4. package/dist/cjs/dom.js +18 -24
  5. package/dist/cjs/index.js +21 -57
  6. package/dist/cjs/m.js +113 -115
  7. package/dist/dom.js +1 -1
  8. package/dist/es/animation/animate/single-value.mjs +1 -2
  9. package/dist/es/animation/animate/subject.mjs +1 -1
  10. package/dist/es/animation/sequence/create.mjs +1 -2
  11. package/dist/es/animation/utils/create-visual-element.mjs +2 -2
  12. package/dist/es/components/AnimatePresence/PopChild.mjs +4 -1
  13. package/dist/es/components/Reorder/Item.mjs +1 -1
  14. package/dist/es/index.mjs +0 -1
  15. package/dist/es/projection/node/create-projection-node.mjs +2 -3
  16. package/dist/es/render/VisualElement.mjs +1 -2
  17. package/dist/es/render/dom/DOMVisualElement.mjs +1 -2
  18. package/dist/es/render/dom/resize/handle-element.mjs +2 -2
  19. package/dist/es/render/dom/scroll/offsets/inset.mjs +3 -1
  20. package/dist/es/render/dom/use-render.mjs +2 -2
  21. package/dist/es/render/html/use-props.mjs +1 -1
  22. package/dist/es/render/html/utils/scrape-motion-values.mjs +1 -1
  23. package/dist/es/render/svg/utils/scrape-motion-values.mjs +1 -2
  24. package/dist/es/render/utils/motion-values.mjs +1 -2
  25. package/dist/es/value/use-motion-template.mjs +1 -1
  26. package/dist/es/value/use-spring.mjs +12 -52
  27. package/dist/es/value/use-will-change/is.mjs +1 -1
  28. package/dist/es/value/utils/resolve-motion-value.mjs +1 -1
  29. package/dist/framer-motion.dev.js +768 -710
  30. package/dist/framer-motion.js +1 -1
  31. package/dist/m.d.ts +4 -4
  32. package/dist/size-rollup-animate.js +1 -1
  33. package/dist/size-rollup-dom-animation-assets.js +1 -1
  34. package/dist/size-rollup-dom-animation-m.js +1 -1
  35. package/dist/size-rollup-dom-animation.js +1 -1
  36. package/dist/size-rollup-dom-max-assets.js +1 -1
  37. package/dist/size-rollup-dom-max.js +1 -1
  38. package/dist/size-rollup-m.js +1 -1
  39. package/dist/size-rollup-motion.js +1 -1
  40. package/dist/size-rollup-scroll.js +1 -1
  41. package/dist/types/client.d.ts +2 -2
  42. package/dist/types/index.d.ts +11 -13
  43. package/dist/{types.d-CQt5spQA.d.ts → types.d-CtuPurYT.d.ts} +3 -3
  44. package/package.json +4 -4
  45. package/dist/es/render/dom/utils/is-svg-element.mjs +0 -5
  46. package/dist/es/value/utils/is-motion-value.mjs +0 -3
@@ -83,591 +83,216 @@
83
83
  const PresenceContext =
84
84
  /* @__PURE__ */ React$1.createContext(null);
85
85
 
86
+ function addUniqueItem(arr, item) {
87
+ if (arr.indexOf(item) === -1)
88
+ arr.push(item);
89
+ }
90
+ function removeItem(arr, item) {
91
+ const index = arr.indexOf(item);
92
+ if (index > -1)
93
+ arr.splice(index, 1);
94
+ }
95
+ // Adapted from array-move
96
+ function moveItem([...arr], fromIndex, toIndex) {
97
+ const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
98
+ if (startIndex >= 0 && startIndex < arr.length) {
99
+ const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
100
+ const [item] = arr.splice(fromIndex, 1);
101
+ arr.splice(endIndex, 0, item);
102
+ }
103
+ return arr;
104
+ }
105
+
106
+ const clamp = (min, max, v) => {
107
+ if (v > max)
108
+ return max;
109
+ if (v < min)
110
+ return min;
111
+ return v;
112
+ };
113
+
114
+ exports.warning = () => { };
115
+ exports.invariant = () => { };
116
+ {
117
+ exports.warning = (check, message) => {
118
+ if (!check && typeof console !== "undefined") {
119
+ console.warn(message);
120
+ }
121
+ };
122
+ exports.invariant = (check, message) => {
123
+ if (!check) {
124
+ throw new Error(message);
125
+ }
126
+ };
127
+ }
128
+
129
+ const MotionGlobalConfig = {};
130
+
86
131
  /**
87
- * @public
132
+ * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
88
133
  */
89
- const MotionConfigContext = React$1.createContext({
90
- transformPagePoint: (p) => p,
91
- isStatic: false,
92
- reducedMotion: "never",
93
- });
134
+ const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
135
+
136
+ function isObject(value) {
137
+ return typeof value === "object" && value !== null;
138
+ }
94
139
 
95
140
  /**
96
- * Measurement functionality has to be within a separate component
97
- * to leverage snapshot lifecycle.
141
+ * Check if the value is a zero value string like "0px" or "0%"
98
142
  */
99
- class PopChildMeasure extends React__namespace.Component {
100
- getSnapshotBeforeUpdate(prevProps) {
101
- const element = this.props.childRef.current;
102
- if (element && prevProps.isPresent && !this.props.isPresent) {
103
- const parent = element.offsetParent;
104
- const parentWidth = parent instanceof HTMLElement ? parent.offsetWidth || 0 : 0;
105
- const size = this.props.sizeRef.current;
106
- size.height = element.offsetHeight || 0;
107
- size.width = element.offsetWidth || 0;
108
- size.top = element.offsetTop;
109
- size.left = element.offsetLeft;
110
- size.right = parentWidth - size.width - size.left;
111
- }
112
- return null;
143
+ const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
144
+
145
+ /*#__NO_SIDE_EFFECTS__*/
146
+ function memo(callback) {
147
+ let result;
148
+ return () => {
149
+ if (result === undefined)
150
+ result = callback();
151
+ return result;
152
+ };
153
+ }
154
+
155
+ /*#__NO_SIDE_EFFECTS__*/
156
+ const noop = (any) => any;
157
+
158
+ /**
159
+ * Pipe
160
+ * Compose other transformers to run linearily
161
+ * pipe(min(20), max(40))
162
+ * @param {...functions} transformers
163
+ * @return {function}
164
+ */
165
+ const combineFunctions = (a, b) => (v) => b(a(v));
166
+ const pipe = (...transformers) => transformers.reduce(combineFunctions);
167
+
168
+ /*
169
+ Progress within given range
170
+
171
+ Given a lower limit and an upper limit, we return the progress
172
+ (expressed as a number 0-1) represented by the given value, and
173
+ limit that progress to within 0-1.
174
+
175
+ @param [number]: Lower limit
176
+ @param [number]: Upper limit
177
+ @param [number]: Value to find progress within given range
178
+ @return [number]: Progress of value within range as expressed 0-1
179
+ */
180
+ /*#__NO_SIDE_EFFECTS__*/
181
+ const progress = (from, to, value) => {
182
+ const toFromDifference = to - from;
183
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
184
+ };
185
+
186
+ class SubscriptionManager {
187
+ constructor() {
188
+ this.subscriptions = [];
113
189
  }
114
- /**
115
- * Required with getSnapshotBeforeUpdate to stop React complaining.
116
- */
117
- componentDidUpdate() { }
118
- render() {
119
- return this.props.children;
190
+ add(handler) {
191
+ addUniqueItem(this.subscriptions, handler);
192
+ return () => removeItem(this.subscriptions, handler);
120
193
  }
121
- }
122
- function PopChild({ children, isPresent, anchorX }) {
123
- const id = React$1.useId();
124
- const ref = React$1.useRef(null);
125
- const size = React$1.useRef({
126
- width: 0,
127
- height: 0,
128
- top: 0,
129
- left: 0,
130
- right: 0,
131
- });
132
- const { nonce } = React$1.useContext(MotionConfigContext);
133
- /**
134
- * We create and inject a style block so we can apply this explicit
135
- * sizing in a non-destructive manner by just deleting the style block.
136
- *
137
- * We can't apply size via render as the measurement happens
138
- * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
139
- * styles directly on the DOM node, we might be overwriting
140
- * styles set via the style prop.
141
- */
142
- React$1.useInsertionEffect(() => {
143
- const { width, height, top, left, right } = size.current;
144
- if (isPresent || !ref.current || !width || !height)
194
+ notify(a, b, c) {
195
+ const numSubscriptions = this.subscriptions.length;
196
+ if (!numSubscriptions)
145
197
  return;
146
- const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`;
147
- ref.current.dataset.motionPopId = id;
148
- const style = document.createElement("style");
149
- if (nonce)
150
- style.nonce = nonce;
151
- document.head.appendChild(style);
152
- if (style.sheet) {
153
- style.sheet.insertRule(`
154
- [data-motion-pop-id="${id}"] {
155
- position: absolute !important;
156
- width: ${width}px !important;
157
- height: ${height}px !important;
158
- ${x}px !important;
159
- top: ${top}px !important;
160
- }
161
- `);
198
+ if (numSubscriptions === 1) {
199
+ /**
200
+ * If there's only a single handler we can just call it without invoking a loop.
201
+ */
202
+ this.subscriptions[0](a, b, c);
162
203
  }
163
- return () => {
164
- if (document.head.contains(style)) {
165
- document.head.removeChild(style);
204
+ else {
205
+ for (let i = 0; i < numSubscriptions; i++) {
206
+ /**
207
+ * Check whether the handler exists before firing as it's possible
208
+ * the subscriptions were modified during this loop running.
209
+ */
210
+ const handler = this.subscriptions[i];
211
+ handler && handler(a, b, c);
166
212
  }
167
- };
168
- }, [isPresent]);
169
- return (jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) }));
170
- }
171
-
172
- const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, }) => {
173
- const presenceChildren = useConstant(newChildrenMap);
174
- const id = React$1.useId();
175
- let isReusedContext = true;
176
- let context = React$1.useMemo(() => {
177
- isReusedContext = false;
178
- return {
179
- id,
180
- initial,
181
- isPresent,
182
- custom,
183
- onExitComplete: (childId) => {
184
- presenceChildren.set(childId, true);
185
- for (const isComplete of presenceChildren.values()) {
186
- if (!isComplete)
187
- return; // can stop searching when any is incomplete
188
- }
189
- onExitComplete && onExitComplete();
190
- },
191
- register: (childId) => {
192
- presenceChildren.set(childId, false);
193
- return () => presenceChildren.delete(childId);
194
- },
195
- };
196
- }, [isPresent, presenceChildren, onExitComplete]);
197
- /**
198
- * If the presence of a child affects the layout of the components around it,
199
- * we want to make a new context value to ensure they get re-rendered
200
- * so they can detect that layout change.
201
- */
202
- if (presenceAffectsLayout && isReusedContext) {
203
- context = { ...context };
213
+ }
204
214
  }
205
- React$1.useMemo(() => {
206
- presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
207
- }, [isPresent]);
208
- /**
209
- * If there's no `motion` components to fire exit animations, we want to remove this
210
- * component immediately.
211
- */
212
- React__namespace.useEffect(() => {
213
- !isPresent &&
214
- !presenceChildren.size &&
215
- onExitComplete &&
216
- onExitComplete();
217
- }, [isPresent]);
218
- if (mode === "popLayout") {
219
- children = (jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, children: children }));
215
+ getSize() {
216
+ return this.subscriptions.length;
217
+ }
218
+ clear() {
219
+ this.subscriptions.length = 0;
220
220
  }
221
- return (jsx(PresenceContext.Provider, { value: context, children: children }));
222
- };
223
- function newChildrenMap() {
224
- return new Map();
225
221
  }
226
222
 
227
223
  /**
228
- * When a component is the child of `AnimatePresence`, it can use `usePresence`
229
- * to access information about whether it's still present in the React tree.
230
- *
231
- * ```jsx
232
- * import { usePresence } from "framer-motion"
233
- *
234
- * export const Component = () => {
235
- * const [isPresent, safeToRemove] = usePresence()
224
+ * Converts seconds to milliseconds
236
225
  *
237
- * useEffect(() => {
238
- * !isPresent && setTimeout(safeToRemove, 1000)
239
- * }, [isPresent])
240
- *
241
- * return <div />
242
- * }
243
- * ```
244
- *
245
- * If `isPresent` is `false`, it means that a component has been removed the tree, but
246
- * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
247
- *
248
- * @public
226
+ * @param seconds - Time in seconds.
227
+ * @return milliseconds - Converted time in milliseconds.
249
228
  */
250
- function usePresence(subscribe = true) {
251
- const context = React$1.useContext(PresenceContext);
252
- if (context === null)
253
- return [true, null];
254
- const { isPresent, onExitComplete, register } = context;
255
- // It's safe to call the following hooks conditionally (after an early return) because the context will always
256
- // either be null or non-null for the lifespan of the component.
257
- const id = React$1.useId();
258
- React$1.useEffect(() => {
259
- if (subscribe) {
260
- return register(id);
261
- }
262
- }, [subscribe]);
263
- const safeToRemove = React$1.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]);
264
- return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
229
+ /*#__NO_SIDE_EFFECTS__*/
230
+ const secondsToMilliseconds = (seconds) => seconds * 1000;
231
+ /*#__NO_SIDE_EFFECTS__*/
232
+ const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
233
+
234
+ /*
235
+ Convert velocity into velocity per second
236
+
237
+ @param [number]: Unit per frame
238
+ @param [number]: Frame duration in ms
239
+ */
240
+ function velocityPerSecond(velocity, frameDuration) {
241
+ return frameDuration ? velocity * (1000 / frameDuration) : 0;
265
242
  }
266
- /**
267
- * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
268
- * There is no `safeToRemove` function.
269
- *
270
- * ```jsx
271
- * import { useIsPresent } from "framer-motion"
272
- *
273
- * export const Component = () => {
274
- * const isPresent = useIsPresent()
275
- *
276
- * useEffect(() => {
277
- * !isPresent && console.log("I've been removed!")
278
- * }, [isPresent])
279
- *
280
- * return <div />
281
- * }
282
- * ```
283
- *
284
- * @public
285
- */
286
- function useIsPresent() {
287
- return isPresent(React$1.useContext(PresenceContext));
243
+
244
+ const warned = new Set();
245
+ function hasWarned$1(message) {
246
+ return warned.has(message);
288
247
  }
289
- function isPresent(context) {
290
- return context === null ? true : context.isPresent;
248
+ function warnOnce(condition, message, element) {
249
+ if (condition || warned.has(message))
250
+ return;
251
+ console.warn(message);
252
+ if (element)
253
+ console.warn(element);
254
+ warned.add(message);
291
255
  }
292
256
 
293
- const getChildKey = (child) => child.key || "";
294
- function onlyElements(children) {
295
- const filtered = [];
296
- // We use forEach here instead of map as map mutates the component key by preprending `.$`
297
- React$1.Children.forEach(children, (child) => {
298
- if (React$1.isValidElement(child))
299
- filtered.push(child);
300
- });
301
- return filtered;
302
- }
257
+ const wrap = (min, max, v) => {
258
+ const rangeSize = max - min;
259
+ return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
260
+ };
303
261
 
304
- /**
305
- * `AnimatePresence` enables the animation of components that have been removed from the tree.
306
- *
307
- * When adding/removing more than a single child, every child **must** be given a unique `key` prop.
308
- *
309
- * Any `motion` components that have an `exit` property defined will animate out when removed from
310
- * the tree.
311
- *
312
- * ```jsx
313
- * import { motion, AnimatePresence } from 'framer-motion'
314
- *
315
- * export const Items = ({ items }) => (
316
- * <AnimatePresence>
317
- * {items.map(item => (
318
- * <motion.div
319
- * key={item.id}
320
- * initial={{ opacity: 0 }}
321
- * animate={{ opacity: 1 }}
322
- * exit={{ opacity: 0 }}
323
- * />
324
- * ))}
325
- * </AnimatePresence>
326
- * )
327
- * ```
328
- *
329
- * You can sequence exit animations throughout a tree using variants.
330
- *
331
- * If a child contains multiple `motion` components with `exit` props, it will only unmount the child
332
- * once all `motion` components have finished animating out. Likewise, any components using
333
- * `usePresence` all need to call `safeToRemove`.
334
- *
335
- * @public
336
- */
337
- const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", }) => {
338
- const [isParentPresent, safeToRemove] = usePresence(propagate);
339
- /**
340
- * Filter any children that aren't ReactElements. We can only track components
341
- * between renders with a props.key.
342
- */
343
- const presentChildren = React$1.useMemo(() => onlyElements(children), [children]);
344
- /**
345
- * Track the keys of the currently rendered children. This is used to
346
- * determine which children are exiting.
347
- */
348
- const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
349
- /**
350
- * If `initial={false}` we only want to pass this to components in the first render.
351
- */
352
- const isInitialRender = React$1.useRef(true);
353
- /**
354
- * A ref containing the currently present children. When all exit animations
355
- * are complete, we use this to re-render the component with the latest children
356
- * *committed* rather than the latest children *rendered*.
357
- */
358
- const pendingPresentChildren = React$1.useRef(presentChildren);
359
- /**
360
- * Track which exiting children have finished animating out.
361
- */
362
- const exitComplete = useConstant(() => new Map());
363
- /**
364
- * Save children to render as React state. To ensure this component is concurrent-safe,
365
- * we check for exiting children via an effect.
366
- */
367
- const [diffedChildren, setDiffedChildren] = React$1.useState(presentChildren);
368
- const [renderedChildren, setRenderedChildren] = React$1.useState(presentChildren);
369
- useIsomorphicLayoutEffect(() => {
370
- isInitialRender.current = false;
371
- pendingPresentChildren.current = presentChildren;
372
- /**
373
- * Update complete status of exiting children.
374
- */
375
- for (let i = 0; i < renderedChildren.length; i++) {
376
- const key = getChildKey(renderedChildren[i]);
377
- if (!presentKeys.includes(key)) {
378
- if (exitComplete.get(key) !== true) {
379
- exitComplete.set(key, false);
380
- }
381
- }
382
- else {
383
- exitComplete.delete(key);
384
- }
385
- }
386
- }, [renderedChildren, presentKeys.length, presentKeys.join("-")]);
387
- const exitingChildren = [];
388
- if (presentChildren !== diffedChildren) {
389
- let nextChildren = [...presentChildren];
390
- /**
391
- * Loop through all the currently rendered components and decide which
392
- * are exiting.
393
- */
394
- for (let i = 0; i < renderedChildren.length; i++) {
395
- const child = renderedChildren[i];
396
- const key = getChildKey(child);
397
- if (!presentKeys.includes(key)) {
398
- nextChildren.splice(i, 0, child);
399
- exitingChildren.push(child);
400
- }
262
+ /*
263
+ Bezier function generator
264
+ This has been modified from Gaëtan Renaudeau's BezierEasing
265
+ https://github.com/gre/bezier-easing/blob/master/src/index.js
266
+ https://github.com/gre/bezier-easing/blob/master/LICENSE
267
+
268
+ I've removed the newtonRaphsonIterate algo because in benchmarking it
269
+ wasn't noticiably faster than binarySubdivision, indeed removing it
270
+ usually improved times, depending on the curve.
271
+ I also removed the lookup table, as for the added bundle size and loop we're
272
+ only cutting ~4 or so subdivision iterations. I bumped the max iterations up
273
+ to 12 to compensate and this still tended to be faster for no perceivable
274
+ loss in accuracy.
275
+ Usage
276
+ const easeOut = cubicBezier(.17,.67,.83,.67);
277
+ const x = easeOut(0.5); // returns 0.627...
278
+ */
279
+ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
280
+ const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
281
+ t;
282
+ const subdivisionPrecision = 0.0000001;
283
+ const subdivisionMaxIterations = 12;
284
+ function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
285
+ let currentX;
286
+ let currentT;
287
+ let i = 0;
288
+ do {
289
+ currentT = lowerBound + (upperBound - lowerBound) / 2.0;
290
+ currentX = calcBezier(currentT, mX1, mX2) - x;
291
+ if (currentX > 0.0) {
292
+ upperBound = currentT;
401
293
  }
402
- /**
403
- * If we're in "wait" mode, and we have exiting children, we want to
404
- * only render these until they've all exited.
405
- */
406
- if (mode === "wait" && exitingChildren.length) {
407
- nextChildren = exitingChildren;
408
- }
409
- setRenderedChildren(onlyElements(nextChildren));
410
- setDiffedChildren(presentChildren);
411
- /**
412
- * Early return to ensure once we've set state with the latest diffed
413
- * children, we can immediately re-render.
414
- */
415
- return null;
416
- }
417
- if (mode === "wait" &&
418
- renderedChildren.length > 1) {
419
- 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.`);
420
- }
421
- /**
422
- * If we've been provided a forceRender function by the LayoutGroupContext,
423
- * we can use it to force a re-render amongst all surrounding components once
424
- * all components have finished animating out.
425
- */
426
- const { forceRender } = React$1.useContext(LayoutGroupContext);
427
- return (jsx(Fragment, { children: renderedChildren.map((child) => {
428
- const key = getChildKey(child);
429
- const isPresent = propagate && !isParentPresent
430
- ? false
431
- : presentChildren === renderedChildren ||
432
- presentKeys.includes(key);
433
- const onExit = () => {
434
- if (exitComplete.has(key)) {
435
- exitComplete.set(key, true);
436
- }
437
- else {
438
- return;
439
- }
440
- let isEveryExitComplete = true;
441
- exitComplete.forEach((isExitComplete) => {
442
- if (!isExitComplete)
443
- isEveryExitComplete = false;
444
- });
445
- if (isEveryExitComplete) {
446
- forceRender?.();
447
- setRenderedChildren(pendingPresentChildren.current);
448
- propagate && safeToRemove?.();
449
- onExitComplete && onExitComplete();
450
- }
451
- };
452
- return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial
453
- ? undefined
454
- : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key));
455
- }) }));
456
- };
457
-
458
- /**
459
- * Note: Still used by components generated by old versions of Framer
460
- *
461
- * @deprecated
462
- */
463
- const DeprecatedLayoutGroupContext = React$1.createContext(null);
464
-
465
- function addUniqueItem(arr, item) {
466
- if (arr.indexOf(item) === -1)
467
- arr.push(item);
468
- }
469
- function removeItem(arr, item) {
470
- const index = arr.indexOf(item);
471
- if (index > -1)
472
- arr.splice(index, 1);
473
- }
474
- // Adapted from array-move
475
- function moveItem([...arr], fromIndex, toIndex) {
476
- const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
477
- if (startIndex >= 0 && startIndex < arr.length) {
478
- const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
479
- const [item] = arr.splice(fromIndex, 1);
480
- arr.splice(endIndex, 0, item);
481
- }
482
- return arr;
483
- }
484
-
485
- const clamp = (min, max, v) => {
486
- if (v > max)
487
- return max;
488
- if (v < min)
489
- return min;
490
- return v;
491
- };
492
-
493
- exports.warning = () => { };
494
- exports.invariant = () => { };
495
- {
496
- exports.warning = (check, message) => {
497
- if (!check && typeof console !== "undefined") {
498
- console.warn(message);
499
- }
500
- };
501
- exports.invariant = (check, message) => {
502
- if (!check) {
503
- throw new Error(message);
504
- }
505
- };
506
- }
507
-
508
- const MotionGlobalConfig = {};
509
-
510
- /**
511
- * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
512
- */
513
- const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
514
-
515
- /**
516
- * Check if the value is a zero value string like "0px" or "0%"
517
- */
518
- const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
519
-
520
- /*#__NO_SIDE_EFFECTS__*/
521
- function memo(callback) {
522
- let result;
523
- return () => {
524
- if (result === undefined)
525
- result = callback();
526
- return result;
527
- };
528
- }
529
-
530
- /*#__NO_SIDE_EFFECTS__*/
531
- const noop = (any) => any;
532
-
533
- /**
534
- * Pipe
535
- * Compose other transformers to run linearily
536
- * pipe(min(20), max(40))
537
- * @param {...functions} transformers
538
- * @return {function}
539
- */
540
- const combineFunctions = (a, b) => (v) => b(a(v));
541
- const pipe = (...transformers) => transformers.reduce(combineFunctions);
542
-
543
- /*
544
- Progress within given range
545
-
546
- Given a lower limit and an upper limit, we return the progress
547
- (expressed as a number 0-1) represented by the given value, and
548
- limit that progress to within 0-1.
549
-
550
- @param [number]: Lower limit
551
- @param [number]: Upper limit
552
- @param [number]: Value to find progress within given range
553
- @return [number]: Progress of value within range as expressed 0-1
554
- */
555
- /*#__NO_SIDE_EFFECTS__*/
556
- const progress = (from, to, value) => {
557
- const toFromDifference = to - from;
558
- return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
559
- };
560
-
561
- class SubscriptionManager {
562
- constructor() {
563
- this.subscriptions = [];
564
- }
565
- add(handler) {
566
- addUniqueItem(this.subscriptions, handler);
567
- return () => removeItem(this.subscriptions, handler);
568
- }
569
- notify(a, b, c) {
570
- const numSubscriptions = this.subscriptions.length;
571
- if (!numSubscriptions)
572
- return;
573
- if (numSubscriptions === 1) {
574
- /**
575
- * If there's only a single handler we can just call it without invoking a loop.
576
- */
577
- this.subscriptions[0](a, b, c);
578
- }
579
- else {
580
- for (let i = 0; i < numSubscriptions; i++) {
581
- /**
582
- * Check whether the handler exists before firing as it's possible
583
- * the subscriptions were modified during this loop running.
584
- */
585
- const handler = this.subscriptions[i];
586
- handler && handler(a, b, c);
587
- }
588
- }
589
- }
590
- getSize() {
591
- return this.subscriptions.length;
592
- }
593
- clear() {
594
- this.subscriptions.length = 0;
595
- }
596
- }
597
-
598
- /**
599
- * Converts seconds to milliseconds
600
- *
601
- * @param seconds - Time in seconds.
602
- * @return milliseconds - Converted time in milliseconds.
603
- */
604
- /*#__NO_SIDE_EFFECTS__*/
605
- const secondsToMilliseconds = (seconds) => seconds * 1000;
606
- /*#__NO_SIDE_EFFECTS__*/
607
- const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
608
-
609
- /*
610
- Convert velocity into velocity per second
611
-
612
- @param [number]: Unit per frame
613
- @param [number]: Frame duration in ms
614
- */
615
- function velocityPerSecond(velocity, frameDuration) {
616
- return frameDuration ? velocity * (1000 / frameDuration) : 0;
617
- }
618
-
619
- const warned = new Set();
620
- function hasWarned$1(message) {
621
- return warned.has(message);
622
- }
623
- function warnOnce(condition, message, element) {
624
- if (condition || warned.has(message))
625
- return;
626
- console.warn(message);
627
- if (element)
628
- console.warn(element);
629
- warned.add(message);
630
- }
631
-
632
- const wrap = (min, max, v) => {
633
- const rangeSize = max - min;
634
- return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
635
- };
636
-
637
- /*
638
- Bezier function generator
639
- This has been modified from Gaëtan Renaudeau's BezierEasing
640
- https://github.com/gre/bezier-easing/blob/master/src/index.js
641
- https://github.com/gre/bezier-easing/blob/master/LICENSE
642
-
643
- I've removed the newtonRaphsonIterate algo because in benchmarking it
644
- wasn't noticiably faster than binarySubdivision, indeed removing it
645
- usually improved times, depending on the curve.
646
- I also removed the lookup table, as for the added bundle size and loop we're
647
- only cutting ~4 or so subdivision iterations. I bumped the max iterations up
648
- to 12 to compensate and this still tended to be faster for no perceivable
649
- loss in accuracy.
650
- Usage
651
- const easeOut = cubicBezier(.17,.67,.83,.67);
652
- const x = easeOut(0.5); // returns 0.627...
653
- */
654
- // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
655
- const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
656
- t;
657
- const subdivisionPrecision = 0.0000001;
658
- const subdivisionMaxIterations = 12;
659
- function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
660
- let currentX;
661
- let currentT;
662
- let i = 0;
663
- do {
664
- currentT = lowerBound + (upperBound - lowerBound) / 2.0;
665
- currentX = calcBezier(currentT, mX1, mX2) - x;
666
- if (currentX > 0.0) {
667
- upperBound = currentT;
668
- }
669
- else {
670
- lowerBound = currentT;
294
+ else {
295
+ lowerBound = currentT;
671
296
  }
672
297
  } while (Math.abs(currentX) > subdivisionPrecision &&
673
298
  ++i < subdivisionMaxIterations);
@@ -1883,7 +1508,7 @@
1883
1508
  * mixColor(0.5) // 'rgba(128, 128, 128, 1)'
1884
1509
  * ```
1885
1510
  *
1886
- * TODO Revist this approach once we've moved to data models for values,
1511
+ * TODO Revisit this approach once we've moved to data models for values,
1887
1512
  * probably not needed to pregenerate mixer functions.
1888
1513
  *
1889
1514
  * @public
@@ -3055,6 +2680,14 @@
3055
2680
  ((type === "spring" || isGenerator(type)) && velocity));
3056
2681
  }
3057
2682
 
2683
+ /**
2684
+ * Checks if an element is an HTML element in a way
2685
+ * that works across iframes
2686
+ */
2687
+ function isHTMLElement(element) {
2688
+ return isObject(element) && "offsetHeight" in element;
2689
+ }
2690
+
3058
2691
  /**
3059
2692
  * A list of values that can be hardware-accelerated.
3060
2693
  */
@@ -3063,16 +2696,13 @@
3063
2696
  "clipPath",
3064
2697
  "filter",
3065
2698
  "transform",
3066
- // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
3067
- // or until we implement support for linear() easing.
2699
+ // TODO: Could be re-enabled now we have support for linear() easing
3068
2700
  // "background-color"
3069
2701
  ]);
3070
2702
  const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
3071
2703
  function supportsBrowserAnimation(options) {
3072
2704
  const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
3073
- if (!motionValue ||
3074
- !motionValue.owner ||
3075
- !(motionValue.owner.current instanceof HTMLElement)) {
2705
+ if (!isHTMLElement(motionValue?.owner?.current)) {
3076
2706
  return false;
3077
2707
  }
3078
2708
  const { onUpdate, transformTemplate } = motionValue.owner.getProps();
@@ -4488,7 +4118,7 @@
4488
4118
  targets.forEach((target) => {
4489
4119
  const pointerDownTarget = options.useGlobalTarget ? window : target;
4490
4120
  pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
4491
- if (target instanceof HTMLElement) {
4121
+ if (isHTMLElement(target)) {
4492
4122
  target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
4493
4123
  if (!isElementKeyboardAccessible(target) &&
4494
4124
  !target.hasAttribute("tabindex")) {
@@ -4633,6 +4263,22 @@
4633
4263
  return reportStats;
4634
4264
  }
4635
4265
 
4266
+ /**
4267
+ * Checks if an element is an SVG element in a way
4268
+ * that works across iframes
4269
+ */
4270
+ function isSVGElement(element) {
4271
+ return isObject(element) && "ownerSVGElement" in element;
4272
+ }
4273
+
4274
+ /**
4275
+ * Checks if an element is specifically an SVGSVGElement (the root SVG element)
4276
+ * in a way that works across iframes
4277
+ */
4278
+ function isSVGSVGElement(element) {
4279
+ return isSVGElement(element) && element.tagName === "svg";
4280
+ }
4281
+
4636
4282
  function transform(...args) {
4637
4283
  const useImmediate = !Array.isArray(args[0]);
4638
4284
  const argOffset = useImmediate ? 0 : -1;
@@ -4727,12 +4373,80 @@
4727
4373
  return transformValue(() => map(inputValue.get()));
4728
4374
  }
4729
4375
 
4376
+ const isMotionValue = (value) => Boolean(value && value.getVelocity);
4377
+
4730
4378
  /**
4731
- * A list of all ValueTypes
4732
- */
4733
- const valueTypes = [...dimensionValueTypes, color, complex];
4734
- /**
4735
- * Tests a value against the list of ValueTypes
4379
+ * Create a `MotionValue` that animates to its latest value using a spring.
4380
+ * Can either be a value or track another `MotionValue`.
4381
+ *
4382
+ * ```jsx
4383
+ * const x = motionValue(0)
4384
+ * const y = transformValue(() => x.get() * 2) // double x
4385
+ * ```
4386
+ *
4387
+ * @param transformer - A transform function. This function must be pure with no side-effects or conditional statements.
4388
+ * @returns `MotionValue`
4389
+ *
4390
+ * @public
4391
+ */
4392
+ function springValue(source, options) {
4393
+ const initialValue = isMotionValue(source) ? source.get() : source;
4394
+ const value = motionValue(initialValue);
4395
+ attachSpring(value, source, options);
4396
+ return value;
4397
+ }
4398
+ function attachSpring(value, source, options) {
4399
+ const initialValue = value.get();
4400
+ let activeAnimation = null;
4401
+ let latestValue = initialValue;
4402
+ let latestSetter;
4403
+ const unit = typeof initialValue === "string"
4404
+ ? initialValue.replace(/[\d.-]/g, "")
4405
+ : undefined;
4406
+ const stopAnimation = () => {
4407
+ if (activeAnimation) {
4408
+ activeAnimation.stop();
4409
+ activeAnimation = null;
4410
+ }
4411
+ };
4412
+ const startAnimation = () => {
4413
+ stopAnimation();
4414
+ activeAnimation = new JSAnimation({
4415
+ keyframes: [asNumber$1(value.get()), asNumber$1(latestValue)],
4416
+ velocity: value.getVelocity(),
4417
+ type: "spring",
4418
+ restDelta: 0.001,
4419
+ restSpeed: 0.01,
4420
+ ...options,
4421
+ onUpdate: latestSetter,
4422
+ });
4423
+ };
4424
+ value.attach((v, set) => {
4425
+ latestValue = v;
4426
+ latestSetter = (latest) => set(parseValue(latest, unit));
4427
+ frame.postRender(startAnimation);
4428
+ return value.get();
4429
+ }, stopAnimation);
4430
+ let unsubscribe = undefined;
4431
+ if (isMotionValue(source)) {
4432
+ unsubscribe = source.on("change", (v) => value.set(parseValue(v, unit)));
4433
+ value.on("destroy", unsubscribe);
4434
+ }
4435
+ return unsubscribe;
4436
+ }
4437
+ function parseValue(v, unit) {
4438
+ return unit ? v + unit : v;
4439
+ }
4440
+ function asNumber$1(v) {
4441
+ return typeof v === "number" ? v : parseFloat(v);
4442
+ }
4443
+
4444
+ /**
4445
+ * A list of all ValueTypes
4446
+ */
4447
+ const valueTypes = [...dimensionValueTypes, color, complex];
4448
+ /**
4449
+ * Tests a value against the list of ValueTypes
4736
4450
  */
4737
4451
  const findValueType = (v) => valueTypes.find(testValueType(v));
4738
4452
 
@@ -5036,32 +4750,413 @@
5036
4750
  if (!targets.has(currentTarget)) {
5037
4751
  targets.set(currentTarget, {});
5038
4752
  }
5039
- const targetData = targets.get(currentTarget);
5040
- targetData[target] = { keyframes, options };
4753
+ const targetData = targets.get(currentTarget);
4754
+ targetData[target] = { keyframes, options };
4755
+ }
4756
+ then(resolve, reject) {
4757
+ return this.readyPromise.then(resolve, reject);
4758
+ }
4759
+ }
4760
+ function animateView(update, defaultOptions = {}) {
4761
+ return new ViewTransitionBuilder(update, defaultOptions);
4762
+ }
4763
+
4764
+ /**
4765
+ * @deprecated
4766
+ *
4767
+ * Import as `frame` instead.
4768
+ */
4769
+ const sync = frame;
4770
+ /**
4771
+ * @deprecated
4772
+ *
4773
+ * Use cancelFrame(callback) instead.
4774
+ */
4775
+ const cancelSync = stepsOrder.reduce((acc, key) => {
4776
+ acc[key] = (process) => cancelFrame(process);
4777
+ return acc;
4778
+ }, {});
4779
+
4780
+ /**
4781
+ * @public
4782
+ */
4783
+ const MotionConfigContext = React$1.createContext({
4784
+ transformPagePoint: (p) => p,
4785
+ isStatic: false,
4786
+ reducedMotion: "never",
4787
+ });
4788
+
4789
+ /**
4790
+ * Measurement functionality has to be within a separate component
4791
+ * to leverage snapshot lifecycle.
4792
+ */
4793
+ class PopChildMeasure extends React__namespace.Component {
4794
+ getSnapshotBeforeUpdate(prevProps) {
4795
+ const element = this.props.childRef.current;
4796
+ if (element && prevProps.isPresent && !this.props.isPresent) {
4797
+ const parent = element.offsetParent;
4798
+ const parentWidth = isHTMLElement(parent)
4799
+ ? parent.offsetWidth || 0
4800
+ : 0;
4801
+ const size = this.props.sizeRef.current;
4802
+ size.height = element.offsetHeight || 0;
4803
+ size.width = element.offsetWidth || 0;
4804
+ size.top = element.offsetTop;
4805
+ size.left = element.offsetLeft;
4806
+ size.right = parentWidth - size.width - size.left;
4807
+ }
4808
+ return null;
4809
+ }
4810
+ /**
4811
+ * Required with getSnapshotBeforeUpdate to stop React complaining.
4812
+ */
4813
+ componentDidUpdate() { }
4814
+ render() {
4815
+ return this.props.children;
4816
+ }
4817
+ }
4818
+ function PopChild({ children, isPresent, anchorX }) {
4819
+ const id = React$1.useId();
4820
+ const ref = React$1.useRef(null);
4821
+ const size = React$1.useRef({
4822
+ width: 0,
4823
+ height: 0,
4824
+ top: 0,
4825
+ left: 0,
4826
+ right: 0,
4827
+ });
4828
+ const { nonce } = React$1.useContext(MotionConfigContext);
4829
+ /**
4830
+ * We create and inject a style block so we can apply this explicit
4831
+ * sizing in a non-destructive manner by just deleting the style block.
4832
+ *
4833
+ * We can't apply size via render as the measurement happens
4834
+ * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
4835
+ * styles directly on the DOM node, we might be overwriting
4836
+ * styles set via the style prop.
4837
+ */
4838
+ React$1.useInsertionEffect(() => {
4839
+ const { width, height, top, left, right } = size.current;
4840
+ if (isPresent || !ref.current || !width || !height)
4841
+ return;
4842
+ const x = anchorX === "left" ? `left: ${left}` : `right: ${right}`;
4843
+ ref.current.dataset.motionPopId = id;
4844
+ const style = document.createElement("style");
4845
+ if (nonce)
4846
+ style.nonce = nonce;
4847
+ document.head.appendChild(style);
4848
+ if (style.sheet) {
4849
+ style.sheet.insertRule(`
4850
+ [data-motion-pop-id="${id}"] {
4851
+ position: absolute !important;
4852
+ width: ${width}px !important;
4853
+ height: ${height}px !important;
4854
+ ${x}px !important;
4855
+ top: ${top}px !important;
4856
+ }
4857
+ `);
4858
+ }
4859
+ return () => {
4860
+ if (document.head.contains(style)) {
4861
+ document.head.removeChild(style);
4862
+ }
4863
+ };
4864
+ }, [isPresent]);
4865
+ return (jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) }));
4866
+ }
4867
+
4868
+ const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, anchorX, }) => {
4869
+ const presenceChildren = useConstant(newChildrenMap);
4870
+ const id = React$1.useId();
4871
+ let isReusedContext = true;
4872
+ let context = React$1.useMemo(() => {
4873
+ isReusedContext = false;
4874
+ return {
4875
+ id,
4876
+ initial,
4877
+ isPresent,
4878
+ custom,
4879
+ onExitComplete: (childId) => {
4880
+ presenceChildren.set(childId, true);
4881
+ for (const isComplete of presenceChildren.values()) {
4882
+ if (!isComplete)
4883
+ return; // can stop searching when any is incomplete
4884
+ }
4885
+ onExitComplete && onExitComplete();
4886
+ },
4887
+ register: (childId) => {
4888
+ presenceChildren.set(childId, false);
4889
+ return () => presenceChildren.delete(childId);
4890
+ },
4891
+ };
4892
+ }, [isPresent, presenceChildren, onExitComplete]);
4893
+ /**
4894
+ * If the presence of a child affects the layout of the components around it,
4895
+ * we want to make a new context value to ensure they get re-rendered
4896
+ * so they can detect that layout change.
4897
+ */
4898
+ if (presenceAffectsLayout && isReusedContext) {
4899
+ context = { ...context };
4900
+ }
4901
+ React$1.useMemo(() => {
4902
+ presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
4903
+ }, [isPresent]);
4904
+ /**
4905
+ * If there's no `motion` components to fire exit animations, we want to remove this
4906
+ * component immediately.
4907
+ */
4908
+ React__namespace.useEffect(() => {
4909
+ !isPresent &&
4910
+ !presenceChildren.size &&
4911
+ onExitComplete &&
4912
+ onExitComplete();
4913
+ }, [isPresent]);
4914
+ if (mode === "popLayout") {
4915
+ children = (jsx(PopChild, { isPresent: isPresent, anchorX: anchorX, children: children }));
4916
+ }
4917
+ return (jsx(PresenceContext.Provider, { value: context, children: children }));
4918
+ };
4919
+ function newChildrenMap() {
4920
+ return new Map();
4921
+ }
4922
+
4923
+ /**
4924
+ * When a component is the child of `AnimatePresence`, it can use `usePresence`
4925
+ * to access information about whether it's still present in the React tree.
4926
+ *
4927
+ * ```jsx
4928
+ * import { usePresence } from "framer-motion"
4929
+ *
4930
+ * export const Component = () => {
4931
+ * const [isPresent, safeToRemove] = usePresence()
4932
+ *
4933
+ * useEffect(() => {
4934
+ * !isPresent && setTimeout(safeToRemove, 1000)
4935
+ * }, [isPresent])
4936
+ *
4937
+ * return <div />
4938
+ * }
4939
+ * ```
4940
+ *
4941
+ * If `isPresent` is `false`, it means that a component has been removed the tree, but
4942
+ * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
4943
+ *
4944
+ * @public
4945
+ */
4946
+ function usePresence(subscribe = true) {
4947
+ const context = React$1.useContext(PresenceContext);
4948
+ if (context === null)
4949
+ return [true, null];
4950
+ const { isPresent, onExitComplete, register } = context;
4951
+ // It's safe to call the following hooks conditionally (after an early return) because the context will always
4952
+ // either be null or non-null for the lifespan of the component.
4953
+ const id = React$1.useId();
4954
+ React$1.useEffect(() => {
4955
+ if (subscribe) {
4956
+ return register(id);
4957
+ }
4958
+ }, [subscribe]);
4959
+ const safeToRemove = React$1.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]);
4960
+ return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
4961
+ }
4962
+ /**
4963
+ * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
4964
+ * There is no `safeToRemove` function.
4965
+ *
4966
+ * ```jsx
4967
+ * import { useIsPresent } from "framer-motion"
4968
+ *
4969
+ * export const Component = () => {
4970
+ * const isPresent = useIsPresent()
4971
+ *
4972
+ * useEffect(() => {
4973
+ * !isPresent && console.log("I've been removed!")
4974
+ * }, [isPresent])
4975
+ *
4976
+ * return <div />
4977
+ * }
4978
+ * ```
4979
+ *
4980
+ * @public
4981
+ */
4982
+ function useIsPresent() {
4983
+ return isPresent(React$1.useContext(PresenceContext));
4984
+ }
4985
+ function isPresent(context) {
4986
+ return context === null ? true : context.isPresent;
4987
+ }
4988
+
4989
+ const getChildKey = (child) => child.key || "";
4990
+ function onlyElements(children) {
4991
+ const filtered = [];
4992
+ // We use forEach here instead of map as map mutates the component key by preprending `.$`
4993
+ React$1.Children.forEach(children, (child) => {
4994
+ if (React$1.isValidElement(child))
4995
+ filtered.push(child);
4996
+ });
4997
+ return filtered;
4998
+ }
4999
+
5000
+ /**
5001
+ * `AnimatePresence` enables the animation of components that have been removed from the tree.
5002
+ *
5003
+ * When adding/removing more than a single child, every child **must** be given a unique `key` prop.
5004
+ *
5005
+ * Any `motion` components that have an `exit` property defined will animate out when removed from
5006
+ * the tree.
5007
+ *
5008
+ * ```jsx
5009
+ * import { motion, AnimatePresence } from 'framer-motion'
5010
+ *
5011
+ * export const Items = ({ items }) => (
5012
+ * <AnimatePresence>
5013
+ * {items.map(item => (
5014
+ * <motion.div
5015
+ * key={item.id}
5016
+ * initial={{ opacity: 0 }}
5017
+ * animate={{ opacity: 1 }}
5018
+ * exit={{ opacity: 0 }}
5019
+ * />
5020
+ * ))}
5021
+ * </AnimatePresence>
5022
+ * )
5023
+ * ```
5024
+ *
5025
+ * You can sequence exit animations throughout a tree using variants.
5026
+ *
5027
+ * If a child contains multiple `motion` components with `exit` props, it will only unmount the child
5028
+ * once all `motion` components have finished animating out. Likewise, any components using
5029
+ * `usePresence` all need to call `safeToRemove`.
5030
+ *
5031
+ * @public
5032
+ */
5033
+ const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, anchorX = "left", }) => {
5034
+ const [isParentPresent, safeToRemove] = usePresence(propagate);
5035
+ /**
5036
+ * Filter any children that aren't ReactElements. We can only track components
5037
+ * between renders with a props.key.
5038
+ */
5039
+ const presentChildren = React$1.useMemo(() => onlyElements(children), [children]);
5040
+ /**
5041
+ * Track the keys of the currently rendered children. This is used to
5042
+ * determine which children are exiting.
5043
+ */
5044
+ const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
5045
+ /**
5046
+ * If `initial={false}` we only want to pass this to components in the first render.
5047
+ */
5048
+ const isInitialRender = React$1.useRef(true);
5049
+ /**
5050
+ * A ref containing the currently present children. When all exit animations
5051
+ * are complete, we use this to re-render the component with the latest children
5052
+ * *committed* rather than the latest children *rendered*.
5053
+ */
5054
+ const pendingPresentChildren = React$1.useRef(presentChildren);
5055
+ /**
5056
+ * Track which exiting children have finished animating out.
5057
+ */
5058
+ const exitComplete = useConstant(() => new Map());
5059
+ /**
5060
+ * Save children to render as React state. To ensure this component is concurrent-safe,
5061
+ * we check for exiting children via an effect.
5062
+ */
5063
+ const [diffedChildren, setDiffedChildren] = React$1.useState(presentChildren);
5064
+ const [renderedChildren, setRenderedChildren] = React$1.useState(presentChildren);
5065
+ useIsomorphicLayoutEffect(() => {
5066
+ isInitialRender.current = false;
5067
+ pendingPresentChildren.current = presentChildren;
5068
+ /**
5069
+ * Update complete status of exiting children.
5070
+ */
5071
+ for (let i = 0; i < renderedChildren.length; i++) {
5072
+ const key = getChildKey(renderedChildren[i]);
5073
+ if (!presentKeys.includes(key)) {
5074
+ if (exitComplete.get(key) !== true) {
5075
+ exitComplete.set(key, false);
5076
+ }
5077
+ }
5078
+ else {
5079
+ exitComplete.delete(key);
5080
+ }
5081
+ }
5082
+ }, [renderedChildren, presentKeys.length, presentKeys.join("-")]);
5083
+ const exitingChildren = [];
5084
+ if (presentChildren !== diffedChildren) {
5085
+ let nextChildren = [...presentChildren];
5086
+ /**
5087
+ * Loop through all the currently rendered components and decide which
5088
+ * are exiting.
5089
+ */
5090
+ for (let i = 0; i < renderedChildren.length; i++) {
5091
+ const child = renderedChildren[i];
5092
+ const key = getChildKey(child);
5093
+ if (!presentKeys.includes(key)) {
5094
+ nextChildren.splice(i, 0, child);
5095
+ exitingChildren.push(child);
5096
+ }
5097
+ }
5098
+ /**
5099
+ * If we're in "wait" mode, and we have exiting children, we want to
5100
+ * only render these until they've all exited.
5101
+ */
5102
+ if (mode === "wait" && exitingChildren.length) {
5103
+ nextChildren = exitingChildren;
5104
+ }
5105
+ setRenderedChildren(onlyElements(nextChildren));
5106
+ setDiffedChildren(presentChildren);
5107
+ /**
5108
+ * Early return to ensure once we've set state with the latest diffed
5109
+ * children, we can immediately re-render.
5110
+ */
5111
+ return null;
5041
5112
  }
5042
- then(resolve, reject) {
5043
- return this.readyPromise.then(resolve, reject);
5113
+ if (mode === "wait" &&
5114
+ renderedChildren.length > 1) {
5115
+ 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.`);
5044
5116
  }
5045
- }
5046
- function animateView(update, defaultOptions = {}) {
5047
- return new ViewTransitionBuilder(update, defaultOptions);
5048
- }
5117
+ /**
5118
+ * If we've been provided a forceRender function by the LayoutGroupContext,
5119
+ * we can use it to force a re-render amongst all surrounding components once
5120
+ * all components have finished animating out.
5121
+ */
5122
+ const { forceRender } = React$1.useContext(LayoutGroupContext);
5123
+ return (jsx(Fragment, { children: renderedChildren.map((child) => {
5124
+ const key = getChildKey(child);
5125
+ const isPresent = propagate && !isParentPresent
5126
+ ? false
5127
+ : presentChildren === renderedChildren ||
5128
+ presentKeys.includes(key);
5129
+ const onExit = () => {
5130
+ if (exitComplete.has(key)) {
5131
+ exitComplete.set(key, true);
5132
+ }
5133
+ else {
5134
+ return;
5135
+ }
5136
+ let isEveryExitComplete = true;
5137
+ exitComplete.forEach((isExitComplete) => {
5138
+ if (!isExitComplete)
5139
+ isEveryExitComplete = false;
5140
+ });
5141
+ if (isEveryExitComplete) {
5142
+ forceRender?.();
5143
+ setRenderedChildren(pendingPresentChildren.current);
5144
+ propagate && safeToRemove?.();
5145
+ onExitComplete && onExitComplete();
5146
+ }
5147
+ };
5148
+ return (jsx(PresenceChild, { isPresent: isPresent, initial: !isInitialRender.current || initial
5149
+ ? undefined
5150
+ : false, custom: custom, presenceAffectsLayout: presenceAffectsLayout, mode: mode, onExitComplete: isPresent ? undefined : onExit, anchorX: anchorX, children: child }, key));
5151
+ }) }));
5152
+ };
5049
5153
 
5050
5154
  /**
5051
- * @deprecated
5155
+ * Note: Still used by components generated by old versions of Framer
5052
5156
  *
5053
- * Import as `frame` instead.
5054
- */
5055
- const sync = frame;
5056
- /**
5057
5157
  * @deprecated
5058
- *
5059
- * Use cancelFrame(callback) instead.
5060
5158
  */
5061
- const cancelSync = stepsOrder.reduce((acc, key) => {
5062
- acc[key] = (process) => cancelFrame(process);
5063
- return acc;
5064
- }, {});
5159
+ const DeprecatedLayoutGroupContext = React$1.createContext(null);
5065
5160
 
5066
5161
  const SCALE_PRECISION = 0.0001;
5067
5162
  const SCALE_MIN = 1 - SCALE_PRECISION;
@@ -5135,8 +5230,6 @@
5135
5230
  };
5136
5231
  }
5137
5232
 
5138
- const isMotionValue = (value) => Boolean(value && value.getVelocity);
5139
-
5140
5233
  const isNotNull = (value) => value !== null;
5141
5234
  function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
5142
5235
  const resolvedKeyframes = keyframes.filter(isNotNull);
@@ -5303,10 +5396,6 @@
5303
5396
  return visualElement.props[optimizedAppearDataAttribute];
5304
5397
  }
5305
5398
 
5306
- function isSVGElement(element) {
5307
- return element instanceof SVGElement && element.tagName !== "svg";
5308
- }
5309
-
5310
5399
  const compareByDepth = (a, b) => a.depth - b.depth;
5311
5400
 
5312
5401
  class FlatTree {
@@ -5356,7 +5445,7 @@
5356
5445
 
5357
5446
  const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
5358
5447
  const numBorders = borders.length;
5359
- const asNumber$1 = (value) => typeof value === "string" ? parseFloat(value) : value;
5448
+ const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
5360
5449
  const isPx = (value) => typeof value === "number" || px.test(value);
5361
5450
  function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
5362
5451
  if (shouldCrossfadeOpacity) {
@@ -5381,7 +5470,7 @@
5381
5470
  leadRadius === 0 ||
5382
5471
  isPx(followRadius) === isPx(leadRadius);
5383
5472
  if (canMix) {
5384
- target[borderLabel] = Math.max(mixNumber$1(asNumber$1(followRadius), asNumber$1(leadRadius), progress), 0);
5473
+ target[borderLabel] = Math.max(mixNumber$1(asNumber(followRadius), asNumber(leadRadius), progress), 0);
5385
5474
  if (percent.test(leadRadius) || percent.test(followRadius)) {
5386
5475
  target[borderLabel] += "%";
5387
5476
  }
@@ -6104,7 +6193,7 @@
6104
6193
  mount(instance) {
6105
6194
  if (this.instance)
6106
6195
  return;
6107
- this.isSVG = isSVGElement(instance);
6196
+ this.isSVG = isSVGElement(instance) && !isSVGSVGElement(instance);
6108
6197
  this.instance = instance;
6109
6198
  const { layoutId, layout, visualElement } = this.options;
6110
6199
  if (visualElement && !visualElement.current) {
@@ -10927,65 +11016,6 @@
10927
11016
  return htmlProps;
10928
11017
  }
10929
11018
 
10930
- /**
10931
- * We keep these listed separately as we use the lowercase tag names as part
10932
- * of the runtime bundle to detect SVG components
10933
- */
10934
- const lowercaseSVGElements = [
10935
- "animate",
10936
- "circle",
10937
- "defs",
10938
- "desc",
10939
- "ellipse",
10940
- "g",
10941
- "image",
10942
- "line",
10943
- "filter",
10944
- "marker",
10945
- "mask",
10946
- "metadata",
10947
- "path",
10948
- "pattern",
10949
- "polygon",
10950
- "polyline",
10951
- "rect",
10952
- "stop",
10953
- "switch",
10954
- "symbol",
10955
- "svg",
10956
- "text",
10957
- "tspan",
10958
- "use",
10959
- "view",
10960
- ];
10961
-
10962
- function isSVGComponent(Component) {
10963
- if (
10964
- /**
10965
- * If it's not a string, it's a custom React component. Currently we only support
10966
- * HTML custom React components.
10967
- */
10968
- typeof Component !== "string" ||
10969
- /**
10970
- * If it contains a dash, the element is a custom HTML webcomponent.
10971
- */
10972
- Component.includes("-")) {
10973
- return false;
10974
- }
10975
- else if (
10976
- /**
10977
- * If it's in our list of lowercase SVG tags, it's an SVG component
10978
- */
10979
- lowercaseSVGElements.indexOf(Component) > -1 ||
10980
- /**
10981
- * If it contains a capital letter, it's an SVG component
10982
- */
10983
- /[A-Z]/u.test(Component)) {
10984
- return true;
10985
- }
10986
- return false;
10987
- }
10988
-
10989
11019
  const dashKeys = {
10990
11020
  offset: "stroke-dashoffset",
10991
11021
  array: "stroke-dasharray",
@@ -11092,6 +11122,65 @@
11092
11122
  return visualProps;
11093
11123
  }
11094
11124
 
11125
+ /**
11126
+ * We keep these listed separately as we use the lowercase tag names as part
11127
+ * of the runtime bundle to detect SVG components
11128
+ */
11129
+ const lowercaseSVGElements = [
11130
+ "animate",
11131
+ "circle",
11132
+ "defs",
11133
+ "desc",
11134
+ "ellipse",
11135
+ "g",
11136
+ "image",
11137
+ "line",
11138
+ "filter",
11139
+ "marker",
11140
+ "mask",
11141
+ "metadata",
11142
+ "path",
11143
+ "pattern",
11144
+ "polygon",
11145
+ "polyline",
11146
+ "rect",
11147
+ "stop",
11148
+ "switch",
11149
+ "symbol",
11150
+ "svg",
11151
+ "text",
11152
+ "tspan",
11153
+ "use",
11154
+ "view",
11155
+ ];
11156
+
11157
+ function isSVGComponent(Component) {
11158
+ if (
11159
+ /**
11160
+ * If it's not a string, it's a custom React component. Currently we only support
11161
+ * HTML custom React components.
11162
+ */
11163
+ typeof Component !== "string" ||
11164
+ /**
11165
+ * If it contains a dash, the element is a custom HTML webcomponent.
11166
+ */
11167
+ Component.includes("-")) {
11168
+ return false;
11169
+ }
11170
+ else if (
11171
+ /**
11172
+ * If it's in our list of lowercase SVG tags, it's an SVG component
11173
+ */
11174
+ lowercaseSVGElements.indexOf(Component) > -1 ||
11175
+ /**
11176
+ * If it contains a capital letter, it's an SVG component
11177
+ */
11178
+ /[A-Z]/u.test(Component)) {
11179
+ return true;
11180
+ }
11181
+ return false;
11182
+ }
11183
+
11095
11184
  function createUseRender(forwardMotionProps = false) {
11096
11185
  const useRender = (Component, props, ref, { latestValues }, isStatic) => {
11097
11186
  const useVisualProps = isSVGComponent(Component)
@@ -11893,7 +11982,7 @@
11893
11982
  latestValues: {},
11894
11983
  },
11895
11984
  };
11896
- const node = isSVGElement(element)
11985
+ const node = isSVGElement(element) && !isSVGSVGElement(element)
11897
11986
  ? new SVGVisualElement(options)
11898
11987
  : new HTMLVisualElement(options);
11899
11988
  node.mount(element);
@@ -12111,7 +12200,7 @@
12111
12200
  const { inlineSize, blockSize } = borderBoxSize[0];
12112
12201
  return { width: inlineSize, height: blockSize };
12113
12202
  }
12114
- else if (target instanceof SVGElement && "getBBox" in target) {
12203
+ else if (isSVGElement(target) && "getBBox" in target) {
12115
12204
  return target.getBBox();
12116
12205
  }
12117
12206
  else {
@@ -12253,7 +12342,7 @@
12253
12342
  const inset = { x: 0, y: 0 };
12254
12343
  let current = element;
12255
12344
  while (current && current !== container) {
12256
- if (current instanceof HTMLElement) {
12345
+ if (isHTMLElement(current)) {
12257
12346
  inset.x += current.offsetLeft;
12258
12347
  inset.y += current.offsetTop;
12259
12348
  current = current.offsetParent;
@@ -12829,56 +12918,19 @@
12829
12918
  return useCombineMotionValues(values.filter(isMotionValue), buildValue);
12830
12919
  }
12831
12920
 
12832
- function useSpring(source, config = {}) {
12921
+ function useSpring(source, options = {}) {
12833
12922
  const { isStatic } = React$1.useContext(MotionConfigContext);
12834
- const activeSpringAnimation = React$1.useRef(null);
12835
- const initialValue = useConstant(() => isMotionValue(source) ? source.get() : source);
12836
- const unit = useConstant(() => typeof initialValue === "string"
12837
- ? initialValue.replace(/[\d.-]/g, "")
12838
- : undefined);
12839
- const value = useMotionValue(initialValue);
12840
- const latestValue = React$1.useRef(initialValue);
12841
- const latestSetter = React$1.useRef(noop);
12842
- const startAnimation = () => {
12843
- stopAnimation();
12844
- activeSpringAnimation.current = new JSAnimation({
12845
- keyframes: [asNumber(value.get()), asNumber(latestValue.current)],
12846
- velocity: value.getVelocity(),
12847
- type: "spring",
12848
- restDelta: 0.001,
12849
- restSpeed: 0.01,
12850
- ...config,
12851
- onUpdate: latestSetter.current,
12852
- });
12853
- };
12854
- const stopAnimation = () => {
12855
- if (activeSpringAnimation.current) {
12856
- activeSpringAnimation.current.stop();
12857
- }
12858
- };
12923
+ const getFromSource = () => (isMotionValue(source) ? source.get() : source);
12924
+ // isStatic will never change, allowing early hooks return
12925
+ if (isStatic) {
12926
+ return useTransform(getFromSource);
12927
+ }
12928
+ const value = useMotionValue(getFromSource());
12859
12929
  React$1.useInsertionEffect(() => {
12860
- return value.attach((v, set) => {
12861
- if (isStatic)
12862
- return set(v);
12863
- latestValue.current = v;
12864
- latestSetter.current = (latest) => set(parseValue(latest, unit));
12865
- frame.postRender(startAnimation);
12866
- return value.get();
12867
- }, stopAnimation);
12868
- }, [JSON.stringify(config)]);
12869
- useIsomorphicLayoutEffect(() => {
12870
- if (isMotionValue(source)) {
12871
- return source.on("change", (v) => value.set(parseValue(v, unit)));
12872
- }
12873
- }, [value, unit]);
12930
+ return attachSpring(value, source, options);
12931
+ }, [value, JSON.stringify(options)]);
12874
12932
  return value;
12875
12933
  }
12876
- function parseValue(v, unit) {
12877
- return unit ? v + unit : v;
12878
- }
12879
- function asNumber(v) {
12880
- return typeof v === "number" ? v : parseFloat(v);
12881
- }
12882
12934
 
12883
12935
  function useAnimationFrame(callback) {
12884
12936
  const initialTimestamp = React$1.useRef(0);
@@ -13761,6 +13813,7 @@
13761
13813
  exports.animations = animations;
13762
13814
  exports.anticipate = anticipate;
13763
13815
  exports.applyPxDefaults = applyPxDefaults;
13816
+ exports.attachSpring = attachSpring;
13764
13817
  exports.backIn = backIn;
13765
13818
  exports.backInOut = backInOut;
13766
13819
  exports.backOut = backOut;
@@ -13838,11 +13891,15 @@
13838
13891
  exports.isDragging = isDragging;
13839
13892
  exports.isEasingArray = isEasingArray;
13840
13893
  exports.isGenerator = isGenerator;
13894
+ exports.isHTMLElement = isHTMLElement;
13841
13895
  exports.isMotionComponent = isMotionComponent;
13842
13896
  exports.isMotionValue = isMotionValue;
13843
13897
  exports.isNodeOrChild = isNodeOrChild;
13844
13898
  exports.isNumericalString = isNumericalString;
13899
+ exports.isObject = isObject;
13845
13900
  exports.isPrimaryPointer = isPrimaryPointer;
13901
+ exports.isSVGElement = isSVGElement;
13902
+ exports.isSVGSVGElement = isSVGSVGElement;
13846
13903
  exports.isValidMotionProp = isValidMotionProp;
13847
13904
  exports.isWaapiSupportedEasing = isWaapiSupportedEasing;
13848
13905
  exports.isZeroValueString = isZeroValueString;
@@ -13897,6 +13954,7 @@
13897
13954
  exports.setDragLock = setDragLock;
13898
13955
  exports.setStyle = setStyle;
13899
13956
  exports.spring = spring;
13957
+ exports.springValue = springValue;
13900
13958
  exports.stagger = stagger;
13901
13959
  exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
13902
13960
  exports.startWaapiAnimation = startWaapiAnimation;