framer-motion 11.16.7 → 11.17.0

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.
@@ -409,7 +409,7 @@ class MotionValue {
409
409
  * This will be replaced by the build step with the latest version number.
410
410
  * When MotionValues are provided to motion components, warn if versions are mixed.
411
411
  */
412
- this.version = "11.16.7";
412
+ this.version = "11.17.0";
413
413
  /**
414
414
  * Tracks whether this value can output a velocity. Currently this is only true
415
415
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5201,7 +5201,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5201
5201
  * and warn against mismatches.
5202
5202
  */
5203
5203
  if (process.env.NODE_ENV === "development") {
5204
- warnOnce(nextValue.version === "11.16.7", `Attempting to mix Motion versions ${nextValue.version} with 11.16.7 may not work as expected.`);
5204
+ warnOnce(nextValue.version === "11.17.0", `Attempting to mix Motion versions ${nextValue.version} with 11.17.0 may not work as expected.`);
5205
5205
  }
5206
5206
  }
5207
5207
  else if (isMotionValue(prevValue)) {
@@ -8204,7 +8204,7 @@ const PresenceContext = react.createContext(null);
8204
8204
  *
8205
8205
  * @public
8206
8206
  */
8207
- function usePresence() {
8207
+ function usePresence(subscribe = true) {
8208
8208
  const context = react.useContext(PresenceContext);
8209
8209
  if (context === null)
8210
8210
  return [true, null];
@@ -8212,8 +8212,11 @@ function usePresence() {
8212
8212
  // It's safe to call the following hooks conditionally (after an early return) because the context will always
8213
8213
  // either be null or non-null for the lifespan of the component.
8214
8214
  const id = react.useId();
8215
- react.useEffect(() => register(id), []);
8216
- const safeToRemove = react.useCallback(() => onExitComplete && onExitComplete(id), [id, onExitComplete]);
8215
+ react.useEffect(() => {
8216
+ if (subscribe)
8217
+ register(id);
8218
+ }, [subscribe]);
8219
+ const safeToRemove = react.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]);
8217
8220
  return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
8218
8221
  }
8219
8222
 
package/dist/cjs/dom.js CHANGED
@@ -994,7 +994,7 @@ class MotionValue {
994
994
  * This will be replaced by the build step with the latest version number.
995
995
  * When MotionValues are provided to motion components, warn if versions are mixed.
996
996
  */
997
- this.version = "11.16.7";
997
+ this.version = "11.17.0";
998
998
  /**
999
999
  * Tracks whether this value can output a velocity. Currently this is only true
1000
1000
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -3924,7 +3924,7 @@ function updateMotionValuesFromProps(element, next, prev) {
3924
3924
  * and warn against mismatches.
3925
3925
  */
3926
3926
  if (process.env.NODE_ENV === "development") {
3927
- warnOnce(nextValue.version === "11.16.7", `Attempting to mix Motion versions ${nextValue.version} with 11.16.7 may not work as expected.`);
3927
+ warnOnce(nextValue.version === "11.17.0", `Attempting to mix Motion versions ${nextValue.version} with 11.17.0 may not work as expected.`);
3928
3928
  }
3929
3929
  }
3930
3930
  else if (isMotionValue(prevValue)) {
package/dist/cjs/index.js CHANGED
@@ -26,157 +26,6 @@ function _interopNamespaceDefault(e) {
26
26
 
27
27
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
28
28
 
29
- /**
30
- * @public
31
- */
32
- const PresenceContext = React.createContext(null);
33
-
34
- /**
35
- * Creates a constant value over the lifecycle of a component.
36
- *
37
- * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
38
- * a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
39
- * you can ensure that initialisers don't execute twice or more.
40
- */
41
- function useConstant(init) {
42
- const ref = React.useRef(null);
43
- if (ref.current === null) {
44
- ref.current = init();
45
- }
46
- return ref.current;
47
- }
48
-
49
- /**
50
- * @public
51
- */
52
- const MotionConfigContext = React.createContext({
53
- transformPagePoint: (p) => p,
54
- isStatic: false,
55
- reducedMotion: "never",
56
- });
57
-
58
- /**
59
- * Measurement functionality has to be within a separate component
60
- * to leverage snapshot lifecycle.
61
- */
62
- class PopChildMeasure extends React__namespace.Component {
63
- getSnapshotBeforeUpdate(prevProps) {
64
- const element = this.props.childRef.current;
65
- if (element && prevProps.isPresent && !this.props.isPresent) {
66
- const size = this.props.sizeRef.current;
67
- size.height = element.offsetHeight || 0;
68
- size.width = element.offsetWidth || 0;
69
- size.top = element.offsetTop;
70
- size.left = element.offsetLeft;
71
- }
72
- return null;
73
- }
74
- /**
75
- * Required with getSnapshotBeforeUpdate to stop React complaining.
76
- */
77
- componentDidUpdate() { }
78
- render() {
79
- return this.props.children;
80
- }
81
- }
82
- function PopChild({ children, isPresent }) {
83
- const id = React.useId();
84
- const ref = React.useRef(null);
85
- const size = React.useRef({
86
- width: 0,
87
- height: 0,
88
- top: 0,
89
- left: 0,
90
- });
91
- const { nonce } = React.useContext(MotionConfigContext);
92
- /**
93
- * We create and inject a style block so we can apply this explicit
94
- * sizing in a non-destructive manner by just deleting the style block.
95
- *
96
- * We can't apply size via render as the measurement happens
97
- * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
98
- * styles directly on the DOM node, we might be overwriting
99
- * styles set via the style prop.
100
- */
101
- React.useInsertionEffect(() => {
102
- const { width, height, top, left } = size.current;
103
- if (isPresent || !ref.current || !width || !height)
104
- return;
105
- ref.current.dataset.motionPopId = id;
106
- const style = document.createElement("style");
107
- if (nonce)
108
- style.nonce = nonce;
109
- document.head.appendChild(style);
110
- if (style.sheet) {
111
- style.sheet.insertRule(`
112
- [data-motion-pop-id="${id}"] {
113
- position: absolute !important;
114
- width: ${width}px !important;
115
- height: ${height}px !important;
116
- top: ${top}px !important;
117
- left: ${left}px !important;
118
- }
119
- `);
120
- }
121
- return () => {
122
- document.head.removeChild(style);
123
- };
124
- }, [isPresent]);
125
- return (jsxRuntime.jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) }));
126
- }
127
-
128
- const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, }) => {
129
- const presenceChildren = useConstant(newChildrenMap);
130
- const id = React.useId();
131
- const memoizedOnExitComplete = React.useCallback((childId) => {
132
- presenceChildren.set(childId, true);
133
- for (const isComplete of presenceChildren.values()) {
134
- if (!isComplete)
135
- return; // can stop searching when any is incomplete
136
- }
137
- onExitComplete && onExitComplete();
138
- }, [presenceChildren, onExitComplete]);
139
- const context = React.useMemo(() => ({
140
- id,
141
- initial,
142
- isPresent,
143
- custom,
144
- onExitComplete: memoizedOnExitComplete,
145
- register: (childId) => {
146
- presenceChildren.set(childId, false);
147
- return () => presenceChildren.delete(childId);
148
- },
149
- }),
150
- /**
151
- * If the presence of a child affects the layout of the components around it,
152
- * we want to make a new context value to ensure they get re-rendered
153
- * so they can detect that layout change.
154
- */
155
- presenceAffectsLayout
156
- ? [Math.random(), memoizedOnExitComplete]
157
- : [isPresent, memoizedOnExitComplete]);
158
- React.useMemo(() => {
159
- presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
160
- }, [isPresent]);
161
- /**
162
- * If there's no `motion` components to fire exit animations, we want to remove this
163
- * component immediately.
164
- */
165
- React__namespace.useEffect(() => {
166
- !isPresent &&
167
- !presenceChildren.size &&
168
- onExitComplete &&
169
- onExitComplete();
170
- }, [isPresent]);
171
- if (mode === "popLayout") {
172
- children = jsxRuntime.jsx(PopChild, { isPresent: isPresent, children: children });
173
- }
174
- return (jsxRuntime.jsx(PresenceContext.Provider, { value: context, children: children }));
175
- };
176
- function newChildrenMap() {
177
- return new Map();
178
- }
179
-
180
29
  const LayoutGroupContext = React.createContext({});
