@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,425 @@
|
|
|
1
|
+
// U7g addScaleCorrector device checks: catalog Layout-Projection-custom-values ("Framer
|
|
2
|
+
// border") on the public MotionView path. iOS has no tap injection, so the screen self-drives.
|
|
3
|
+
// Discriminators are the channel (Decision 82 analog of 77): worklet registration accepted,
|
|
4
|
+
// CSS-variable / boxShadow / non-worklet refuse loud, catalog layout flight starts then
|
|
5
|
+
// completes. Visual constancy (computed × scale ≈ authored) is the web e2e witness; the math
|
|
6
|
+
// is unit-proven (applyInstalledScaleCorrector). Native cannot read
|
|
7
|
+
// host paint.
|
|
8
|
+
|
|
9
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
10
|
+
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
|
11
|
+
import { addScaleCorrector } from '../../native/motion/addScaleCorrector'
|
|
12
|
+
import { MotionView } from '../../native/motion/MotionView'
|
|
13
|
+
import { ambientNativeSeverity } from '../../native/motion/severity'
|
|
14
|
+
|
|
15
|
+
const ROW_REGISTER = 'worklet catalog registration accepted (four border-width correctors)'
|
|
16
|
+
const ROW_CSS_VAR = 'CSS-variable key refuses loud'
|
|
17
|
+
const ROW_BOX_SHADOW = 'boxShadow refuses loud (F3: no silent native built-in)'
|
|
18
|
+
const ROW_WORKLET = 'non-worklet corrector refuses loud (F1)'
|
|
19
|
+
const ROW_FLIGHT = 'catalog layout flight: start then complete'
|
|
20
|
+
const ROW_CLEAN = 'clean run: only the three expected refusal reports'
|
|
21
|
+
const ROWS = [
|
|
22
|
+
ROW_REGISTER,
|
|
23
|
+
ROW_CSS_VAR,
|
|
24
|
+
ROW_BOX_SHADOW,
|
|
25
|
+
ROW_WORKLET,
|
|
26
|
+
ROW_FLIGHT,
|
|
27
|
+
ROW_CLEAN,
|
|
28
|
+
] as const
|
|
29
|
+
|
|
30
|
+
const TRANSITION = { duration: 0.8, ease: 'circIn' } as const
|
|
31
|
+
const T_REGISTER = 250
|
|
32
|
+
const T_REFUSALS = 500
|
|
33
|
+
const T_FLIGHT = 800
|
|
34
|
+
const START_DEADLINE_MS = 4000
|
|
35
|
+
const SETTLE_DEADLINE_MS = 6000
|
|
36
|
+
const STABILITY_MS = 400
|
|
37
|
+
|
|
38
|
+
interface RowResult {
|
|
39
|
+
readonly pass: boolean
|
|
40
|
+
readonly detail: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function correctBorderX(
|
|
44
|
+
latest: number | string,
|
|
45
|
+
context: {
|
|
46
|
+
targetDelta: { x: { scale: number }; y: { scale: number } }
|
|
47
|
+
treeScale: { x: number; y: number }
|
|
48
|
+
},
|
|
49
|
+
): number {
|
|
50
|
+
'worklet'
|
|
51
|
+
const value = typeof latest === 'string' ? Number.parseFloat(String(latest)) : latest
|
|
52
|
+
return value / context.targetDelta.x.scale / context.treeScale.x
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function correctBorderY(
|
|
56
|
+
latest: number | string,
|
|
57
|
+
context: {
|
|
58
|
+
targetDelta: { x: { scale: number }; y: { scale: number } }
|
|
59
|
+
treeScale: { x: number; y: number }
|
|
60
|
+
},
|
|
61
|
+
): number {
|
|
62
|
+
'worklet'
|
|
63
|
+
const value = typeof latest === 'string' ? Number.parseFloat(String(latest)) : latest
|
|
64
|
+
return value / context.targetDelta.y.scale / context.treeScale.y
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const catalogCorrectors = {
|
|
68
|
+
borderTopWidth: { correct: correctBorderY },
|
|
69
|
+
borderLeftWidth: { correct: correctBorderX },
|
|
70
|
+
borderRightWidth: { correct: correctBorderX },
|
|
71
|
+
borderBottomWidth: { correct: correctBorderY },
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function ScaleCorrectorChecksScreen() {
|
|
75
|
+
const severity = ambientNativeSeverity()
|
|
76
|
+
const [isOn, setOn] = useState(false)
|
|
77
|
+
const [stage, setStage] = useState('boot')
|
|
78
|
+
const [rows, setRows] = useState<Partial<Record<(typeof ROWS)[number], RowResult>>>({})
|
|
79
|
+
const [starts, setStarts] = useState(0)
|
|
80
|
+
const [completes, setCompletes] = useState(0)
|
|
81
|
+
|
|
82
|
+
const reportsRef = useRef<string[]>([])
|
|
83
|
+
const startsRef = useRef(0)
|
|
84
|
+
const completesRef = useRef(0)
|
|
85
|
+
const rafsRef = useRef<ReturnType<typeof requestAnimationFrame>[]>([])
|
|
86
|
+
const mountStampRef = useRef(Date.now())
|
|
87
|
+
|
|
88
|
+
const now = (): number => Date.now() - mountStampRef.current
|
|
89
|
+
|
|
90
|
+
const finalize = (id: (typeof ROWS)[number], pass: boolean, detail: string): void => {
|
|
91
|
+
setRows((previous) => ({ ...previous, [id]: { pass, detail } }))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const onStart = useCallback((): void => {
|
|
95
|
+
startsRef.current += 1
|
|
96
|
+
setStarts(startsRef.current)
|
|
97
|
+
}, [])
|
|
98
|
+
|
|
99
|
+
const onComplete = useCallback((): void => {
|
|
100
|
+
completesRef.current += 1
|
|
101
|
+
setCompletes(completesRef.current)
|
|
102
|
+
}, [])
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
const originalError = console.error
|
|
106
|
+
console.error = (...args: unknown[]) => {
|
|
107
|
+
for (const arg of args) {
|
|
108
|
+
const message = arg instanceof Error ? arg.message : typeof arg === 'string' ? arg : null
|
|
109
|
+
if (
|
|
110
|
+
message !== null &&
|
|
111
|
+
/motion|addScaleCorrector|worklet|boxShadow|CSS variable/i.test(message)
|
|
112
|
+
) {
|
|
113
|
+
reportsRef.current.push(message)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
originalError(...args)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const timers: ReturnType<typeof setTimeout>[] = []
|
|
120
|
+
const at = (ms: number, run: () => void): void => {
|
|
121
|
+
timers.push(setTimeout(run, ms))
|
|
122
|
+
}
|
|
123
|
+
const trackRaf = (id: ReturnType<typeof requestAnimationFrame>): void => {
|
|
124
|
+
rafsRef.current.push(id)
|
|
125
|
+
}
|
|
126
|
+
const pollUntil = (
|
|
127
|
+
condition: () => boolean,
|
|
128
|
+
deadlineAt: number,
|
|
129
|
+
onResolve: (ok: boolean) => void,
|
|
130
|
+
): void => {
|
|
131
|
+
const step = (): void => {
|
|
132
|
+
if (condition()) {
|
|
133
|
+
onResolve(true)
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
if (now() >= deadlineAt) {
|
|
137
|
+
onResolve(false)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
trackRaf(requestAnimationFrame(step))
|
|
141
|
+
}
|
|
142
|
+
trackRaf(requestAnimationFrame(step))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const captureRefusal = (run: () => void, pattern: RegExp): { hit: boolean; detail: string } => {
|
|
146
|
+
const before = reportsRef.current.length
|
|
147
|
+
let thrown: string | null = null
|
|
148
|
+
try {
|
|
149
|
+
run()
|
|
150
|
+
} catch (error) {
|
|
151
|
+
thrown = error instanceof Error ? error.message : String(error)
|
|
152
|
+
}
|
|
153
|
+
const reported = reportsRef.current.slice(before)
|
|
154
|
+
const text = thrown ?? reported.join(' | ')
|
|
155
|
+
const hit =
|
|
156
|
+
(thrown !== null && pattern.test(thrown)) ||
|
|
157
|
+
reported.some((message) => pattern.test(message))
|
|
158
|
+
return {
|
|
159
|
+
hit,
|
|
160
|
+
detail: hit
|
|
161
|
+
? `${thrown !== null ? 'threw' : 'reported'}: ${text.slice(0, 120)}`
|
|
162
|
+
: `no matching refusal (thrown=${thrown ?? 'none'}; new reports=${reported.length})`,
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
at(T_REGISTER, () => {
|
|
167
|
+
let thrown: string | null = null
|
|
168
|
+
try {
|
|
169
|
+
addScaleCorrector(catalogCorrectors)
|
|
170
|
+
} catch (error) {
|
|
171
|
+
thrown = error instanceof Error ? error.message : String(error)
|
|
172
|
+
}
|
|
173
|
+
finalize(
|
|
174
|
+
ROW_REGISTER,
|
|
175
|
+
thrown === null,
|
|
176
|
+
thrown === null
|
|
177
|
+
? 'four catalog border-width worklet correctors registered'
|
|
178
|
+
: `registration threw: ${thrown.slice(0, 120)}`,
|
|
179
|
+
)
|
|
180
|
+
setStage('registered')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
at(T_REFUSALS, () => {
|
|
184
|
+
const cssVar = captureRefusal(
|
|
185
|
+
() => addScaleCorrector({ '--frame-border': { correct: correctBorderX } }),
|
|
186
|
+
/CSS variable|--frame-border/,
|
|
187
|
+
)
|
|
188
|
+
finalize(ROW_CSS_VAR, cssVar.hit, cssVar.detail)
|
|
189
|
+
const boxShadow = captureRefusal(
|
|
190
|
+
() => addScaleCorrector({ boxShadow: { correct: correctBorderX } }),
|
|
191
|
+
/boxShadow/,
|
|
192
|
+
)
|
|
193
|
+
finalize(ROW_BOX_SHADOW, boxShadow.hit, boxShadow.detail)
|
|
194
|
+
const plain = captureRefusal(
|
|
195
|
+
() =>
|
|
196
|
+
addScaleCorrector({
|
|
197
|
+
paddingTop: {
|
|
198
|
+
correct: (latest) => latest,
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
201
|
+
/worklet/,
|
|
202
|
+
)
|
|
203
|
+
finalize(ROW_WORKLET, plain.hit, plain.detail)
|
|
204
|
+
setStage('refusals sampled')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
at(T_FLIGHT, () => {
|
|
208
|
+
setOn(true)
|
|
209
|
+
setStage('layout flight pressed (100×100 → 280×160)')
|
|
210
|
+
pollUntil(
|
|
211
|
+
() => startsRef.current >= 1,
|
|
212
|
+
now() + START_DEADLINE_MS,
|
|
213
|
+
(okStart) => {
|
|
214
|
+
if (!okStart) {
|
|
215
|
+
finalize(
|
|
216
|
+
ROW_FLIGHT,
|
|
217
|
+
false,
|
|
218
|
+
`layout start never fired within ${START_DEADLINE_MS}ms (starts ${startsRef.current})`,
|
|
219
|
+
)
|
|
220
|
+
setStage('flight row finalized (FAIL)')
|
|
221
|
+
at(STABILITY_MS, () => finishClean())
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
pollUntil(
|
|
225
|
+
() => completesRef.current >= 1,
|
|
226
|
+
now() + SETTLE_DEADLINE_MS,
|
|
227
|
+
(okSettle) => {
|
|
228
|
+
finalize(
|
|
229
|
+
ROW_FLIGHT,
|
|
230
|
+
okSettle && startsRef.current >= 1,
|
|
231
|
+
okSettle
|
|
232
|
+
? `start ${startsRef.current} then complete ${completesRef.current} on the catalog outer`
|
|
233
|
+
: `layout complete never fired within ${SETTLE_DEADLINE_MS}ms (starts ${startsRef.current}, completes ${completesRef.current})`,
|
|
234
|
+
)
|
|
235
|
+
setStage(okSettle ? 'flight row finalized' : 'flight row finalized (FAIL)')
|
|
236
|
+
at(STABILITY_MS, () => finishClean())
|
|
237
|
+
},
|
|
238
|
+
)
|
|
239
|
+
},
|
|
240
|
+
)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
const finishClean = (): void => {
|
|
244
|
+
const expected = reportsRef.current.filter(
|
|
245
|
+
(message) =>
|
|
246
|
+
/CSS variable|--frame-border/.test(message) ||
|
|
247
|
+
/boxShadow/.test(message) ||
|
|
248
|
+
/worklet/.test(message),
|
|
249
|
+
)
|
|
250
|
+
const unexpected = reportsRef.current.filter(
|
|
251
|
+
(message) =>
|
|
252
|
+
!/CSS variable|--frame-border/.test(message) &&
|
|
253
|
+
!/boxShadow/.test(message) &&
|
|
254
|
+
!/worklet/.test(message),
|
|
255
|
+
)
|
|
256
|
+
finalize(
|
|
257
|
+
ROW_CLEAN,
|
|
258
|
+
unexpected.length === 0,
|
|
259
|
+
unexpected.length === 0
|
|
260
|
+
? `0 unexpected reports; ${expected.length} expected refusal(s) (css-var/boxShadow/worklet)`
|
|
261
|
+
: `${unexpected.length} unexpected: ${unexpected.map((message) => message.slice(0, 80)).join(' | ')}`,
|
|
262
|
+
)
|
|
263
|
+
setStage('done')
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return () => {
|
|
267
|
+
for (const timer of timers) clearTimeout(timer)
|
|
268
|
+
for (const raf of rafsRef.current) cancelAnimationFrame(raf)
|
|
269
|
+
console.error = originalError
|
|
270
|
+
}
|
|
271
|
+
}, [])
|
|
272
|
+
|
|
273
|
+
const done = ROWS.every((id) => rows[id] !== undefined)
|
|
274
|
+
const failing = ROWS.filter((id) => rows[id] !== undefined && !rows[id]!.pass)
|
|
275
|
+
const verdict = !done ? 'RUNNING' : failing.length === 0 ? 'PASS' : 'FAIL'
|
|
276
|
+
const passed = Object.keys(rows).length - failing.length
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<ScrollView style={styles.scroll} contentContainerStyle={styles.container}>
|
|
280
|
+
<Text style={styles.title}>U7g scale-corrector checks</Text>
|
|
281
|
+
<Text style={styles.subtitle}>
|
|
282
|
+
catalog Framer-border channel + F1/F3 refusals ({severity} severity)
|
|
283
|
+
</Text>
|
|
284
|
+
<View
|
|
285
|
+
style={[
|
|
286
|
+
styles.banner,
|
|
287
|
+
verdict === 'PASS' ? styles.pass : verdict === 'FAIL' ? styles.fail : styles.running,
|
|
288
|
+
]}
|
|
289
|
+
>
|
|
290
|
+
<Text style={styles.bannerText}>{verdict}</Text>
|
|
291
|
+
</View>
|
|
292
|
+
<Text style={styles.tally}>
|
|
293
|
+
{done
|
|
294
|
+
? `${passed} PASS / ${failing.length} FAIL of ${ROWS.length} rows`
|
|
295
|
+
: `${Object.keys(rows).length}/${ROWS.length} rows complete — RUNNING`}
|
|
296
|
+
</Text>
|
|
297
|
+
<Text style={styles.tallyDetail}>
|
|
298
|
+
{done
|
|
299
|
+
? failing.length === 0
|
|
300
|
+
? 'no failing rows'
|
|
301
|
+
: `FAILING: ${failing.join(' | ')}`
|
|
302
|
+
: `stage: ${stage}`}
|
|
303
|
+
</Text>
|
|
304
|
+
<Text style={styles.status}>
|
|
305
|
+
on={isOn ? 1 : 0} · starts={starts} · completes={completes} · {severity}
|
|
306
|
+
</Text>
|
|
307
|
+
|
|
308
|
+
<View style={styles.stage} accessibilityLabel="u7g-scale-corrector-stage">
|
|
309
|
+
<MotionView
|
|
310
|
+
layout
|
|
311
|
+
transition={TRANSITION}
|
|
312
|
+
onLayoutAnimationStart={onStart}
|
|
313
|
+
onLayoutAnimationComplete={onComplete}
|
|
314
|
+
style={[styles.outer, isOn ? styles.outerOn : styles.outerOff]}
|
|
315
|
+
accessibilityLabel="u7g-scale-corrector-outer"
|
|
316
|
+
>
|
|
317
|
+
<MotionView
|
|
318
|
+
layout
|
|
319
|
+
initial={false}
|
|
320
|
+
animate={
|
|
321
|
+
isOn
|
|
322
|
+
? {
|
|
323
|
+
borderTopWidth: 5,
|
|
324
|
+
borderRightWidth: 5,
|
|
325
|
+
borderLeftWidth: 5,
|
|
326
|
+
borderBottomWidth: 30,
|
|
327
|
+
borderColor: '#000000',
|
|
328
|
+
}
|
|
329
|
+
: {
|
|
330
|
+
borderTopWidth: 50,
|
|
331
|
+
borderRightWidth: 50,
|
|
332
|
+
borderLeftWidth: 50,
|
|
333
|
+
borderBottomWidth: 50,
|
|
334
|
+
borderColor: '#9900ff',
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
transition={TRANSITION}
|
|
338
|
+
style={styles.inner}
|
|
339
|
+
accessibilityLabel="u7g-scale-corrector-inner"
|
|
340
|
+
/>
|
|
341
|
+
</MotionView>
|
|
342
|
+
</View>
|
|
343
|
+
|
|
344
|
+
{ROWS.map((id) => {
|
|
345
|
+
const row = rows[id]
|
|
346
|
+
return (
|
|
347
|
+
<View key={id} style={styles.row}>
|
|
348
|
+
<Text
|
|
349
|
+
style={[
|
|
350
|
+
styles.verdict,
|
|
351
|
+
row === undefined ? styles.pending : row.pass ? styles.ok : styles.bad,
|
|
352
|
+
]}
|
|
353
|
+
>
|
|
354
|
+
{row === undefined ? '…' : row.pass ? 'PASS' : 'FAIL'}
|
|
355
|
+
</Text>
|
|
356
|
+
<View style={styles.rowBody}>
|
|
357
|
+
<Text style={styles.rowId}>{id}</Text>
|
|
358
|
+
<Text style={styles.rowDetail} numberOfLines={3}>
|
|
359
|
+
{row === undefined ? 'pending' : row.detail}
|
|
360
|
+
</Text>
|
|
361
|
+
</View>
|
|
362
|
+
</View>
|
|
363
|
+
)
|
|
364
|
+
})}
|
|
365
|
+
</ScrollView>
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const styles = StyleSheet.create({
|
|
370
|
+
scroll: { flex: 1, backgroundColor: '#0b0b12' },
|
|
371
|
+
container: { paddingVertical: 72, paddingHorizontal: 8 },
|
|
372
|
+
title: { color: '#fff', fontSize: 22, fontWeight: '700', textAlign: 'center' },
|
|
373
|
+
subtitle: { color: '#8a8aa0', fontSize: 12, marginTop: 6, textAlign: 'center' },
|
|
374
|
+
banner: {
|
|
375
|
+
alignSelf: 'center',
|
|
376
|
+
paddingHorizontal: 36,
|
|
377
|
+
paddingVertical: 10,
|
|
378
|
+
borderRadius: 10,
|
|
379
|
+
marginTop: 18,
|
|
380
|
+
marginBottom: 10,
|
|
381
|
+
},
|
|
382
|
+
pass: { backgroundColor: '#1d7a3e' },
|
|
383
|
+
fail: { backgroundColor: '#a32633' },
|
|
384
|
+
running: { backgroundColor: '#7a641d' },
|
|
385
|
+
bannerText: { color: '#fff', fontSize: 22, fontWeight: '800', letterSpacing: 2 },
|
|
386
|
+
tally: { color: '#e6e6f0', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
387
|
+
tallyDetail: { color: '#f87171', fontSize: 11, marginTop: 3, textAlign: 'center' },
|
|
388
|
+
status: {
|
|
389
|
+
color: '#c9c9d8',
|
|
390
|
+
fontSize: 11,
|
|
391
|
+
marginTop: 8,
|
|
392
|
+
textAlign: 'center',
|
|
393
|
+
fontFamily: 'Menlo',
|
|
394
|
+
},
|
|
395
|
+
stage: { height: 180, marginTop: 14 },
|
|
396
|
+
outer: {
|
|
397
|
+
position: 'absolute',
|
|
398
|
+
top: 0,
|
|
399
|
+
left: 8,
|
|
400
|
+
backgroundColor: '#e6e6f0',
|
|
401
|
+
},
|
|
402
|
+
outerOff: { width: 100, height: 100 },
|
|
403
|
+
outerOn: { width: 280, height: 160 },
|
|
404
|
+
inner: {
|
|
405
|
+
position: 'absolute',
|
|
406
|
+
top: 0,
|
|
407
|
+
left: 0,
|
|
408
|
+
right: 0,
|
|
409
|
+
bottom: 0,
|
|
410
|
+
borderStyle: 'solid',
|
|
411
|
+
borderColor: '#9900ff',
|
|
412
|
+
borderTopWidth: 50,
|
|
413
|
+
borderRightWidth: 50,
|
|
414
|
+
borderLeftWidth: 50,
|
|
415
|
+
borderBottomWidth: 50,
|
|
416
|
+
},
|
|
417
|
+
row: { flexDirection: 'row', marginTop: 10 },
|
|
418
|
+
verdict: { width: 52, fontSize: 12, fontWeight: '800', paddingTop: 1 },
|
|
419
|
+
ok: { color: '#4ade80' },
|
|
420
|
+
bad: { color: '#f87171' },
|
|
421
|
+
pending: { color: '#8a8aa0' },
|
|
422
|
+
rowBody: { flex: 1 },
|
|
423
|
+
rowId: { color: '#e6e6f0', fontSize: 13, fontWeight: '600' },
|
|
424
|
+
rowDetail: { color: '#6f6f85', fontSize: 11, marginTop: 2 },
|
|
425
|
+
})
|