framer-motion 9.0.2 → 9.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/cjs/index.js +3344 -3260
  2. package/dist/es/animation/hooks/use-animated-state.mjs +3 -3
  3. package/dist/es/animation/index.mjs +3 -5
  4. package/dist/es/animation/legacy-popmotion/index.mjs +6 -2
  5. package/dist/es/animation/legacy-popmotion/inertia.mjs +4 -5
  6. package/dist/es/components/AnimatePresence/use-presence.mjs +0 -1
  7. package/dist/es/events/add-dom-event.mjs +6 -0
  8. package/dist/es/events/add-pointer-event.mjs +8 -0
  9. package/dist/es/events/use-dom-event.mjs +2 -5
  10. package/dist/es/gestures/drag/VisualElementDragControls.mjs +31 -30
  11. package/dist/es/gestures/drag/index.mjs +27 -0
  12. package/dist/es/gestures/focus.mjs +42 -0
  13. package/dist/es/gestures/hover.mjs +32 -0
  14. package/dist/es/gestures/{PanSession.mjs → pan/PanSession.mjs} +8 -8
  15. package/dist/es/gestures/pan/index.mjs +38 -0
  16. package/dist/es/gestures/press.mjs +109 -0
  17. package/dist/es/index.mjs +1 -1
  18. package/dist/es/motion/features/Feature.mjs +9 -0
  19. package/dist/es/motion/features/animation/exit.mjs +32 -0
  20. package/dist/es/motion/features/animation/index.mjs +38 -0
  21. package/dist/es/motion/features/animations.mjs +8 -37
  22. package/dist/es/motion/features/definitions.mjs +17 -23
  23. package/dist/es/motion/features/drag.mjs +12 -5
  24. package/dist/es/motion/features/gestures.mjs +16 -9
  25. package/dist/es/motion/features/layout/MeasureLayout.mjs +5 -5
  26. package/dist/es/motion/features/layout.mjs +11 -0
  27. package/dist/es/motion/features/load-features.mjs +4 -6
  28. package/dist/es/motion/features/viewport/index.mjs +96 -0
  29. package/dist/es/motion/index.mjs +12 -13
  30. package/dist/es/motion/utils/use-visual-element.mjs +8 -2
  31. package/dist/es/projection/geometry/delta-apply.mjs +9 -2
  32. package/dist/es/projection/geometry/delta-calc.mjs +2 -2
  33. package/dist/es/projection/geometry/delta-remove.mjs +2 -2
  34. package/dist/es/projection/node/DocumentProjectionNode.mjs +1 -1
  35. package/dist/es/projection/node/create-projection-node.mjs +65 -57
  36. package/dist/es/projection/node/group.mjs +5 -3
  37. package/dist/es/projection/shared/stack.mjs +7 -5
  38. package/dist/es/render/VisualElement.mjs +68 -47
  39. package/dist/es/render/dom/DOMVisualElement.mjs +1 -2
  40. package/dist/es/render/dom/features-max.mjs +2 -4
  41. package/dist/es/render/dom/motion.mjs +5 -6
  42. package/dist/es/render/dom/utils/create-config.mjs +1 -2
  43. package/dist/es/render/dom/value-types/animatable-none.mjs +3 -2
  44. package/dist/es/render/svg/SVGVisualElement.mjs +2 -2
  45. package/dist/es/render/utils/animation-state.mjs +6 -2
  46. package/dist/es/render/utils/animation.mjs +4 -4
  47. package/dist/es/render/utils/motion-values.mjs +1 -1
  48. package/dist/es/render/utils/setters.mjs +14 -9
  49. package/dist/es/value/index.mjs +1 -1
  50. package/dist/es/value/utils/is-motion-value.mjs +1 -1
  51. package/dist/framer-motion.dev.js +3341 -3257
  52. package/dist/framer-motion.js +1 -1
  53. package/dist/index.d.ts +85 -77
  54. package/dist/projection.dev.js +217 -177
  55. package/dist/size-rollup-dom-animation-assets.js +1 -1
  56. package/dist/size-rollup-dom-animation-m.js +1 -1
  57. package/dist/size-rollup-dom-animation.js +1 -1
  58. package/dist/size-rollup-dom-max-assets.js +1 -1
  59. package/dist/size-rollup-dom-max.js +1 -1
  60. package/dist/size-rollup-m.js +1 -1
  61. package/dist/size-rollup-motion.js +1 -1
  62. package/dist/size-webpack-dom-animation.js +1 -1
  63. package/dist/size-webpack-dom-max.js +1 -1
  64. package/dist/size-webpack-m.js +1 -1
  65. package/dist/three-entry.d.ts +74 -38
  66. package/package.json +8 -8
  67. package/dist/es/events/use-pointer-event.mjs +0 -11
  68. package/dist/es/gestures/drag/use-drag.mjs +0 -20
  69. package/dist/es/gestures/use-focus-gesture.mjs +0 -37
  70. package/dist/es/gestures/use-hover-gesture.mjs +0 -32
  71. package/dist/es/gestures/use-pan-gesture.mjs +0 -46
  72. package/dist/es/gestures/use-tap-gesture.mjs +0 -115
  73. package/dist/es/motion/features/layout/index.mjs +0 -7
  74. package/dist/es/motion/features/viewport/use-viewport.mjs +0 -97
  75. package/dist/es/motion/utils/VisualElementHandler.mjs +0 -19
  76. package/dist/es/motion/utils/make-renderless-component.mjs +0 -6
