motion 12.4.13 → 12.5.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.
- package/dist/cjs/debug.js +12 -12
- package/dist/cjs/index.js +1277 -1302
- package/dist/cjs/mini.js +7 -7
- package/dist/cjs/react-client.js +535 -540
- package/dist/cjs/react-m.js +20 -19
- package/dist/cjs/react-mini.js +1 -1
- package/dist/es/framer-motion/dist/es/animation/animate/single-value.mjs +2 -1
- package/dist/es/framer-motion/dist/es/animation/animators/BaseAnimation.mjs +2 -1
- package/dist/es/framer-motion/dist/es/animation/animators/MainThreadAnimation.mjs +1 -1
- package/dist/es/framer-motion/dist/es/animation/animators/drivers/driver-frameloop.mjs +3 -2
- package/dist/es/framer-motion/dist/es/animation/animators/waapi/index.mjs +2 -2
- package/dist/es/framer-motion/dist/es/animation/generators/utils/velocity.mjs +2 -1
- package/dist/es/framer-motion/dist/es/animation/interfaces/motion-value.mjs +2 -2
- package/dist/es/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs +1 -1
- package/dist/es/framer-motion/dist/es/animation/optimized-appear/start.mjs +4 -4
- package/dist/es/framer-motion/dist/es/animation/sequence/utils/edit.mjs +2 -1
- package/dist/es/framer-motion/dist/es/components/AnimatePresence/index.mjs +1 -1
- package/dist/es/framer-motion/dist/es/components/Reorder/utils/check-reorder.mjs +2 -1
- package/dist/es/framer-motion/dist/es/gestures/drag/VisualElementDragControls.mjs +1 -1
- package/dist/es/framer-motion/dist/es/gestures/hover.mjs +1 -1
- package/dist/es/framer-motion/dist/es/gestures/pan/PanSession.mjs +1 -1
- package/dist/es/framer-motion/dist/es/gestures/pan/index.mjs +1 -1
- package/dist/es/framer-motion/dist/es/gestures/press.mjs +1 -1
- package/dist/es/framer-motion/dist/es/motion/features/layout/MeasureLayout.mjs +3 -2
- package/dist/es/framer-motion/dist/es/motion/utils/use-visual-element.mjs +7 -6
- package/dist/es/framer-motion/dist/es/projection/node/create-projection-node.mjs +6 -6
- package/dist/es/framer-motion/dist/es/projection/shared/stack.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/VisualElement.mjs +6 -5
- package/dist/es/framer-motion/dist/es/render/components/create-proxy.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/dom/DOMVisualElement.mjs +1 -1
- package/dist/es/framer-motion/dist/es/render/dom/scroll/info.mjs +1 -1
- package/dist/es/framer-motion/dist/es/render/dom/scroll/observe.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/dom/scroll/on-scroll-handler.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/dom/scroll/track.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/svg/SVGVisualElement.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/svg/config-motion.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/utils/KeyframesResolver.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/utils/flat-tree.mjs +2 -1
- package/dist/es/framer-motion/dist/es/render/utils/motion-values.mjs +4 -3
- package/dist/es/framer-motion/dist/es/render/utils/setters.mjs +2 -1
- package/dist/es/framer-motion/dist/es/utils/delay.mjs +2 -2
- package/dist/es/framer-motion/dist/es/utils/reduced-motion/use-reduced-motion.mjs +2 -1
- package/dist/es/framer-motion/dist/es/utils/use-animation-frame.mjs +2 -1
- package/dist/es/framer-motion/dist/es/utils/use-force-update.mjs +2 -1
- package/dist/es/framer-motion/dist/es/utils/use-instant-transition.mjs +2 -1
- package/dist/es/framer-motion/dist/es/value/scroll/use-element-scroll.mjs +2 -1
- package/dist/es/framer-motion/dist/es/value/scroll/use-viewport-scroll.mjs +2 -1
- package/dist/es/framer-motion/dist/es/value/use-combine-values.mjs +3 -2
- package/dist/es/framer-motion/dist/es/value/use-computed.mjs +2 -1
- package/dist/es/framer-motion/dist/es/value/use-inverted-scale.mjs +3 -3
- package/dist/es/framer-motion/dist/es/value/use-motion-value.mjs +2 -1
- package/dist/es/framer-motion/dist/es/value/use-scroll.mjs +4 -4
- package/dist/es/framer-motion/dist/es/value/use-spring.mjs +1 -1
- package/dist/es/framer-motion/dist/es/value/use-transform.mjs +1 -1
- package/dist/es/framer-motion/dist/es/value/use-velocity.mjs +2 -1
- package/dist/es/framer-motion/dist/es/value/use-will-change/WillChangeMotionValue.mjs +3 -2
- package/dist/es/motion/lib/debug.mjs +1 -1
- package/dist/es/motion/lib/index.mjs +3 -4
- package/dist/es/motion/lib/react.mjs +19 -20
- package/dist/es/{framer-motion → motion-dom}/dist/es/frameloop/batcher.mjs +2 -1
- package/dist/es/{framer-motion → motion-dom}/dist/es/frameloop/frame.mjs +1 -1
- package/dist/es/motion-dom/dist/es/frameloop/microtask.mjs +6 -0
- package/dist/es/{framer-motion → motion-dom}/dist/es/frameloop/sync-time.mjs +2 -1
- package/dist/es/motion-dom/dist/es/utils/supports/scroll-timeline.mjs +1 -1
- package/dist/es/{framer-motion → motion-dom}/dist/es/value/index.mjs +6 -11
- package/dist/motion.dev.js +1277 -1302
- package/dist/motion.js +1 -1
- package/package.json +3 -3
- package/dist/es/framer-motion/dist/es/frameloop/index-legacy.mjs +0 -20
- package/dist/es/framer-motion/dist/es/frameloop/microtask.mjs +0 -5
- package/dist/es/{framer-motion → motion-dom}/dist/es/frameloop/order.mjs +0 -0
- package/dist/es/{framer-motion → motion-dom}/dist/es/frameloop/render-step.mjs +0 -0
- package/dist/es/{framer-motion → motion-dom}/dist/es/stats/animation-count.mjs +0 -0
- package/dist/es/{framer-motion → motion-dom}/dist/es/stats/buffer.mjs +0 -0
- package/dist/es/{framer-motion → motion-dom}/dist/es/stats/index.mjs +1 -1
- /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/array.mjs +0 -0
- /package/dist/es/{framer-motion/dist/es/utils/GlobalConfig.mjs → motion-utils/dist/es/global-config.mjs} +0 -0
- /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/subscription-manager.mjs +0 -0
- /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/velocity-per-second.mjs +0 -0
- /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/warn-once.mjs +0 -0
package/dist/cjs/react-client.js
CHANGED
|
@@ -51,6 +51,16 @@ function resolveVariant(visualElement, definition, custom) {
|
|
|
51
51
|
return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function addUniqueItem(arr, item) {
|
|
55
|
+
if (arr.indexOf(item) === -1)
|
|
56
|
+
arr.push(item);
|
|
57
|
+
}
|
|
58
|
+
function removeItem(arr, item) {
|
|
59
|
+
const index = arr.indexOf(item);
|
|
60
|
+
if (index > -1)
|
|
61
|
+
arr.splice(index, 1);
|
|
62
|
+
}
|
|
63
|
+
|
|
54
64
|
/*#__NO_SIDE_EFFECTS__*/
|
|
55
65
|
const noop = (any) => any;
|
|
56
66
|
|
|
@@ -69,6 +79,11 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
69
79
|
};
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
const MotionGlobalConfig = {
|
|
83
|
+
skipAnimations: false,
|
|
84
|
+
useManualTiming: false,
|
|
85
|
+
};
|
|
86
|
+
|
|
72
87
|
/*#__NO_SIDE_EFFECTS__*/
|
|
73
88
|
function memo(callback) {
|
|
74
89
|
let result;
|
|
@@ -97,6 +112,43 @@ const progress = (from, to, value) => {
|
|
|
97
112
|
return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
|
|
98
113
|
};
|
|
99
114
|
|
|
115
|
+
class SubscriptionManager {
|
|
116
|
+
constructor() {
|
|
117
|
+
this.subscriptions = [];
|
|
118
|
+
}
|
|
119
|
+
add(handler) {
|
|
120
|
+
addUniqueItem(this.subscriptions, handler);
|
|
121
|
+
return () => removeItem(this.subscriptions, handler);
|
|
122
|
+
}
|
|
123
|
+
notify(a, b, c) {
|
|
124
|
+
const numSubscriptions = this.subscriptions.length;
|
|
125
|
+
if (!numSubscriptions)
|
|
126
|
+
return;
|
|
127
|
+
if (numSubscriptions === 1) {
|
|
128
|
+
/**
|
|
129
|
+
* If there's only a single handler we can just call it without invoking a loop.
|
|
130
|
+
*/
|
|
131
|
+
this.subscriptions[0](a, b, c);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
for (let i = 0; i < numSubscriptions; i++) {
|
|
135
|
+
/**
|
|
136
|
+
* Check whether the handler exists before firing as it's possible
|
|
137
|
+
* the subscriptions were modified during this loop running.
|
|
138
|
+
*/
|
|
139
|
+
const handler = this.subscriptions[i];
|
|
140
|
+
handler && handler(a, b, c);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
getSize() {
|
|
145
|
+
return this.subscriptions.length;
|
|
146
|
+
}
|
|
147
|
+
clear() {
|
|
148
|
+
this.subscriptions.length = 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
100
152
|
/**
|
|
101
153
|
* Converts seconds to milliseconds
|
|
102
154
|
*
|
|
@@ -108,7 +160,27 @@ const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
|
108
160
|
/*#__NO_SIDE_EFFECTS__*/
|
|
109
161
|
const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
|
|
110
162
|
|
|
111
|
-
|
|
163
|
+
/*
|
|
164
|
+
Convert velocity into velocity per second
|
|
165
|
+
|
|
166
|
+
@param [number]: Unit per frame
|
|
167
|
+
@param [number]: Frame duration in ms
|
|
168
|
+
*/
|
|
169
|
+
function velocityPerSecond(velocity, frameDuration) {
|
|
170
|
+
return frameDuration ? velocity * (1000 / frameDuration) : 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const warned = new Set();
|
|
174
|
+
function warnOnce(condition, message, element) {
|
|
175
|
+
if (condition || warned.has(message))
|
|
176
|
+
return;
|
|
177
|
+
console.warn(message);
|
|
178
|
+
if (element)
|
|
179
|
+
console.warn(element);
|
|
180
|
+
warned.add(message);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
|
|
112
184
|
|
|
113
185
|
class BaseGroupPlaybackControls {
|
|
114
186
|
constructor(animations) {
|
|
@@ -309,454 +381,107 @@ function mapEasingToNativeEasing(easing, duration) {
|
|
|
309
381
|
}
|
|
310
382
|
}
|
|
311
383
|
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
384
|
+
const stepsOrder = [
|
|
385
|
+
"read", // Read
|
|
386
|
+
"resolveKeyframes", // Write/Read/Write/Read
|
|
387
|
+
"update", // Compute
|
|
388
|
+
"preRender", // Compute
|
|
389
|
+
"render", // Write
|
|
390
|
+
"postRender", // Compute
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
const statsBuffer = {
|
|
394
|
+
value: null,
|
|
395
|
+
addProjectionMetrics: null,
|
|
315
396
|
};
|
|
316
|
-
function isDragActive() {
|
|
317
|
-
return isDragging.x || isDragging.y;
|
|
318
|
-
}
|
|
319
397
|
|
|
320
|
-
function
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
398
|
+
function createRenderStep(runNextFrame, stepName) {
|
|
399
|
+
/**
|
|
400
|
+
* We create and reuse two queues, one to queue jobs for the current frame
|
|
401
|
+
* and one for the next. We reuse to avoid triggering GC after x frames.
|
|
402
|
+
*/
|
|
403
|
+
let thisFrame = new Set();
|
|
404
|
+
let nextFrame = new Set();
|
|
405
|
+
/**
|
|
406
|
+
* Track whether we're currently processing jobs in this step. This way
|
|
407
|
+
* we can decide whether to schedule new jobs for this frame or next.
|
|
408
|
+
*/
|
|
409
|
+
let isProcessing = false;
|
|
410
|
+
let flushNextFrame = false;
|
|
411
|
+
/**
|
|
412
|
+
* A set of processes which were marked keepAlive when scheduled.
|
|
413
|
+
*/
|
|
414
|
+
const toKeepAlive = new WeakSet();
|
|
415
|
+
let latestFrameData = {
|
|
416
|
+
delta: 0.0,
|
|
417
|
+
timestamp: 0.0,
|
|
418
|
+
isProcessing: false,
|
|
419
|
+
};
|
|
420
|
+
let numCalls = 0;
|
|
421
|
+
function triggerCallback(callback) {
|
|
422
|
+
if (toKeepAlive.has(callback)) {
|
|
423
|
+
step.schedule(callback);
|
|
424
|
+
runNextFrame();
|
|
334
425
|
}
|
|
335
|
-
|
|
336
|
-
|
|
426
|
+
numCalls++;
|
|
427
|
+
callback(latestFrameData);
|
|
337
428
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (typeof onHoverEnd !== "function" || !target)
|
|
371
|
-
return;
|
|
372
|
-
const onPointerLeave = (leaveEvent) => {
|
|
373
|
-
if (!isValidHover(leaveEvent))
|
|
429
|
+
const step = {
|
|
430
|
+
/**
|
|
431
|
+
* Schedule a process to run on the next frame.
|
|
432
|
+
*/
|
|
433
|
+
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
434
|
+
const addToCurrentFrame = immediate && isProcessing;
|
|
435
|
+
const queue = addToCurrentFrame ? thisFrame : nextFrame;
|
|
436
|
+
if (keepAlive)
|
|
437
|
+
toKeepAlive.add(callback);
|
|
438
|
+
if (!queue.has(callback))
|
|
439
|
+
queue.add(callback);
|
|
440
|
+
return callback;
|
|
441
|
+
},
|
|
442
|
+
/**
|
|
443
|
+
* Cancel the provided callback from running on the next frame.
|
|
444
|
+
*/
|
|
445
|
+
cancel: (callback) => {
|
|
446
|
+
nextFrame.delete(callback);
|
|
447
|
+
toKeepAlive.delete(callback);
|
|
448
|
+
},
|
|
449
|
+
/**
|
|
450
|
+
* Execute all schedule callbacks.
|
|
451
|
+
*/
|
|
452
|
+
process: (frameData) => {
|
|
453
|
+
latestFrameData = frameData;
|
|
454
|
+
/**
|
|
455
|
+
* If we're already processing we've probably been triggered by a flushSync
|
|
456
|
+
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
457
|
+
* as true and ensure we flush the following frame at the end of this one.
|
|
458
|
+
*/
|
|
459
|
+
if (isProcessing) {
|
|
460
|
+
flushNextFrame = true;
|
|
374
461
|
return;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
462
|
+
}
|
|
463
|
+
isProcessing = true;
|
|
464
|
+
[thisFrame, nextFrame] = [nextFrame, thisFrame];
|
|
465
|
+
// Execute this frame
|
|
466
|
+
thisFrame.forEach(triggerCallback);
|
|
467
|
+
/**
|
|
468
|
+
* If we're recording stats then
|
|
469
|
+
*/
|
|
470
|
+
if (stepName && statsBuffer.value) {
|
|
471
|
+
statsBuffer.value.frameloop[stepName].push(numCalls);
|
|
472
|
+
}
|
|
473
|
+
numCalls = 0;
|
|
474
|
+
// Clear the frame so no callbacks remain. This is to avoid
|
|
475
|
+
// memory leaks should this render step not run for a while.
|
|
476
|
+
thisFrame.clear();
|
|
477
|
+
isProcessing = false;
|
|
478
|
+
if (flushNextFrame) {
|
|
479
|
+
flushNextFrame = false;
|
|
480
|
+
step.process(frameData);
|
|
481
|
+
}
|
|
482
|
+
},
|
|
379
483
|
};
|
|
380
|
-
|
|
381
|
-
element.addEventListener("pointerenter", onPointerEnter, eventOptions);
|
|
382
|
-
});
|
|
383
|
-
return cancel;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function capturePointer(event, action) {
|
|
387
|
-
const actionName = `${action}PointerCapture`;
|
|
388
|
-
if (event.target instanceof Element &&
|
|
389
|
-
actionName in event.target &&
|
|
390
|
-
event.pointerId !== undefined) {
|
|
391
|
-
try {
|
|
392
|
-
event.target[actionName](event.pointerId);
|
|
393
|
-
}
|
|
394
|
-
catch (e) { }
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Recursively traverse up the tree to check whether the provided child node
|
|
400
|
-
* is the parent or a descendant of it.
|
|
401
|
-
*
|
|
402
|
-
* @param parent - Element to find
|
|
403
|
-
* @param child - Element to test against parent
|
|
404
|
-
*/
|
|
405
|
-
const isNodeOrChild = (parent, child) => {
|
|
406
|
-
if (!child) {
|
|
407
|
-
return false;
|
|
408
|
-
}
|
|
409
|
-
else if (parent === child) {
|
|
410
|
-
return true;
|
|
411
|
-
}
|
|
412
|
-
else {
|
|
413
|
-
return isNodeOrChild(parent, child.parentElement);
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
const isPrimaryPointer = (event) => {
|
|
418
|
-
if (event.pointerType === "mouse") {
|
|
419
|
-
return typeof event.button !== "number" || event.button <= 0;
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
/**
|
|
423
|
-
* isPrimary is true for all mice buttons, whereas every touch point
|
|
424
|
-
* is regarded as its own input. So subsequent concurrent touch points
|
|
425
|
-
* will be false.
|
|
426
|
-
*
|
|
427
|
-
* Specifically match against false here as incomplete versions of
|
|
428
|
-
* PointerEvents in very old browser might have it set as undefined.
|
|
429
|
-
*/
|
|
430
|
-
return event.isPrimary !== false;
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const focusableElements = new Set([
|
|
435
|
-
"BUTTON",
|
|
436
|
-
"INPUT",
|
|
437
|
-
"SELECT",
|
|
438
|
-
"TEXTAREA",
|
|
439
|
-
"A",
|
|
440
|
-
]);
|
|
441
|
-
function isElementKeyboardAccessible(element) {
|
|
442
|
-
return (focusableElements.has(element.tagName) ||
|
|
443
|
-
element.tabIndex !== -1);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const isPressing = new WeakSet();
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Filter out events that are not "Enter" keys.
|
|
450
|
-
*/
|
|
451
|
-
function filterEvents(callback) {
|
|
452
|
-
return (event) => {
|
|
453
|
-
if (event.key !== "Enter")
|
|
454
|
-
return;
|
|
455
|
-
callback(event);
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function firePointerEvent(target, type) {
|
|
459
|
-
target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
|
|
460
|
-
}
|
|
461
|
-
const enableKeyboardPress = (focusEvent, eventOptions) => {
|
|
462
|
-
const element = focusEvent.currentTarget;
|
|
463
|
-
if (!element)
|
|
464
|
-
return;
|
|
465
|
-
const handleKeydown = filterEvents(() => {
|
|
466
|
-
if (isPressing.has(element))
|
|
467
|
-
return;
|
|
468
|
-
firePointerEvent(element, "down");
|
|
469
|
-
const handleKeyup = filterEvents(() => {
|
|
470
|
-
firePointerEvent(element, "up");
|
|
471
|
-
});
|
|
472
|
-
const handleBlur = () => firePointerEvent(element, "cancel");
|
|
473
|
-
element.addEventListener("keyup", handleKeyup, eventOptions);
|
|
474
|
-
element.addEventListener("blur", handleBlur, eventOptions);
|
|
475
|
-
});
|
|
476
|
-
element.addEventListener("keydown", handleKeydown, eventOptions);
|
|
477
|
-
/**
|
|
478
|
-
* Add an event listener that fires on blur to remove the keydown events.
|
|
479
|
-
*/
|
|
480
|
-
element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Filter out events that are not primary pointer events, or are triggering
|
|
485
|
-
* while a Motion gesture is active.
|
|
486
|
-
*/
|
|
487
|
-
function isValidPressEvent(event) {
|
|
488
|
-
return isPrimaryPointer(event) && !isDragActive();
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Create a press gesture.
|
|
492
|
-
*
|
|
493
|
-
* Press is different to `"pointerdown"`, `"pointerup"` in that it
|
|
494
|
-
* automatically filters out secondary pointer events like right
|
|
495
|
-
* click and multitouch.
|
|
496
|
-
*
|
|
497
|
-
* It also adds accessibility support for keyboards, where
|
|
498
|
-
* an element with a press gesture will receive focus and
|
|
499
|
-
* trigger on Enter `"keydown"` and `"keyup"` events.
|
|
500
|
-
*
|
|
501
|
-
* This is different to a browser's `"click"` event, which does
|
|
502
|
-
* respond to keyboards but only for the `"click"` itself, rather
|
|
503
|
-
* than the press start and end/cancel. The element also needs
|
|
504
|
-
* to be focusable for this to work, whereas a press gesture will
|
|
505
|
-
* make an element focusable by default.
|
|
506
|
-
*
|
|
507
|
-
* @public
|
|
508
|
-
*/
|
|
509
|
-
function press(targetOrSelector, onPressStart, options = {}) {
|
|
510
|
-
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
|
|
511
|
-
const startPress = (startEvent) => {
|
|
512
|
-
const target = startEvent.currentTarget;
|
|
513
|
-
if (!target || !isValidPressEvent(startEvent) || isPressing.has(target))
|
|
514
|
-
return;
|
|
515
|
-
isPressing.add(target);
|
|
516
|
-
capturePointer(startEvent, "set");
|
|
517
|
-
const onPressEnd = onPressStart(target, startEvent);
|
|
518
|
-
const onPointerEnd = (endEvent, success) => {
|
|
519
|
-
target.removeEventListener("pointerup", onPointerUp);
|
|
520
|
-
target.removeEventListener("pointercancel", onPointerCancel);
|
|
521
|
-
capturePointer(endEvent, "release");
|
|
522
|
-
if (!isValidPressEvent(endEvent) || !isPressing.has(target)) {
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
isPressing.delete(target);
|
|
526
|
-
if (typeof onPressEnd === "function") {
|
|
527
|
-
onPressEnd(endEvent, { success });
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
const onPointerUp = (upEvent) => {
|
|
531
|
-
const isOutside = !upEvent.isTrusted
|
|
532
|
-
? false
|
|
533
|
-
: checkOutside(upEvent, target instanceof Element
|
|
534
|
-
? target.getBoundingClientRect()
|
|
535
|
-
: {
|
|
536
|
-
left: 0,
|
|
537
|
-
top: 0,
|
|
538
|
-
right: window.innerWidth,
|
|
539
|
-
bottom: window.innerHeight,
|
|
540
|
-
});
|
|
541
|
-
if (isOutside) {
|
|
542
|
-
onPointerEnd(upEvent, false);
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
onPointerEnd(upEvent, !(target instanceof Element) ||
|
|
546
|
-
isNodeOrChild(target, upEvent.target));
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
const onPointerCancel = (cancelEvent) => {
|
|
550
|
-
onPointerEnd(cancelEvent, false);
|
|
551
|
-
};
|
|
552
|
-
target.addEventListener("pointerup", onPointerUp, eventOptions);
|
|
553
|
-
target.addEventListener("pointercancel", onPointerCancel, eventOptions);
|
|
554
|
-
target.addEventListener("lostpointercapture", onPointerCancel, eventOptions);
|
|
555
|
-
};
|
|
556
|
-
targets.forEach((target) => {
|
|
557
|
-
target = options.useGlobalTarget ? window : target;
|
|
558
|
-
let canAddKeyboardAccessibility = false;
|
|
559
|
-
if (target instanceof HTMLElement) {
|
|
560
|
-
canAddKeyboardAccessibility = true;
|
|
561
|
-
if (!isElementKeyboardAccessible(target) &&
|
|
562
|
-
target.getAttribute("tabindex") === null) {
|
|
563
|
-
target.tabIndex = 0;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
target.addEventListener("pointerdown", startPress, eventOptions);
|
|
567
|
-
if (canAddKeyboardAccessibility) {
|
|
568
|
-
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions), eventOptions);
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
return cancelEvents;
|
|
572
|
-
}
|
|
573
|
-
function checkOutside(event, rect) {
|
|
574
|
-
return (event.clientX < rect.left ||
|
|
575
|
-
event.clientX > rect.right ||
|
|
576
|
-
event.clientY < rect.top ||
|
|
577
|
-
event.clientY > rect.bottom);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function setDragLock(axis) {
|
|
581
|
-
if (axis === "x" || axis === "y") {
|
|
582
|
-
if (isDragging[axis]) {
|
|
583
|
-
return null;
|
|
584
|
-
}
|
|
585
|
-
else {
|
|
586
|
-
isDragging[axis] = true;
|
|
587
|
-
return () => {
|
|
588
|
-
isDragging[axis] = false;
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
if (isDragging.x || isDragging.y) {
|
|
594
|
-
return null;
|
|
595
|
-
}
|
|
596
|
-
else {
|
|
597
|
-
isDragging.x = isDragging.y = true;
|
|
598
|
-
return () => {
|
|
599
|
-
isDragging.x = isDragging.y = false;
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Generate a list of every possible transform key.
|
|
607
|
-
*/
|
|
608
|
-
const transformPropOrder = [
|
|
609
|
-
"transformPerspective",
|
|
610
|
-
"x",
|
|
611
|
-
"y",
|
|
612
|
-
"z",
|
|
613
|
-
"translateX",
|
|
614
|
-
"translateY",
|
|
615
|
-
"translateZ",
|
|
616
|
-
"scale",
|
|
617
|
-
"scaleX",
|
|
618
|
-
"scaleY",
|
|
619
|
-
"rotate",
|
|
620
|
-
"rotateX",
|
|
621
|
-
"rotateY",
|
|
622
|
-
"rotateZ",
|
|
623
|
-
"skew",
|
|
624
|
-
"skewX",
|
|
625
|
-
"skewY",
|
|
626
|
-
];
|
|
627
|
-
/**
|
|
628
|
-
* A quick lookup for transform props.
|
|
629
|
-
*/
|
|
630
|
-
const transformProps = new Set(transformPropOrder);
|
|
631
|
-
|
|
632
|
-
const positionalKeys = new Set([
|
|
633
|
-
"width",
|
|
634
|
-
"height",
|
|
635
|
-
"top",
|
|
636
|
-
"left",
|
|
637
|
-
"right",
|
|
638
|
-
"bottom",
|
|
639
|
-
...transformPropOrder,
|
|
640
|
-
]);
|
|
641
|
-
|
|
642
|
-
const isKeyframesTarget = (v) => {
|
|
643
|
-
return Array.isArray(v);
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
const isCustomValue = (v) => {
|
|
647
|
-
return Boolean(v && typeof v === "object" && v.mix && v.toValue);
|
|
648
|
-
};
|
|
649
|
-
const resolveFinalValueInKeyframes = (v) => {
|
|
650
|
-
// TODO maybe throw if v.length - 1 is placeholder token?
|
|
651
|
-
return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
|
|
652
|
-
};
|
|
653
|
-
|
|
654
|
-
const MotionGlobalConfig = {
|
|
655
|
-
skipAnimations: false,
|
|
656
|
-
useManualTiming: false,
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
const stepsOrder = [
|
|
660
|
-
"read", // Read
|
|
661
|
-
"resolveKeyframes", // Write/Read/Write/Read
|
|
662
|
-
"update", // Compute
|
|
663
|
-
"preRender", // Compute
|
|
664
|
-
"render", // Write
|
|
665
|
-
"postRender", // Compute
|
|
666
|
-
];
|
|
667
|
-
|
|
668
|
-
const statsBuffer = {
|
|
669
|
-
value: null,
|
|
670
|
-
addProjectionMetrics: null,
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
function createRenderStep(runNextFrame, stepName) {
|
|
674
|
-
/**
|
|
675
|
-
* We create and reuse two queues, one to queue jobs for the current frame
|
|
676
|
-
* and one for the next. We reuse to avoid triggering GC after x frames.
|
|
677
|
-
*/
|
|
678
|
-
let thisFrame = new Set();
|
|
679
|
-
let nextFrame = new Set();
|
|
680
|
-
/**
|
|
681
|
-
* Track whether we're currently processing jobs in this step. This way
|
|
682
|
-
* we can decide whether to schedule new jobs for this frame or next.
|
|
683
|
-
*/
|
|
684
|
-
let isProcessing = false;
|
|
685
|
-
let flushNextFrame = false;
|
|
686
|
-
/**
|
|
687
|
-
* A set of processes which were marked keepAlive when scheduled.
|
|
688
|
-
*/
|
|
689
|
-
const toKeepAlive = new WeakSet();
|
|
690
|
-
let latestFrameData = {
|
|
691
|
-
delta: 0.0,
|
|
692
|
-
timestamp: 0.0,
|
|
693
|
-
isProcessing: false,
|
|
694
|
-
};
|
|
695
|
-
let numCalls = 0;
|
|
696
|
-
function triggerCallback(callback) {
|
|
697
|
-
if (toKeepAlive.has(callback)) {
|
|
698
|
-
step.schedule(callback);
|
|
699
|
-
runNextFrame();
|
|
700
|
-
}
|
|
701
|
-
numCalls++;
|
|
702
|
-
callback(latestFrameData);
|
|
703
|
-
}
|
|
704
|
-
const step = {
|
|
705
|
-
/**
|
|
706
|
-
* Schedule a process to run on the next frame.
|
|
707
|
-
*/
|
|
708
|
-
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
709
|
-
const addToCurrentFrame = immediate && isProcessing;
|
|
710
|
-
const queue = addToCurrentFrame ? thisFrame : nextFrame;
|
|
711
|
-
if (keepAlive)
|
|
712
|
-
toKeepAlive.add(callback);
|
|
713
|
-
if (!queue.has(callback))
|
|
714
|
-
queue.add(callback);
|
|
715
|
-
return callback;
|
|
716
|
-
},
|
|
717
|
-
/**
|
|
718
|
-
* Cancel the provided callback from running on the next frame.
|
|
719
|
-
*/
|
|
720
|
-
cancel: (callback) => {
|
|
721
|
-
nextFrame.delete(callback);
|
|
722
|
-
toKeepAlive.delete(callback);
|
|
723
|
-
},
|
|
724
|
-
/**
|
|
725
|
-
* Execute all schedule callbacks.
|
|
726
|
-
*/
|
|
727
|
-
process: (frameData) => {
|
|
728
|
-
latestFrameData = frameData;
|
|
729
|
-
/**
|
|
730
|
-
* If we're already processing we've probably been triggered by a flushSync
|
|
731
|
-
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
732
|
-
* as true and ensure we flush the following frame at the end of this one.
|
|
733
|
-
*/
|
|
734
|
-
if (isProcessing) {
|
|
735
|
-
flushNextFrame = true;
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
isProcessing = true;
|
|
739
|
-
[thisFrame, nextFrame] = [nextFrame, thisFrame];
|
|
740
|
-
// Execute this frame
|
|
741
|
-
thisFrame.forEach(triggerCallback);
|
|
742
|
-
/**
|
|
743
|
-
* If we're recording stats then
|
|
744
|
-
*/
|
|
745
|
-
if (stepName && statsBuffer.value) {
|
|
746
|
-
statsBuffer.value.frameloop[stepName].push(numCalls);
|
|
747
|
-
}
|
|
748
|
-
numCalls = 0;
|
|
749
|
-
// Clear the frame so no callbacks remain. This is to avoid
|
|
750
|
-
// memory leaks should this render step not run for a while.
|
|
751
|
-
thisFrame.clear();
|
|
752
|
-
isProcessing = false;
|
|
753
|
-
if (flushNextFrame) {
|
|
754
|
-
flushNextFrame = false;
|
|
755
|
-
step.process(frameData);
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
};
|
|
759
|
-
return step;
|
|
484
|
+
return step;
|
|
760
485
|
}
|
|
761
486
|
|
|
762
487
|
const maxElapsed = 40;
|
|
@@ -818,103 +543,332 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
|
|
818
543
|
steps[stepsOrder[i]].cancel(process);
|
|
819
544
|
}
|
|
820
545
|
};
|
|
821
|
-
return { schedule, cancel, state, steps };
|
|
546
|
+
return { schedule, cancel, state, steps };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
|
|
550
|
+
|
|
551
|
+
const { schedule: microtask, cancel: cancelMicrotask } =
|
|
552
|
+
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
|
553
|
+
|
|
554
|
+
let now;
|
|
555
|
+
function clearTime() {
|
|
556
|
+
now = undefined;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* An eventloop-synchronous alternative to performance.now().
|
|
560
|
+
*
|
|
561
|
+
* Ensures that time measurements remain consistent within a synchronous context.
|
|
562
|
+
* Usually calling performance.now() twice within the same synchronous context
|
|
563
|
+
* will return different values which isn't useful for animations when we're usually
|
|
564
|
+
* trying to sync animations to the same frame.
|
|
565
|
+
*/
|
|
566
|
+
const time = {
|
|
567
|
+
now: () => {
|
|
568
|
+
if (now === undefined) {
|
|
569
|
+
time.set(frameData.isProcessing || MotionGlobalConfig.useManualTiming
|
|
570
|
+
? frameData.timestamp
|
|
571
|
+
: performance.now());
|
|
572
|
+
}
|
|
573
|
+
return now;
|
|
574
|
+
},
|
|
575
|
+
set: (newTime) => {
|
|
576
|
+
now = newTime;
|
|
577
|
+
queueMicrotask(clearTime);
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const isDragging = {
|
|
582
|
+
x: false,
|
|
583
|
+
y: false,
|
|
584
|
+
};
|
|
585
|
+
function isDragActive() {
|
|
586
|
+
return isDragging.x || isDragging.y;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function setDragLock(axis) {
|
|
590
|
+
if (axis === "x" || axis === "y") {
|
|
591
|
+
if (isDragging[axis]) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
isDragging[axis] = true;
|
|
596
|
+
return () => {
|
|
597
|
+
isDragging[axis] = false;
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
if (isDragging.x || isDragging.y) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
isDragging.x = isDragging.y = true;
|
|
607
|
+
return () => {
|
|
608
|
+
isDragging.x = isDragging.y = false;
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function resolveElements(elementOrSelector, scope, selectorCache) {
|
|
615
|
+
var _a;
|
|
616
|
+
if (elementOrSelector instanceof EventTarget) {
|
|
617
|
+
return [elementOrSelector];
|
|
618
|
+
}
|
|
619
|
+
else if (typeof elementOrSelector === "string") {
|
|
620
|
+
let root = document;
|
|
621
|
+
if (scope) {
|
|
622
|
+
// TODO: Refactor to utils package
|
|
623
|
+
// invariant(
|
|
624
|
+
// Boolean(scope.current),
|
|
625
|
+
// "Scope provided, but no element detected."
|
|
626
|
+
// )
|
|
627
|
+
root = scope.current;
|
|
628
|
+
}
|
|
629
|
+
const elements = (_a = selectorCache === null || selectorCache === void 0 ? void 0 : selectorCache[elementOrSelector]) !== null && _a !== void 0 ? _a : root.querySelectorAll(elementOrSelector);
|
|
630
|
+
return elements ? Array.from(elements) : [];
|
|
631
|
+
}
|
|
632
|
+
return Array.from(elementOrSelector);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function setupGesture(elementOrSelector, options) {
|
|
636
|
+
const elements = resolveElements(elementOrSelector);
|
|
637
|
+
const gestureAbortController = new AbortController();
|
|
638
|
+
const eventOptions = {
|
|
639
|
+
passive: true,
|
|
640
|
+
...options,
|
|
641
|
+
signal: gestureAbortController.signal,
|
|
642
|
+
};
|
|
643
|
+
const cancel = () => gestureAbortController.abort();
|
|
644
|
+
return [elements, eventOptions, cancel];
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function isValidHover(event) {
|
|
648
|
+
return !(event.pointerType === "touch" || isDragActive());
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Create a hover gesture. hover() is different to .addEventListener("pointerenter")
|
|
652
|
+
* in that it has an easier syntax, filters out polyfilled touch events, interoperates
|
|
653
|
+
* with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends.
|
|
654
|
+
*
|
|
655
|
+
* @public
|
|
656
|
+
*/
|
|
657
|
+
function hover(elementOrSelector, onHoverStart, options = {}) {
|
|
658
|
+
const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options);
|
|
659
|
+
const onPointerEnter = (enterEvent) => {
|
|
660
|
+
if (!isValidHover(enterEvent))
|
|
661
|
+
return;
|
|
662
|
+
const { target } = enterEvent;
|
|
663
|
+
const onHoverEnd = onHoverStart(target, enterEvent);
|
|
664
|
+
if (typeof onHoverEnd !== "function" || !target)
|
|
665
|
+
return;
|
|
666
|
+
const onPointerLeave = (leaveEvent) => {
|
|
667
|
+
if (!isValidHover(leaveEvent))
|
|
668
|
+
return;
|
|
669
|
+
onHoverEnd(leaveEvent);
|
|
670
|
+
target.removeEventListener("pointerleave", onPointerLeave);
|
|
671
|
+
};
|
|
672
|
+
target.addEventListener("pointerleave", onPointerLeave, eventOptions);
|
|
673
|
+
};
|
|
674
|
+
elements.forEach((element) => {
|
|
675
|
+
element.addEventListener("pointerenter", onPointerEnter, eventOptions);
|
|
676
|
+
});
|
|
677
|
+
return cancel;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function capturePointer(event, action) {
|
|
681
|
+
const actionName = `${action}PointerCapture`;
|
|
682
|
+
if (event.target instanceof Element &&
|
|
683
|
+
actionName in event.target &&
|
|
684
|
+
event.pointerId !== undefined) {
|
|
685
|
+
try {
|
|
686
|
+
event.target[actionName](event.pointerId);
|
|
687
|
+
}
|
|
688
|
+
catch (e) { }
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Recursively traverse up the tree to check whether the provided child node
|
|
694
|
+
* is the parent or a descendant of it.
|
|
695
|
+
*
|
|
696
|
+
* @param parent - Element to find
|
|
697
|
+
* @param child - Element to test against parent
|
|
698
|
+
*/
|
|
699
|
+
const isNodeOrChild = (parent, child) => {
|
|
700
|
+
if (!child) {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
else if (parent === child) {
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
return isNodeOrChild(parent, child.parentElement);
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
const isPrimaryPointer = (event) => {
|
|
712
|
+
if (event.pointerType === "mouse") {
|
|
713
|
+
return typeof event.button !== "number" || event.button <= 0;
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
/**
|
|
717
|
+
* isPrimary is true for all mice buttons, whereas every touch point
|
|
718
|
+
* is regarded as its own input. So subsequent concurrent touch points
|
|
719
|
+
* will be false.
|
|
720
|
+
*
|
|
721
|
+
* Specifically match against false here as incomplete versions of
|
|
722
|
+
* PointerEvents in very old browser might have it set as undefined.
|
|
723
|
+
*/
|
|
724
|
+
return event.isPrimary !== false;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
const focusableElements = new Set([
|
|
729
|
+
"BUTTON",
|
|
730
|
+
"INPUT",
|
|
731
|
+
"SELECT",
|
|
732
|
+
"TEXTAREA",
|
|
733
|
+
"A",
|
|
734
|
+
]);
|
|
735
|
+
function isElementKeyboardAccessible(element) {
|
|
736
|
+
return (focusableElements.has(element.tagName) ||
|
|
737
|
+
element.tabIndex !== -1);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const isPressing = new WeakSet();
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Filter out events that are not "Enter" keys.
|
|
744
|
+
*/
|
|
745
|
+
function filterEvents(callback) {
|
|
746
|
+
return (event) => {
|
|
747
|
+
if (event.key !== "Enter")
|
|
748
|
+
return;
|
|
749
|
+
callback(event);
|
|
750
|
+
};
|
|
822
751
|
}
|
|
752
|
+
function firePointerEvent(target, type) {
|
|
753
|
+
target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
|
|
754
|
+
}
|
|
755
|
+
const enableKeyboardPress = (focusEvent, eventOptions) => {
|
|
756
|
+
const element = focusEvent.currentTarget;
|
|
757
|
+
if (!element)
|
|
758
|
+
return;
|
|
759
|
+
const handleKeydown = filterEvents(() => {
|
|
760
|
+
if (isPressing.has(element))
|
|
761
|
+
return;
|
|
762
|
+
firePointerEvent(element, "down");
|
|
763
|
+
const handleKeyup = filterEvents(() => {
|
|
764
|
+
firePointerEvent(element, "up");
|
|
765
|
+
});
|
|
766
|
+
const handleBlur = () => firePointerEvent(element, "cancel");
|
|
767
|
+
element.addEventListener("keyup", handleKeyup, eventOptions);
|
|
768
|
+
element.addEventListener("blur", handleBlur, eventOptions);
|
|
769
|
+
});
|
|
770
|
+
element.addEventListener("keydown", handleKeydown, eventOptions);
|
|
771
|
+
/**
|
|
772
|
+
* Add an event listener that fires on blur to remove the keydown events.
|
|
773
|
+
*/
|
|
774
|
+
element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
|
|
775
|
+
};
|
|
823
776
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
777
|
+
/**
|
|
778
|
+
* Filter out events that are not primary pointer events, or are triggering
|
|
779
|
+
* while a Motion gesture is active.
|
|
780
|
+
*/
|
|
781
|
+
function isValidPressEvent(event) {
|
|
782
|
+
return isPrimaryPointer(event) && !isDragActive();
|
|
829
783
|
}
|
|
830
784
|
/**
|
|
831
|
-
*
|
|
785
|
+
* Create a press gesture.
|
|
832
786
|
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
787
|
+
* Press is different to `"pointerdown"`, `"pointerup"` in that it
|
|
788
|
+
* automatically filters out secondary pointer events like right
|
|
789
|
+
* click and multitouch.
|
|
790
|
+
*
|
|
791
|
+
* It also adds accessibility support for keyboards, where
|
|
792
|
+
* an element with a press gesture will receive focus and
|
|
793
|
+
* trigger on Enter `"keydown"` and `"keyup"` events.
|
|
794
|
+
*
|
|
795
|
+
* This is different to a browser's `"click"` event, which does
|
|
796
|
+
* respond to keyboards but only for the `"click"` itself, rather
|
|
797
|
+
* than the press start and end/cancel. The element also needs
|
|
798
|
+
* to be focusable for this to work, whereas a press gesture will
|
|
799
|
+
* make an element focusable by default.
|
|
800
|
+
*
|
|
801
|
+
* @public
|
|
837
802
|
*/
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
: performance.now());
|
|
844
|
-
}
|
|
845
|
-
return now;
|
|
846
|
-
},
|
|
847
|
-
set: (newTime) => {
|
|
848
|
-
now = newTime;
|
|
849
|
-
queueMicrotask(clearTime);
|
|
850
|
-
},
|
|
851
|
-
};
|
|
852
|
-
|
|
853
|
-
function addUniqueItem(arr, item) {
|
|
854
|
-
if (arr.indexOf(item) === -1)
|
|
855
|
-
arr.push(item);
|
|
856
|
-
}
|
|
857
|
-
function removeItem(arr, item) {
|
|
858
|
-
const index = arr.indexOf(item);
|
|
859
|
-
if (index > -1)
|
|
860
|
-
arr.splice(index, 1);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
class SubscriptionManager {
|
|
864
|
-
constructor() {
|
|
865
|
-
this.subscriptions = [];
|
|
866
|
-
}
|
|
867
|
-
add(handler) {
|
|
868
|
-
addUniqueItem(this.subscriptions, handler);
|
|
869
|
-
return () => removeItem(this.subscriptions, handler);
|
|
870
|
-
}
|
|
871
|
-
notify(a, b, c) {
|
|
872
|
-
const numSubscriptions = this.subscriptions.length;
|
|
873
|
-
if (!numSubscriptions)
|
|
803
|
+
function press(targetOrSelector, onPressStart, options = {}) {
|
|
804
|
+
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
|
|
805
|
+
const startPress = (startEvent) => {
|
|
806
|
+
const target = startEvent.currentTarget;
|
|
807
|
+
if (!target || !isValidPressEvent(startEvent) || isPressing.has(target))
|
|
874
808
|
return;
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
809
|
+
isPressing.add(target);
|
|
810
|
+
capturePointer(startEvent, "set");
|
|
811
|
+
const onPressEnd = onPressStart(target, startEvent);
|
|
812
|
+
const onPointerEnd = (endEvent, success) => {
|
|
813
|
+
target.removeEventListener("pointerup", onPointerUp);
|
|
814
|
+
target.removeEventListener("pointercancel", onPointerCancel);
|
|
815
|
+
capturePointer(endEvent, "release");
|
|
816
|
+
if (!isValidPressEvent(endEvent) || !isPressing.has(target)) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
isPressing.delete(target);
|
|
820
|
+
if (typeof onPressEnd === "function") {
|
|
821
|
+
onPressEnd(endEvent, { success });
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
const onPointerUp = (upEvent) => {
|
|
825
|
+
const isOutside = !upEvent.isTrusted
|
|
826
|
+
? false
|
|
827
|
+
: checkOutside(upEvent, target instanceof Element
|
|
828
|
+
? target.getBoundingClientRect()
|
|
829
|
+
: {
|
|
830
|
+
left: 0,
|
|
831
|
+
top: 0,
|
|
832
|
+
right: window.innerWidth,
|
|
833
|
+
bottom: window.innerHeight,
|
|
834
|
+
});
|
|
835
|
+
if (isOutside) {
|
|
836
|
+
onPointerEnd(upEvent, false);
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
onPointerEnd(upEvent, !(target instanceof Element) ||
|
|
840
|
+
isNodeOrChild(target, upEvent.target));
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
const onPointerCancel = (cancelEvent) => {
|
|
844
|
+
onPointerEnd(cancelEvent, false);
|
|
845
|
+
};
|
|
846
|
+
target.addEventListener("pointerup", onPointerUp, eventOptions);
|
|
847
|
+
target.addEventListener("pointercancel", onPointerCancel, eventOptions);
|
|
848
|
+
target.addEventListener("lostpointercapture", onPointerCancel, eventOptions);
|
|
849
|
+
};
|
|
850
|
+
targets.forEach((target) => {
|
|
851
|
+
target = options.useGlobalTarget ? window : target;
|
|
852
|
+
let canAddKeyboardAccessibility = false;
|
|
853
|
+
if (target instanceof HTMLElement) {
|
|
854
|
+
canAddKeyboardAccessibility = true;
|
|
855
|
+
if (!isElementKeyboardAccessible(target) &&
|
|
856
|
+
target.getAttribute("tabindex") === null) {
|
|
857
|
+
target.tabIndex = 0;
|
|
889
858
|
}
|
|
890
859
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
/*
|
|
901
|
-
Convert velocity into velocity per second
|
|
902
|
-
|
|
903
|
-
@param [number]: Unit per frame
|
|
904
|
-
@param [number]: Frame duration in ms
|
|
905
|
-
*/
|
|
906
|
-
function velocityPerSecond(velocity, frameDuration) {
|
|
907
|
-
return frameDuration ? velocity * (1000 / frameDuration) : 0;
|
|
860
|
+
target.addEventListener("pointerdown", startPress, eventOptions);
|
|
861
|
+
if (canAddKeyboardAccessibility) {
|
|
862
|
+
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions), eventOptions);
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
return cancelEvents;
|
|
908
866
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
console.warn(message);
|
|
915
|
-
if (element)
|
|
916
|
-
console.warn(element);
|
|
917
|
-
warned.add(message);
|
|
867
|
+
function checkOutside(event, rect) {
|
|
868
|
+
return (event.clientX < rect.left ||
|
|
869
|
+
event.clientX > rect.right ||
|
|
870
|
+
event.clientY < rect.top ||
|
|
871
|
+
event.clientY > rect.bottom);
|
|
918
872
|
}
|
|
919
873
|
|
|
920
874
|
/**
|
|
@@ -936,15 +890,13 @@ class MotionValue {
|
|
|
936
890
|
* @param config - Optional configuration options
|
|
937
891
|
*
|
|
938
892
|
* - `transformer`: A function to transform incoming values with.
|
|
939
|
-
*
|
|
940
|
-
* @internal
|
|
941
893
|
*/
|
|
942
894
|
constructor(init, options = {}) {
|
|
943
895
|
/**
|
|
944
896
|
* This will be replaced by the build step with the latest version number.
|
|
945
897
|
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
946
898
|
*/
|
|
947
|
-
this.version = "12.
|
|
899
|
+
this.version = "12.5.0";
|
|
948
900
|
/**
|
|
949
901
|
* Tracks whether this value can output a velocity. Currently this is only true
|
|
950
902
|
* if the value is numerical, but we might be able to widen the scope here and support
|
|
@@ -1067,8 +1019,6 @@ class MotionValue {
|
|
|
1067
1019
|
}
|
|
1068
1020
|
/**
|
|
1069
1021
|
* Attaches a passive effect to the `MotionValue`.
|
|
1070
|
-
*
|
|
1071
|
-
* @internal
|
|
1072
1022
|
*/
|
|
1073
1023
|
attach(passiveEffect, stopPassiveEffect) {
|
|
1074
1024
|
this.passiveEffect = passiveEffect;
|
|
@@ -1159,8 +1109,6 @@ class MotionValue {
|
|
|
1159
1109
|
* ```
|
|
1160
1110
|
*
|
|
1161
1111
|
* @param animation - A function that starts the provided animation
|
|
1162
|
-
*
|
|
1163
|
-
* @internal
|
|
1164
1112
|
*/
|
|
1165
1113
|
start(startAnimation) {
|
|
1166
1114
|
this.stop();
|
|
@@ -1223,6 +1171,55 @@ function motionValue(init, options) {
|
|
|
1223
1171
|
return new MotionValue(init, options);
|
|
1224
1172
|
}
|
|
1225
1173
|
|
|
1174
|
+
/**
|
|
1175
|
+
* Generate a list of every possible transform key.
|
|
1176
|
+
*/
|
|
1177
|
+
const transformPropOrder = [
|
|
1178
|
+
"transformPerspective",
|
|
1179
|
+
"x",
|
|
1180
|
+
"y",
|
|
1181
|
+
"z",
|
|
1182
|
+
"translateX",
|
|
1183
|
+
"translateY",
|
|
1184
|
+
"translateZ",
|
|
1185
|
+
"scale",
|
|
1186
|
+
"scaleX",
|
|
1187
|
+
"scaleY",
|
|
1188
|
+
"rotate",
|
|
1189
|
+
"rotateX",
|
|
1190
|
+
"rotateY",
|
|
1191
|
+
"rotateZ",
|
|
1192
|
+
"skew",
|
|
1193
|
+
"skewX",
|
|
1194
|
+
"skewY",
|
|
1195
|
+
];
|
|
1196
|
+
/**
|
|
1197
|
+
* A quick lookup for transform props.
|
|
1198
|
+
*/
|
|
1199
|
+
const transformProps = new Set(transformPropOrder);
|
|
1200
|
+
|
|
1201
|
+
const positionalKeys = new Set([
|
|
1202
|
+
"width",
|
|
1203
|
+
"height",
|
|
1204
|
+
"top",
|
|
1205
|
+
"left",
|
|
1206
|
+
"right",
|
|
1207
|
+
"bottom",
|
|
1208
|
+
...transformPropOrder,
|
|
1209
|
+
]);
|
|
1210
|
+
|
|
1211
|
+
const isKeyframesTarget = (v) => {
|
|
1212
|
+
return Array.isArray(v);
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
const isCustomValue = (v) => {
|
|
1216
|
+
return Boolean(v && typeof v === "object" && v.mix && v.toValue);
|
|
1217
|
+
};
|
|
1218
|
+
const resolveFinalValueInKeyframes = (v) => {
|
|
1219
|
+
// TODO maybe throw if v.length - 1 is placeholder token?
|
|
1220
|
+
return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1226
1223
|
/**
|
|
1227
1224
|
* Set VisualElement's MotionValue, creating a new MotionValue for it if
|
|
1228
1225
|
* it doesn't exist.
|
|
@@ -5822,8 +5819,6 @@ const LayoutGroupContext = react.createContext({});
|
|
|
5822
5819
|
*/
|
|
5823
5820
|
const SwitchLayoutGroupContext = react.createContext({});
|
|
5824
5821
|
|
|
5825
|
-
const { schedule: microtask, cancel: cancelMicrotask } = createRenderBatcher(queueMicrotask, false);
|
|
5826
|
-
|
|
5827
5822
|
/**
|
|
5828
5823
|
* This should only ever be modified on the client otherwise it'll
|
|
5829
5824
|
* persist through server requests. If we need instanced states we
|
|
@@ -9379,7 +9374,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
9379
9374
|
* and warn against mismatches.
|
|
9380
9375
|
*/
|
|
9381
9376
|
if (process.env.NODE_ENV === "development") {
|
|
9382
|
-
warnOnce(nextValue.version === "12.
|
|
9377
|
+
warnOnce(nextValue.version === "12.5.0", `Attempting to mix Motion versions ${nextValue.version} with 12.5.0 may not work as expected.`);
|
|
9383
9378
|
}
|
|
9384
9379
|
}
|
|
9385
9380
|
else if (isMotionValue(prevValue)) {
|