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.
- package/dist/cjs/index.js +3344 -3260
- package/dist/es/animation/hooks/use-animated-state.mjs +3 -3
- package/dist/es/animation/index.mjs +3 -5
- package/dist/es/animation/legacy-popmotion/index.mjs +6 -2
- package/dist/es/animation/legacy-popmotion/inertia.mjs +4 -5
- package/dist/es/components/AnimatePresence/use-presence.mjs +0 -1
- package/dist/es/events/add-dom-event.mjs +6 -0
- package/dist/es/events/add-pointer-event.mjs +8 -0
- package/dist/es/events/use-dom-event.mjs +2 -5
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +31 -30
- package/dist/es/gestures/drag/index.mjs +27 -0
- package/dist/es/gestures/focus.mjs +42 -0
- package/dist/es/gestures/hover.mjs +32 -0
- package/dist/es/gestures/{PanSession.mjs → pan/PanSession.mjs} +8 -8
- package/dist/es/gestures/pan/index.mjs +38 -0
- package/dist/es/gestures/press.mjs +109 -0
- package/dist/es/index.mjs +1 -1
- package/dist/es/motion/features/Feature.mjs +9 -0
- package/dist/es/motion/features/animation/exit.mjs +32 -0
- package/dist/es/motion/features/animation/index.mjs +38 -0
- package/dist/es/motion/features/animations.mjs +8 -37
- package/dist/es/motion/features/definitions.mjs +17 -23
- package/dist/es/motion/features/drag.mjs +12 -5
- package/dist/es/motion/features/gestures.mjs +16 -9
- package/dist/es/motion/features/layout/MeasureLayout.mjs +5 -5
- package/dist/es/motion/features/layout.mjs +11 -0
- package/dist/es/motion/features/load-features.mjs +4 -6
- package/dist/es/motion/features/viewport/index.mjs +96 -0
- package/dist/es/motion/index.mjs +12 -13
- package/dist/es/motion/utils/use-visual-element.mjs +8 -2
- package/dist/es/projection/geometry/delta-apply.mjs +9 -2
- package/dist/es/projection/geometry/delta-calc.mjs +2 -2
- package/dist/es/projection/geometry/delta-remove.mjs +2 -2
- package/dist/es/projection/node/DocumentProjectionNode.mjs +1 -1
- package/dist/es/projection/node/create-projection-node.mjs +65 -57
- package/dist/es/projection/node/group.mjs +5 -3
- package/dist/es/projection/shared/stack.mjs +7 -5
- package/dist/es/render/VisualElement.mjs +68 -47
- package/dist/es/render/dom/DOMVisualElement.mjs +1 -2
- package/dist/es/render/dom/features-max.mjs +2 -4
- package/dist/es/render/dom/motion.mjs +5 -6
- package/dist/es/render/dom/utils/create-config.mjs +1 -2
- package/dist/es/render/dom/value-types/animatable-none.mjs +3 -2
- package/dist/es/render/svg/SVGVisualElement.mjs +2 -2
- package/dist/es/render/utils/animation-state.mjs +6 -2
- package/dist/es/render/utils/animation.mjs +4 -4
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/render/utils/setters.mjs +14 -9
- package/dist/es/value/index.mjs +1 -1
- package/dist/es/value/utils/is-motion-value.mjs +1 -1
- package/dist/framer-motion.dev.js +3341 -3257
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +85 -77
- package/dist/projection.dev.js +217 -177
- package/dist/size-rollup-dom-animation-assets.js +1 -1
- package/dist/size-rollup-dom-animation-m.js +1 -1
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max-assets.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-m.js +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-webpack-dom-animation.js +1 -1
- package/dist/size-webpack-dom-max.js +1 -1
- package/dist/size-webpack-m.js +1 -1
- package/dist/three-entry.d.ts +74 -38
- package/package.json +8 -8
- package/dist/es/events/use-pointer-event.mjs +0 -11
- package/dist/es/gestures/drag/use-drag.mjs +0 -20
- package/dist/es/gestures/use-focus-gesture.mjs +0 -37
- package/dist/es/gestures/use-hover-gesture.mjs +0 -32
- package/dist/es/gestures/use-pan-gesture.mjs +0 -46
- package/dist/es/gestures/use-tap-gesture.mjs +0 -115
- package/dist/es/motion/features/layout/index.mjs +0 -7
- package/dist/es/motion/features/viewport/use-viewport.mjs +0 -97
- package/dist/es/motion/utils/VisualElementHandler.mjs +0 -19
- 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.
|
|
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 (
|
|
97
|
-
|
|
98
|
-
!
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
27
|
-
onUpdate
|
|
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
|
|
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,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 {
|
|
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/
|
|
6
|
-
import { applyConstraints, calcRelativeConstraints, resolveDragElastic,
|
|
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/
|
|
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
|
-
|
|
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
|
|
88
|
-
if (
|
|
89
|
-
const
|
|
90
|
-
|
|
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
|
|
97
|
-
|
|
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
|
|
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
|
|
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
|
|
150
|
+
onDragEnd && onDragEnd(event, info);
|
|
148
151
|
}
|
|
149
152
|
cancel() {
|
|
150
|
-
var _a, _b;
|
|
151
153
|
this.isDragging = false;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
const { projection, animationState } = this.visualElement;
|
|
155
|
+
if (projection) {
|
|
156
|
+
projection.isAnimationBlocked = false;
|
|
154
157
|
}
|
|
155
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
290
|
+
const props = this.visualElement.getProps();
|
|
291
|
+
const externalMotionValue = props[dragKey];
|
|
289
292
|
return externalMotionValue
|
|
290
293
|
? externalMotionValue
|
|
291
|
-
: this.visualElement.getValue(axis, (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 '
|
|
2
|
-
import { sync, cancelSync } from '
|
|
3
|
-
import { secondsToMilliseconds } from '
|
|
4
|
-
import { addPointerEvent } from '
|
|
5
|
-
import { pipe } from '
|
|
6
|
-
import { distance2D } from '
|
|
7
|
-
import { frameData } from '
|
|
8
|
-
import { isPrimaryPointer } from '
|
|
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/
|
|
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,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 };
|