framer-motion 8.5.5 → 9.0.1-css-var-fix

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -809,6 +809,10 @@ function useHTMLProps(props, visualState, isStatic) {
809
809
  ? "none"
810
810
  : `pan-${props.drag === "x" ? "y" : "x"}`;
811
811
  }
812
+ if (props.tabIndex === undefined &&
813
+ (props.onTap || props.onTapStart || props.whileTap)) {
814
+ htmlProps.tabIndex = 0;
815
+ }
812
816
  htmlProps.style = style;
813
817
  return htmlProps;
814
818
  }
@@ -1353,19 +1357,33 @@ function useDomEvent(ref, eventName, handler, options) {
1353
1357
  }, [ref, eventName, handler, options]);
1354
1358
  }
1355
1359
 
1356
- /**
1357
- *
1358
- * @param props
1359
- * @param ref
1360
- * @internal
1361
- */
1362
1360
  function useFocusGesture({ whileFocus, visualElement, }) {
1361
+ const isFocusActive = React.useRef(false);
1363
1362
  const { animationState } = visualElement;
1364
1363
  const onFocus = React.useCallback(() => {
1365
- animationState && animationState.setActive(exports.AnimationType.Focus, true);
1364
+ let isFocusVisible = false;
1365
+ /**
1366
+ * If this element doesn't match focus-visible then don't
1367
+ * apply whileHover. But, if matches throws that focus-visible
1368
+ * is not a valid selector then in that browser outline styles will be applied
1369
+ * to the element by default and we want to match that behaviour with whileFocus.
1370
+ */
1371
+ try {
1372
+ isFocusVisible = visualElement.current.matches(":focus-visible");
1373
+ }
1374
+ catch (e) {
1375
+ isFocusVisible = true;
1376
+ }
1377
+ if (!isFocusVisible || !animationState)
1378
+ return;
1379
+ animationState.setActive(exports.AnimationType.Focus, true);
1380
+ isFocusActive.current = true;
1366
1381
  }, [animationState]);
1367
1382
  const onBlur = React.useCallback(() => {
1368
- animationState && animationState.setActive(exports.AnimationType.Focus, false);
1383
+ if (!isFocusActive.current || !animationState)
1384
+ return;
1385
+ animationState.setActive(exports.AnimationType.Focus, false);
1386
+ isFocusActive.current = false;
1369
1387
  }, [animationState]);
1370
1388
  useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
1371
1389
  useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
@@ -1518,6 +1536,12 @@ function useUnmountEffect(callback) {
1518
1536
  const combineFunctions = (a, b) => (v) => b(a(v));
1519
1537
  const pipe = (...transformers) => transformers.reduce(combineFunctions);
1520
1538
 
1539
+ function fireSyntheticPointerEvent(name, handler) {
1540
+ if (!handler)
1541
+ return;
1542
+ const syntheticPointerEvent = new PointerEvent("pointer" + name);
1543
+ handler(syntheticPointerEvent, extractEventInfo(syntheticPointerEvent));
1544
+ }
1521
1545
  /**
1522
1546
  * @param handlers -
1523
1547
  * @internal
@@ -1566,13 +1590,8 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1566
1590
  return;
1567
1591
  (_b = (_a = visualElement.getProps()).onTapCancel) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
1568
1592
  }
1569
- const startPress = React.useCallback((event, info) => {
1593
+ function onPointerStart(event, info) {
1570
1594
  var _a;
1571
- removePointerEndListener();
1572
- if (isPressing.current)
1573
- return;
1574
- isPressing.current = true;
1575
- cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
1576
1595
  const latestProps = visualElement.getProps();
1577
1596
  /**
1578
1597
  * Ensure we trigger animations before firing event callback
@@ -1581,8 +1600,43 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1581
1600
  visualElement.animationState.setActive(exports.AnimationType.Tap, true);
1582
1601
  }
1583
1602
  (_a = latestProps.onTapStart) === null || _a === void 0 ? void 0 : _a.call(latestProps, event, info);
1584
- }, [Boolean(onTapStart), visualElement]);
1603
+ }
1604
+ const callbackDependencies = [
1605
+ Boolean(onTapStart),
1606
+ Boolean(onTap),
1607
+ Boolean(whileTap),
1608
+ visualElement,
1609
+ ];
1610
+ const startPress = React.useCallback((event, info) => {
1611
+ removePointerEndListener();
1612
+ if (isPressing.current)
1613
+ return;
1614
+ isPressing.current = true;
1615
+ cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
1616
+ onPointerStart(event, info);
1617
+ }, callbackDependencies);
1585
1618
  usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
1619
+ const startAccessiblePress = React.useCallback(() => {
1620
+ const stopKeydownListener = addDomEvent(visualElement.current, "keydown", (event) => {
1621
+ if (event.key !== "Enter" || isPressing.current)
1622
+ return;
1623
+ isPressing.current = true;
1624
+ cancelPointerEndListeners.current = addDomEvent(visualElement.current, "keyup", () => {
1625
+ if (event.key !== "Enter" || !checkPointerEnd())
1626
+ return;
1627
+ fireSyntheticPointerEvent("up", visualElement.getProps().onTap);
1628
+ }, eventOptions);
1629
+ fireSyntheticPointerEvent("down", onPointerStart);
1630
+ });
1631
+ const stopBlurListener = addDomEvent(visualElement.current, "blur", () => {
1632
+ stopKeydownListener();
1633
+ stopBlurListener();
1634
+ if (isPressing.current) {
1635
+ fireSyntheticPointerEvent("cancel", onPointerCancel);
1636
+ }
1637
+ });
1638
+ }, callbackDependencies);
1639
+ useDomEvent(visualElement, "focus", hasPressListeners ? startAccessiblePress : undefined);
1586
1640
  useUnmountEffect(removePointerEndListener);
1587
1641
  }
1588
1642
 
@@ -2079,7 +2133,7 @@ class MotionValue {
2079
2133
  * This will be replaced by the build step with the latest version number.
2080
2134
  * When MotionValues are provided to motion components, warn if versions are mixed.
2081
2135
  */
2082
- this.version = "8.5.5";
2136
+ this.version = "9.0.1-css-var-fix";
2083
2137
  /**
2084
2138
  * Duration, in milliseconds, since last updating frame.
2085
2139
  *
@@ -5650,10 +5704,12 @@ function resolveCSSVariables(visualElement, { ...target }, transitionEnd) {
5650
5704
  continue;
5651
5705
  // Clone target if it hasn't already been
5652
5706
  target[key] = resolved;
5707
+ if (!transitionEnd)
5708
+ transitionEnd = {};
5653
5709
  // If the user hasn't already set this key on `transitionEnd`, set it to the unresolved
5654
5710
  // CSS variable. This will ensure that after the animation the component will reflect
5655
5711
  // changes in the value of the CSS variable.
5656
- if (transitionEnd && transitionEnd[key] === undefined) {
5712
+ if (transitionEnd[key] === undefined) {
5657
5713
  transitionEnd[key] = current;
5658
5714
  }
5659
5715
  }
@@ -5928,7 +5984,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5928
5984
  * and warn against mismatches.
5929
5985
  */
5930
5986
  if (process.env.NODE_ENV === "development") {
5931
- warnOnce(nextValue.version === "8.5.5", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.5.5 may not work as expected.`);
5987
+ warnOnce(nextValue.version === "9.0.1-css-var-fix", `Attempting to mix Framer Motion versions ${nextValue.version} with 9.0.1-css-var-fix may not work as expected.`);
5932
5988
  }
5933
5989
  }
5934
5990
  else if (isMotionValue(prevValue)) {
@@ -1,20 +1,34 @@
1
1
  import { AnimationType } from '../render/utils/types.mjs';
2
2
  import { useDomEvent } from '../events/use-dom-event.mjs';
3
- import { useCallback } from 'react';
3
+ import { useRef, useCallback } from 'react';
4
4
 
5
- /**
6
- *
7
- * @param props
8
- * @param ref
9
- * @internal
10
- */
11
5
  function useFocusGesture({ whileFocus, visualElement, }) {
6
+ const isFocusActive = useRef(false);
12
7
  const { animationState } = visualElement;
13
8
  const onFocus = useCallback(() => {
14
- animationState && animationState.setActive(AnimationType.Focus, true);
9
+ let isFocusVisible = false;
10
+ /**
11
+ * If this element doesn't match focus-visible then don't
12
+ * apply whileHover. But, if matches throws that focus-visible
13
+ * is not a valid selector then in that browser outline styles will be applied
14
+ * to the element by default and we want to match that behaviour with whileFocus.
15
+ */
16
+ try {
17
+ isFocusVisible = visualElement.current.matches(":focus-visible");
18
+ }
19
+ catch (e) {
20
+ isFocusVisible = true;
21
+ }
22
+ if (!isFocusVisible || !animationState)
23
+ return;
24
+ animationState.setActive(AnimationType.Focus, true);
25
+ isFocusActive.current = true;
15
26
  }, [animationState]);
16
27
  const onBlur = useCallback(() => {
17
- animationState && animationState.setActive(AnimationType.Focus, false);
28
+ if (!isFocusActive.current || !animationState)
29
+ return;
30
+ animationState.setActive(AnimationType.Focus, false);
31
+ isFocusActive.current = false;
18
32
  }, [animationState]);
19
33
  useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
20
34
  useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
@@ -5,7 +5,15 @@ import { useUnmountEffect } from '../utils/use-unmount-effect.mjs';
5
5
  import { AnimationType } from '../render/utils/types.mjs';
6
6
  import { isDragActive } from './drag/utils/lock.mjs';
7
7
  import { pipe } from '../utils/pipe.mjs';
8
+ import { addDomEvent, useDomEvent } from '../events/use-dom-event.mjs';
9
+ import { extractEventInfo } from '../events/event-info.mjs';
8
10
 
11
+ function fireSyntheticPointerEvent(name, handler) {
12
+ if (!handler)
13
+ return;
14
+ const syntheticPointerEvent = new PointerEvent("pointer" + name);
15
+ handler(syntheticPointerEvent, extractEventInfo(syntheticPointerEvent));
16
+ }
9
17
  /**
10
18
  * @param handlers -
11
19
  * @internal
@@ -54,13 +62,8 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
54
62
  return;
55
63
  (_b = (_a = visualElement.getProps()).onTapCancel) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
56
64
  }
57
- const startPress = useCallback((event, info) => {
65
+ function onPointerStart(event, info) {
58
66
  var _a;
59
- removePointerEndListener();
60
- if (isPressing.current)
61
- return;
62
- isPressing.current = true;
63
- cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
64
67
  const latestProps = visualElement.getProps();
65
68
  /**
66
69
  * Ensure we trigger animations before firing event callback
@@ -69,8 +72,43 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
69
72
  visualElement.animationState.setActive(AnimationType.Tap, true);
70
73
  }
71
74
  (_a = latestProps.onTapStart) === null || _a === void 0 ? void 0 : _a.call(latestProps, event, info);
72
- }, [Boolean(onTapStart), visualElement]);
75
+ }
76
+ const callbackDependencies = [
77
+ Boolean(onTapStart),
78
+ Boolean(onTap),
79
+ Boolean(whileTap),
80
+ visualElement,
81
+ ];
82
+ const startPress = useCallback((event, info) => {
83
+ removePointerEndListener();
84
+ if (isPressing.current)
85
+ return;
86
+ isPressing.current = true;
87
+ cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
88
+ onPointerStart(event, info);
89
+ }, callbackDependencies);
73
90
  usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
91
+ const startAccessiblePress = useCallback(() => {
92
+ const stopKeydownListener = addDomEvent(visualElement.current, "keydown", (event) => {
93
+ if (event.key !== "Enter" || isPressing.current)
94
+ return;
95
+ isPressing.current = true;
96
+ cancelPointerEndListeners.current = addDomEvent(visualElement.current, "keyup", () => {
97
+ if (event.key !== "Enter" || !checkPointerEnd())
98
+ return;
99
+ fireSyntheticPointerEvent("up", visualElement.getProps().onTap);
100
+ }, eventOptions);
101
+ fireSyntheticPointerEvent("down", onPointerStart);
102
+ });
103
+ const stopBlurListener = addDomEvent(visualElement.current, "blur", () => {
104
+ stopKeydownListener();
105
+ stopBlurListener();
106
+ if (isPressing.current) {
107
+ fireSyntheticPointerEvent("cancel", onPointerCancel);
108
+ }
109
+ });
110
+ }, callbackDependencies);
111
+ useDomEvent(visualElement, "focus", hasPressListeners ? startAccessiblePress : undefined);
74
112
  useUnmountEffect(removePointerEndListener);
75
113
  }
76
114
 
@@ -74,10 +74,12 @@ function resolveCSSVariables(visualElement, { ...target }, transitionEnd) {
74
74
  continue;
75
75
  // Clone target if it hasn't already been
76
76
  target[key] = resolved;
77
+ if (!transitionEnd)
78
+ transitionEnd = {};
77
79
  // If the user hasn't already set this key on `transitionEnd`, set it to the unresolved
78
80
  // CSS variable. This will ensure that after the animation the component will reflect
79
81
  // changes in the value of the CSS variable.
80
- if (transitionEnd && transitionEnd[key] === undefined) {
82
+ if (transitionEnd[key] === undefined) {
81
83
  transitionEnd[key] = current;
82
84
  }
83
85
  }
@@ -46,6 +46,10 @@ function useHTMLProps(props, visualState, isStatic) {
46
46
  ? "none"
47
47
  : `pan-${props.drag === "x" ? "y" : "x"}`;
48
48
  }
49
+ if (props.tabIndex === undefined &&
50
+ (props.onTap || props.onTapStart || props.whileTap)) {
51
+ htmlProps.tabIndex = 0;
52
+ }
49
53
  htmlProps.style = style;
50
54
  return htmlProps;
51
55
  }
@@ -22,7 +22,7 @@ function updateMotionValuesFromProps(element, next, prev) {
22
22
  * and warn against mismatches.
23
23
  */
24
24
  if (process.env.NODE_ENV === "development") {
25
- warnOnce(nextValue.version === "8.5.5", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.5.5 may not work as expected.`);
25
+ warnOnce(nextValue.version === "9.0.1-css-var-fix", `Attempting to mix Framer Motion versions ${nextValue.version} with 9.0.1-css-var-fix may not work as expected.`);
26
26
  }
27
27
  }
28
28
  else if (isMotionValue(prevValue)) {
@@ -25,7 +25,7 @@ class MotionValue {
25
25
  * This will be replaced by the build step with the latest version number.
26
26
  * When MotionValues are provided to motion components, warn if versions are mixed.
27
27
  */
28
- this.version = "8.5.5";
28
+ this.version = "9.0.1-css-var-fix";
29
29
  /**
30
30
  * Duration, in milliseconds, since last updating frame.
31
31
  *
@@ -807,6 +807,10 @@
807
807
  ? "none"
808
808
  : `pan-${props.drag === "x" ? "y" : "x"}`;
809
809
  }
810
+ if (props.tabIndex === undefined &&
811
+ (props.onTap || props.onTapStart || props.whileTap)) {
812
+ htmlProps.tabIndex = 0;
813
+ }
810
814
  htmlProps.style = style;
811
815
  return htmlProps;
812
816
  }
@@ -1351,19 +1355,33 @@
1351
1355
  }, [ref, eventName, handler, options]);
1352
1356
  }
1353
1357
 
1354
- /**
1355
- *
1356
- * @param props
1357
- * @param ref
1358
- * @internal
1359
- */
1360
1358
  function useFocusGesture({ whileFocus, visualElement, }) {
1359
+ const isFocusActive = React.useRef(false);
1361
1360
  const { animationState } = visualElement;
1362
1361
  const onFocus = React.useCallback(() => {
1363
- animationState && animationState.setActive(exports.AnimationType.Focus, true);
1362
+ let isFocusVisible = false;
1363
+ /**
1364
+ * If this element doesn't match focus-visible then don't
1365
+ * apply whileHover. But, if matches throws that focus-visible
1366
+ * is not a valid selector then in that browser outline styles will be applied
1367
+ * to the element by default and we want to match that behaviour with whileFocus.
1368
+ */
1369
+ try {
1370
+ isFocusVisible = visualElement.current.matches(":focus-visible");
1371
+ }
1372
+ catch (e) {
1373
+ isFocusVisible = true;
1374
+ }
1375
+ if (!isFocusVisible || !animationState)
1376
+ return;
1377
+ animationState.setActive(exports.AnimationType.Focus, true);
1378
+ isFocusActive.current = true;
1364
1379
  }, [animationState]);
1365
1380
  const onBlur = React.useCallback(() => {
1366
- animationState && animationState.setActive(exports.AnimationType.Focus, false);
1381
+ if (!isFocusActive.current || !animationState)
1382
+ return;
1383
+ animationState.setActive(exports.AnimationType.Focus, false);
1384
+ isFocusActive.current = false;
1367
1385
  }, [animationState]);
1368
1386
  useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
1369
1387
  useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
@@ -1516,6 +1534,12 @@
1516
1534
  const combineFunctions = (a, b) => (v) => b(a(v));
1517
1535
  const pipe = (...transformers) => transformers.reduce(combineFunctions);
1518
1536
 
1537
+ function fireSyntheticPointerEvent(name, handler) {
1538
+ if (!handler)
1539
+ return;
1540
+ const syntheticPointerEvent = new PointerEvent("pointer" + name);
1541
+ handler(syntheticPointerEvent, extractEventInfo(syntheticPointerEvent));
1542
+ }
1519
1543
  /**
1520
1544
  * @param handlers -
1521
1545
  * @internal
@@ -1564,13 +1588,8 @@
1564
1588
  return;
1565
1589
  (_b = (_a = visualElement.getProps()).onTapCancel) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
1566
1590
  }
1567
- const startPress = React.useCallback((event, info) => {
1591
+ function onPointerStart(event, info) {
1568
1592
  var _a;
1569
- removePointerEndListener();
1570
- if (isPressing.current)
1571
- return;
1572
- isPressing.current = true;
1573
- cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
1574
1593
  const latestProps = visualElement.getProps();
1575
1594
  /**
1576
1595
  * Ensure we trigger animations before firing event callback
@@ -1579,8 +1598,43 @@
1579
1598
  visualElement.animationState.setActive(exports.AnimationType.Tap, true);
1580
1599
  }
1581
1600
  (_a = latestProps.onTapStart) === null || _a === void 0 ? void 0 : _a.call(latestProps, event, info);
1582
- }, [Boolean(onTapStart), visualElement]);
1601
+ }
1602
+ const callbackDependencies = [
1603
+ Boolean(onTapStart),
1604
+ Boolean(onTap),
1605
+ Boolean(whileTap),
1606
+ visualElement,
1607
+ ];
1608
+ const startPress = React.useCallback((event, info) => {
1609
+ removePointerEndListener();
1610
+ if (isPressing.current)
1611
+ return;
1612
+ isPressing.current = true;
1613
+ cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
1614
+ onPointerStart(event, info);
1615
+ }, callbackDependencies);
1583
1616
  usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
1617
+ const startAccessiblePress = React.useCallback(() => {
1618
+ const stopKeydownListener = addDomEvent(visualElement.current, "keydown", (event) => {
1619
+ if (event.key !== "Enter" || isPressing.current)
1620
+ return;
1621
+ isPressing.current = true;
1622
+ cancelPointerEndListeners.current = addDomEvent(visualElement.current, "keyup", () => {
1623
+ if (event.key !== "Enter" || !checkPointerEnd())
1624
+ return;
1625
+ fireSyntheticPointerEvent("up", visualElement.getProps().onTap);
1626
+ }, eventOptions);
1627
+ fireSyntheticPointerEvent("down", onPointerStart);
1628
+ });
1629
+ const stopBlurListener = addDomEvent(visualElement.current, "blur", () => {
1630
+ stopKeydownListener();
1631
+ stopBlurListener();
1632
+ if (isPressing.current) {
1633
+ fireSyntheticPointerEvent("cancel", onPointerCancel);
1634
+ }
1635
+ });
1636
+ }, callbackDependencies);
1637
+ useDomEvent(visualElement, "focus", hasPressListeners ? startAccessiblePress : undefined);
1584
1638
  useUnmountEffect(removePointerEndListener);
1585
1639
  }
1586
1640
 
@@ -2077,7 +2131,7 @@
2077
2131
  * This will be replaced by the build step with the latest version number.
2078
2132
  * When MotionValues are provided to motion components, warn if versions are mixed.
2079
2133
  */
2080
- this.version = "8.5.5";
2134
+ this.version = "9.0.1-css-var-fix";
2081
2135
  /**
2082
2136
  * Duration, in milliseconds, since last updating frame.
2083
2137
  *
@@ -5663,10 +5717,12 @@
5663
5717
  continue;
5664
5718
  // Clone target if it hasn't already been
5665
5719
  target[key] = resolved;
5720
+ if (!transitionEnd)
5721
+ transitionEnd = {};
5666
5722
  // If the user hasn't already set this key on `transitionEnd`, set it to the unresolved
5667
5723
  // CSS variable. This will ensure that after the animation the component will reflect
5668
5724
  // changes in the value of the CSS variable.
5669
- if (transitionEnd && transitionEnd[key] === undefined) {
5725
+ if (transitionEnd[key] === undefined) {
5670
5726
  transitionEnd[key] = current;
5671
5727
  }
5672
5728
  }
@@ -5941,7 +5997,7 @@
5941
5997
  * and warn against mismatches.
5942
5998
  */
5943
5999
  {
5944
- warnOnce(nextValue.version === "8.5.5", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.5.5 may not work as expected.`);
6000
+ warnOnce(nextValue.version === "9.0.1-css-var-fix", `Attempting to mix Framer Motion versions ${nextValue.version} with 9.0.1-css-var-fix may not work as expected.`);
5945
6001
  }
5946
6002
  }
5947
6003
  else if (isMotionValue(prevValue)) {