@unrulysystems/native-motion 0.1.0-alpha.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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/android/build.gradle +24 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/unrulysystems/nativemotion/GestureExclusionModule.kt +55 -0
- package/android/src/main/java/com/unrulysystems/nativemotion/NativeMotionPackage.kt +27 -0
- package/package.json +57 -0
- package/react-native.config.js +14 -0
- package/src/native/driver/instantWindow.ts +31 -0
- package/src/native/driver/strictModeReplay.ts +20 -0
- package/src/native/driver/uiLayoutEngine.ts +845 -0
- package/src/native/driver/uiLayoutGraph.ts +338 -0
- package/src/native/driver/uiValueChannel.ts +4640 -0
- package/src/native/driver/workletDriver.ts +3860 -0
- package/src/native/motion/AnimatePresence.tsx +1605 -0
- package/src/native/motion/LayoutGroup.tsx +163 -0
- package/src/native/motion/MotionConfig.tsx +400 -0
- package/src/native/motion/MotionRoot.tsx +250 -0
- package/src/native/motion/MotionView.tsx +6761 -0
- package/src/native/motion/addScaleCorrector.ts +97 -0
- package/src/native/motion/colorEndpointUiFeed.ts +32 -0
- package/src/native/motion/colorProjection.ts +39 -0
- package/src/native/motion/composeTransform.ts +271 -0
- package/src/native/motion/constraintMeasure.ts +157 -0
- package/src/native/motion/deferredPendingSnapshots.ts +25 -0
- package/src/native/motion/discreteProjection.ts +36 -0
- package/src/native/motion/dragAncestorPanContext.ts +28 -0
- package/src/native/motion/dragControls.ts +152 -0
- package/src/native/motion/dragGestureWiring.ts +1168 -0
- package/src/native/motion/dragHandoffBinding.ts +179 -0
- package/src/native/motion/dragHubStream.ts +756 -0
- package/src/native/motion/dragPropagationLock.ts +57 -0
- package/src/native/motion/driverValueChannel.ts +3288 -0
- package/src/native/motion/externalDragDriver.ts +214 -0
- package/src/native/motion/frameData.ts +31 -0
- package/src/native/motion/gestureBinding.ts +107 -0
- package/src/native/motion/gestureStateGate.ts +563 -0
- package/src/native/motion/gestureStateResolver.ts +286 -0
- package/src/native/motion/identityValueChannelControllerAdapter.ts +2043 -0
- package/src/native/motion/identityValueChannelControllerReconciliation.ts +176 -0
- package/src/native/motion/identityValueChannelLaneMarker.ts +14 -0
- package/src/native/motion/imperativeAnimate.ts +816 -0
- package/src/native/motion/keyframeTiming.ts +7 -0
- package/src/native/motion/layoutIdBinding.ts +261 -0
- package/src/native/motion/layoutIdFlightConfig.ts +51 -0
- package/src/native/motion/layoutProjection.ts +89 -0
- package/src/native/motion/layoutScroll.ts +204 -0
- package/src/native/motion/layoutTransition.ts +1302 -0
- package/src/native/motion/lengthLayoutContext.tsx +100 -0
- package/src/native/motion/lengthLayoutHost.ts +44 -0
- package/src/native/motion/mappedKeys.ts +184 -0
- package/src/native/motion/motionViewController.ts +3180 -0
- package/src/native/motion/nativeHostMarker.ts +22 -0
- package/src/native/motion/panSessionWiring.ts +127 -0
- package/src/native/motion/pathTransition.ts +101 -0
- package/src/native/motion/popLayout.ts +176 -0
- package/src/native/motion/presenceBinding.ts +382 -0
- package/src/native/motion/scaleCorrectorRegistry.ts +123 -0
- package/src/native/motion/scrollValues.ts +260 -0
- package/src/native/motion/serializablePayload.ts +151 -0
- package/src/native/motion/severity.ts +16 -0
- package/src/native/motion/shippedSurface.ts +843 -0
- package/src/native/motion/staticLengthGate.ts +86 -0
- package/src/native/motion/styleBaseGate.ts +72 -0
- package/src/native/motion/styleValueBinding.ts +657 -0
- package/src/native/motion/systemGestureExclusion.ts +61 -0
- package/src/native/motion/tapGestureWiring.ts +215 -0
- package/src/native/motion/transformOrder.ts +38 -0
- package/src/native/motion/transformStringBinding.ts +200 -0
- package/src/native/motion/transformTemplateGate.ts +45 -0
- package/src/native/motion/transitionGate.ts +435 -0
- package/src/native/motion/useAnimate.ts +52 -0
- package/src/native/motion/useCycle.ts +44 -0
- package/src/native/motion/useInstantTransition.ts +68 -0
- package/src/native/motion/useReducedMotion.ts +58 -0
- package/src/native/motion/useScroll.ts +162 -0
- package/src/native/motion/useViewportScroll.ts +29 -0
- package/src/native/motion/valueChannel.ts +7121 -0
- package/src/native/motion/valueHooks.ts +901 -0
- package/src/native/motion/variantChildRegistry.ts +67 -0
- package/src/native/motion/variantContext.tsx +172 -0
- package/src/native/motion/variantProps.ts +598 -0
- package/src/native.ts +159 -0
- package/src/verification/harnessMetrics.ts +78 -0
- package/src/verification/probe/LayoutIdentityWorkletProbe.tsx +251 -0
- package/src/verification/probe/WorkletParityProbe.tsx +162 -0
- package/src/verification/probe/layoutIdentityProbeEngine.ts +429 -0
- package/src/verification/screens/ArcPathChecksScreen.tsx +273 -0
- package/src/verification/screens/BooleanAnimateChecksScreen.tsx +358 -0
- package/src/verification/screens/ChoreographyGalleryScreen.tsx +1010 -0
- package/src/verification/screens/ColorBindingChecksScreen.tsx +568 -0
- package/src/verification/screens/CompletionChecksScreen.tsx +294 -0
- package/src/verification/screens/ConformanceScreen.tsx +573 -0
- package/src/verification/screens/ContentionProbeScreen.tsx +224 -0
- package/src/verification/screens/DriverSmokeScreen.tsx +99 -0
- package/src/verification/screens/DurationOnlyTweenChecksScreen.tsx +294 -0
- package/src/verification/screens/DynamicDragConfigChecksScreen.tsx +237 -0
- package/src/verification/screens/FrameDataChecksScreen.tsx +252 -0
- package/src/verification/screens/GestureChecksScreen.tsx +685 -0
- package/src/verification/screens/InstantTransitionChecksScreen.tsx +839 -0
- package/src/verification/screens/LayoutAnimationStartChecksScreen.tsx +648 -0
- package/src/verification/screens/LayoutChecksScreen.tsx +822 -0
- package/src/verification/screens/LayoutCommitSpikeScreen.tsx +142 -0
- package/src/verification/screens/MotionViewChecksScreen.tsx +930 -0
- package/src/verification/screens/PresenceChecksScreen.tsx +614 -0
- package/src/verification/screens/ReducedMotionChecksScreen.tsx +614 -0
- package/src/verification/screens/RestThresholdChecksScreen.tsx +634 -0
- package/src/verification/screens/ScaleCorrectorChecksScreen.tsx +425 -0
- package/src/verification/screens/SharedLayoutContinuityChecksScreen.tsx +338 -0
- package/src/verification/screens/SharedLayoutCrossfadeChecksScreen.tsx +425 -0
- package/src/verification/screens/TransitionDefaultSelectionChecksScreen.tsx +514 -0
- package/src/verification/screens/ViewportScrollAliasChecksScreen.tsx +303 -0
- package/src/verification/screens/conformanceBanner.ts +21 -0
- package/src/verification/screens/proofConsoleTap.ts +18 -0
- package/src/verification.ts +47 -0
- package/src/web.ts +98 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// REQ-GESTURE-022 at the binding altitude (FLAG 5a, specs/SPEC-GESTURE.md) —
|
|
2
|
+
// the per-axis HANDOFF binding: platform Pan events → validated translations/velocities →
|
|
3
|
+
// handoff-session calls. The session owns EVERY semantic (dead-zone, clamp, elastic, release
|
|
4
|
+
// target selection — REQ-GESTURE-011/-015/-016/-017); this layer owns exactly the M2.4 binding's
|
|
5
|
+
// two concerns: (a) fail-loud event-shape validation (never coerce a missing field to 0 — the
|
|
6
|
+
// M2.2/M2.3 boundary lesson) and (b) the A3 axis parametrization: 'x' reads
|
|
7
|
+
// translationX/velocityX, 'y' reads translationY/velocityY — never a cross-axis coercion. No
|
|
8
|
+
// clock: the handoff session is a time-free event fold; the driver owns integration time.
|
|
9
|
+
//
|
|
10
|
+
// RNGH end/finalize routing (review cycle 1 of the M2.4 binding, carried verbatim):
|
|
11
|
+
// gesture-handler fires `onEnd(event, false)` for gestures that FAIL or are CANCELLED — before
|
|
12
|
+
// `onFinalize(event, false)`. Only a SUCCESSFUL end is a release; an unsuccessful end must leave
|
|
13
|
+
// the session active so the finalize path can cancel to the pre-grab value (REQ-GESTURE-018
|
|
14
|
+
// totality). routeEnd/routeFinalize ARE the component's wiring, exported so tests drive the
|
|
15
|
+
// exact routing.
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
createGestureHandoffSession,
|
|
19
|
+
describeValue,
|
|
20
|
+
type DragAxis,
|
|
21
|
+
type DragConstraints,
|
|
22
|
+
type GestureHandoffSession,
|
|
23
|
+
type ReleaseHandoff,
|
|
24
|
+
type ResolvedDragAxisConfig,
|
|
25
|
+
} from '@unrulysystems/native-motion-core'
|
|
26
|
+
|
|
27
|
+
// The fields this binding reads off a gesture-handler Pan event, both axes. Kept loose (unknown)
|
|
28
|
+
// because the validation IS the contract — a malformed event fails loud at this boundary.
|
|
29
|
+
export interface DragPanEventShape {
|
|
30
|
+
readonly translationX?: unknown
|
|
31
|
+
readonly translationY?: unknown
|
|
32
|
+
readonly velocityX?: unknown
|
|
33
|
+
readonly velocityY?: unknown
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface DragHandoffBinding {
|
|
37
|
+
// `seed.value` is the driver's live value at the grab instant (REQ-DRIVER-025 → REQ-GESTURE-012).
|
|
38
|
+
// `constraints` are the GRAB-TIME measured bounds for the element-ref channel
|
|
39
|
+
// (REQ-GESTURE-028): applied to this grab only, replacing the constructed config.
|
|
40
|
+
onStart(
|
|
41
|
+
event: DragPanEventShape,
|
|
42
|
+
seed: { readonly value: number },
|
|
43
|
+
constraints?: DragConstraints,
|
|
44
|
+
): void
|
|
45
|
+
onUpdate(event: DragPanEventShape): number
|
|
46
|
+
// `releaseOrigin` is the driver's LIVE value at the release instant (REQ-DRIVER-025), threaded from
|
|
47
|
+
// the wiring (which owns the driver): the handoff ORIGIN, so a contending declarative drift on the
|
|
48
|
+
// axis (REQ-DRIVER-026) selects the release/snap target, not the finger-tracked session value.
|
|
49
|
+
onEnd(event: DragPanEventShape, releaseOrigin: number): ReleaseHandoff
|
|
50
|
+
// The release edge split into two phases so the wiring can reconcile the DRIVER between them
|
|
51
|
+
// (r11 major 32). `beginRelease` validates both release fields (fail-loud, no half-apply) and
|
|
52
|
+
// COMMITS the final drag sample to the session (G-INV-8: the end translation is never dropped — the
|
|
53
|
+
// M1 session commits it too), returning the finger's final value (so the wiring can catch the driver
|
|
54
|
+
// up iff the finger moved) and the platform velocity. `finishRelease` then finalizes from the
|
|
55
|
+
// driver's live origin (contention-aware). `onEnd` is `beginRelease`+`finishRelease` with no driver
|
|
56
|
+
// reconcile (the test/routing path passes the origin directly).
|
|
57
|
+
beginRelease(event: DragPanEventShape): {
|
|
58
|
+
readonly fingerValue: number
|
|
59
|
+
readonly velocity: number
|
|
60
|
+
// Whether the finger MOVED since the last update, by RAW translation (r12 major 10): true even
|
|
61
|
+
// when a hard clamp pins the constrained value, so the caller catches the driver up to the bound.
|
|
62
|
+
readonly moved: boolean
|
|
63
|
+
}
|
|
64
|
+
finishRelease(velocity: number, releaseOrigin: number): ReleaseHandoff
|
|
65
|
+
// RNGH routing: a successful end releases; an unsuccessful one returns null and leaves the
|
|
66
|
+
// session active for routeFinalize to cancel.
|
|
67
|
+
routeEnd(event: DragPanEventShape, success: boolean, releaseOrigin: number): ReleaseHandoff | null
|
|
68
|
+
// `releaseOrigin` is the driver's LIVE value at the finalize instant (REQ-DRIVER-025): the cancel
|
|
69
|
+
// return spring starts from it, not the finger, so a contending drift does not rewind (r11 major 31).
|
|
70
|
+
routeFinalize(success: boolean, releaseOrigin: number): ReleaseHandoff | null
|
|
71
|
+
readonly session: GestureHandoffSession
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fail-loud field extraction (the M2.4 binding's rule verbatim): a missing or non-numeric
|
|
75
|
+
// recognizer field is a caller/platform bug — naming the field beats silently coercing to 0.
|
|
76
|
+
function requireNumber(value: unknown, field: string): number {
|
|
77
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
78
|
+
// describeValue (core, single-sourced) is a SAFE formatter (review r4 minor 844ee5e): a BigInt
|
|
79
|
+
// recognizer field produces this typed error naming the field, never a raw JSON.stringify crash.
|
|
80
|
+
throw new Error(
|
|
81
|
+
`drag binding: Pan event field '${field}' is ${describeValue(value)} — a finite number ` +
|
|
82
|
+
'is required and is never coerced (specs/SPEC-GESTURE.md).',
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
return value
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function createDragHandoffBinding(
|
|
89
|
+
axis: DragAxis,
|
|
90
|
+
config: ResolvedDragAxisConfig,
|
|
91
|
+
momentum?: boolean,
|
|
92
|
+
): DragHandoffBinding {
|
|
93
|
+
const session = createGestureHandoffSession({
|
|
94
|
+
...(config.constraints === undefined ? {} : { constraints: config.constraints }),
|
|
95
|
+
...(config.snap === undefined ? {} : { snap: config.snap }),
|
|
96
|
+
// REQ-GESTURE-027 (R16): momentum gating moved INTO the session so the projection velocity
|
|
97
|
+
// itself is zeroed (settle-at-release), not merely the integrator seed.
|
|
98
|
+
...(momentum === undefined ? {} : { momentum }),
|
|
99
|
+
})
|
|
100
|
+
// Axis field selection (packet A3): exactly the declared axis's fields, never a fallback pair.
|
|
101
|
+
const translationField = axis === 'x' ? 'translationX' : 'translationY'
|
|
102
|
+
const velocityField = axis === 'x' ? 'velocityX' : 'velocityY'
|
|
103
|
+
// The last RAW recognizer translation applied (grab or drag). The release edge decides "did the
|
|
104
|
+
// finger move" from THIS — the raw translation, not the constrained value: a finger dragging PAST a
|
|
105
|
+
// hard bound moves while its clamped value stays pinned at the bound (r12 major 10). Null between
|
|
106
|
+
// gestures; each grab reseeds it.
|
|
107
|
+
let lastTranslation: number | null = null
|
|
108
|
+
// Last finite platform velocity applied on an update (or grab). Some recognizers report near-zero
|
|
109
|
+
// velocity on end after a fling sample already carried momentum on the prior update — seed release
|
|
110
|
+
// from the larger of |end velocity| and |last update velocity| so free-drag projection and boundary
|
|
111
|
+
// spring-back are not silently dropped (M2 r2: fling freeze at end pose with ends:1).
|
|
112
|
+
let lastUpdateVelocity = 0
|
|
113
|
+
|
|
114
|
+
function translation(event: DragPanEventShape): number {
|
|
115
|
+
return requireNumber(event[translationField], translationField)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function releaseVelocity(endVelocity: number): number {
|
|
119
|
+
// Prefer the end sample when it carries magnitude; otherwise the last update's velocity.
|
|
120
|
+
if (Math.abs(endVelocity) >= Math.abs(lastUpdateVelocity)) return endVelocity
|
|
121
|
+
return lastUpdateVelocity
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const binding: DragHandoffBinding = {
|
|
125
|
+
onStart(event, seed, constraints) {
|
|
126
|
+
const t = translation(event)
|
|
127
|
+
session.begin(t, seed, constraints === undefined ? undefined : { constraints })
|
|
128
|
+
lastTranslation = t
|
|
129
|
+
lastUpdateVelocity = 0
|
|
130
|
+
},
|
|
131
|
+
onUpdate(event) {
|
|
132
|
+
// Validate BEFORE the session call so a malformed event never half-applies, then return the
|
|
133
|
+
// constrained value for the caller's driver write (REQ-DRIVER-024).
|
|
134
|
+
const t = translation(event)
|
|
135
|
+
const v = requireNumber(event[velocityField], velocityField)
|
|
136
|
+
session.active(t)
|
|
137
|
+
lastTranslation = t
|
|
138
|
+
lastUpdateVelocity = v
|
|
139
|
+
return session.value()
|
|
140
|
+
},
|
|
141
|
+
onEnd(event, releaseOrigin) {
|
|
142
|
+
// The release edge with NO driver reconcile (the test/routing path supplies the origin directly):
|
|
143
|
+
// commit the final sample, then finalize from the passed origin.
|
|
144
|
+
const { velocity } = binding.beginRelease(event)
|
|
145
|
+
return binding.finishRelease(velocity, releaseOrigin)
|
|
146
|
+
},
|
|
147
|
+
beginRelease(event) {
|
|
148
|
+
// Both fields validate BEFORE any session mutation: a malformed release leaves the session active
|
|
149
|
+
// at its last good value, never half-released (major 6 law). Then COMMIT the final drag sample
|
|
150
|
+
// (session.active) so session.value() / the onDragEnd info reflect the finger's final position —
|
|
151
|
+
// the M1 session commits the end sample too; dropping it violates G-INV-8 (r11 major 32).
|
|
152
|
+
const t = translation(event)
|
|
153
|
+
const endV = requireNumber(event[velocityField], velocityField)
|
|
154
|
+
const v = releaseVelocity(endV)
|
|
155
|
+
// "Moved" is decided on the RAW translation, not the constrained value (r12 major 10): a finger
|
|
156
|
+
// dragging past a hard bound changes translation while its clamped value stays pinned. A held
|
|
157
|
+
// finger reports the same translation ⇒ not moved ⇒ the caller preserves a contending drift.
|
|
158
|
+
const moved = lastTranslation === null || t !== lastTranslation
|
|
159
|
+
session.active(t)
|
|
160
|
+
lastTranslation = t
|
|
161
|
+
return { fingerValue: session.value(), velocity: v, moved }
|
|
162
|
+
},
|
|
163
|
+
finishRelease(velocity, releaseOrigin) {
|
|
164
|
+
// Finalize from the driver's live origin (contention-aware, REQ-GESTURE-022): the finger's final
|
|
165
|
+
// position if it moved, or the contending drift if it held.
|
|
166
|
+
return session.end(velocity, releaseOrigin)
|
|
167
|
+
},
|
|
168
|
+
routeEnd(event, success, releaseOrigin) {
|
|
169
|
+
if (!success) return null
|
|
170
|
+
return binding.onEnd(event, releaseOrigin)
|
|
171
|
+
},
|
|
172
|
+
routeFinalize(success, releaseOrigin) {
|
|
173
|
+
if (!success && session.state() === 'active') return session.cancel(releaseOrigin)
|
|
174
|
+
return null
|
|
175
|
+
},
|
|
176
|
+
session,
|
|
177
|
+
}
|
|
178
|
+
return binding
|
|
179
|
+
}
|