solidjs-motion 0.1.4 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -5,6 +5,64 @@ All notable changes to `solidjs-motion` / `@solidjs-motion/motion` are documente
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.6] — 2026-05-21
9
+
10
+ ### Fixed
11
+
12
+ - **Nested motion inside `<Presence>` now reliably runs its first
13
+ animate.** Previously, motion's enter-readiness gate flipped true via
14
+ Presence's `beforeMount` callback unconditionally — but for nested
15
+ motion elements inside a deeper `<Presence>` whose `onEnter` fires
16
+ synchronously during render (while the surrounding tracked subtree is
17
+ still off-DOM in a wait-mode holding pen), this caused the diff
18
+ effect's first `animate(el, ...)` to fire against a disconnected
19
+ element. WAAPI ran the animation to completion off-DOM and silently
20
+ dropped `commitStyles`, so the element painted at its `initial` target
21
+ with no visible transition.
22
+
23
+ Both signals that could trip readiness now route through a shared
24
+ `isConnected` check: Presence's `beforeMount` callback AND the
25
+ microtask fallback flip ready only when `el.isConnected === true`,
26
+ otherwise schedule a `requestAnimationFrame` retry until connectedness
27
+ flips (or the owner is disposed). This handles three previously-broken
28
+ scenarios:
29
+ - Nested motion descendants of a Presence's tracked subtree
30
+ - Initial children of `<Presence initial={false}>` (appear=false)
31
+ - Direct tracked children whose nested `<Presence>` fires
32
+ `beforeMount` synchronously while the parent subtree is still
33
+ off-DOM (the canonical case: a page-transition wrapper around
34
+ routes that themselves contain `<Presence>`)
35
+
36
+ New regression test in `tests/presence.test.tsx` asserts a nested
37
+ motion descendant's animate target reaches the animate spy on both
38
+ initial mount AND on a `<Presence>` swap.
39
+
40
+ ## [0.1.5] — 2026-05-21
41
+
42
+ ### Fixed
43
+
44
+ - **Drag now sets `touch-action` upfront on the element** when `drag` is
45
+ configured, not just inside `handlePanStart` after threshold cross.
46
+ Previously, on mobile, the browser would arbitrate the gesture as
47
+ native scroll/zoom and fire `pointercancel` before motion's own
48
+ touch-action write could take effect — manifesting as missed drags
49
+ or, when the user's `onDragEnd` made a threshold-based decision off
50
+ stale `info.offset`, an immediate dismiss-on-touch (visible in the
51
+ swipe-stack demo: every press fired a left-swipe).
52
+
53
+ Axis mapping mirrors motion-react:
54
+ - `drag="x"` → `touch-action: pan-y` (browser keeps vertical scroll)
55
+ - `drag="y"` → `touch-action: pan-x`
56
+ - `drag={true}` → `touch-action: none`
57
+
58
+ User-supplied `style.touch-action` still overrides via natural
59
+ spread precedence — e.g., `style={{ touchAction: "auto" }}` opts back
60
+ into the browser's default arbitration. Three regression tests added.
61
+
62
+ Removes the need for users to remember to set `touch-action: none` on
63
+ every draggable element — `<motion.div drag="x">` now Just Works on
64
+ touch devices.
65
+
8
66
  ## [0.1.4] — 2026-05-21
9
67
 
10
68
  ### Added
package/dist/index.js CHANGED
@@ -2097,10 +2097,20 @@ function createMotion(el, getOpts, config) {
2097
2097
  const inPresence = presence.registerEnter !== void 0;
2098
2098
  const [enterReady, setEnterReady] = createSignal(!inPresence);
2099
2099
  if (inPresence && presence.registerEnter) {
2100
- presence.registerEnter(el, () => setEnterReady(true));
2101
- queueMicrotask(() => {
2102
- if (el.isConnected) setEnterReady(true);
2100
+ let live = true;
2101
+ onCleanup(() => {
2102
+ live = false;
2103
2103
  });
2104
+ const checkConnection = () => {
2105
+ if (!live) return;
2106
+ if (el.isConnected) {
2107
+ setEnterReady(true);
2108
+ return;
2109
+ }
2110
+ requestAnimationFrame(checkConnection);
2111
+ };
2112
+ presence.registerEnter(el, checkConnection);
2113
+ queueMicrotask(checkConnection);
2104
2114
  }
2105
2115
  const { setActive, onceExitComplete } = createGestureStateMachine({
2106
2116
  el,
@@ -2308,12 +2318,18 @@ function useMotion(opts) {
2308
2318
  return mergeProps$1(userProps ?? {}, {
2309
2319
  get style() {
2310
2320
  const cleaned = stripStyleEntriesOwnedByRegistry(userProps?.style);
2311
- if (renderedOnce) return cleaned;
2321
+ const dragOpts = getOpts().drag;
2322
+ const dragTouchAction = dragOpts ? dragOpts === "x" ? "pan-y" : dragOpts === "y" ? "pan-x" : "none" : void 0;
2323
+ const baseWithDefaults = dragTouchAction ? {
2324
+ "touch-action": dragTouchAction,
2325
+ ...cleaned
2326
+ } : cleaned;
2327
+ if (renderedOnce) return baseWithDefaults;
2312
2328
  const composed = composeFirstPaintStyle(userProps?.style);
2313
2329
  return composed ? {
2314
- ...cleaned,
2330
+ ...baseWithDefaults,
2315
2331
  ...composed
2316
- } : cleaned;
2332
+ } : baseWithDefaults;
2317
2333
  },
2318
2334
  ref: mergeRefs(userProps?.ref, motionRef),
2319
2335
  ...wroteFirstPaintStyle ? { "data-motion-hydrated": "" } : {}