@@ -46,18 +46,18 @@ function useAnimatedState(initialState) {
46
46
  const [animationState, setAnimationState] = useState(initialState);
47
47
  const visualState = useVisualState({}, false);
48
48
  const element = useConstant(() => {
49
- return new StateVisualElement({ props: {}, visualState }, { initialState });
49
+ return new StateVisualElement({ props: {}, visualState, presenceContext: null }, { initialState });
50
50
  });
51
51
  useEffect(() => {
52
52
  element.mount({});
53
53
  return () => element.unmount();
54
54
  }, [element]);
55
55
  useEffect(() => {
56
- element.setProps({
56
+ element.update({
57
57
  onUpdate: (v) => {
58
58
  setAnimationState({ ...v });
59
59
  },
60
- });
60
+ }, null);
61
61
  }, [setAnimationState, element]);
62
62
  const startAnimation = useConstant(() => (animationDefinition) => {
63
63
  return animateVisualElement(element, animationDefinition);
@@ -88,14 +88,12 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
88
88
  if (options.repeatDelay) {
89
89
  options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
90
90
  }
91
- const visualElement = value.owner;
92
- const element = visualElement && visualElement.current;
93
91
  /**
94
92
  * Animate via WAAPI if possible.
95
93
  */
96
- if (visualElement &&
97
- element instanceof HTMLElement &&
98
- !(visualElement === null || visualElement === void 0 ? void 0 : visualElement.getProps().onUpdate)) {
94
+ if (value.owner &&
95
+ value.owner.current instanceof HTMLElement &&
96
+ !value.owner.getProps().onUpdate) {
99
97
  const acceleratedAnimation = createAcceleratedAnimation(value, valueName, options);
100
98
  if (acceleratedAnimation)
101
99
  return acceleratedAnimation;
@@ -29,7 +29,6 @@ const framesync = (update) => {
29
29
  };
30
30
  };
31
31
  function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes: keyframes$1, autoplay = true, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
32
- var _a, _b;
33
32
  const initialElapsed = elapsed;
34
33
  let driverControls;
35
34
  let repeatCount = 0;
@@ -41,7 +40,12 @@ function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax
41
40
  const origin = keyframes$1[0];
42
41
  const target = keyframes$1[keyframes$1.length - 1];
43
42
  let state = { done: false, value: origin };
44
- if ((_b = (_a = animator).needsInterpolation) === null || _b === void 0 ? void 0 : _b.call(_a, origin, target)) {
43
+ /**
44
+ * If this value needs interpolation (ie is non-numerical), set up an interpolator.
45
+ * TODO: Keyframes animation also performs this step. This could be removed so it only happens here.
46
+ */
47
+ const { needsInterpolation } = animator;
48
+ if (needsInterpolation && needsInterpolation(origin, target)) {
45
49
  interpolateFromNumber = interpolate([0, 100], [origin, target], {
46
50
  clamp: false,
47
51
  });
@@ -16,16 +16,15 @@ function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant
16
16
  return Math.abs(min - v) < Math.abs(max - v) ? min : max;
17
17
  }
18
18
  function startAnimation(options) {
19
- currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop();
19
+ currentAnimation && currentAnimation.stop();
20
20
  currentAnimation = animate({
21
21
  keyframes: [0, 1],
22
22
  velocity: 0,
23
23
  ...options,
24
24
  driver,
25
25
  onUpdate: (v) => {
26
- var _a;
27
- onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(v);
28
- (_a = options.onUpdate) === null || _a === void 0 ? void 0 : _a.call(options, v);
26
+ onUpdate && onUpdate(v);
27
+ options.onUpdate && options.onUpdate(v);
29
28
  },
30
29
  onComplete,
31
30
  onStop,
@@ -84,7 +83,7 @@ function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant
84
83
  });
85
84
  }
86
85
  return {
87
- stop: () => currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop(),
86
+ stop: () => currentAnimation && currentAnimation.stop(),
88
87
  };
89
88
  }
90
89
 
@@ -31,7 +31,6 @@ function usePresence() {
31
31
  const { isPresent, onExitComplete, register } = context;
32
32
  // It's safe to call the following hooks conditionally (after an early return) because the context will always
33
33
  // either be null or non-null for the lifespan of the component.
34
- // Replace with useId when released in React
35
34
  const id = useId();
36
35
  useEffect(() => register(id), []);
37
36
  const safeToRemove = () => onExitComplete && onExitComplete(id);
@@ -0,0 +1,6 @@
1
+ function addDomEvent(target, eventName, handler, options = { passive: true }) {
2
+ target.addEventListener(eventName, handler, options);
3
+ return () => target.removeEventListener(eventName, handler);
4
+ }
5
+
6
+ export { addDomEvent };
@@ -0,0 +1,8 @@
1
+ import { addDomEvent } from './add-dom-event.mjs';
2
+ import { addPointerInfo } from './event-info.mjs';
3
+
4
+ function addPointerEvent(target, eventName, handler, options) {
5
+ return addDomEvent(target, eventName, addPointerInfo(handler), options);
6
+ }
7
+
8
+ export { addPointerEvent };
@@ -1,9 +1,6 @@
1
1
  import { useEffect } from 'react';
2
+ import { addDomEvent } from './add-dom-event.mjs';
2
3
 
3
- function addDomEvent(target, eventName, handler, options = { passive: true }) {
4
- target.addEventListener(eventName, handler, options);
5
- return () => target.removeEventListener(eventName, handler);
6
- }
7
4
  /**
8
5
  * Attaches an event listener directly to the provided DOM element.
9
6
  *
@@ -34,4 +31,4 @@ function useDomEvent(ref, eventName, handler, options) {
34
31
  }, [ref, eventName, handler, options]);
35
32
  }
36
33
 
37
- export { addDomEvent, useDomEvent };
34
+ export { useDomEvent };
@@ -1,16 +1,16 @@
1
1
  import { invariant } from 'hey-listen';
2
- import { PanSession } from '../PanSession.mjs';
2
+ import { PanSession } from '../pan/PanSession.mjs';
3
3
  import { getGlobalLock } from './utils/lock.mjs';
4
4
  import { isRefObject } from '../../utils/is-ref-object.mjs';
5
- import { addPointerEvent } from '../../events/use-pointer-event.mjs';
6
- import { applyConstraints, calcRelativeConstraints, resolveDragElastic, rebaseAxisConstraints, calcViewportConstraints, calcOrigin, defaultElastic } from './utils/constraints.mjs';
5
+ import { addPointerEvent } from '../../events/add-pointer-event.mjs';
6
+ import { applyConstraints, calcRelativeConstraints, resolveDragElastic, calcViewportConstraints, defaultElastic, rebaseAxisConstraints, calcOrigin } from './utils/constraints.mjs';
7
7
  import { AnimationType } from '../../render/utils/types.mjs';
8
8
  import { createBox } from '../../projection/geometry/models.mjs';
9
9
  import { eachAxis } from '../../projection/utils/each-axis.mjs';
10
10
  import { measurePageBox } from '../../projection/utils/measure.mjs';
11
11
  import { extractEventInfo } from '../../events/event-info.mjs';
12
12
  import { convertBoxToBoundingBox, convertBoundingBoxToBox } from '../../projection/geometry/conversion.mjs';
13
- import { addDomEvent } from '../../events/use-dom-event.mjs';
13
+ import { addDomEvent } from '../../events/add-dom-event.mjs';
14
14
  import { calcLength } from '../../projection/geometry/delta-calc.mjs';
15
15
  import { mix } from '../../utils/mix.mjs';
16
16
  import { percent } from '../../value/types/numbers/units.mjs';
@@ -45,7 +45,8 @@ class VisualElementDragControls {
45
45
  /**
46
46
  * Don't start dragging if this component is exiting
47
47
  */
48
- if (this.visualElement.isPresent === false)
48
+ const { presenceContext } = this.visualElement;
49
+ if (presenceContext && presenceContext.isPresent === false)
49
50
  return;
50
51
  const onSessionStart = (event) => {
51
52
  // Stop any animations on both axis values immediately. This allows the user to throw and catch
@@ -56,7 +57,6 @@ class VisualElementDragControls {
56
57
  }
57
58
  };
58
59
  const onStart = (event, info) => {
59
- var _a;
60
60
  // Attempt to grab the global drag gesture lock - maybe make this part of PanSession
61
61
  const { drag, dragPropagation, onDragStart } = this.getProps();
62
62
  if (drag && !dragPropagation) {
@@ -78,23 +78,26 @@ class VisualElementDragControls {
78
78
  * Record gesture origin
79
79
  */
80
80
  eachAxis((axis) => {
81
- var _a, _b;
82
81
  let current = this.getAxisMotionValue(axis).get() || 0;
83
82
  /**
84
83
  * If the MotionValue is a percentage value convert to px
85
84
  */
86
85
  if (percent.test(current)) {
87
- const measuredAxis = (_b = (_a = this.visualElement.projection) === null || _a === void 0 ? void 0 : _a.layout) === null || _b === void 0 ? void 0 : _b.layoutBox[axis];
88
- if (measuredAxis) {
89
- const length = calcLength(measuredAxis);
90
- current = length * (parseFloat(current) / 100);
86
+ const { projection } = this.visualElement;
87
+ if (projection && projection.layout) {
88
+ const measuredAxis = projection.layout.layoutBox[axis];
89
+ if (measuredAxis) {
90
+ const length = calcLength(measuredAxis);
91
+ current = length * (parseFloat(current) / 100);
92
+ }
91
93
  }
92
94
  }
93
95
  this.originPoint[axis] = current;
94
96
  });
95
97
  // Fire onDragStart event
96
- onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(event, info);
97
- (_a = this.visualElement.animationState) === null || _a === void 0 ? void 0 : _a.setActive(AnimationType.Drag, true);
98
+ onDragStart && onDragStart(event, info);
99
+ const { animationState } = this.visualElement;
100
+ animationState && animationState.setActive(AnimationType.Drag, true);
98
101
  };
99
102
  const onMove = (event, info) => {
100
103
  // latestPointerEvent = event
@@ -108,7 +111,7 @@ class VisualElementDragControls {
108
111
  this.currentDirection = getCurrentDirection(offset);
109
112
  // If we've successfully set a direction, notify listener
110
113
  if (this.currentDirection !== null) {
111
- onDirectionLock === null || onDirectionLock === void 0 ? void 0 : onDirectionLock(this.currentDirection);
114
+ onDirectionLock && onDirectionLock(this.currentDirection);
112
115
  }
113
116
  return;
114
117
  }
@@ -126,7 +129,7 @@ class VisualElementDragControls {
126
129
  * This must fire after the render call as it might trigger a state
127
130
  * change which itself might trigger a layout update.
128
131
  */
129
- onDrag === null || onDrag === void 0 ? void 0 : onDrag(event, info);
132
+ onDrag && onDrag(event, info);
130
133
  };
131
134
  const onSessionEnd = (event, info) => this.stop(event, info);
132
135
  this.panSession = new PanSession(originEvent, {
@@ -144,22 +147,22 @@ class VisualElementDragControls {
144
147
  const { velocity } = info;
145
148
  this.startAnimation(velocity);
146
149
  const { onDragEnd } = this.getProps();
147
- onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(event, info);
150
+ onDragEnd && onDragEnd(event, info);
148
151
  }
149
152
  cancel() {
150
- var _a, _b;
151
153
  this.isDragging = false;
152
- if (this.visualElement.projection) {
153
- this.visualElement.projection.isAnimationBlocked = false;
154
+ const { projection, animationState } = this.visualElement;
155
+ if (projection) {
156
+ projection.isAnimationBlocked = false;
154
157
  }
155
- (_a = this.panSession) === null || _a === void 0 ? void 0 : _a.end();
158
+ this.panSession && this.panSession.end();
156
159
  this.panSession = undefined;
157
160
  const { dragPropagation } = this.getProps();
158
161
  if (!dragPropagation && this.openGlobalLock) {
159
162
  this.openGlobalLock();
160
163
  this.openGlobalLock = null;
161
164
  }
162
- (_b = this.visualElement.animationState) === null || _b === void 0 ? void 0 : _b.setActive(AnimationType.Drag, false);
165
+ animationState && animationState.setActive(AnimationType.Drag, false);
163
166
  }
164
167
  updateAxis(axis, _point, offset) {
165
168
  const { drag } = this.getProps();
@@ -239,7 +242,7 @@ class VisualElementDragControls {
239
242
  if (!shouldDrag(axis, drag, this.currentDirection)) {
240
243
  return;
241
244
  }
242
- let transition = (constraints === null || constraints === void 0 ? void 0 : constraints[axis]) || {};
245
+ let transition = (constraints && constraints[axis]) || {};
243
246
  if (dragSnapToOrigin)
244
247
  transition = { min: 0, max: 0 };
245
248
  /**
@@ -283,12 +286,12 @@ class VisualElementDragControls {
283
286
  * - Otherwise, we apply the delta to the x/y motion values.
284
287
  */
285
288
  getAxisMotionValue(axis) {
286
- var _a;
287
289
  const dragKey = "_drag" + axis.toUpperCase();
288
- const externalMotionValue = this.visualElement.getProps()[dragKey];
290
+ const props = this.visualElement.getProps();
291
+ const externalMotionValue = props[dragKey];
289
292
  return externalMotionValue
290
293
  ? externalMotionValue
291
- : this.visualElement.getValue(axis, ((_a = this.visualElement.getProps().initial) === null || _a === void 0 ? void 0 : _a[axis]) || 0);
294
+ : this.visualElement.getValue(axis, (props.initial ? props.initial[axis] : undefined) || 0);
292
295
  }
293
296
  snapToCursor(point) {
294
297
  eachAxis((axis) => {
@@ -310,7 +313,6 @@ class VisualElementDragControls {
310
313
  * relative to where it was before the resize.
311
314
  */
312
315
  scalePositionWithinConstraints() {
313
- var _a;
314
316
  if (!this.visualElement.current)
315
317
  return;
316
318
  const { drag, dragConstraints } = this.getProps();
@@ -341,7 +343,7 @@ class VisualElementDragControls {
341
343
  this.visualElement.current.style.transform = transformTemplate
342
344
  ? transformTemplate({}, "")
343
345
  : "none";
344
- (_a = projection.root) === null || _a === void 0 ? void 0 : _a.updateScroll();
346
+ projection.root && projection.root.updateScroll();
345
347
  projection.updateLayout();
346
348
  this.resolveConstraints();
347
349
  /**
@@ -360,7 +362,6 @@ class VisualElementDragControls {
360
362
  });
361
363
  }
362
364
  addListeners() {
363
- var _a;
364
365
  if (!this.visualElement.current)
365
366
  return;
366
367
  elementDragControls.set(this.visualElement, this);
@@ -381,7 +382,7 @@ class VisualElementDragControls {
381
382
  const { projection } = this.visualElement;
382
383
  const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
383
384
  if (projection && !projection.layout) {
384
- (_a = projection.root) === null || _a === void 0 ? void 0 : _a.updateScroll();
385
+ projection.root && projection.root.updateScroll();
385
386
  projection.updateLayout();
386
387
  }
387
388
  measureDragConstraints();
@@ -410,7 +411,7 @@ class VisualElementDragControls {
410
411
  stopResizeListener();
411
412
  stopPointerListener();
412
413
  stopMeasureLayoutListener();
413
- stopLayoutUpdateListener === null || stopLayoutUpdateListener === void 0 ? void 0 : stopLayoutUpdateListener();
414
+ stopLayoutUpdateListener && stopLayoutUpdateListener();
414
415
  };
415
416
  }
416
417
  getProps() {
@@ -0,0 +1,27 @@
1
+ import { Feature } from '../../motion/features/Feature.mjs';
2
+ import { noop } from '../../utils/noop.mjs';
3
+ import { VisualElementDragControls } from './VisualElementDragControls.mjs';
4
+
5
+ class DragGesture extends Feature {
6
+ constructor(node) {
7
+ super(node);
8
+ this.removeGroupControls = noop;
9
+ this.removeListeners = noop;
10
+ this.controls = new VisualElementDragControls(node);
11
+ }
12
+ mount() {
13
+ // If we've been provided a DragControls for manual control over the drag gesture,
14
+ // subscribe this component to it on mount.
15
+ const { dragControls } = this.node.getProps();
16
+ if (dragControls) {
17
+ this.removeGroupControls = dragControls.subscribe(this.controls);
18
+ }
19
+ this.removeListeners = this.controls.addListeners() || noop;
20
+ }
21
+ unmount() {
22
+ this.removeGroupControls();
23
+ this.removeListeners();
24
+ }
25
+ }
26
+
27
+ export { DragGesture };
@@ -0,0 +1,42 @@
1
+ import { addDomEvent } from '../events/add-dom-event.mjs';
2
+ import { Feature } from '../motion/features/Feature.mjs';
3
+ import { AnimationType } from '../render/utils/types.mjs';
4
+ import { pipe } from '../utils/pipe.mjs';
5
+
6
+ class FocusGesture extends Feature {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.isActive = false;
10
+ }
11
+ onFocus() {
12
+ let isFocusVisible = false;
13
+ /**
14
+ * If this element doesn't match focus-visible then don't
15
+ * apply whileHover. But, if matches throws that focus-visible
16
+ * is not a valid selector then in that browser outline styles will be applied
17
+ * to the element by default and we want to match that behaviour with whileFocus.
18
+ */
19
+ try {
20
+ isFocusVisible = this.node.current.matches(":focus-visible");
21
+ }
22
+ catch (e) {
23
+ isFocusVisible = true;
24
+ }
25
+ if (!isFocusVisible || !this.node.animationState)
26
+ return;
27
+ this.node.animationState.setActive(AnimationType.Focus, true);
28
+ this.isActive = true;
29
+ }
30
+ onBlur() {
31
+ if (!this.isActive || !this.node.animationState)
32
+ return;
33
+ this.node.animationState.setActive(AnimationType.Focus, false);
34
+ this.isActive = false;
35
+ }
36
+ mount() {
37
+ this.unmount = pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur()));
38
+ }
39
+ unmount() { }
40
+ }
41
+
42
+ export { FocusGesture };
@@ -0,0 +1,32 @@
1
+ import { addPointerEvent } from '../events/add-pointer-event.mjs';
2
+ import { AnimationType } from '../render/utils/types.mjs';
3
+ import { pipe } from '../utils/pipe.mjs';
4
+ import { isDragActive } from './drag/utils/lock.mjs';
5
+ import { Feature } from '../motion/features/Feature.mjs';
6
+
7
+ function addHoverEvent(node, isActive) {
8
+ const eventName = "pointer" + (isActive ? "enter" : "leave");
9
+ const callbackName = "onHover" + (isActive ? "Start" : "End");
10
+ const handleEvent = (event, info) => {
11
+ if (event.type === "touch" || isDragActive())
12
+ return;
13
+ const props = node.getProps();
14
+ if (node.animationState && props.whileHover) {
15
+ node.animationState.setActive(AnimationType.Hover, isActive);
16
+ }
17
+ if (props[callbackName]) {
18
+ props[callbackName](event, info);
19
+ }
20
+ };
21
+ return addPointerEvent(node.current, eventName, handleEvent, {
22
+ passive: !node.getProps()[callbackName],
23
+ });
24
+ }
25
+ class HoverGesture extends Feature {
26
+ mount() {
27
+ this.unmount = pipe(addHoverEvent(this.node, true), addHoverEvent(this.node, false));
28
+ }
29
+ unmount() { }
30
+ }
31
+
32
+ export { HoverGesture };
@@ -1,11 +1,11 @@
1
- import { extractEventInfo } from '../events/event-info.mjs';
2
- import { sync, cancelSync } from '../frameloop/index.mjs';
3
- import { secondsToMilliseconds } from '../utils/time-conversion.mjs';
4
- import { addPointerEvent } from '../events/use-pointer-event.mjs';
5
- import { pipe } from '../utils/pipe.mjs';
6
- import { distance2D } from '../utils/distance.mjs';
7
- import { frameData } from '../frameloop/data.mjs';
8
- import { isPrimaryPointer } from '../events/utils/is-primary-pointer.mjs';
1
+ import { extractEventInfo } from '../../events/event-info.mjs';
2
+ import { sync, cancelSync } from '../../frameloop/index.mjs';
3
+ import { secondsToMilliseconds } from '../../utils/time-conversion.mjs';
4
+ import { addPointerEvent } from '../../events/add-pointer-event.mjs';
5
+ import { pipe } from '../../utils/pipe.mjs';
6
+ import { distance2D } from '../../utils/distance.mjs';
7
+ import { frameData } from '../../frameloop/data.mjs';
8
+ import { isPrimaryPointer } from '../../events/utils/is-primary-pointer.mjs';
9
9
 
10
10
  /**
11
11
  * @internal
@@ -0,0 +1,38 @@
1
+ import { PanSession } from './PanSession.mjs';
2
+ import { addPointerEvent } from '../../events/add-pointer-event.mjs';
3
+ import { Feature } from '../../motion/features/Feature.mjs';
4
+ import { noop } from '../../utils/noop.mjs';
5
+
6
+ class PanGesture extends Feature {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.removePointerDownListener = noop;
10
+ }
11
+ onPointerDown(pointerDownEvent) {
12
+ this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), { transformPagePoint: this.node.getTransformPagePoint() });
13
+ }
14
+ createPanHandlers() {
15
+ const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps();
16
+ return {
17
+ onSessionStart: onPanSessionStart,
18
+ onStart: onPanStart,
19
+ onMove: onPan,
20
+ onEnd: (event, info) => {
21
+ delete this.session;
22
+ onPanEnd && onPanEnd(event, info);
23
+ },
24
+ };
25
+ }
26
+ mount() {
27
+ this.removePointerDownListener = addPointerEvent(this.node.current, "pointerdown", (event) => this.onPointerDown(event));
28
+ }
29
+ update() {
30
+ this.session && this.session.updateHandlers(this.createPanHandlers());
31
+ }
32
+ unmount() {
33
+ this.removePointerDownListener();
34
+ this.session && this.session.end();
35
+ }
36
+ }
37
+
38
+ export { PanGesture };
@@ -0,0 +1,109 @@
1
+ import { extractEventInfo } from '../events/event-info.mjs';
2
+ import { addDomEvent } from '../events/add-dom-event.mjs';
3
+ import { addPointerEvent } from '../events/add-pointer-event.mjs';
4
+ import { Feature } from '../motion/features/Feature.mjs';
5
+ import { AnimationType } from '../render/utils/types.mjs';
6
+ import { pipe } from '../utils/pipe.mjs';
7
+ import { isDragActive } from './drag/utils/lock.mjs';
8
+ import { isNodeOrChild } from './utils/is-node-or-child.mjs';
9
+ import { noop } from '../utils/noop.mjs';
10
+
11
+ function fireSyntheticPointerEvent(name, handler) {
12
+ if (!handler)
13
+ return;
14
+ const syntheticPointerEvent = new PointerEvent("pointer" + name);
15
+ handler(syntheticPointerEvent, extractEventInfo(syntheticPointerEvent));
16
+ }
17
+ class PressGesture extends Feature {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.removeStartListeners = noop;
21
+ this.removeEndListeners = noop;
22
+ this.removeAccessibleListeners = noop;
23
+ this.startPointerPress = (startEvent, startInfo) => {
24
+ this.removeEndListeners();
25
+ if (this.isPressing)
26
+ return;
27
+ const props = this.node.getProps();
28
+ const endPointerPress = (endEvent, endInfo) => {
29
+ if (!this.checkPressEnd())
30
+ return;
31
+ const { onTap, onTapCancel } = this.node.getProps();
32
+ /**
33
+ * We only count this as a tap gesture if the event.target is the same
34
+ * as, or a child of, this component's element
35
+ */
36
+ !isNodeOrChild(this.node.current, endEvent.target)
37
+ ? onTapCancel && onTapCancel(endEvent, endInfo)
38
+ : onTap && onTap(endEvent, endInfo);
39
+ };
40
+ const removePointerUpListener = addPointerEvent(window, "pointerup", endPointerPress, { passive: !(props.onTap || props["onPointerUp"]) });
41
+ const removePointerCancelListener = addPointerEvent(window, "pointercancel", (cancelEvent, cancelInfo) => this.cancelPress(cancelEvent, cancelInfo), { passive: !(props.onTapCancel || props["onPointerCancel"]) });
42
+ this.removeEndListeners = pipe(removePointerUpListener, removePointerCancelListener);
43
+ this.startPress(startEvent, startInfo);
44
+ };
45
+ this.startAccessiblePress = () => {
46
+ const handleKeydown = (keydownEvent) => {
47
+ if (keydownEvent.key !== "Enter" || this.isPressing)
48
+ return;
49
+ const handleKeyup = (keyupEvent) => {
50
+ if (keyupEvent.key !== "Enter" || !this.checkPressEnd())
51
+ return;
52
+ fireSyntheticPointerEvent("up", this.node.getProps().onTap);
53
+ };
54
+ this.removeEndListeners();
55
+ this.removeEndListeners = addDomEvent(this.node.current, "keyup", handleKeyup);
56
+ fireSyntheticPointerEvent("down", (event, info) => {
57
+ this.startPress(event, info);
58
+ });
59
+ };
60
+ const removeKeydownListener = addDomEvent(this.node.current, "keydown", handleKeydown);
61
+ const handleBlur = () => {
62
+ if (!this.isPressing)
63
+ return;
64
+ fireSyntheticPointerEvent("cancel", (cancelEvent, cancelInfo) => this.cancelPress(cancelEvent, cancelInfo));
65
+ };
66
+ const removeBlurListener = addDomEvent(this.node.current, "blur", handleBlur);
67
+ this.removeAccessibleListeners = pipe(removeKeydownListener, removeBlurListener);
68
+ };
69
+ }
70
+ startPress(event, info) {
71
+ this.isPressing = true;
72
+ const { onTapStart, whileTap } = this.node.getProps();
73
+ /**
74
+ * Ensure we trigger animations before firing event callback
75
+ */
76
+ if (whileTap && this.node.animationState) {
77
+ this.node.animationState.setActive(AnimationType.Tap, true);
78
+ }
79
+ onTapStart && onTapStart(event, info);
80
+ }
81
+ checkPressEnd() {
82
+ this.removeEndListeners();
83
+ this.isPressing = false;
84
+ const props = this.node.getProps();
85
+ if (props.whileTap && this.node.animationState) {
86
+ this.node.animationState.setActive(AnimationType.Tap, false);
87
+ }
88
+ return !isDragActive();
89
+ }
90
+ cancelPress(event, info) {
91
+ if (!this.checkPressEnd())
92
+ return;
93
+ const { onTapCancel } = this.node.getProps();
94
+ onTapCancel && onTapCancel(event, info);
95
+ }
96
+ mount() {
97
+ const props = this.node.getProps();
98
+ const removePointerListener = addPointerEvent(this.node.current, "pointerdown", this.startPointerPress, { passive: !(props.onTapStart || props["onPointerStart"]) });
99
+ const removeFocusListener = addDomEvent(this.node.current, "focus", this.startAccessiblePress);
100
+ this.removeStartListeners = pipe(removePointerListener, removeFocusListener);
101
+ }
102
+ unmount() {
103
+ this.removeStartListeners();
104
+ this.removeEndListeners();
105
+ this.removeAccessibleListeners();
106
+ }
107
+ }
108
+
109
+ export { PressGesture };
package/dist/es/index.mjs CHANGED
@@ -77,7 +77,7 @@ export { calcLength } from './projection/geometry/delta-calc.mjs';
77
77
  export { filterProps } from './render/dom/utils/filter-props.mjs';
78
78
  export { makeUseVisualState } from './motion/utils/use-visual-state.mjs';
79
79
  export { isDragActive } from './gestures/drag/utils/lock.mjs';
80
- export { addPointerEvent } from './events/use-pointer-event.mjs';
80
+ export { addPointerEvent } from './events/add-pointer-event.mjs';
81
81
  export { addPointerInfo } from './events/event-info.mjs';
82
82
  export { isMotionValue } from './value/utils/is-motion-value.mjs';
83
83
  export { isBrowser } from './utils/is-browser.mjs';
@@ -0,0 +1,9 @@
1
+ class Feature {
2
+ constructor(node) {
3
+ this.isMounted = false;
4
+ this.node = node;
5
+ }
6
+ update() { }
7
+ }
8
+
9
+ export { Feature };
@@ -0,0 +1,32 @@
1
+ import { AnimationType } from '../../../render/utils/types.mjs';
2
+ import { Feature } from '../Feature.mjs';
3
+
4
+ let id = 0;
5
+ class ExitAnimationFeature extends Feature {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.id = id++;
9
+ }
10
+ update() {
11
+ if (!this.node.presenceContext)
12
+ return;
13
+ const { isPresent, onExitComplete, custom } = this.node.presenceContext;
14
+ const { isPresent: prevIsPresent } = this.node.prevPresenceContext || {};
15
+ if (!this.node.animationState || isPresent === prevIsPresent) {
16
+ return;
17
+ }
18
+ const exitAnimation = this.node.animationState.setActive(AnimationType.Exit, !isPresent, { custom: custom !== null && custom !== void 0 ? custom : this.node.getProps().custom });
19
+ if (onExitComplete && !isPresent) {
20
+ exitAnimation.then(() => onExitComplete(this.id));
21
+ }
22
+ }
23
+ mount() {
24
+ const { register } = this.node.presenceContext || {};
25
+ if (register) {
26
+ this.unmount = register(this.id);
27
+ }
28
+ }
29
+ unmount() { }
30
+ }
31
+
32
+ export { ExitAnimationFeature };