181
30
 
182
31
  function isAnimationControls(v) {
@@ -596,7 +445,7 @@ class MotionValue {
596
445
  * This will be replaced by the build step with the latest version number.
597
446
  * When MotionValues are provided to motion components, warn if versions are mixed.
598
447
  */
599
- this.version = "11.16.7";
448
+ this.version = "11.17.0";
600
449
  /**
601
450
  * Tracks whether this value can output a velocity. Currently this is only true
602
451
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -4401,6 +4250,26 @@ function filterProps(props, isDom, forwardMotionProps) {
4401
4250
  return filteredProps;
4402
4251
  }
4403
4252
 
4253
+ /**
4254
+ * @public
4255
+ */
4256
+ const PresenceContext = React.createContext(null);
4257
+
4258
+ /**
4259
+ * Creates a constant value over the lifecycle of a component.
4260
+ *
4261
+ * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
4262
+ * a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
4263
+ * you can ensure that initialisers don't execute twice or more.
4264
+ */
4265
+ function useConstant(init) {
4266
+ const ref = React.useRef(null);
4267
+ if (ref.current === null) {
4268
+ ref.current = init();
4269
+ }
4270
+ return ref.current;
4271
+ }
4272
+
4404
4273
  /**
4405
4274
  * If the provided value is a MotionValue, this returns the actual value, otherwise just the value itself
4406
4275
  *
@@ -4547,6 +4416,202 @@ function useForceUpdate() {
4547
4416
  return [deferredForceRender, forcedRenderCount];
4548
4417
  }
4549
4418
 
4419
+ /**
4420
+ * @public
4421
+ */
4422
+ const MotionConfigContext = React.createContext({
4423
+ transformPagePoint: (p) => p,
4424
+ isStatic: false,
4425
+ reducedMotion: "never",
4426
+ });
4427
+
4428
+ /**
4429
+ * Measurement functionality has to be within a separate component
4430
+ * to leverage snapshot lifecycle.
4431
+ */
4432
+ class PopChildMeasure extends React__namespace.Component {
4433
+ getSnapshotBeforeUpdate(prevProps) {
4434
+ const element = this.props.childRef.current;
4435
+ if (element && prevProps.isPresent && !this.props.isPresent) {
4436
+ const size = this.props.sizeRef.current;
4437
+ size.height = element.offsetHeight || 0;
4438
+ size.width = element.offsetWidth || 0;
4439
+ size.top = element.offsetTop;
4440
+ size.left = element.offsetLeft;
4441
+ }
4442
+ return null;
4443
+ }
4444
+ /**
4445
+ * Required with getSnapshotBeforeUpdate to stop React complaining.
4446
+ */
4447
+ componentDidUpdate() { }
4448
+ render() {
4449
+ return this.props.children;
4450
+ }
4451
+ }
4452
+ function PopChild({ children, isPresent }) {
4453
+ const id = React.useId();
4454
+ const ref = React.useRef(null);
4455
+ const size = React.useRef({
4456
+ width: 0,
4457
+ height: 0,
4458
+ top: 0,
4459
+ left: 0,
4460
+ });
4461
+ const { nonce } = React.useContext(MotionConfigContext);
4462
+ /**
4463
+ * We create and inject a style block so we can apply this explicit
4464
+ * sizing in a non-destructive manner by just deleting the style block.
4465
+ *
4466
+ * We can't apply size via render as the measurement happens
4467
+ * in getSnapshotBeforeUpdate (post-render), likewise if we apply the
4468
+ * styles directly on the DOM node, we might be overwriting
4469
+ * styles set via the style prop.
4470
+ */
4471
+ React.useInsertionEffect(() => {
4472
+ const { width, height, top, left } = size.current;
4473
+ if (isPresent || !ref.current || !width || !height)
4474
+ return;
4475
+ ref.current.dataset.motionPopId = id;
4476
+ const style = document.createElement("style");
4477
+ if (nonce)
4478
+ style.nonce = nonce;
4479
+ document.head.appendChild(style);
4480
+ if (style.sheet) {
4481
+ style.sheet.insertRule(`
4482
+ [data-motion-pop-id="${id}"] {
4483
+ position: absolute !important;
4484
+ width: ${width}px !important;
4485
+ height: ${height}px !important;
4486
+ top: ${top}px !important;
4487
+ left: ${left}px !important;
4488
+ }
4489
+ `);
4490
+ }
4491
+ return () => {
4492
+ document.head.removeChild(style);
4493
+ };
4494
+ }, [isPresent]);
4495
+ return (jsxRuntime.jsx(PopChildMeasure, { isPresent: isPresent, childRef: ref, sizeRef: size, children: React__namespace.cloneElement(children, { ref }) }));
4496
+ }
4497
+
4498
+ const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, presenceAffectsLayout, mode, }) => {
4499
+ const presenceChildren = useConstant(newChildrenMap);
4500
+ const id = React.useId();
4501
+ const memoizedOnExitComplete = React.useCallback((childId) => {
4502
+ presenceChildren.set(childId, true);
4503
+ for (const isComplete of presenceChildren.values()) {
4504
+ if (!isComplete)
4505
+ return; // can stop searching when any is incomplete
4506
+ }
4507
+ onExitComplete && onExitComplete();
4508
+ }, [presenceChildren, onExitComplete]);
4509
+ const context = React.useMemo(() => ({
4510
+ id,
4511
+ initial,
4512
+ isPresent,
4513
+ custom,
4514
+ onExitComplete: memoizedOnExitComplete,
4515
+ register: (childId) => {
4516
+ presenceChildren.set(childId, false);
4517
+ return () => presenceChildren.delete(childId);
4518
+ },
4519
+ }),
4520
+ /**
4521
+ * If the presence of a child affects the layout of the components around it,
4522
+ * we want to make a new context value to ensure they get re-rendered
4523
+ * so they can detect that layout change.
4524
+ */
4525
+ presenceAffectsLayout
4526
+ ? [Math.random(), memoizedOnExitComplete]
4527
+ : [isPresent, memoizedOnExitComplete]);
4528
+ React.useMemo(() => {
4529
+ presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
4530
+ }, [isPresent]);
4531
+ /**
4532
+ * If there's no `motion` components to fire exit animations, we want to remove this
4533
+ * component immediately.
4534
+ */
4535
+ React__namespace.useEffect(() => {
4536
+ !isPresent &&
4537
+ !presenceChildren.size &&
4538
+ onExitComplete &&
4539
+ onExitComplete();
4540
+ }, [isPresent]);
4541
+ if (mode === "popLayout") {
4542
+ children = jsxRuntime.jsx(PopChild, { isPresent: isPresent, children: children });
4543
+ }
4544
+ return (jsxRuntime.jsx(PresenceContext.Provider, { value: context, children: children }));
4545
+ };
4546
+ function newChildrenMap() {
4547
+ return new Map();
4548
+ }
4549
+
4550
+ /**
4551
+ * When a component is the child of `AnimatePresence`, it can use `usePresence`
4552
+ * to access information about whether it's still present in the React tree.
4553
+ *
4554
+ * ```jsx
4555
+ * import { usePresence } from "framer-motion"
4556
+ *
4557
+ * export const Component = () => {
4558
+ * const [isPresent, safeToRemove] = usePresence()
4559
+ *
4560
+ * useEffect(() => {
4561
+ * !isPresent && setTimeout(safeToRemove, 1000)
4562
+ * }, [isPresent])
4563
+ *
4564
+ * return <div />
4565
+ * }
4566
+ * ```
4567
+ *
4568
+ * If `isPresent` is `false`, it means that a component has been removed the tree, but
4569
+ * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
4570
+ *
4571
+ * @public
4572
+ */
4573
+ function usePresence(subscribe = true) {
4574
+ const context = React.useContext(PresenceContext);
4575
+ if (context === null)
4576
+ return [true, null];
4577
+ const { isPresent, onExitComplete, register } = context;
4578
+ // It's safe to call the following hooks conditionally (after an early return) because the context will always
4579
+ // either be null or non-null for the lifespan of the component.
4580
+ const id = React.useId();
4581
+ React.useEffect(() => {
4582
+ if (subscribe)
4583
+ register(id);
4584
+ }, [subscribe]);
4585
+ const safeToRemove = React.useCallback(() => subscribe && onExitComplete && onExitComplete(id), [id, onExitComplete, subscribe]);
4586
+ return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
4587
+ }
4588
+ /**
4589
+ * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
4590
+ * There is no `safeToRemove` function.
4591
+ *
4592
+ * ```jsx
4593
+ * import { useIsPresent } from "framer-motion"
4594
+ *
4595
+ * export const Component = () => {
4596
+ * const isPresent = useIsPresent()
4597
+ *
4598
+ * useEffect(() => {
4599
+ * !isPresent && console.log("I've been removed!")
4600
+ * }, [isPresent])
4601
+ *
4602
+ * return <div />
4603
+ * }
4604
+ * ```
4605
+ *
4606
+ * @public
4607
+ */
4608
+ function useIsPresent() {
4609
+ return isPresent(React.useContext(PresenceContext));
4610
+ }
4611
+ function isPresent(context) {
4612
+ return context === null ? true : context.isPresent;
4613
+ }
4614
+
4550
4615
  const getChildKey = (child) => child.key || "";
