@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,67 @@
|
|
|
1
|
+
// T21 (REQ-API-053) — the per-node DIRECT variant-child registry: the native authority for the
|
|
2
|
+
// orchestration (index, total) pair. Order is MOUNT REGISTRATION order (packet L3): sibling
|
|
3
|
+
// mount effects run in tree order, so first render matches JSX order; a later React reorder of
|
|
4
|
+
// existing keys does NOT re-register, so the index keeps first-mount order — a RECORDED
|
|
5
|
+
// divergence from the pin's document-position re-sort (live reorder stagger is a declared
|
|
6
|
+
// deferred sibling, SPEC-COMPONENT REQ-API-053). Unmounts compact the order; later mounts
|
|
7
|
+
// append. React-free on purpose: the registry is pure bookkeeping the variant tree wires up
|
|
8
|
+
// (one per variant node), unit-floored without a host.
|
|
9
|
+
|
|
10
|
+
export interface VariantChildPosition {
|
|
11
|
+
/** Position among the parent's LIVE direct variant children, 0-based. */
|
|
12
|
+
readonly index: number
|
|
13
|
+
readonly total: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface VariantChildRegistration {
|
|
17
|
+
/**
|
|
18
|
+
* The child's CURRENT (index, total) as one atomic read — never two getters a concurrent
|
|
19
|
+
* unregister could skew between. Start-time only (lifecycle edge).
|
|
20
|
+
*/
|
|
21
|
+
position(): VariantChildPosition
|
|
22
|
+
/** Leave the registry, compacting later siblings' indices. Idempotence is a caller bug: a
|
|
23
|
+
* second call throws (a -1 without its +1 is bookkeeping broken, the ledger law's shape). */
|
|
24
|
+
unregister(): void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface VariantChildRegistry {
|
|
28
|
+
/** Join in mount order; the returned registration is this child's only handle. */
|
|
29
|
+
register(): VariantChildRegistration
|
|
30
|
+
/** Live member count (= every position().total). */
|
|
31
|
+
size(): number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createVariantChildRegistry(): VariantChildRegistry {
|
|
35
|
+
// Each member is its registration's identity token; order IS the array order.
|
|
36
|
+
const members: object[] = []
|
|
37
|
+
return {
|
|
38
|
+
register() {
|
|
39
|
+
const token = {}
|
|
40
|
+
members.push(token)
|
|
41
|
+
let live = true
|
|
42
|
+
return {
|
|
43
|
+
position() {
|
|
44
|
+
if (!live) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
'variantChildRegistry: position() on an unregistered child — a stale handle ' +
|
|
47
|
+
'must never seed an orchestration delay (internal invariant, REQ-API-053)',
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
// alloc-ok: lifecycle-edge — read once per child start, never per frame.
|
|
51
|
+
return { index: members.indexOf(token), total: members.length }
|
|
52
|
+
},
|
|
53
|
+
unregister() {
|
|
54
|
+
if (!live) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'variantChildRegistry: double unregister — every unregister must pair with ' +
|
|
57
|
+
'exactly one register (internal invariant, REQ-API-053)',
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
live = false
|
|
61
|
+
members.splice(members.indexOf(token), 1)
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
size: () => members.length,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// R7 (REQ-API-032 law e) — the controlling-node variant tree. An element is CONTROLLING
|
|
2
|
+
// when any variant prop carries a label; it OWNS a tree store and provides it to its
|
|
3
|
+
// subtree. A non-controlling variant node (dictionary carrier) joins its closest
|
|
4
|
+
// controlling ancestor's tree UNCONDITIONALLY (the pinned registration consults no prop)
|
|
5
|
+
// and re-resolves the ancestor's active labels against its OWN dictionary on the
|
|
6
|
+
// ancestor's activation edges. `inherit={false}` is the pinned VISUAL-STATE switch only:
|
|
7
|
+
// it gates inherited initial/variant-state resolution AT MOUNT — never tree membership
|
|
8
|
+
// (severing is the recorded FAIL shape). NO orchestration timing crosses this context.
|
|
9
|
+
//
|
|
10
|
+
// Mechanics: the snapshot is REF-CURRENT during the owner's render (children rendering in
|
|
11
|
+
// the same pass read it synchronously for first paint), while notification is edge-driven
|
|
12
|
+
// — gesture activation flips and post-commit definition changes publish to subscribers,
|
|
13
|
+
// which re-render (memo-proof) and mirror gesture edges through their own resolvers.
|
|
14
|
+
|
|
15
|
+
import { createContext } from 'react'
|
|
16
|
+
import type { OrchestrationEpisode, VariantLabels } from '@unrulysystems/native-motion-core'
|
|
17
|
+
import type { VariantChildRegistration } from './variantChildRegistry'
|
|
18
|
+
|
|
19
|
+
/** One gesture lane's propagated state: the labels and whether the lane is ACTIVE. */
|
|
20
|
+
export interface VariantTreeGestureLane {
|
|
21
|
+
readonly labels: readonly string[]
|
|
22
|
+
readonly active: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface VariantTreeSnapshot {
|
|
26
|
+
/**
|
|
27
|
+
* The controlling node's initial definition when propagatable: a label form, or the
|
|
28
|
+
* `false` suppression sentinel (REQ-API-010 — suppression propagates). An object-form
|
|
29
|
+
* initial is not a label and does not propagate.
|
|
30
|
+
*/
|
|
31
|
+
readonly initial: VariantLabels | false | undefined
|
|
32
|
+
/** The authored animation-time label definition (label form only; shape is preserved because
|
|
33
|
+
* Motion distinguishes `"a"` from `["a"]` when detecting a variant edge). The exit episode
|
|
34
|
+
* rides this lane too — the retained clone's animate IS the authored exit definition. */
|
|
35
|
+
readonly animate: VariantLabels | undefined
|
|
36
|
+
readonly whileTap: VariantTreeGestureLane | undefined
|
|
37
|
+
readonly whileDrag: VariantTreeGestureLane | undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface VariantTreeStore {
|
|
41
|
+
getSnapshot(): VariantTreeSnapshot
|
|
42
|
+
/** Subscribe to activation edges; returns the unsubscriber. */
|
|
43
|
+
subscribe(listener: () => void): () => void
|
|
44
|
+
/** Silent snapshot swap — the owner's RENDER path (same-pass children read fresh). */
|
|
45
|
+
set(next: VariantTreeSnapshot): void
|
|
46
|
+
/** Snapshot swap + notify — the owner's EDGE path (gesture flips, post-commit changes). */
|
|
47
|
+
publish(next: VariantTreeSnapshot): void
|
|
48
|
+
/**
|
|
49
|
+
* R7 laws (d)/(e): the subtree settle ledger — joined descendants report their in-flight
|
|
50
|
+
* inherited-lane animations (+1 on first command, -1 on their element settle) so an
|
|
51
|
+
* exiting controlling owner can hold Presence removal until the WHOLE subtree settles
|
|
52
|
+
* (a time-simulated retention can see neither descendant dictionaries nor their
|
|
53
|
+
* embedded per-label transitions).
|
|
54
|
+
*/
|
|
55
|
+
descendantPending(): number
|
|
56
|
+
reportDescendantPending(delta: 1 | -1): void
|
|
57
|
+
/** Notified on every pending change; the exit watcher re-checks completion. */
|
|
58
|
+
subscribeDescendantSettle(listener: () => void): () => void
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const EMPTY_VARIANT_TREE_SNAPSHOT: VariantTreeSnapshot = {
|
|
62
|
+
initial: undefined,
|
|
63
|
+
animate: undefined,
|
|
64
|
+
whileTap: undefined,
|
|
65
|
+
whileDrag: undefined,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function createVariantTreeStore(
|
|
69
|
+
initial: VariantTreeSnapshot = EMPTY_VARIANT_TREE_SNAPSHOT,
|
|
70
|
+
): VariantTreeStore {
|
|
71
|
+
let snapshot = initial
|
|
72
|
+
const listeners = new Set<() => void>()
|
|
73
|
+
let pending = 0
|
|
74
|
+
const settleListeners = new Set<() => void>()
|
|
75
|
+
return {
|
|
76
|
+
getSnapshot: () => snapshot,
|
|
77
|
+
subscribe(listener) {
|
|
78
|
+
listeners.add(listener)
|
|
79
|
+
return () => {
|
|
80
|
+
listeners.delete(listener)
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
set(next) {
|
|
84
|
+
snapshot = next
|
|
85
|
+
},
|
|
86
|
+
publish(next) {
|
|
87
|
+
snapshot = next
|
|
88
|
+
// Live-set iteration is delete-safe: an unsubscribe during notification skips cleanly.
|
|
89
|
+
for (const listener of listeners) listener()
|
|
90
|
+
},
|
|
91
|
+
descendantPending: () => pending,
|
|
92
|
+
reportDescendantPending(delta) {
|
|
93
|
+
pending += delta
|
|
94
|
+
if (pending < 0) {
|
|
95
|
+
// A settle without a matching in-flight report is the reporter's bookkeeping broken
|
|
96
|
+
// — degrading to zero would silently release an exit early; fail loud instead.
|
|
97
|
+
throw new Error(
|
|
98
|
+
'variantTreeStore: descendant settle ledger went negative — every -1 must pair ' +
|
|
99
|
+
'with a prior +1 (internal invariant, R7 law d).',
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
for (const listener of settleListeners) listener()
|
|
103
|
+
},
|
|
104
|
+
subscribeDescendantSettle(listener) {
|
|
105
|
+
settleListeners.add(listener)
|
|
106
|
+
return () => {
|
|
107
|
+
settleListeners.delete(listener)
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The closest controlling ancestor's tree; null outside any controlling subtree. */
|
|
114
|
+
export const VariantTreeContext = createContext<VariantTreeStore | null>(null)
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* T21 (REQ-API-053) — the per-NODE orchestration channel. Every variant node (controlling or
|
|
118
|
+
* joined) provides one for its DIRECT variant children — the pin's `variantChildren` is
|
|
119
|
+
* per-parent, so recursion is per level, not per tree. Children register in mount order (the
|
|
120
|
+
* L3 index authority) and pull the parent's CURRENT episode at their own label application;
|
|
121
|
+
* the channel reads render-fresh refs, so a child rendering after its parent in the same
|
|
122
|
+
* commit always sees that commit's episode.
|
|
123
|
+
*/
|
|
124
|
+
export interface VariantOrchestrationParent {
|
|
125
|
+
/** Join the parent's DIRECT-child registry (mount order); unregister on unmount. */
|
|
126
|
+
registerChild(): VariantChildRegistration
|
|
127
|
+
/**
|
|
128
|
+
* The parent's episode for `label` on the CURRENT edge, or null when trivial (no
|
|
129
|
+
* orchestration options and no cascaded delay) — a null episode means the child starts
|
|
130
|
+
* undelayed, byte-identical to the pre-T21 command stream.
|
|
131
|
+
*/
|
|
132
|
+
getEpisode(label: string): OrchestrationEpisode | null
|
|
133
|
+
/**
|
|
134
|
+
* 4c mount flush: a child whose MOUNT entrance is orchestrated defers it here instead of
|
|
135
|
+
* commanding in-render — sibling (index, total) is complete only once the whole mount
|
|
136
|
+
* wave's registration effects ran. The parent drains the queue top-down (its own effect,
|
|
137
|
+
* or its own deferred-entrance thunk when it too is orchestrated — the cascade must be
|
|
138
|
+
* set before children compute). After the first drain the latch is OPEN and later
|
|
139
|
+
* enqueues run synchronously (a late-mounting child computes against the live registry —
|
|
140
|
+
* the pin's `manuallyAnimateOnMount` sibling, packet law 9).
|
|
141
|
+
*/
|
|
142
|
+
queueMountEntrance(run: () => void): void
|
|
143
|
+
/** Drain + open the latch. Idempotent; every drain preserves enqueue order. */
|
|
144
|
+
flushMountEntrances(): void
|
|
145
|
+
/**
|
|
146
|
+
* 4c `when: "beforeChildren"` (pin law 2): run `run` once the parent's OWN values for the
|
|
147
|
+
* current wave settle — immediately when no own start is pending. A superseding wave at
|
|
148
|
+
* the parent DROPS parked runs (children re-park from their own re-renders in the same
|
|
149
|
+
* commit). The release edge is the parent's element-batch settle; per-label settle
|
|
150
|
+
* granularity does not exist below it, so multi-label waves conflate (packet note).
|
|
151
|
+
*/
|
|
152
|
+
parkForOwnSettle(run: () => void): void
|
|
153
|
+
/**
|
|
154
|
+
* 4c `when: "afterChildren"` (pin law 2 — any truthy non-beforeChildren string): a child
|
|
155
|
+
* that COMMANDS under a sequenced-after episode reports its start and receives the
|
|
156
|
+
* parent's current wave token; it reports settle with that token from its own settle
|
|
157
|
+
* edge. The parent's own start parks until the wave's pending count drains; a superseded
|
|
158
|
+
* wave invalidates old tokens (their late settles no-op).
|
|
159
|
+
*/
|
|
160
|
+
reportChildEpisodeStart(): number
|
|
161
|
+
reportChildEpisodeSettle(waveToken: number): void
|
|
162
|
+
/**
|
|
163
|
+
* The parent's CURRENT wave token. A child holding a token from a superseded wave
|
|
164
|
+
* (retargeted before its old settle) compares against this to re-report its start into
|
|
165
|
+
* the new wave's accounting (review r1 major 4) — a stale stored token must not read
|
|
166
|
+
* as "already counted".
|
|
167
|
+
*/
|
|
168
|
+
currentWaveToken(): number
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** The closest variant PARENT's orchestration channel; null outside any variant tree. */
|
|
172
|
+
export const VariantOrchestrationContext = createContext<VariantOrchestrationParent | null>(null)
|