@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,294 @@
|
|
|
1
|
+
// REQ-API-060 device checks (finding-B resolution): the property-lane onAnimationComplete
|
|
2
|
+
// delivery chain end to end on device. Leg A: an un-raced single flight completes. Leg B: a
|
|
3
|
+
// deliberately RACED label retarget — the superseded outgoing flight NEVER completes (pin
|
|
4
|
+
// MotionValue.start stops it) and the incoming flight completes with the BARE label string
|
|
5
|
+
// (the inherited-lane array fix, F5). Leg C: a typed-spring settle completes. iOS has no tap
|
|
6
|
+
// injection, so the screen self-drives. The completion ledger is the discriminator (Decision 94
|
|
7
|
+
// analog): every row judges the callback-sourced ledger — a missing or mis-shaped completion
|
|
8
|
+
// fails the row, so a late-but-healthy landing fails CLOSED rather than passing vacated.
|
|
9
|
+
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
11
|
+
import { StyleSheet, Text, View } from 'react-native'
|
|
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_A = 'leg A: un-raced single tween flight completes'
|
|
18
|
+
const ROW_B = 'leg B: raced retarget — only the incoming flight completes, with the bare label'
|
|
19
|
+
const ROW_C = 'leg C: typed-spring settle completes'
|
|
20
|
+
const ROW_CLEAN = 'clean run: zero unexpected reports'
|
|
21
|
+
const ROWS = [ROW_A, ROW_B, ROW_C, ROW_CLEAN] as const
|
|
22
|
+
|
|
23
|
+
const T_A_START = 80
|
|
24
|
+
const T_A_JUDGE = 1400
|
|
25
|
+
const T_B_HIDDEN = 1900
|
|
26
|
+
const T_B_JUDGE = 3200
|
|
27
|
+
const T_C_JUDGE = 4800
|
|
28
|
+
const T_CLEAN = 4900
|
|
29
|
+
|
|
30
|
+
interface RowResult {
|
|
31
|
+
readonly pass: boolean
|
|
32
|
+
readonly detail: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface Completion {
|
|
36
|
+
readonly at: number
|
|
37
|
+
readonly definition: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function describe(definition: unknown): string {
|
|
41
|
+
if (typeof definition === 'string') return definition
|
|
42
|
+
if (Array.isArray(definition)) return `array(${definition.map(String).join(',')})`
|
|
43
|
+
return typeof definition
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const VARIANTS = {
|
|
47
|
+
visible: { opacity: 1, transition: { duration: 0.8 } },
|
|
48
|
+
hidden: { opacity: 0, transition: { duration: 0.8 } },
|
|
49
|
+
} as const
|
|
50
|
+
|
|
51
|
+
export function CompletionChecksScreen() {
|
|
52
|
+
const severity = ambientNativeSeverity()
|
|
53
|
+
const [targetA, setTargetA] = useState(0)
|
|
54
|
+
const [labelB, setLabelB] = useState<'hidden' | 'visible'>('hidden')
|
|
55
|
+
const [targetC, setTargetC] = useState(0)
|
|
56
|
+
const [stage, setStage] = useState('boot')
|
|
57
|
+
const [rows, setRows] = useState<Partial<Record<(typeof ROWS)[number], RowResult>>>({})
|
|
58
|
+
const completionsA = useRef<Completion[]>([])
|
|
59
|
+
const completionsB = useRef<Completion[]>([])
|
|
60
|
+
const completionsC = useRef<Completion[]>([])
|
|
61
|
+
const interiorA = useRef(0)
|
|
62
|
+
const interiorB = useRef(0)
|
|
63
|
+
const t0 = useRef(Date.now())
|
|
64
|
+
const reportsRef = useRef<string[]>([])
|
|
65
|
+
const restoreTapRef = useRef<null | (() => void)>(null)
|
|
66
|
+
if (restoreTapRef.current === null) {
|
|
67
|
+
restoreTapRef.current = armProofConsoleTap(reportsRef.current, (message) =>
|
|
68
|
+
/motion|transition|settle|complete/i.test(message),
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const finalize = (id: (typeof ROWS)[number], pass: boolean, detail: string): void => {
|
|
73
|
+
setRows((previous) => ({ ...previous, [id]: { pass, detail } }))
|
|
74
|
+
}
|
|
75
|
+
const stamp = (): number => Date.now() - t0.current
|
|
76
|
+
|
|
77
|
+
const onCompleteA = useCallback((definition: unknown): void => {
|
|
78
|
+
completionsA.current.push({ at: stamp(), definition: describe(definition) })
|
|
79
|
+
}, [])
|
|
80
|
+
const onCompleteB = useCallback((definition: unknown): void => {
|
|
81
|
+
completionsB.current.push({ at: stamp(), definition: describe(definition) })
|
|
82
|
+
}, [])
|
|
83
|
+
const onCompleteC = useCallback((definition: unknown): void => {
|
|
84
|
+
completionsC.current.push({ at: stamp(), definition: describe(definition) })
|
|
85
|
+
}, [])
|
|
86
|
+
const onUpdateA = useCallback((latest: Readonly<Record<string, string | number>>): void => {
|
|
87
|
+
const value = latest.opacity
|
|
88
|
+
if (typeof value === 'number' && value > 0.02 && value < 0.98) interiorA.current += 1
|
|
89
|
+
}, [])
|
|
90
|
+
const onUpdateB = useCallback((latest: Readonly<Record<string, string | number>>): void => {
|
|
91
|
+
const value = latest.opacity
|
|
92
|
+
if (typeof value === 'number' && value > 0.02 && value < 0.98) interiorB.current += 1
|
|
93
|
+
}, [])
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const timers: ReturnType<typeof setTimeout>[] = []
|
|
97
|
+
const at = (ms: number, run: () => void): void => {
|
|
98
|
+
timers.push(setTimeout(run, ms))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
at(T_A_START, () => {
|
|
102
|
+
setTargetA(1)
|
|
103
|
+
setStage('leg A: single tween flight')
|
|
104
|
+
})
|
|
105
|
+
at(T_A_JUDGE, () => {
|
|
106
|
+
const completions = completionsA.current
|
|
107
|
+
const pass = completions.length === 1 && interiorA.current > 3
|
|
108
|
+
finalize(
|
|
109
|
+
ROW_A,
|
|
110
|
+
pass,
|
|
111
|
+
`completions=${completions.length} (${
|
|
112
|
+
completions.map((c) => `+${c.at}ms ${c.definition}`).join(' | ') || 'none'
|
|
113
|
+
}), ${interiorA.current} interior samples (the flight ran)`,
|
|
114
|
+
)
|
|
115
|
+
setLabelB('visible')
|
|
116
|
+
setStage('leg B: label flight started — retarget races it at +400ms')
|
|
117
|
+
})
|
|
118
|
+
at(T_B_HIDDEN, () => {
|
|
119
|
+
setLabelB('hidden')
|
|
120
|
+
})
|
|
121
|
+
at(T_B_JUDGE, () => {
|
|
122
|
+
const completions = completionsB.current
|
|
123
|
+
const pass =
|
|
124
|
+
completions.length === 1 && completions[0]!.definition === 'hidden' && interiorB.current > 0
|
|
125
|
+
finalize(
|
|
126
|
+
ROW_B,
|
|
127
|
+
pass,
|
|
128
|
+
`completions=${completions.length} (${
|
|
129
|
+
completions.map((c) => `+${c.at}ms ${c.definition}`).join(' | ') || 'none'
|
|
130
|
+
}) — the pin fires only the incoming flight, with the bare ` +
|
|
131
|
+
'label string; the outgoing (superseded) flight never completes',
|
|
132
|
+
)
|
|
133
|
+
setTargetC(100)
|
|
134
|
+
setStage('leg C: typed-spring settle')
|
|
135
|
+
})
|
|
136
|
+
at(T_C_JUDGE, () => {
|
|
137
|
+
const completions = completionsC.current
|
|
138
|
+
const pass = completions.length === 1
|
|
139
|
+
finalize(
|
|
140
|
+
ROW_C,
|
|
141
|
+
pass,
|
|
142
|
+
`completions=${completions.length} (${
|
|
143
|
+
completions.map((c) => `+${c.at}ms ${c.definition}`).join(' | ') || 'none'
|
|
144
|
+
})`,
|
|
145
|
+
)
|
|
146
|
+
setStage('legs judged')
|
|
147
|
+
})
|
|
148
|
+
at(T_CLEAN, () => {
|
|
149
|
+
const pass = reportsRef.current.length === 0
|
|
150
|
+
finalize(
|
|
151
|
+
ROW_CLEAN,
|
|
152
|
+
pass,
|
|
153
|
+
pass
|
|
154
|
+
? '0 unexpected reports'
|
|
155
|
+
: `${reportsRef.current.length} unexpected: ${reportsRef.current
|
|
156
|
+
.map((message) => message.slice(0, 80))
|
|
157
|
+
.join(' | ')}`,
|
|
158
|
+
)
|
|
159
|
+
setStage('done')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return () => {
|
|
163
|
+
for (const timer of timers) clearTimeout(timer)
|
|
164
|
+
restoreTapRef.current?.()
|
|
165
|
+
restoreTapRef.current = null
|
|
166
|
+
}
|
|
167
|
+
// The timeline runs once per mount; all captured values are refs or stable setters.
|
|
168
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
169
|
+
}, [])
|
|
170
|
+
|
|
171
|
+
const done = ROWS.every((id) => rows[id] !== undefined)
|
|
172
|
+
const failing = ROWS.filter((id) => rows[id] !== undefined && !rows[id]!.pass)
|
|
173
|
+
const verdict = !done ? 'RUNNING' : failing.length === 0 ? 'PASS' : 'FAIL'
|
|
174
|
+
const passed = Object.keys(rows).length - failing.length
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<MotionRoot>
|
|
178
|
+
<View style={styles.scroll}>
|
|
179
|
+
<View style={styles.container}>
|
|
180
|
+
<Text style={styles.title}>onAnimationComplete checks</Text>
|
|
181
|
+
<Text style={styles.subtitle}>delivery + supersession law ({severity} severity)</Text>
|
|
182
|
+
<View
|
|
183
|
+
style={[
|
|
184
|
+
styles.banner,
|
|
185
|
+
verdict === 'PASS' ? styles.pass : verdict === 'FAIL' ? styles.fail : styles.running,
|
|
186
|
+
]}
|
|
187
|
+
>
|
|
188
|
+
<Text style={styles.bannerText}>{verdict}</Text>
|
|
189
|
+
</View>
|
|
190
|
+
<Text style={styles.tally}>
|
|
191
|
+
{done
|
|
192
|
+
? `${passed} PASS / ${failing.length} FAIL of ${ROWS.length} rows`
|
|
193
|
+
: `${Object.keys(rows).length}/${ROWS.length} rows complete — RUNNING`}
|
|
194
|
+
</Text>
|
|
195
|
+
<Text style={styles.tallyDetail}>
|
|
196
|
+
{done
|
|
197
|
+
? failing.length === 0
|
|
198
|
+
? 'no failing rows'
|
|
199
|
+
: `FAILING: ${failing.join(' | ')}`
|
|
200
|
+
: `stage: ${stage}`}
|
|
201
|
+
</Text>
|
|
202
|
+
|
|
203
|
+
<MotionView
|
|
204
|
+
animate={{ opacity: targetA }}
|
|
205
|
+
initial={false}
|
|
206
|
+
transition={{ duration: 0.8 }}
|
|
207
|
+
onUpdate={onUpdateA}
|
|
208
|
+
onAnimationComplete={onCompleteA}
|
|
209
|
+
style={styles.box}
|
|
210
|
+
accessibilityLabel="completion-legA-box"
|
|
211
|
+
/>
|
|
212
|
+
<MotionView animate={labelB} initial={false}>
|
|
213
|
+
<MotionView
|
|
214
|
+
variants={VARIANTS}
|
|
215
|
+
onUpdate={onUpdateB}
|
|
216
|
+
onAnimationComplete={onCompleteB}
|
|
217
|
+
style={[styles.box, styles.boxB]}
|
|
218
|
+
accessibilityLabel="completion-legB-box"
|
|
219
|
+
/>
|
|
220
|
+
</MotionView>
|
|
221
|
+
<MotionView
|
|
222
|
+
animate={{ x: targetC }}
|
|
223
|
+
initial={false}
|
|
224
|
+
transition={{ type: 'spring', duration: 1 }}
|
|
225
|
+
onAnimationComplete={onCompleteC}
|
|
226
|
+
style={[styles.box, styles.boxC]}
|
|
227
|
+
accessibilityLabel="completion-legC-box"
|
|
228
|
+
/>
|
|
229
|
+
|
|
230
|
+
{ROWS.map((id) => {
|
|
231
|
+
const row = rows[id]
|
|
232
|
+
return (
|
|
233
|
+
<View key={id} style={styles.row}>
|
|
234
|
+
<Text
|
|
235
|
+
style={[
|
|
236
|
+
styles.verdict,
|
|
237
|
+
row === undefined ? styles.pending : row.pass ? styles.ok : styles.bad,
|
|
238
|
+
]}
|
|
239
|
+
>
|
|
240
|
+
{row === undefined ? '…' : row.pass ? 'PASS' : 'FAIL'}
|
|
241
|
+
</Text>
|
|
242
|
+
<View style={styles.rowBody}>
|
|
243
|
+
<Text style={styles.rowId}>{id}</Text>
|
|
244
|
+
<Text style={styles.rowDetail} numberOfLines={3}>
|
|
245
|
+
{row === undefined ? 'pending' : row.detail}
|
|
246
|
+
</Text>
|
|
247
|
+
</View>
|
|
248
|
+
</View>
|
|
249
|
+
)
|
|
250
|
+
})}
|
|
251
|
+
</View>
|
|
252
|
+
</View>
|
|
253
|
+
</MotionRoot>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const styles = StyleSheet.create({
|
|
258
|
+
scroll: { flex: 1, backgroundColor: '#0b0b12' },
|
|
259
|
+
container: { paddingVertical: 72, paddingHorizontal: 8 },
|
|
260
|
+
title: { color: '#fff', fontSize: 22, fontWeight: '700', textAlign: 'center' },
|
|
261
|
+
subtitle: { color: '#8a8aa0', fontSize: 12, marginTop: 6, textAlign: 'center' },
|
|
262
|
+
banner: {
|
|
263
|
+
alignSelf: 'center',
|
|
264
|
+
paddingHorizontal: 36,
|
|
265
|
+
paddingVertical: 10,
|
|
266
|
+
borderRadius: 10,
|
|
267
|
+
marginTop: 18,
|
|
268
|
+
marginBottom: 10,
|
|
269
|
+
},
|
|
270
|
+
pass: { backgroundColor: '#1d7a3e' },
|
|
271
|
+
fail: { backgroundColor: '#a32633' },
|
|
272
|
+
running: { backgroundColor: '#7a641d' },
|
|
273
|
+
bannerText: { color: '#fff', fontSize: 22, fontWeight: '800', letterSpacing: 2 },
|
|
274
|
+
tally: { color: '#e6e6f0', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
275
|
+
tallyDetail: { color: '#f87171', fontSize: 11, marginTop: 3, textAlign: 'center' },
|
|
276
|
+
box: {
|
|
277
|
+
width: 40,
|
|
278
|
+
height: 40,
|
|
279
|
+
marginTop: 12,
|
|
280
|
+
backgroundColor: '#ef4444',
|
|
281
|
+
borderRadius: 8,
|
|
282
|
+
opacity: 0,
|
|
283
|
+
},
|
|
284
|
+
boxB: { backgroundColor: '#38bdf8' },
|
|
285
|
+
boxC: { backgroundColor: '#f97316', opacity: 1 },
|
|
286
|
+
row: { flexDirection: 'row', marginTop: 10 },
|
|
287
|
+
verdict: { width: 52, fontSize: 12, fontWeight: '800', paddingTop: 1 },
|
|
288
|
+
ok: { color: '#4ade80' },
|
|
289
|
+
bad: { color: '#f87171' },
|
|
290
|
+
pending: { color: '#8a8aa0' },
|
|
291
|
+
rowBody: { flex: 1 },
|
|
292
|
+
rowId: { color: '#e6e6f0', fontSize: 13, fontWeight: '600' },
|
|
293
|
+
rowDetail: { color: '#6f6f85', fontSize: 11, marginTop: 2 },
|
|
294
|
+
})
|