motion 12.9.7 → 12.10.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/index.js +770 -666
- package/dist/cjs/react-client.js +268 -246
- package/dist/cjs/react-m.js +3 -3
- package/dist/es/motion/lib/index.mjs +1 -1
- package/dist/es/motion/lib/react.mjs +1 -1
- package/dist/es/motion-dom/dist/es/effects/MotionValueState.mjs +41 -0
- package/dist/es/motion-dom/dist/es/effects/style/index.mjs +51 -0
- package/dist/es/motion-dom/dist/es/effects/style/transform.mjs +38 -0
- package/dist/es/motion-dom/dist/es/gestures/press/index.mjs +4 -2
- package/dist/es/motion-dom/dist/es/value/index.mjs +20 -0
- package/dist/motion.dev.js +770 -666
- package/dist/motion.js +1 -1
- package/package.json +3 -3
- package/dist/es/motion-dom/dist/es/effects/style-effect.mjs +0 -36
package/dist/cjs/react-client.js
CHANGED
|
@@ -3187,243 +3187,6 @@ function resolveElements(elementOrSelector, scope, selectorCache) {
|
|
|
3187
3187
|
return Array.from(elementOrSelector);
|
|
3188
3188
|
}
|
|
3189
3189
|
|
|
3190
|
-
const { schedule: microtask, cancel: cancelMicrotask } =
|
|
3191
|
-
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
|
3192
|
-
|
|
3193
|
-
const isDragging = {
|
|
3194
|
-
x: false,
|
|
3195
|
-
y: false,
|
|
3196
|
-
};
|
|
3197
|
-
function isDragActive() {
|
|
3198
|
-
return isDragging.x || isDragging.y;
|
|
3199
|
-
}
|
|
3200
|
-
|
|
3201
|
-
function setDragLock(axis) {
|
|
3202
|
-
if (axis === "x" || axis === "y") {
|
|
3203
|
-
if (isDragging[axis]) {
|
|
3204
|
-
return null;
|
|
3205
|
-
}
|
|
3206
|
-
else {
|
|
3207
|
-
isDragging[axis] = true;
|
|
3208
|
-
return () => {
|
|
3209
|
-
isDragging[axis] = false;
|
|
3210
|
-
};
|
|
3211
|
-
}
|
|
3212
|
-
}
|
|
3213
|
-
else {
|
|
3214
|
-
if (isDragging.x || isDragging.y) {
|
|
3215
|
-
return null;
|
|
3216
|
-
}
|
|
3217
|
-
else {
|
|
3218
|
-
isDragging.x = isDragging.y = true;
|
|
3219
|
-
return () => {
|
|
3220
|
-
isDragging.x = isDragging.y = false;
|
|
3221
|
-
};
|
|
3222
|
-
}
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
|
|
3226
|
-
function setupGesture(elementOrSelector, options) {
|
|
3227
|
-
const elements = resolveElements(elementOrSelector);
|
|
3228
|
-
const gestureAbortController = new AbortController();
|
|
3229
|
-
const eventOptions = {
|
|
3230
|
-
passive: true,
|
|
3231
|
-
...options,
|
|
3232
|
-
signal: gestureAbortController.signal,
|
|
3233
|
-
};
|
|
3234
|
-
const cancel = () => gestureAbortController.abort();
|
|
3235
|
-
return [elements, eventOptions, cancel];
|
|
3236
|
-
}
|
|
3237
|
-
|
|
3238
|
-
function isValidHover(event) {
|
|
3239
|
-
return !(event.pointerType === "touch" || isDragActive());
|
|
3240
|
-
}
|
|
3241
|
-
/**
|
|
3242
|
-
* Create a hover gesture. hover() is different to .addEventListener("pointerenter")
|
|
3243
|
-
* in that it has an easier syntax, filters out polyfilled touch events, interoperates
|
|
3244
|
-
* with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends.
|
|
3245
|
-
*
|
|
3246
|
-
* @public
|
|
3247
|
-
*/
|
|
3248
|
-
function hover(elementOrSelector, onHoverStart, options = {}) {
|
|
3249
|
-
const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options);
|
|
3250
|
-
const onPointerEnter = (enterEvent) => {
|
|
3251
|
-
if (!isValidHover(enterEvent))
|
|
3252
|
-
return;
|
|
3253
|
-
const { target } = enterEvent;
|
|
3254
|
-
const onHoverEnd = onHoverStart(target, enterEvent);
|
|
3255
|
-
if (typeof onHoverEnd !== "function" || !target)
|
|
3256
|
-
return;
|
|
3257
|
-
const onPointerLeave = (leaveEvent) => {
|
|
3258
|
-
if (!isValidHover(leaveEvent))
|
|
3259
|
-
return;
|
|
3260
|
-
onHoverEnd(leaveEvent);
|
|
3261
|
-
target.removeEventListener("pointerleave", onPointerLeave);
|
|
3262
|
-
};
|
|
3263
|
-
target.addEventListener("pointerleave", onPointerLeave, eventOptions);
|
|
3264
|
-
};
|
|
3265
|
-
elements.forEach((element) => {
|
|
3266
|
-
element.addEventListener("pointerenter", onPointerEnter, eventOptions);
|
|
3267
|
-
});
|
|
3268
|
-
return cancel;
|
|
3269
|
-
}
|
|
3270
|
-
|
|
3271
|
-
/**
|
|
3272
|
-
* Recursively traverse up the tree to check whether the provided child node
|
|
3273
|
-
* is the parent or a descendant of it.
|
|
3274
|
-
*
|
|
3275
|
-
* @param parent - Element to find
|
|
3276
|
-
* @param child - Element to test against parent
|
|
3277
|
-
*/
|
|
3278
|
-
const isNodeOrChild = (parent, child) => {
|
|
3279
|
-
if (!child) {
|
|
3280
|
-
return false;
|
|
3281
|
-
}
|
|
3282
|
-
else if (parent === child) {
|
|
3283
|
-
return true;
|
|
3284
|
-
}
|
|
3285
|
-
else {
|
|
3286
|
-
return isNodeOrChild(parent, child.parentElement);
|
|
3287
|
-
}
|
|
3288
|
-
};
|
|
3289
|
-
|
|
3290
|
-
const isPrimaryPointer = (event) => {
|
|
3291
|
-
if (event.pointerType === "mouse") {
|
|
3292
|
-
return typeof event.button !== "number" || event.button <= 0;
|
|
3293
|
-
}
|
|
3294
|
-
else {
|
|
3295
|
-
/**
|
|
3296
|
-
* isPrimary is true for all mice buttons, whereas every touch point
|
|
3297
|
-
* is regarded as its own input. So subsequent concurrent touch points
|
|
3298
|
-
* will be false.
|
|
3299
|
-
*
|
|
3300
|
-
* Specifically match against false here as incomplete versions of
|
|
3301
|
-
* PointerEvents in very old browser might have it set as undefined.
|
|
3302
|
-
*/
|
|
3303
|
-
return event.isPrimary !== false;
|
|
3304
|
-
}
|
|
3305
|
-
};
|
|
3306
|
-
|
|
3307
|
-
const focusableElements = new Set([
|
|
3308
|
-
"BUTTON",
|
|
3309
|
-
"INPUT",
|
|
3310
|
-
"SELECT",
|
|
3311
|
-
"TEXTAREA",
|
|
3312
|
-
"A",
|
|
3313
|
-
]);
|
|
3314
|
-
function isElementKeyboardAccessible(element) {
|
|
3315
|
-
return (focusableElements.has(element.tagName) ||
|
|
3316
|
-
element.tabIndex !== -1);
|
|
3317
|
-
}
|
|
3318
|
-
|
|
3319
|
-
const isPressing = new WeakSet();
|
|
3320
|
-
|
|
3321
|
-
/**
|
|
3322
|
-
* Filter out events that are not "Enter" keys.
|
|
3323
|
-
*/
|
|
3324
|
-
function filterEvents(callback) {
|
|
3325
|
-
return (event) => {
|
|
3326
|
-
if (event.key !== "Enter")
|
|
3327
|
-
return;
|
|
3328
|
-
callback(event);
|
|
3329
|
-
};
|
|
3330
|
-
}
|
|
3331
|
-
function firePointerEvent(target, type) {
|
|
3332
|
-
target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
|
|
3333
|
-
}
|
|
3334
|
-
const enableKeyboardPress = (focusEvent, eventOptions) => {
|
|
3335
|
-
const element = focusEvent.currentTarget;
|
|
3336
|
-
if (!element)
|
|
3337
|
-
return;
|
|
3338
|
-
const handleKeydown = filterEvents(() => {
|
|
3339
|
-
if (isPressing.has(element))
|
|
3340
|
-
return;
|
|
3341
|
-
firePointerEvent(element, "down");
|
|
3342
|
-
const handleKeyup = filterEvents(() => {
|
|
3343
|
-
firePointerEvent(element, "up");
|
|
3344
|
-
});
|
|
3345
|
-
const handleBlur = () => firePointerEvent(element, "cancel");
|
|
3346
|
-
element.addEventListener("keyup", handleKeyup, eventOptions);
|
|
3347
|
-
element.addEventListener("blur", handleBlur, eventOptions);
|
|
3348
|
-
});
|
|
3349
|
-
element.addEventListener("keydown", handleKeydown, eventOptions);
|
|
3350
|
-
/**
|
|
3351
|
-
* Add an event listener that fires on blur to remove the keydown events.
|
|
3352
|
-
*/
|
|
3353
|
-
element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
|
|
3354
|
-
};
|
|
3355
|
-
|
|
3356
|
-
/**
|
|
3357
|
-
* Filter out events that are not primary pointer events, or are triggering
|
|
3358
|
-
* while a Motion gesture is active.
|
|
3359
|
-
*/
|
|
3360
|
-
function isValidPressEvent(event) {
|
|
3361
|
-
return isPrimaryPointer(event) && !isDragActive();
|
|
3362
|
-
}
|
|
3363
|
-
/**
|
|
3364
|
-
* Create a press gesture.
|
|
3365
|
-
*
|
|
3366
|
-
* Press is different to `"pointerdown"`, `"pointerup"` in that it
|
|
3367
|
-
* automatically filters out secondary pointer events like right
|
|
3368
|
-
* click and multitouch.
|
|
3369
|
-
*
|
|
3370
|
-
* It also adds accessibility support for keyboards, where
|
|
3371
|
-
* an element with a press gesture will receive focus and
|
|
3372
|
-
* trigger on Enter `"keydown"` and `"keyup"` events.
|
|
3373
|
-
*
|
|
3374
|
-
* This is different to a browser's `"click"` event, which does
|
|
3375
|
-
* respond to keyboards but only for the `"click"` itself, rather
|
|
3376
|
-
* than the press start and end/cancel. The element also needs
|
|
3377
|
-
* to be focusable for this to work, whereas a press gesture will
|
|
3378
|
-
* make an element focusable by default.
|
|
3379
|
-
*
|
|
3380
|
-
* @public
|
|
3381
|
-
*/
|
|
3382
|
-
function press(targetOrSelector, onPressStart, options = {}) {
|
|
3383
|
-
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
|
|
3384
|
-
const startPress = (startEvent) => {
|
|
3385
|
-
const target = startEvent.currentTarget;
|
|
3386
|
-
if (!isValidPressEvent(startEvent) || isPressing.has(target))
|
|
3387
|
-
return;
|
|
3388
|
-
isPressing.add(target);
|
|
3389
|
-
const onPressEnd = onPressStart(target, startEvent);
|
|
3390
|
-
const onPointerEnd = (endEvent, success) => {
|
|
3391
|
-
window.removeEventListener("pointerup", onPointerUp);
|
|
3392
|
-
window.removeEventListener("pointercancel", onPointerCancel);
|
|
3393
|
-
if (!isValidPressEvent(endEvent) || !isPressing.has(target)) {
|
|
3394
|
-
return;
|
|
3395
|
-
}
|
|
3396
|
-
isPressing.delete(target);
|
|
3397
|
-
if (typeof onPressEnd === "function") {
|
|
3398
|
-
onPressEnd(endEvent, { success });
|
|
3399
|
-
}
|
|
3400
|
-
};
|
|
3401
|
-
const onPointerUp = (upEvent) => {
|
|
3402
|
-
onPointerEnd(upEvent, target === window ||
|
|
3403
|
-
target === document ||
|
|
3404
|
-
options.useGlobalTarget ||
|
|
3405
|
-
isNodeOrChild(target, upEvent.target));
|
|
3406
|
-
};
|
|
3407
|
-
const onPointerCancel = (cancelEvent) => {
|
|
3408
|
-
onPointerEnd(cancelEvent, false);
|
|
3409
|
-
};
|
|
3410
|
-
window.addEventListener("pointerup", onPointerUp, eventOptions);
|
|
3411
|
-
window.addEventListener("pointercancel", onPointerCancel, eventOptions);
|
|
3412
|
-
};
|
|
3413
|
-
targets.forEach((target) => {
|
|
3414
|
-
const pointerDownTarget = options.useGlobalTarget ? window : target;
|
|
3415
|
-
pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
|
|
3416
|
-
if (target instanceof HTMLElement) {
|
|
3417
|
-
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
|
|
3418
|
-
if (!isElementKeyboardAccessible(target) &&
|
|
3419
|
-
!target.hasAttribute("tabindex")) {
|
|
3420
|
-
target.tabIndex = 0;
|
|
3421
|
-
}
|
|
3422
|
-
}
|
|
3423
|
-
});
|
|
3424
|
-
return cancelEvents;
|
|
3425
|
-
}
|
|
3426
|
-
|
|
3427
3190
|
/**
|
|
3428
3191
|
* Maximum time between the value of two frames, beyond which we
|
|
3429
3192
|
* assume the velocity has since been 0.
|
|
@@ -3477,6 +3240,11 @@ class MotionValue {
|
|
|
3477
3240
|
// Update update subscribers
|
|
3478
3241
|
if (this.current !== this.prev) {
|
|
3479
3242
|
this.events.change?.notify(this.current);
|
|
3243
|
+
if (this.dependents) {
|
|
3244
|
+
for (const dependent of this.dependents) {
|
|
3245
|
+
dependent.dirty();
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3480
3248
|
}
|
|
3481
3249
|
// Update render subscribers
|
|
3482
3250
|
if (render) {
|
|
@@ -3618,6 +3386,20 @@ class MotionValue {
|
|
|
3618
3386
|
if (this.stopPassiveEffect)
|
|
3619
3387
|
this.stopPassiveEffect();
|
|
3620
3388
|
}
|
|
3389
|
+
dirty() {
|
|
3390
|
+
this.events.change?.notify(this.current);
|
|
3391
|
+
}
|
|
3392
|
+
addDependent(dependent) {
|
|
3393
|
+
if (!this.dependents) {
|
|
3394
|
+
this.dependents = new Set();
|
|
3395
|
+
}
|
|
3396
|
+
this.dependents.add(dependent);
|
|
3397
|
+
}
|
|
3398
|
+
removeDependent(dependent) {
|
|
3399
|
+
if (this.dependents) {
|
|
3400
|
+
this.dependents.delete(dependent);
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3621
3403
|
/**
|
|
3622
3404
|
* Returns the latest state of `MotionValue`
|
|
3623
3405
|
*
|
|
@@ -3713,6 +3495,7 @@ class MotionValue {
|
|
|
3713
3495
|
* @public
|
|
3714
3496
|
*/
|
|
3715
3497
|
destroy() {
|
|
3498
|
+
this.dependents?.clear();
|
|
3716
3499
|
this.events.destroy?.notify();
|
|
3717
3500
|
this.clearListeners();
|
|
3718
3501
|
this.stop();
|
|
@@ -3725,15 +3508,6 @@ function motionValue(init, options) {
|
|
|
3725
3508
|
return new MotionValue(init, options);
|
|
3726
3509
|
}
|
|
3727
3510
|
|
|
3728
|
-
/**
|
|
3729
|
-
* A list of all ValueTypes
|
|
3730
|
-
*/
|
|
3731
|
-
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
3732
|
-
/**
|
|
3733
|
-
* Tests a value against the list of ValueTypes
|
|
3734
|
-
*/
|
|
3735
|
-
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
3736
|
-
|
|
3737
3511
|
/**
|
|
3738
3512
|
* Provided a value and a ValueType, returns the value as that value type.
|
|
3739
3513
|
*/
|
|
@@ -3743,6 +3517,254 @@ const getValueAsType = (value, type) => {
|
|
|
3743
3517
|
: value;
|
|
3744
3518
|
};
|
|
3745
3519
|
|
|
3520
|
+
const { schedule: microtask, cancel: cancelMicrotask } =
|
|
3521
|
+
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
|
3522
|
+
|
|
3523
|
+
const isDragging = {
|
|
3524
|
+
x: false,
|
|
3525
|
+
y: false,
|
|
3526
|
+
};
|
|
3527
|
+
function isDragActive() {
|
|
3528
|
+
return isDragging.x || isDragging.y;
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
function setDragLock(axis) {
|
|
3532
|
+
if (axis === "x" || axis === "y") {
|
|
3533
|
+
if (isDragging[axis]) {
|
|
3534
|
+
return null;
|
|
3535
|
+
}
|
|
3536
|
+
else {
|
|
3537
|
+
isDragging[axis] = true;
|
|
3538
|
+
return () => {
|
|
3539
|
+
isDragging[axis] = false;
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
else {
|
|
3544
|
+
if (isDragging.x || isDragging.y) {
|
|
3545
|
+
return null;
|
|
3546
|
+
}
|
|
3547
|
+
else {
|
|
3548
|
+
isDragging.x = isDragging.y = true;
|
|
3549
|
+
return () => {
|
|
3550
|
+
isDragging.x = isDragging.y = false;
|
|
3551
|
+
};
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
|
|
3556
|
+
function setupGesture(elementOrSelector, options) {
|
|
3557
|
+
const elements = resolveElements(elementOrSelector);
|
|
3558
|
+
const gestureAbortController = new AbortController();
|
|
3559
|
+
const eventOptions = {
|
|
3560
|
+
passive: true,
|
|
3561
|
+
...options,
|
|
3562
|
+
signal: gestureAbortController.signal,
|
|
3563
|
+
};
|
|
3564
|
+
const cancel = () => gestureAbortController.abort();
|
|
3565
|
+
return [elements, eventOptions, cancel];
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
function isValidHover(event) {
|
|
3569
|
+
return !(event.pointerType === "touch" || isDragActive());
|
|
3570
|
+
}
|
|
3571
|
+
/**
|
|
3572
|
+
* Create a hover gesture. hover() is different to .addEventListener("pointerenter")
|
|
3573
|
+
* in that it has an easier syntax, filters out polyfilled touch events, interoperates
|
|
3574
|
+
* with drag gestures, and automatically removes the "pointerennd" event listener when the hover ends.
|
|
3575
|
+
*
|
|
3576
|
+
* @public
|
|
3577
|
+
*/
|
|
3578
|
+
function hover(elementOrSelector, onHoverStart, options = {}) {
|
|
3579
|
+
const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options);
|
|
3580
|
+
const onPointerEnter = (enterEvent) => {
|
|
3581
|
+
if (!isValidHover(enterEvent))
|
|
3582
|
+
return;
|
|
3583
|
+
const { target } = enterEvent;
|
|
3584
|
+
const onHoverEnd = onHoverStart(target, enterEvent);
|
|
3585
|
+
if (typeof onHoverEnd !== "function" || !target)
|
|
3586
|
+
return;
|
|
3587
|
+
const onPointerLeave = (leaveEvent) => {
|
|
3588
|
+
if (!isValidHover(leaveEvent))
|
|
3589
|
+
return;
|
|
3590
|
+
onHoverEnd(leaveEvent);
|
|
3591
|
+
target.removeEventListener("pointerleave", onPointerLeave);
|
|
3592
|
+
};
|
|
3593
|
+
target.addEventListener("pointerleave", onPointerLeave, eventOptions);
|
|
3594
|
+
};
|
|
3595
|
+
elements.forEach((element) => {
|
|
3596
|
+
element.addEventListener("pointerenter", onPointerEnter, eventOptions);
|
|
3597
|
+
});
|
|
3598
|
+
return cancel;
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
/**
|
|
3602
|
+
* Recursively traverse up the tree to check whether the provided child node
|
|
3603
|
+
* is the parent or a descendant of it.
|
|
3604
|
+
*
|
|
3605
|
+
* @param parent - Element to find
|
|
3606
|
+
* @param child - Element to test against parent
|
|
3607
|
+
*/
|
|
3608
|
+
const isNodeOrChild = (parent, child) => {
|
|
3609
|
+
if (!child) {
|
|
3610
|
+
return false;
|
|
3611
|
+
}
|
|
3612
|
+
else if (parent === child) {
|
|
3613
|
+
return true;
|
|
3614
|
+
}
|
|
3615
|
+
else {
|
|
3616
|
+
return isNodeOrChild(parent, child.parentElement);
|
|
3617
|
+
}
|
|
3618
|
+
};
|
|
3619
|
+
|
|
3620
|
+
const isPrimaryPointer = (event) => {
|
|
3621
|
+
if (event.pointerType === "mouse") {
|
|
3622
|
+
return typeof event.button !== "number" || event.button <= 0;
|
|
3623
|
+
}
|
|
3624
|
+
else {
|
|
3625
|
+
/**
|
|
3626
|
+
* isPrimary is true for all mice buttons, whereas every touch point
|
|
3627
|
+
* is regarded as its own input. So subsequent concurrent touch points
|
|
3628
|
+
* will be false.
|
|
3629
|
+
*
|
|
3630
|
+
* Specifically match against false here as incomplete versions of
|
|
3631
|
+
* PointerEvents in very old browser might have it set as undefined.
|
|
3632
|
+
*/
|
|
3633
|
+
return event.isPrimary !== false;
|
|
3634
|
+
}
|
|
3635
|
+
};
|
|
3636
|
+
|
|
3637
|
+
const focusableElements = new Set([
|
|
3638
|
+
"BUTTON",
|
|
3639
|
+
"INPUT",
|
|
3640
|
+
"SELECT",
|
|
3641
|
+
"TEXTAREA",
|
|
3642
|
+
"A",
|
|
3643
|
+
]);
|
|
3644
|
+
function isElementKeyboardAccessible(element) {
|
|
3645
|
+
return (focusableElements.has(element.tagName) ||
|
|
3646
|
+
element.tabIndex !== -1);
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
const isPressing = new WeakSet();
|
|
3650
|
+
|
|
3651
|
+
/**
|
|
3652
|
+
* Filter out events that are not "Enter" keys.
|
|
3653
|
+
*/
|
|
3654
|
+
function filterEvents(callback) {
|
|
3655
|
+
return (event) => {
|
|
3656
|
+
if (event.key !== "Enter")
|
|
3657
|
+
return;
|
|
3658
|
+
callback(event);
|
|
3659
|
+
};
|
|
3660
|
+
}
|
|
3661
|
+
function firePointerEvent(target, type) {
|
|
3662
|
+
target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
|
|
3663
|
+
}
|
|
3664
|
+
const enableKeyboardPress = (focusEvent, eventOptions) => {
|
|
3665
|
+
const element = focusEvent.currentTarget;
|
|
3666
|
+
if (!element)
|
|
3667
|
+
return;
|
|
3668
|
+
const handleKeydown = filterEvents(() => {
|
|
3669
|
+
if (isPressing.has(element))
|
|
3670
|
+
return;
|
|
3671
|
+
firePointerEvent(element, "down");
|
|
3672
|
+
const handleKeyup = filterEvents(() => {
|
|
3673
|
+
firePointerEvent(element, "up");
|
|
3674
|
+
});
|
|
3675
|
+
const handleBlur = () => firePointerEvent(element, "cancel");
|
|
3676
|
+
element.addEventListener("keyup", handleKeyup, eventOptions);
|
|
3677
|
+
element.addEventListener("blur", handleBlur, eventOptions);
|
|
3678
|
+
});
|
|
3679
|
+
element.addEventListener("keydown", handleKeydown, eventOptions);
|
|
3680
|
+
/**
|
|
3681
|
+
* Add an event listener that fires on blur to remove the keydown events.
|
|
3682
|
+
*/
|
|
3683
|
+
element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
|
|
3684
|
+
};
|
|
3685
|
+
|
|
3686
|
+
/**
|
|
3687
|
+
* Filter out events that are not primary pointer events, or are triggering
|
|
3688
|
+
* while a Motion gesture is active.
|
|
3689
|
+
*/
|
|
3690
|
+
function isValidPressEvent(event) {
|
|
3691
|
+
return isPrimaryPointer(event) && !isDragActive();
|
|
3692
|
+
}
|
|
3693
|
+
/**
|
|
3694
|
+
* Create a press gesture.
|
|
3695
|
+
*
|
|
3696
|
+
* Press is different to `"pointerdown"`, `"pointerup"` in that it
|
|
3697
|
+
* automatically filters out secondary pointer events like right
|
|
3698
|
+
* click and multitouch.
|
|
3699
|
+
*
|
|
3700
|
+
* It also adds accessibility support for keyboards, where
|
|
3701
|
+
* an element with a press gesture will receive focus and
|
|
3702
|
+
* trigger on Enter `"keydown"` and `"keyup"` events.
|
|
3703
|
+
*
|
|
3704
|
+
* This is different to a browser's `"click"` event, which does
|
|
3705
|
+
* respond to keyboards but only for the `"click"` itself, rather
|
|
3706
|
+
* than the press start and end/cancel. The element also needs
|
|
3707
|
+
* to be focusable for this to work, whereas a press gesture will
|
|
3708
|
+
* make an element focusable by default.
|
|
3709
|
+
*
|
|
3710
|
+
* @public
|
|
3711
|
+
*/
|
|
3712
|
+
function press(targetOrSelector, onPressStart, options = {}) {
|
|
3713
|
+
const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
|
|
3714
|
+
const startPress = (startEvent) => {
|
|
3715
|
+
const target = startEvent.currentTarget;
|
|
3716
|
+
if (!isValidPressEvent(startEvent) || isPressing.has(target))
|
|
3717
|
+
return;
|
|
3718
|
+
isPressing.add(target);
|
|
3719
|
+
const onPressEnd = onPressStart(target, startEvent);
|
|
3720
|
+
const onPointerEnd = (endEvent, success) => {
|
|
3721
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
3722
|
+
window.removeEventListener("pointercancel", onPointerCancel);
|
|
3723
|
+
if (isPressing.has(target)) {
|
|
3724
|
+
isPressing.delete(target);
|
|
3725
|
+
}
|
|
3726
|
+
if (!isValidPressEvent(endEvent)) {
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
if (typeof onPressEnd === "function") {
|
|
3730
|
+
onPressEnd(endEvent, { success });
|
|
3731
|
+
}
|
|
3732
|
+
};
|
|
3733
|
+
const onPointerUp = (upEvent) => {
|
|
3734
|
+
onPointerEnd(upEvent, target === window ||
|
|
3735
|
+
target === document ||
|
|
3736
|
+
options.useGlobalTarget ||
|
|
3737
|
+
isNodeOrChild(target, upEvent.target));
|
|
3738
|
+
};
|
|
3739
|
+
const onPointerCancel = (cancelEvent) => {
|
|
3740
|
+
onPointerEnd(cancelEvent, false);
|
|
3741
|
+
};
|
|
3742
|
+
window.addEventListener("pointerup", onPointerUp, eventOptions);
|
|
3743
|
+
window.addEventListener("pointercancel", onPointerCancel, eventOptions);
|
|
3744
|
+
};
|
|
3745
|
+
targets.forEach((target) => {
|
|
3746
|
+
const pointerDownTarget = options.useGlobalTarget ? window : target;
|
|
3747
|
+
pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
|
|
3748
|
+
if (target instanceof HTMLElement) {
|
|
3749
|
+
target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
|
|
3750
|
+
if (!isElementKeyboardAccessible(target) &&
|
|
3751
|
+
!target.hasAttribute("tabindex")) {
|
|
3752
|
+
target.tabIndex = 0;
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
});
|
|
3756
|
+
return cancelEvents;
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
/**
|
|
3760
|
+
* A list of all ValueTypes
|
|
3761
|
+
*/
|
|
3762
|
+
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
3763
|
+
/**
|
|
3764
|
+
* Tests a value against the list of ValueTypes
|
|
3765
|
+
*/
|
|
3766
|
+
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
3767
|
+
|
|
3746
3768
|
const isKeyframesTarget = (v) => {
|
|
3747
3769
|
return Array.isArray(v);
|
|
3748
3770
|
};
|
package/dist/cjs/react-m.js
CHANGED
|
@@ -485,9 +485,6 @@ const numberValueTypes = {
|
|
|
485
485
|
numOctaves: int,
|
|
486
486
|
};
|
|
487
487
|
|
|
488
|
-
const { schedule: microtask, cancel: cancelMicrotask } =
|
|
489
|
-
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
|
490
|
-
|
|
491
488
|
/**
|
|
492
489
|
* Provided a value and a ValueType, returns the value as that value type.
|
|
493
490
|
*/
|
|
@@ -497,6 +494,9 @@ const getValueAsType = (value, type) => {
|
|
|
497
494
|
: value;
|
|
498
495
|
};
|
|
499
496
|
|
|
497
|
+
const { schedule: microtask, cancel: cancelMicrotask } =
|
|
498
|
+
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
|
499
|
+
|
|
500
500
|
/**
|
|
501
501
|
* Convert camelCase to dash-case properties.
|
|
502
502
|
*/
|
|
@@ -39,7 +39,7 @@ export { supportsPartialKeyframes } from '../../motion-dom/dist/es/animation/waa
|
|
|
39
39
|
export { supportsBrowserAnimation } from '../../motion-dom/dist/es/animation/waapi/supports/waapi.mjs';
|
|
40
40
|
export { acceleratedValues } from '../../motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs';
|
|
41
41
|
export { generateLinearEasing } from '../../motion-dom/dist/es/animation/waapi/utils/linear.mjs';
|
|
42
|
-
export { styleEffect } from '../../motion-dom/dist/es/effects/style
|
|
42
|
+
export { styleEffect } from '../../motion-dom/dist/es/effects/style/index.mjs';
|
|
43
43
|
export { createRenderBatcher } from '../../motion-dom/dist/es/frameloop/batcher.mjs';
|
|
44
44
|
export { cancelMicrotask, microtask } from '../../motion-dom/dist/es/frameloop/microtask.mjs';
|
|
45
45
|
export { time } from '../../motion-dom/dist/es/frameloop/sync-time.mjs';
|
|
@@ -138,7 +138,7 @@ export { supportsPartialKeyframes } from '../../motion-dom/dist/es/animation/waa
|
|
|
138
138
|
export { supportsBrowserAnimation } from '../../motion-dom/dist/es/animation/waapi/supports/waapi.mjs';
|
|
139
139
|
export { acceleratedValues } from '../../motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs';
|
|
140
140
|
export { generateLinearEasing } from '../../motion-dom/dist/es/animation/waapi/utils/linear.mjs';
|
|
141
|
-
export { styleEffect } from '../../motion-dom/dist/es/effects/style
|
|
141
|
+
export { styleEffect } from '../../motion-dom/dist/es/effects/style/index.mjs';
|
|
142
142
|
export { createRenderBatcher } from '../../motion-dom/dist/es/frameloop/batcher.mjs';
|
|
143
143
|
export { cancelMicrotask, microtask } from '../../motion-dom/dist/es/frameloop/microtask.mjs';
|
|
144
144
|
export { time } from '../../motion-dom/dist/es/frameloop/sync-time.mjs';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { frame, cancelFrame } from '../frameloop/frame.mjs';
|
|
2
|
+
import { numberValueTypes } from '../value/types/maps/number.mjs';
|
|
3
|
+
import { getValueAsType } from '../value/types/utils/get-as-type.mjs';
|
|
4
|
+
|
|
5
|
+
class MotionValueState {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.latest = {};
|
|
8
|
+
this.values = new Map();
|
|
9
|
+
}
|
|
10
|
+
set(name, value, render, computed) {
|
|
11
|
+
const existingValue = this.values.get(name);
|
|
12
|
+
if (existingValue) {
|
|
13
|
+
existingValue.onRemove();
|
|
14
|
+
}
|
|
15
|
+
const onChange = () => {
|
|
16
|
+
this.latest[name] = getValueAsType(value.get(), numberValueTypes[name]);
|
|
17
|
+
render && frame.render(render);
|
|
18
|
+
};
|
|
19
|
+
onChange();
|
|
20
|
+
const cancelOnChange = value.on("change", onChange);
|
|
21
|
+
computed && value.addDependent(computed);
|
|
22
|
+
const remove = () => {
|
|
23
|
+
cancelOnChange();
|
|
24
|
+
render && cancelFrame(render);
|
|
25
|
+
this.values.delete(name);
|
|
26
|
+
computed && value.removeDependent(computed);
|
|
27
|
+
};
|
|
28
|
+
this.values.set(name, { value, onRemove: remove });
|
|
29
|
+
return remove;
|
|
30
|
+
}
|
|
31
|
+
get(name) {
|
|
32
|
+
return this.values.get(name)?.value;
|
|
33
|
+
}
|
|
34
|
+
destroy() {
|
|
35
|
+
for (const value of this.values.values()) {
|
|
36
|
+
value.onRemove();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { MotionValueState };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { isCSSVar } from '../../render/dom/is-css-var.mjs';
|
|
2
|
+
import { transformProps } from '../../render/utils/keys-transform.mjs';
|
|
3
|
+
import { resolveElements } from '../../utils/resolve-elements.mjs';
|
|
4
|
+
import { MotionValue } from '../../value/index.mjs';
|
|
5
|
+
import { MotionValueState } from '../MotionValueState.mjs';
|
|
6
|
+
import { buildTransform } from './transform.mjs';
|
|
7
|
+
|
|
8
|
+
const stateMap = new WeakMap();
|
|
9
|
+
function styleEffect(subject, values) {
|
|
10
|
+
const elements = resolveElements(subject);
|
|
11
|
+
const subscriptions = [];
|
|
12
|
+
for (let i = 0; i < elements.length; i++) {
|
|
13
|
+
const element = elements[i];
|
|
14
|
+
const state = stateMap.get(element) ?? new MotionValueState();
|
|
15
|
+
stateMap.set(element, state);
|
|
16
|
+
for (const key in values) {
|
|
17
|
+
const value = values[key];
|
|
18
|
+
const remove = addValue(element, state, key, value);
|
|
19
|
+
subscriptions.push(remove);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return () => {
|
|
23
|
+
for (const cancel of subscriptions)
|
|
24
|
+
cancel();
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function addValue(element, state, key, value) {
|
|
28
|
+
let render = undefined;
|
|
29
|
+
let computed = undefined;
|
|
30
|
+
if (transformProps.has(key)) {
|
|
31
|
+
if (!state.get("transform")) {
|
|
32
|
+
state.set("transform", new MotionValue("none"), () => {
|
|
33
|
+
element.style.transform = buildTransform(state);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
computed = state.get("transform");
|
|
37
|
+
}
|
|
38
|
+
else if (isCSSVar(key)) {
|
|
39
|
+
render = () => {
|
|
40
|
+
element.style.setProperty(key, state.latest[key]);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
render = () => {
|
|
45
|
+
element.style[key] = state.latest[key];
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return state.set(key, value, render, computed);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { styleEffect };
|