@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,358 @@
|
|
|
1
|
+
// U7h boolean-animate device checks: catalog Shared-layout-sibling-to-child on the public
|
|
2
|
+
// MotionView path. iOS has no tap injection, so the screen self-drives. Discriminators are
|
|
3
|
+
// acceptance (Decision 82 analog of 77): bare / true / false `animate` mount without a typed
|
|
4
|
+
// InvalidTargetError, and the sibling→child layoutId exclusive remount still completes
|
|
5
|
+
// (inert — boolean is not a disable switch). Distinct Child/Sibling types (R13: inlining
|
|
6
|
+
// the ternary reuses the first MotionView fiber and leaves settles at 0). Host paint of
|
|
7
|
+
// the crossfade is the web e2e.
|
|
8
|
+
|
|
9
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
10
|
+
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
|
11
|
+
import { LayoutGroup } from '../../native/motion/LayoutGroup'
|
|
12
|
+
import { MotionRoot } from '../../native/motion/MotionRoot'
|
|
13
|
+
import { MotionView } from '../../native/motion/MotionView'
|
|
14
|
+
import { ambientNativeSeverity } from '../../native/motion/severity'
|
|
15
|
+
import { armProofConsoleTap } from './proofConsoleTap'
|
|
16
|
+
|
|
17
|
+
const ROW_BARE = 'bare animate mounts without InvalidTargetError'
|
|
18
|
+
const ROW_TRUE = 'animate={true} mounts without InvalidTargetError'
|
|
19
|
+
const ROW_FALSE = 'animate={false} mounts without InvalidTargetError'
|
|
20
|
+
const ROW_FLIGHT = 'sibling→child layoutId flight: exclusive remount completes'
|
|
21
|
+
const ROW_CLEAN = 'clean run: zero InvalidTargetError reports'
|
|
22
|
+
const ROWS = [ROW_BARE, ROW_TRUE, ROW_FALSE, ROW_FLIGHT, ROW_CLEAN] as const
|
|
23
|
+
|
|
24
|
+
// Family-3 / REQ-LAYOUT-023: layoutId identity flights use duration/ease, not a spring.
|
|
25
|
+
const TRANSITION = { duration: 0.4, ease: 'easeOut' } as const
|
|
26
|
+
const T_ACCEPT = 400
|
|
27
|
+
const T_FLIGHT = 1200
|
|
28
|
+
const SETTLE_DEADLINE_MS = 4000
|
|
29
|
+
const STABILITY_MS = 400
|
|
30
|
+
|
|
31
|
+
function isKnownProjectionRadiusDebt(message: string): boolean {
|
|
32
|
+
return /borderRadius correction is anisotropic/i.test(message)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface RowResult {
|
|
36
|
+
readonly pass: boolean
|
|
37
|
+
readonly detail: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isAnimateRefusal(message: string): boolean {
|
|
41
|
+
return /InvalidTargetError|malformed animate target|animate-target/i.test(message)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function U7hChild(props: { onComplete: () => void }) {
|
|
45
|
+
return (
|
|
46
|
+
<MotionView
|
|
47
|
+
animate
|
|
48
|
+
layoutId="u7h-big"
|
|
49
|
+
transition={TRANSITION}
|
|
50
|
+
onLayoutAnimationComplete={props.onComplete}
|
|
51
|
+
style={styles.childBig}
|
|
52
|
+
accessibilityLabel="u7h-boolean-animate-big"
|
|
53
|
+
>
|
|
54
|
+
<MotionView
|
|
55
|
+
animate
|
|
56
|
+
layoutId="u7h-small"
|
|
57
|
+
transition={TRANSITION}
|
|
58
|
+
style={styles.childSmall}
|
|
59
|
+
accessibilityLabel="u7h-boolean-animate-small"
|
|
60
|
+
/>
|
|
61
|
+
</MotionView>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function U7hSibling(props: { onComplete: () => void }) {
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<MotionView
|
|
69
|
+
animate
|
|
70
|
+
layoutId="u7h-big"
|
|
71
|
+
transition={TRANSITION}
|
|
72
|
+
onLayoutAnimationComplete={props.onComplete}
|
|
73
|
+
style={styles.siblingBig}
|
|
74
|
+
accessibilityLabel="u7h-boolean-animate-big"
|
|
75
|
+
/>
|
|
76
|
+
<MotionView
|
|
77
|
+
animate
|
|
78
|
+
layoutId="u7h-small"
|
|
79
|
+
transition={TRANSITION}
|
|
80
|
+
style={styles.siblingSmall}
|
|
81
|
+
accessibilityLabel="u7h-boolean-animate-small"
|
|
82
|
+
/>
|
|
83
|
+
</>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function BooleanAnimateChecksScreen() {
|
|
88
|
+
const severity = ambientNativeSeverity()
|
|
89
|
+
const [isOn, setOn] = useState(false)
|
|
90
|
+
const [stage, setStage] = useState('boot')
|
|
91
|
+
const [rows, setRows] = useState<Partial<Record<(typeof ROWS)[number], RowResult>>>({})
|
|
92
|
+
const [completes, setCompletes] = useState(0)
|
|
93
|
+
|
|
94
|
+
const reportsRef = useRef<string[]>([])
|
|
95
|
+
const completesRef = useRef(0)
|
|
96
|
+
const rafsRef = useRef<ReturnType<typeof requestAnimationFrame>[]>([])
|
|
97
|
+
const mountStampRef = useRef(Date.now())
|
|
98
|
+
const restoreTapRef = useRef<null | (() => void)>(null)
|
|
99
|
+
// Arm BEFORE children render. Production resolveAnimateProp reports via console.error
|
|
100
|
+
// on the child's render (review r1 major, agent-2026-08-20-304390). A parent useEffect
|
|
101
|
+
// tap would miss the first-mount InvalidTargetError and 5/5 the pre-fix tree.
|
|
102
|
+
if (restoreTapRef.current === null) {
|
|
103
|
+
restoreTapRef.current = armProofConsoleTap(reportsRef.current, (message) =>
|
|
104
|
+
/motion|InvalidTargetError|animate/i.test(message),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const now = (): number => Date.now() - mountStampRef.current
|
|
109
|
+
|
|
110
|
+
const finalize = (id: (typeof ROWS)[number], pass: boolean, detail: string): void => {
|
|
111
|
+
setRows((previous) => ({ ...previous, [id]: { pass, detail } }))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const onComplete = useCallback((): void => {
|
|
115
|
+
completesRef.current += 1
|
|
116
|
+
setCompletes(completesRef.current)
|
|
117
|
+
}, [])
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const timers: ReturnType<typeof setTimeout>[] = []
|
|
121
|
+
const at = (ms: number, run: () => void): void => {
|
|
122
|
+
timers.push(setTimeout(run, ms))
|
|
123
|
+
}
|
|
124
|
+
const trackRaf = (id: ReturnType<typeof requestAnimationFrame>): void => {
|
|
125
|
+
rafsRef.current.push(id)
|
|
126
|
+
}
|
|
127
|
+
const pollUntil = (
|
|
128
|
+
condition: () => boolean,
|
|
129
|
+
deadlineAt: number,
|
|
130
|
+
onResolve: (ok: boolean) => void,
|
|
131
|
+
): void => {
|
|
132
|
+
const step = (): void => {
|
|
133
|
+
if (condition()) {
|
|
134
|
+
onResolve(true)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
if (now() >= deadlineAt) {
|
|
138
|
+
onResolve(false)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
trackRaf(requestAnimationFrame(step))
|
|
142
|
+
}
|
|
143
|
+
trackRaf(requestAnimationFrame(step))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
at(T_ACCEPT, () => {
|
|
147
|
+
const refusals = reportsRef.current.filter(isAnimateRefusal)
|
|
148
|
+
const pass = refusals.length === 0
|
|
149
|
+
const detail = pass
|
|
150
|
+
? 'bare/true/false mounted with zero animate-target refusals'
|
|
151
|
+
: `${refusals.length} animate refusal(s): ${refusals.map((message) => message.slice(0, 80)).join(' | ')}`
|
|
152
|
+
finalize(ROW_BARE, pass, pass ? 'catalog sibling tree mounted with bare animate' : detail)
|
|
153
|
+
finalize(ROW_TRUE, pass, pass ? 'true-peer mounted beside the catalog tree' : detail)
|
|
154
|
+
finalize(ROW_FALSE, pass, pass ? 'false-peer mounted beside the catalog tree' : detail)
|
|
155
|
+
setStage('boolean mounts sampled')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
at(T_FLIGHT, () => {
|
|
159
|
+
setOn(true)
|
|
160
|
+
setStage('layoutId flight pressed (sibling → child)')
|
|
161
|
+
pollUntil(
|
|
162
|
+
() => completesRef.current >= 1,
|
|
163
|
+
now() + SETTLE_DEADLINE_MS,
|
|
164
|
+
(okSettle) => {
|
|
165
|
+
finalize(
|
|
166
|
+
ROW_FLIGHT,
|
|
167
|
+
okSettle,
|
|
168
|
+
okSettle
|
|
169
|
+
? `exclusive remount complete ${completesRef.current} on layoutId big`
|
|
170
|
+
: `layoutId complete never fired within ${SETTLE_DEADLINE_MS}ms (completes ${completesRef.current})`,
|
|
171
|
+
)
|
|
172
|
+
setStage(okSettle ? 'flight row finalized' : 'flight row finalized (FAIL)')
|
|
173
|
+
at(STABILITY_MS, () => finishClean())
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const finishClean = (): void => {
|
|
179
|
+
const refusals = reportsRef.current.filter(isAnimateRefusal)
|
|
180
|
+
const known = reportsRef.current.filter(isKnownProjectionRadiusDebt)
|
|
181
|
+
const unexpected = reportsRef.current.filter(
|
|
182
|
+
(message) => !isAnimateRefusal(message) && !isKnownProjectionRadiusDebt(message),
|
|
183
|
+
)
|
|
184
|
+
finalize(
|
|
185
|
+
ROW_CLEAN,
|
|
186
|
+
refusals.length === 0 && unexpected.length === 0,
|
|
187
|
+
refusals.length === 0 && unexpected.length === 0
|
|
188
|
+
? `0 InvalidTargetError reports; ${known.length} T19 anisotropic radius disclosure(s)`
|
|
189
|
+
: `${refusals.length} animate + ${unexpected.length} other: ${reportsRef.current
|
|
190
|
+
.map((message) => message.slice(0, 80))
|
|
191
|
+
.join(' | ')}`,
|
|
192
|
+
)
|
|
193
|
+
setStage('done')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return () => {
|
|
197
|
+
for (const timer of timers) clearTimeout(timer)
|
|
198
|
+
for (const raf of rafsRef.current) cancelAnimationFrame(raf)
|
|
199
|
+
restoreTapRef.current?.()
|
|
200
|
+
restoreTapRef.current = null
|
|
201
|
+
}
|
|
202
|
+
}, [])
|
|
203
|
+
|
|
204
|
+
const done = ROWS.every((id) => rows[id] !== undefined)
|
|
205
|
+
const failing = ROWS.filter((id) => rows[id] !== undefined && !rows[id]!.pass)
|
|
206
|
+
const verdict = !done ? 'RUNNING' : failing.length === 0 ? 'PASS' : 'FAIL'
|
|
207
|
+
const passed = Object.keys(rows).length - failing.length
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<MotionRoot>
|
|
211
|
+
<ScrollView style={styles.scroll} contentContainerStyle={styles.container}>
|
|
212
|
+
<Text style={styles.title}>U7h boolean-animate checks</Text>
|
|
213
|
+
<Text style={styles.subtitle}>
|
|
214
|
+
catalog sibling↔child + true/false peers ({severity} severity)
|
|
215
|
+
</Text>
|
|
216
|
+
<View
|
|
217
|
+
style={[
|
|
218
|
+
styles.banner,
|
|
219
|
+
verdict === 'PASS' ? styles.pass : verdict === 'FAIL' ? styles.fail : styles.running,
|
|
220
|
+
]}
|
|
221
|
+
>
|
|
222
|
+
<Text style={styles.bannerText}>{verdict}</Text>
|
|
223
|
+
</View>
|
|
224
|
+
<Text style={styles.tally}>
|
|
225
|
+
{done
|
|
226
|
+
? `${passed} PASS / ${failing.length} FAIL of ${ROWS.length} rows`
|
|
227
|
+
: `${Object.keys(rows).length}/${ROWS.length} rows complete — RUNNING`}
|
|
228
|
+
</Text>
|
|
229
|
+
<Text style={styles.tallyDetail}>
|
|
230
|
+
{done
|
|
231
|
+
? failing.length === 0
|
|
232
|
+
? 'no failing rows'
|
|
233
|
+
: `FAILING: ${failing.join(' | ')}`
|
|
234
|
+
: `stage: ${stage}`}
|
|
235
|
+
</Text>
|
|
236
|
+
<Text style={styles.status}>
|
|
237
|
+
on={isOn ? 1 : 0} · completes={completes} · {severity}
|
|
238
|
+
</Text>
|
|
239
|
+
|
|
240
|
+
<LayoutGroup>
|
|
241
|
+
<View style={styles.stage} accessibilityLabel="u7h-boolean-animate-stage">
|
|
242
|
+
{isOn ? <U7hChild onComplete={onComplete} /> : <U7hSibling onComplete={onComplete} />}
|
|
243
|
+
</View>
|
|
244
|
+
</LayoutGroup>
|
|
245
|
+
|
|
246
|
+
<View style={styles.peers}>
|
|
247
|
+
<MotionView animate={true} style={[styles.peer, styles.peerTrue]} />
|
|
248
|
+
<MotionView animate={false} style={[styles.peer, styles.peerFalse]} />
|
|
249
|
+
</View>
|
|
250
|
+
|
|
251
|
+
{ROWS.map((id) => {
|
|
252
|
+
const row = rows[id]
|
|
253
|
+
return (
|
|
254
|
+
<View key={id} style={styles.row}>
|
|
255
|
+
<Text
|
|
256
|
+
style={[
|
|
257
|
+
styles.verdict,
|
|
258
|
+
row === undefined ? styles.pending : row.pass ? styles.ok : styles.bad,
|
|
259
|
+
]}
|
|
260
|
+
>
|
|
261
|
+
{row === undefined ? '…' : row.pass ? 'PASS' : 'FAIL'}
|
|
262
|
+
</Text>
|
|
263
|
+
<View style={styles.rowBody}>
|
|
264
|
+
<Text style={styles.rowId}>{id}</Text>
|
|
265
|
+
<Text style={styles.rowDetail} numberOfLines={3}>
|
|
266
|
+
{row === undefined ? 'pending' : row.detail}
|
|
267
|
+
</Text>
|
|
268
|
+
</View>
|
|
269
|
+
</View>
|
|
270
|
+
)
|
|
271
|
+
})}
|
|
272
|
+
</ScrollView>
|
|
273
|
+
</MotionRoot>
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const styles = StyleSheet.create({
|
|
278
|
+
scroll: { flex: 1, backgroundColor: '#0b0b12' },
|
|
279
|
+
container: { paddingVertical: 72, paddingHorizontal: 8 },
|
|
280
|
+
title: { color: '#fff', fontSize: 22, fontWeight: '700', textAlign: 'center' },
|
|
281
|
+
subtitle: { color: '#8a8aa0', fontSize: 12, marginTop: 6, textAlign: 'center' },
|
|
282
|
+
banner: {
|
|
283
|
+
alignSelf: 'center',
|
|
284
|
+
paddingHorizontal: 36,
|
|
285
|
+
paddingVertical: 10,
|
|
286
|
+
borderRadius: 10,
|
|
287
|
+
marginTop: 18,
|
|
288
|
+
marginBottom: 10,
|
|
289
|
+
},
|
|
290
|
+
pass: { backgroundColor: '#1d7a3e' },
|
|
291
|
+
fail: { backgroundColor: '#a32633' },
|
|
292
|
+
running: { backgroundColor: '#7a641d' },
|
|
293
|
+
bannerText: { color: '#fff', fontSize: 22, fontWeight: '800', letterSpacing: 2 },
|
|
294
|
+
tally: { color: '#e6e6f0', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
295
|
+
tallyDetail: { color: '#f87171', fontSize: 11, marginTop: 3, textAlign: 'center' },
|
|
296
|
+
status: {
|
|
297
|
+
color: '#c9c9d8',
|
|
298
|
+
fontSize: 11,
|
|
299
|
+
marginTop: 8,
|
|
300
|
+
textAlign: 'center',
|
|
301
|
+
fontFamily: 'Menlo',
|
|
302
|
+
},
|
|
303
|
+
stage: {
|
|
304
|
+
height: 280,
|
|
305
|
+
marginTop: 14,
|
|
306
|
+
backgroundColor: '#f3f3f3',
|
|
307
|
+
borderRadius: 16,
|
|
308
|
+
overflow: 'visible',
|
|
309
|
+
},
|
|
310
|
+
childBig: {
|
|
311
|
+
position: 'absolute',
|
|
312
|
+
top: 97,
|
|
313
|
+
left: 26,
|
|
314
|
+
width: 120,
|
|
315
|
+
height: 120,
|
|
316
|
+
borderRadius: 20,
|
|
317
|
+
backgroundColor: 'rgba(136, 85, 255, 0.3)',
|
|
318
|
+
},
|
|
319
|
+
childSmall: {
|
|
320
|
+
position: 'absolute',
|
|
321
|
+
top: 30,
|
|
322
|
+
left: 30,
|
|
323
|
+
width: 60,
|
|
324
|
+
height: 60,
|
|
325
|
+
borderRadius: 10,
|
|
326
|
+
backgroundColor: '#8855ff',
|
|
327
|
+
},
|
|
328
|
+
siblingBig: {
|
|
329
|
+
position: 'absolute',
|
|
330
|
+
top: 70,
|
|
331
|
+
left: 40,
|
|
332
|
+
width: 60,
|
|
333
|
+
height: 60,
|
|
334
|
+
borderRadius: 10,
|
|
335
|
+
backgroundColor: 'rgba(0, 153, 255, 0.3)',
|
|
336
|
+
},
|
|
337
|
+
siblingSmall: {
|
|
338
|
+
position: 'absolute',
|
|
339
|
+
top: 132,
|
|
340
|
+
left: 102,
|
|
341
|
+
width: 60,
|
|
342
|
+
height: 60,
|
|
343
|
+
borderRadius: 10,
|
|
344
|
+
backgroundColor: '#0099ff',
|
|
345
|
+
},
|
|
346
|
+
peers: { flexDirection: 'row', justifyContent: 'center', marginTop: 12 },
|
|
347
|
+
peer: { width: 32, height: 32, borderRadius: 6 },
|
|
348
|
+
peerTrue: { backgroundColor: '#16a34a', marginRight: 12 },
|
|
349
|
+
peerFalse: { backgroundColor: '#eab308' },
|
|
350
|
+
row: { flexDirection: 'row', marginTop: 10 },
|
|
351
|
+
verdict: { width: 52, fontSize: 12, fontWeight: '800', paddingTop: 1 },
|
|
352
|
+
ok: { color: '#4ade80' },
|
|
353
|
+
bad: { color: '#f87171' },
|
|
354
|
+
pending: { color: '#8a8aa0' },
|
|
355
|
+
rowBody: { flex: 1 },
|
|
356
|
+
rowId: { color: '#e6e6f0', fontSize: 13, fontWeight: '600' },
|
|
357
|
+
rowDetail: { color: '#6f6f85', fontSize: 11, marginTop: 2 },
|
|
358
|
+
})
|