4551
4616
  function onlyElements(children) {
4552
4617
  const filtered = [];
@@ -4591,8 +4656,8 @@ function onlyElements(children) {
4591
4656
  *
4592
4657
  * @public
4593
4658
  */
4594
- const AnimatePresence = ({ children, exitBeforeEnter, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", }) => {
4595
- motionUtils.invariant(!exitBeforeEnter, "Replace exitBeforeEnter with mode='wait'");
4659
+ const AnimatePresence = ({ children, custom, initial = true, onExitComplete, presenceAffectsLayout = true, mode = "sync", propagate = false, }) => {
4660
+ const [isParentPresent, safeToRemove] = usePresence(propagate);
4596
4661
  /**
4597
4662
  * Filter any children that aren't ReactElements. We can only track components
4598
4663
  * between renders with a props.key.
@@ -4602,7 +4667,7 @@ const AnimatePresence = ({ children, exitBeforeEnter, custom, initial = true, on
4602
4667
  * Track the keys of the currently rendered children. This is used to
4603
4668
  * determine which children are exiting.
4604
4669
  */
4605
- const presentKeys = presentChildren.map(getChildKey);
4670
+ const presentKeys = propagate && !isParentPresent ? [] : presentChildren.map(getChildKey);
4606
4671
  /**
4607
4672
  * If `initial={false}` we only want to pass this to components in the first render.
4608
4673
  */
@@ -4684,8 +4749,10 @@ const AnimatePresence = ({ children, exitBeforeEnter, custom, initial = true, on
4684
4749
  const { forceRender } = React.useContext(LayoutGroupContext);
4685
4750
  return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderedChildren.map((child) => {
4686
4751
  const key = getChildKey(child);
4687
- const isPresent = presentChildren === renderedChildren ||
4688
- presentKeys.includes(key);
4752
+ const isPresent = propagate && !isParentPresent
4753
+ ? false
4754
+ : presentChildren === renderedChildren ||
4755
+ presentKeys.includes(key);
4689
4756
  const onExit = () => {
4690
4757
  if (exitComplete.has(key)) {
4691
4758
  exitComplete.set(key, true);
@@ -4701,6 +4768,7 @@ const AnimatePresence = ({ children, exitBeforeEnter, custom, initial = true, on
4701
4768
  if (isEveryExitComplete) {
4702
4769
  forceRender === null || forceRender === void 0 ? void 0 : forceRender();
4703
4770
  setRenderedChildren(pendingPresentChildren.current);
4771
+ propagate && (safeToRemove === null || safeToRemove === void 0 ? void 0 : safeToRemove());
4704
4772
  onExitComplete && onExitComplete();
4705
4773
  }
4706
4774
  };
@@ -5141,7 +5209,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5141
5209
  * and warn against mismatches.
5142
5210
  */
5143
5211
  if (process.env.NODE_ENV === "development") {
5144
- warnOnce(nextValue.version === "11.16.7", `Attempting to mix Motion versions ${nextValue.version} with 11.16.7 may not work as expected.`);
5212
+ warnOnce(nextValue.version === "11.17.0", `Attempting to mix Motion versions ${nextValue.version} with 11.17.0 may not work as expected.`);
5145
5213
  }
5146
5214
  }
5147
5215
  else if (isMotionValue(prevValue)) {
@@ -10274,68 +10342,6 @@ class PanGesture extends Feature {
10274
10342
  }
10275
10343
  }
10276
10344
 
10277
- /**
10278
- * When a component is the child of `AnimatePresence`, it can use `usePresence`
10279
- * to access information about whether it's still present in the React tree.
10280
- *
10281
- * ```jsx
10282
- * import { usePresence } from "framer-motion"
10283
- *
10284
- * export const Component = () => {
10285
- * const [isPresent, safeToRemove] = usePresence()
10286
- *
10287
- * useEffect(() => {
10288
- * !isPresent && setTimeout(safeToRemove, 1000)
10289
- * }, [isPresent])
10290
- *
10291
- * return <div />
10292
- * }
10293
- * ```
10294
- *
10295
- * If `isPresent` is `false`, it means that a component has been removed the tree, but
10296
- * `AnimatePresence` won't really remove it until `safeToRemove` has been called.
10297
- *
10298
- * @public
10299
- */
10300
- function usePresence() {
10301
- const context = React.useContext(PresenceContext);
10302
- if (context === null)
10303
- return [true, null];
10304
- const { isPresent, onExitComplete, register } = context;
10305
- // It's safe to call the following hooks conditionally (after an early return) because the context will always
10306
- // either be null or non-null for the lifespan of the component.
10307
- const id = React.useId();
10308
- React.useEffect(() => register(id), []);
10309
- const safeToRemove = React.useCallback(() => onExitComplete && onExitComplete(id), [id, onExitComplete]);
10310
- return !isPresent && onExitComplete ? [false, safeToRemove] : [true];
10311
- }
10312
- /**
10313
- * Similar to `usePresence`, except `useIsPresent` simply returns whether or not the component is present.
10314
- * There is no `safeToRemove` function.
10315
- *
10316
- * ```jsx
10317
- * import { useIsPresent } from "framer-motion"
10318
- *
10319
- * export const Component = () => {
10320
- * const isPresent = useIsPresent()
10321
- *
10322
- * useEffect(() => {
10323
- * !isPresent && console.log("I've been removed!")
10324
- * }, [isPresent])
10325
- *
10326
- * return <div />
10327
- * }
10328
- * ```
10329
- *
10330
- * @public
10331
- */
10332
- function useIsPresent() {
10333
- return isPresent(React.useContext(PresenceContext));
10334
- }
10335
- function isPresent(context) {
10336
- return context === null ? true : context.isPresent;
10337
- }
10338
-
10339
10345
  /**
10340
10346
  * Internal, exported only for usage in Framer
10341
10347
  */