@kitlangton/solid-motion 0.1.0 → 0.2.1

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.
@@ -1,8 +1,7 @@
1
- import { addValueToWillChange, animateMotionValue, calcLength, convertBoundingBoxToBox, convertBoxToBoundingBox, createBox, eachAxis, frame, cancelFrame, measurePageBox, mixNumber, percent, setDragLock, } from "motion-dom";
1
+ import { addValueToWillChange, animateMotionValue, calcLength, convertBoundingBoxToBox, convertBoxToBoundingBox, createBox, eachAxis, frame, isElementTextInput, cancelFrame, measurePageBox, mixNumber, percent, resize, setDragLock, } from "motion-dom";
2
2
  import { invariant } from "motion-utils";
3
3
  import { addDomEvent } from "motion-dom";
4
4
  import { addPointerEvent } from "../../events/add-pointer-event";
5
- import { extractEventInfo } from "../../events/event-info";
6
5
  import { getContextWindow } from "../../utils/get-context-window";
7
6
  import { isRefObject } from "../../utils/is-ref-object";
8
7
  import { PanSession } from "../pan/PanSession";
@@ -41,21 +40,11 @@ export class VisualElementDragControls {
41
40
  if (presenceContext && presenceContext.isPresent === false)
42
41
  return;
43
42
  const onSessionStart = (event) => {
44
- // Stop or pause animations based on context:
45
- // - snapToCursor: stop because we'll set new position values
46
- // - otherwise: pause to allow resume if no drag starts (for constraint animations)
47
- if (snapToCursor) {
48
- this.stopAnimation();
49
- this.snapToCursor(extractEventInfo(event).point);
50
- }
51
- else {
52
- this.pauseAnimation();
53
- }
43
+ if (snapToCursor)
44
+ this.snapToCursor(event);
45
+ this.stopAnimation();
54
46
  };
55
47
  const onStart = (event, info) => {
56
- // Stop any paused animation so motion values reflect true current position
57
- // (pauseAnimation was called in onSessionStart to allow resume if no drag started)
58
- this.stopAnimation();
59
48
  // Attempt to grab the global drag gesture lock - maybe make this part of PanSession
60
49
  const { drag, dragPropagation, onDragStart } = this.getProps();
61
50
  if (drag && !dragPropagation) {
@@ -97,7 +86,7 @@ export class VisualElementDragControls {
97
86
  });
98
87
  // Fire onDragStart event
99
88
  if (onDragStart) {
100
- frame.postRender(() => onDragStart(event, info));
89
+ frame.update(() => onDragStart(event, info), false, true);
101
90
  }
102
91
  addValueToWillChange(this.visualElement, "transform");
103
92
  const { animationState } = this.visualElement;
@@ -134,7 +123,9 @@ export class VisualElementDragControls {
134
123
  * This must fire after the render call as it might trigger a state
135
124
  * change which itself might trigger a layout update.
136
125
  */
137
- onDrag && onDrag(event, info);
126
+ if (onDrag) {
127
+ frame.update(() => onDrag(event, info), false, true);
128
+ }
138
129
  };
139
130
  const onSessionEnd = (event, info) => {
140
131
  this.latestPointerEvent = event;
@@ -143,8 +134,12 @@ export class VisualElementDragControls {
143
134
  this.latestPointerEvent = null;
144
135
  this.latestPanInfo = null;
145
136
  };
146
- const resumeAnimation = () => eachAxis((axis) => this.getAnimationState(axis) === "paused" &&
147
- this.getAxisMotionValue(axis).animation?.play());
137
+ const resumeAnimation = () => {
138
+ const { dragSnapToOrigin: snap } = this.getProps();
139
+ if (snap || this.constraints) {
140
+ this.startAnimation({ x: 0, y: 0 });
141
+ }
142
+ };
148
143
  const { dragSnapToOrigin } = this.getProps();
149
144
  this.panSession = new PanSession(originEvent, {
150
145
  onSessionStart,
@@ -246,9 +241,12 @@ export class VisualElementDragControls {
246
241
  this.elastic = resolveDragElastic(dragElastic);
247
242
  /**
248
243
  * If we're outputting to external MotionValues, we want to rebase the measured constraints
249
- * from viewport-relative to component-relative.
244
+ * from viewport-relative to component-relative. This only applies to relative (non-ref)
245
+ * constraints, as ref-based constraints from calcViewportConstraints are already in the
246
+ * correct coordinate space for the motion value transform offset.
250
247
  */
251
248
  if (prevConstraints !== this.constraints &&
249
+ !isRefObject(dragConstraints) &&
252
250
  layout &&
253
251
  this.constraints &&
254
252
  !this.hasMutatedConstraints) {
@@ -270,6 +268,20 @@ export class VisualElementDragControls {
270
268
  // TODO
271
269
  if (!projection || !projection.layout)
272
270
  return false;
271
+ /**
272
+ * Refresh the root scroll offset so the constraint's viewport box
273
+ * translates to correct page coordinates. The scroll captured at
274
+ * drag mount can be stale if the document was scrolled afterwards —
275
+ * e.g. via the browser restoring scroll on refresh, or an ancestor
276
+ * layout effect running after this element's mount (#2829).
277
+ *
278
+ * Clear the cached scroll first so `updateScroll` bypasses its
279
+ * per-animationId cache and re-reads the live value.
280
+ */
281
+ if (projection.root) {
282
+ projection.root.scroll = undefined;
283
+ projection.root.updateScroll();
284
+ }
273
285
  const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint());
274
286
  let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox);
275
287
  /**
@@ -293,7 +305,8 @@ export class VisualElementDragControls {
293
305
  return;
294
306
  }
295
307
  let transition = (constraints && constraints[axis]) || {};
296
- if (dragSnapToOrigin)
308
+ if (dragSnapToOrigin === true ||
309
+ dragSnapToOrigin === axis)
297
310
  transition = { min: 0, max: 0 };
298
311
  /**
299
312
  * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame
@@ -330,12 +343,6 @@ export class VisualElementDragControls {
330
343
  stopAnimation() {
331
344
  eachAxis((axis) => this.getAxisMotionValue(axis).stop());
332
345
  }
333
- pauseAnimation() {
334
- eachAxis((axis) => this.getAxisMotionValue(axis).animation?.pause());
335
- }
336
- getAnimationState(axis) {
337
- return this.getAxisMotionValue(axis).animation?.state;
338
- }
339
346
  /**
340
347
  * Drag works differently depending on which props are provided.
341
348
  *
@@ -348,29 +355,23 @@ export class VisualElementDragControls {
348
355
  const externalMotionValue = props[dragKey];
349
356
  return externalMotionValue
350
357
  ? externalMotionValue
351
- : this.visualElement.getValue(axis, (props.initial
352
- ? props.initial[axis]
353
- : undefined) || 0);
358
+ : this.visualElement.getValue(axis, this.visualElement.latestValues[axis] ?? 0);
354
359
  }
355
- snapToCursor(point) {
360
+ /**
361
+ * Measure the live box - projection.layout may or may not include the
362
+ * drag transform, depending on when it was measured.
363
+ */
364
+ snapToCursor({ clientX, clientY }) {
365
+ const { drag } = this.getProps();
366
+ const point = { x: clientX, y: clientY };
367
+ const cursor = this.visualElement.getTransformPagePoint()?.(point) || point;
368
+ const box = this.visualElement.measureViewportBox();
356
369
  eachAxis((axis) => {
357
- const { drag } = this.getProps();
358
- // If we're not dragging this axis, do an early return.
359
370
  if (!shouldDrag(axis, drag, this.currentDirection))
360
371
  return;
361
- const { projection } = this.visualElement;
362
372
  const axisValue = this.getAxisMotionValue(axis);
363
- if (projection && projection.layout) {
364
- const { min, max } = projection.layout.layoutBox[axis];
365
- /**
366
- * The layout measurement includes the current transform value,
367
- * so we need to add it back to get the correct snap position.
368
- * This fixes an issue where elements with initial coordinates
369
- * would snap to the wrong position on the first drag.
370
- */
371
- const current = axisValue.get() || 0;
372
- axisValue.set(point[axis] - mixNumber(min, max, 0.5) + current);
373
- }
373
+ const { min, max } = box[axis];
374
+ axisValue.set((axisValue.get() || 0) + cursor[axis] - mixNumber(min, max, 0.5));
374
375
  });
375
376
  }
376
377
  /**
@@ -394,13 +395,11 @@ export class VisualElementDragControls {
394
395
  * Record the relative position of the dragged element relative to the
395
396
  * constraints box and save as a progress value.
396
397
  */
398
+ const constraints = this.constraints;
397
399
  const boxProgress = { x: 0, y: 0 };
398
400
  eachAxis((axis) => {
399
- const axisValue = this.getAxisMotionValue(axis);
400
- if (axisValue && this.constraints !== false) {
401
- const latest = axisValue.get();
402
- boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]);
403
- }
401
+ const latest = this.getAxisMotionValue(axis).get();
402
+ boxProgress[axis] = calcOrigin({ min: latest, max: latest }, constraints[axis]);
404
403
  });
405
404
  /**
406
405
  * Update the layout of this element and resolve the latest drag constraints
@@ -411,21 +410,36 @@ export class VisualElementDragControls {
411
410
  : "none";
412
411
  projection.root && projection.root.updateScroll();
413
412
  projection.updateLayout();
413
+ /**
414
+ * Reset constraints so resolveConstraints() will recalculate them
415
+ * with the freshly measured layout rather than returning the cached value.
416
+ */
417
+ this.constraints = false;
414
418
  this.resolveConstraints();
415
419
  /**
416
420
  * For each axis, calculate the current progress of the layout axis
417
421
  * within the new constraints.
418
422
  */
419
423
  eachAxis((axis) => {
420
- if (!shouldDrag(axis, drag, null))
424
+ const axisValue = this.getAxisMotionValue(axis);
425
+ /**
426
+ * An element resting at its origin has no offset to preserve,
427
+ * and re-projecting 0 into the new constraints would move it.
428
+ */
429
+ if (!shouldDrag(axis, drag, null) || !axisValue.get())
421
430
  return;
422
431
  /**
423
432
  * Calculate a new transform based on the previous box progress
424
433
  */
425
- const axisValue = this.getAxisMotionValue(axis);
426
434
  const { min, max } = this.constraints[axis];
427
435
  axisValue.set(mixNumber(min, max, boxProgress[axis]));
428
436
  });
437
+ /**
438
+ * Flush the updated transform to the DOM synchronously to prevent
439
+ * a visual flash at the element's CSS layout position (0,0) when
440
+ * the transform was stripped for measurement.
441
+ */
442
+ this.visualElement.render();
429
443
  }
430
444
  addListeners() {
431
445
  if (!this.visualElement.current)
@@ -439,20 +453,32 @@ export class VisualElementDragControls {
439
453
  const { drag, dragListener = true } = this.getProps();
440
454
  const target = event.target;
441
455
  /**
442
- * Allow dragging from buttons/links (e.g. carousel items), while
443
- * preserving text editing and native input interactions.
456
+ * Only block drag if clicking on a text input child element
457
+ * (input, textarea, select, contenteditable) where users might
458
+ * want to select text or interact with the control.
459
+ *
460
+ * Buttons and links don't block drag since they don't have
461
+ * click-and-move actions of their own.
444
462
  */
445
- const isClickingTextInputChild = target !== element &&
446
- (Boolean(target.closest("input, textarea, select")) ||
447
- (target instanceof HTMLElement && target.isContentEditable));
463
+ const isClickingTextInputChild = target !== element && isElementTextInput(target);
448
464
  if (drag && dragListener && !isClickingTextInputChild) {
449
465
  this.start(event);
450
466
  }
451
467
  });
468
+ /**
469
+ * If using ref-based constraints, observe both the draggable element
470
+ * and the constraint container for size changes via ResizeObserver.
471
+ * Setup is deferred because dragConstraints.current is null when
472
+ * addListeners first runs (React hasn't committed the ref yet).
473
+ */
474
+ let stopResizeObservers;
452
475
  const measureDragConstraints = () => {
453
476
  const { dragConstraints } = this.getProps();
454
477
  if (isRefObject(dragConstraints) && dragConstraints.current) {
455
478
  this.constraints = this.resolveRefConstraints();
479
+ if (!stopResizeObservers) {
480
+ stopResizeObservers = startResizeObservers(element, dragConstraints.current, () => this.scalePositionWithinConstraints());
481
+ }
456
482
  }
457
483
  };
458
484
  const { projection } = this.visualElement;
@@ -489,6 +515,7 @@ export class VisualElementDragControls {
489
515
  stopPointerListener();
490
516
  stopMeasureLayoutListener();
491
517
  stopLayoutUpdateListener && stopLayoutUpdateListener();
518
+ stopResizeObservers && stopResizeObservers();
492
519
  };
493
520
  }
494
521
  getProps() {
@@ -505,6 +532,24 @@ export class VisualElementDragControls {
505
532
  };
506
533
  }
507
534
  }
535
+ function skipFirstCall(callback) {
536
+ let isFirst = true;
537
+ return () => {
538
+ if (isFirst) {
539
+ isFirst = false;
540
+ return;
541
+ }
542
+ callback();
543
+ };
544
+ }
545
+ function startResizeObservers(element, constraintsElement, onResize) {
546
+ const stopElement = resize(element, skipFirstCall(onResize));
547
+ const stopContainer = resize(constraintsElement, skipFirstCall(onResize));
548
+ return () => {
549
+ stopElement();
550
+ stopContainer();
551
+ };
552
+ }
508
553
  function shouldDrag(direction, drag, currentDirection) {
509
554
  return ((drag === true || drag === direction) &&
510
555
  (currentDirection === null || currentDirection === direction));
@@ -82,7 +82,7 @@ export function calcViewportConstraints(layoutBox, constraintsBox) {
82
82
  }
83
83
  /**
84
84
  * Calculate a transform origin relative to the source axis, between 0-1, that results
85
- * in an asthetically pleasing scale/transform needed to project from source to target.
85
+ * in an aesthetically pleasing scale/transform needed to project from source to target.
86
86
  */
87
87
  export function calcOrigin(source, target) {
88
88
  let origin = 0.5;
@@ -1,4 +1,4 @@
1
- import { cancelFrame, frame, frameData, isPrimaryPointer } from "motion-dom";
1
+ import { cancelFrame, frame, frameData, isPrimaryPointer, time, } from "motion-dom";
2
2
  import { millisecondsToSeconds, pipe, secondsToMilliseconds, } from "motion-utils";
3
3
  import { addPointerEvent } from "../../events/add-pointer-event";
4
4
  import { extractEventInfo } from "../../events/event-info";
@@ -25,6 +25,12 @@ export class PanSession {
25
25
  * @internal
26
26
  */
27
27
  this.lastMoveEventInfo = null;
28
+ /**
29
+ * Raw (untransformed) event info, re-transformed each frame
30
+ * so transformPagePoint sees the current parent matrix.
31
+ * @internal
32
+ */
33
+ this.lastRawMoveEventInfo = null;
28
34
  /**
29
35
  * @internal
30
36
  */
@@ -34,7 +40,7 @@ export class PanSession {
34
40
  */
35
41
  this.removeListeners = () => { };
36
42
  /**
37
- * For determining if an animation should resume after it is interupted
43
+ * For determining if an animation should resume after it is interrupted
38
44
  *
39
45
  * @internal
40
46
  */
@@ -68,6 +74,12 @@ export class PanSession {
68
74
  this.updatePoint = () => {
69
75
  if (!(this.lastMoveEvent && this.lastMoveEventInfo))
70
76
  return;
77
+ this.hasPendingMove = false;
78
+ // Re-transform raw point through current transformPagePoint so
79
+ // animated parent transforms (e.g. rotation) are picked up each frame
80
+ if (this.lastRawMoveEventInfo) {
81
+ this.lastMoveEventInfo = transformPoint(this.lastRawMoveEventInfo, this.transformPagePoint);
82
+ }
71
83
  const info = getPanInfo(this.lastMoveEventInfo, this.history);
72
84
  const isPanStarted = this.startEvent !== null;
73
85
  // Only start panning if the offset is larger than 3 pixels. If we make it
@@ -77,8 +89,7 @@ export class PanSession {
77
89
  if (!isPanStarted && !isDistancePastThreshold)
78
90
  return;
79
91
  const { point } = info;
80
- const { timestamp } = frameData;
81
- this.history.push({ ...point, timestamp });
92
+ this.history.push({ ...point, timestamp: time.now() });
82
93
  const { onStart, onMove } = this.handlers;
83
94
  if (!isPanStarted) {
84
95
  onStart && onStart(this.lastMoveEvent, info);
@@ -88,11 +99,16 @@ export class PanSession {
88
99
  };
89
100
  this.handlePointerMove = (event, info) => {
90
101
  this.lastMoveEvent = event;
102
+ this.lastRawMoveEventInfo = info;
91
103
  this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
104
+ this.hasPendingMove = true;
92
105
  // Throttle mouse move event to once per frame
93
106
  frame.update(this.updatePoint, true);
94
107
  };
95
108
  this.handlePointerUp = (event, info) => {
109
+ // Browsers flush a coalesced pointermove immediately before
110
+ // pointerup, so the final move can still be waiting for a frame.
111
+ this.hasPendingMove && this.updatePoint();
96
112
  this.end();
97
113
  const { onEnd, onSessionEnd, resumeAnimation } = this.handlers;
98
114
  // Resume animation if dragSnapToOrigin is set OR if no drag started (user just clicked)
@@ -126,7 +142,11 @@ export class PanSession {
126
142
  const { onSessionStart } = handlers;
127
143
  onSessionStart &&
128
144
  onSessionStart(event, getPanInfo(initialInfo, this.history));
129
- this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp));
145
+ // Listen in the capture phase so a descendant calling
146
+ // stopPropagation() (e.g. in its own pointerup handler) can't
147
+ // prevent the gesture from ending. See #2794.
148
+ const eventOptions = { passive: true, capture: true };
149
+ this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove, eventOptions), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp, eventOptions), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp, eventOptions));
130
150
  // Start scroll tracking if element provided
131
151
  if (element) {
132
152
  this.startScrollTracking(element);
@@ -157,12 +177,9 @@ export class PanSession {
157
177
  // Capture listener catches element scroll events as they bubble
158
178
  window.addEventListener("scroll", this.onElementScroll, {
159
179
  capture: true,
160
- passive: true,
161
180
  });
162
181
  // Direct window scroll listener (window scroll doesn't bubble)
163
- window.addEventListener("scroll", this.onWindowScroll, {
164
- passive: true,
165
- });
182
+ window.addEventListener("scroll", this.onWindowScroll);
166
183
  this.removeScrollListeners = () => {
167
184
  window.removeEventListener("scroll", this.onElementScroll, {
168
185
  capture: true,
@@ -255,13 +272,26 @@ function getVelocity(history, timeDelta) {
255
272
  if (!timestampedPoint) {
256
273
  return { x: 0, y: 0 };
257
274
  }
258
- const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp);
259
- if (time === 0) {
275
+ /**
276
+ * If the selected point is the pointer-down origin (history[0]),
277
+ * there are better movement points available, and the time gap
278
+ * is suspiciously large (>2x timeDelta), use the next point instead.
279
+ * This prevents stale pointer-down points from diluting velocity
280
+ * in hold-then-flick gestures.
281
+ */
282
+ if (timestampedPoint === history[0] &&
283
+ history.length > 2 &&
284
+ lastPoint.timestamp - timestampedPoint.timestamp >
285
+ secondsToMilliseconds(timeDelta) * 2) {
286
+ timestampedPoint = history[1];
287
+ }
288
+ const seconds = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp);
289
+ if (seconds === 0) {
260
290
  return { x: 0, y: 0 };
261
291
  }
262
292
  const currentVelocity = {
263
- x: (lastPoint.x - timestampedPoint.x) / time,
264
- y: (lastPoint.y - timestampedPoint.y) / time,
293
+ x: (lastPoint.x - timestampedPoint.x) / seconds,
294
+ y: (lastPoint.y - timestampedPoint.y) / seconds,
265
295
  };
266
296
  if (currentVelocity.x === Infinity) {
267
297
  currentVelocity.x = 0;
@@ -5,7 +5,7 @@ import { getContextWindow } from "../../utils/get-context-window";
5
5
  import { PanSession } from "./PanSession";
6
6
  const asyncHandler = (handler) => (event, info) => {
7
7
  if (handler) {
8
- frame.postRender(() => handler(event, info));
8
+ frame.update(() => handler(event, info), false, true);
9
9
  }
10
10
  };
11
11
  export class PanGesture extends Feature {
@@ -24,7 +24,7 @@ export class PanGesture extends Feature {
24
24
  return {
25
25
  onSessionStart: asyncHandler(onPanSessionStart),
26
26
  onStart: asyncHandler(onPanStart),
27
- onMove: onPan,
27
+ onMove: asyncHandler(onPan),
28
28
  onEnd: (event, info) => {
29
29
  delete this.session;
30
30
  if (onPanEnd) {
@@ -19,10 +19,14 @@ export class PressGesture extends Feature {
19
19
  const { current } = this.node;
20
20
  if (!current)
21
21
  return;
22
+ const { globalTapTarget, propagate } = this.node.props;
22
23
  this.unmount = press(current, (_element, startEvent) => {
23
24
  handlePressEvent(this.node, startEvent, "Start");
24
25
  return (endEvent, { success }) => handlePressEvent(this.node, endEvent, success ? "End" : "Cancel");
25
- }, { useGlobalTarget: this.node.props.globalTapTarget });
26
+ }, {
27
+ useGlobalTarget: globalTapTarget,
28
+ stopPropagation: propagate?.tap === false,
29
+ });
26
30
  }
27
31
  unmount() { }
28
32
  }
@@ -1,6 +1,6 @@
1
1
  import { createEffect, createMemo, onCleanup, onMount, splitProps, useContext } from "solid-js";
2
2
  import { Dynamic } from "solid-js/web";
3
- import { scrapeHTMLMotionValuesFromProps, scrapeSVGMotionValuesFromProps } from "motion-dom";
3
+ import { isAnimationControls, scrapeHTMLMotionValuesFromProps, scrapeSVGMotionValuesFromProps, setTarget } from "motion-dom";
4
4
  import { MotionConfigContext } from "../components/MotionConfig";
5
5
  import { MotionContext } from "../context/MotionContext";
6
6
  import { PresenceContext } from "../context/PresenceContext";
@@ -102,6 +102,7 @@ export function createMotionComponent(Component, { forwardMotionProps = false, t
102
102
  return;
103
103
  visualElement.scheduleRenderMicrotask();
104
104
  });
105
+ let mounted = false;
105
106
  createEffect(() => {
106
107
  if (!visualElement)
107
108
  return;
@@ -110,9 +111,20 @@ export function createMotionComponent(Component, { forwardMotionProps = false, t
110
111
  visualElement.update(nextProps, nextPresenceContext);
111
112
  prepareDragProjection(visualElement, nextProps);
112
113
  visualElement.updateFeatures();
113
- if (visualElement.current && visualElement.animationState) {
114
- visualElement.animationState.animateChanges();
114
+ if (!visualElement.current || !visualElement.animationState)
115
+ return;
116
+ if (!mounted) {
117
+ mounted = true;
118
+ // The visual state was built when the element was created, but in Solid an earlier
119
+ // effect in the same flush can change `animate` before it mounts. React can't render a
120
+ // stale target, so a blocked initial animation would otherwise skip the newer one.
121
+ const { animate } = nextProps;
122
+ const blocked = nextProps.initial === false || nextPresenceContext?.initial === false;
123
+ if (blocked && animate && typeof animate !== "boolean" && !isAnimationControls(animate)) {
124
+ setTarget(visualElement, animate);
125
+ }
115
126
  }
127
+ visualElement.animationState.animateChanges();
116
128
  });
117
129
  onCleanup(() => {
118
130
  visualElement?.unmount();
@@ -20,7 +20,7 @@ export function useSpring(source, options = {}) {
20
20
  cancelFrame(start);
21
21
  value.stop();
22
22
  };
23
- // motion-dom 12.30's springValue queues an anonymous callback that survives
23
+ // motion-dom's springValue queues an anonymous callback that survives
24
24
  // destroy(). Keep the scheduled work owned and cancellable by this Solid scope.
25
25
  const start = () => {
26
26
  if (disposed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitlangton/solid-motion",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Motion for SolidJS: springs, presence, drag and gestures on Motion's engine.",
5
5
  "license": "MIT",
6
6
  "author": "Kit Langton",
@@ -46,8 +46,8 @@
46
46
  "prepublishOnly": "bun run typecheck && bun run build"
47
47
  },
48
48
  "dependencies": {
49
- "motion-dom": "^12.30.0",
50
- "motion-utils": "^12.29.2"
49
+ "motion-dom": "14.0.0",
50
+ "motion-utils": "14.0.0"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "solid-js": "^1.8.0"