@incodetech/prizma-core-screens 0.2.3 → 0.2.7
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/dist/CurpEnterScreen.d.ts +7 -0
- package/dist/CurpEnterScreen.js +17 -3
- package/dist/CurpGenerateScreen.d.ts +8 -0
- package/dist/CurpGenerateScreen.js +28 -13
- package/dist/CurpLayout.css +8 -34
- package/dist/CurpStrings.d.ts +16 -2
- package/dist/EkybAddressDetailsScreen.d.ts +7 -0
- package/dist/EkybAddressDetailsScreen.js +25 -7
- package/dist/EkybBusinessDetailsScreen.d.ts +6 -0
- package/dist/EkybBusinessDetailsScreen.js +23 -6
- package/dist/EkybCountryScreen.d.ts +13 -5
- package/dist/EkybCountryScreen.js +27 -9
- package/dist/EkybOwnersScreen.d.ts +8 -0
- package/dist/EkybOwnersScreen.js +26 -3
- package/dist/EkybStrings.d.ts +27 -10
- package/dist/EkycCountryCatalog.d.ts +4 -1
- package/dist/EkycCountryCatalog.js +5 -3
- package/dist/EkycCountryFormScreen.d.ts +12 -3
- package/dist/EkycCountryFormScreen.js +28 -10
- package/dist/EkycDetailsScreen.d.ts +7 -0
- package/dist/EkycDetailsScreen.js +21 -4
- package/dist/EkycStrings.d.ts +14 -2
- package/dist/ElectronicSignatureLayout.css +6 -0
- package/dist/EmailInputStrings.d.ts +11 -1
- package/dist/EmailInputVerifyScreen.d.ts +7 -0
- package/dist/EmailInputVerifyScreen.js +17 -3
- package/dist/FaceMatchComparisonScreen.d.ts +3 -3
- package/dist/FaceMatchLayout.css +21 -12
- package/dist/FormScreen.css +16 -0
- package/dist/FormsLongFormScreen.d.ts +8 -0
- package/dist/FormsLongFormScreen.js +34 -13
- package/dist/FormsStrings.d.ts +11 -1
- package/dist/GeolocationBrowserStepsScreen.d.ts +6 -2
- package/dist/GeolocationBrowserStepsScreen.js +7 -3
- package/dist/GeolocationStrings.d.ts +3 -0
- package/dist/IdCaptureLayout.css +40 -0
- package/dist/ListScreen.d.ts +2 -0
- package/dist/ListScreen.js +1 -1
- package/dist/PhoneInputLayout.css +10 -9
- package/dist/PhoneInputStrings.d.ts +11 -1
- package/dist/PhoneInputVerifyScreen.d.ts +8 -0
- package/dist/PhoneInputVerifyScreen.js +19 -6
- package/dist/QualifiedSignatureAcceptScreen.js +6 -1
- package/dist/QualifiedSignatureDocumentScreen.d.ts +13 -9
- package/dist/QualifiedSignatureDocumentScreen.js +18 -16
- package/dist/QualifiedSignatureLayout.css +76 -0
- package/dist/QualifiedSignatureStrings.d.ts +10 -0
- package/dist/ScreenNav.css +8 -0
- package/dist/ScreenNav.d.ts +15 -2
- package/dist/ScreenNav.js +10 -3
- package/dist/VideoSelfieIdCaptureScreen.d.ts +10 -3
- package/dist/VideoSelfieIdCaptureScreen.js +44 -13
- package/dist/VideoSelfieLayout.css +164 -33
- package/dist/VideoSelfieSelfieCaptureScreen.d.ts +44 -27
- package/dist/VideoSelfieSelfieCaptureScreen.js +65 -39
- package/dist/VideoSelfieSpeakScreen.js +18 -1
- package/dist/VideoSelfieStrings.d.ts +78 -12
- package/dist/VideoSelfieTurnHeadScreen.d.ts +51 -0
- package/dist/VideoSelfieTurnHeadScreen.js +63 -0
- package/dist/VideoSelfieTutorialScreen.js +13 -5
- package/dist/VideoSelfieWaveScreen.d.ts +53 -0
- package/dist/VideoSelfieWaveScreen.js +62 -0
- package/dist/WatchlistBusinessFormScreen.d.ts +11 -7
- package/dist/WatchlistBusinessFormScreen.js +27 -13
- package/dist/WatchlistBusinessStrings.d.ts +21 -7
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +3 -1
- package/package.json +14 -3
- package/samples/video-selfie-room.png +0 -0
- package/src/CurpEnterScreen.tsx +21 -4
- package/src/CurpGenerateScreen.tsx +46 -22
- package/src/CurpLayout.css +8 -34
- package/src/CurpStrings.ts +22 -2
- package/src/EkybAddressDetailsScreen.tsx +32 -4
- package/src/EkybBusinessDetailsScreen.tsx +23 -5
- package/src/EkybCountryScreen.tsx +28 -7
- package/src/EkybOwnersScreen.tsx +34 -3
- package/src/EkybStrings.ts +29 -10
- package/src/EkycCountryCatalog.ts +9 -4
- package/src/EkycCountryFormScreen.tsx +51 -12
- package/src/EkycDetailsScreen.tsx +22 -1
- package/src/EkycStrings.ts +14 -2
- package/src/ElectronicSignatureLayout.css +6 -0
- package/src/EmailInputStrings.ts +12 -1
- package/src/EmailInputVerifyScreen.tsx +21 -4
- package/src/FaceMatchComparisonScreen.tsx +3 -3
- package/src/FaceMatchLayout.css +21 -12
- package/src/FormScreen.css +16 -0
- package/src/FormsLongFormScreen.tsx +41 -13
- package/src/FormsStrings.ts +12 -1
- package/src/GeolocationBrowserStepsScreen.tsx +6 -3
- package/src/GeolocationStrings.ts +3 -0
- package/src/IdCaptureLayout.css +40 -0
- package/src/ListScreen.tsx +10 -1
- package/src/PhoneInputLayout.css +10 -9
- package/src/PhoneInputStrings.ts +12 -1
- package/src/PhoneInputVerifyScreen.tsx +21 -5
- package/src/QualifiedSignatureAcceptScreen.tsx +12 -1
- package/src/QualifiedSignatureDocumentScreen.tsx +27 -17
- package/src/QualifiedSignatureLayout.css +76 -0
- package/src/QualifiedSignatureStrings.ts +10 -0
- package/src/ScreenNav.css +8 -0
- package/src/ScreenNav.tsx +35 -5
- package/src/VideoSelfieIdCaptureScreen.tsx +116 -30
- package/src/VideoSelfieLayout.css +164 -33
- package/src/VideoSelfieSelfieCaptureScreen.tsx +104 -59
- package/src/VideoSelfieSpeakScreen.tsx +25 -14
- package/src/VideoSelfieStrings.ts +85 -12
- package/src/VideoSelfieTurnHeadScreen.tsx +151 -0
- package/src/VideoSelfieTutorialScreen.tsx +34 -23
- package/src/VideoSelfieWaveScreen.tsx +135 -0
- package/src/WatchlistBusinessFormScreen.tsx +35 -16
- package/src/WatchlistBusinessStrings.ts +21 -7
- package/src/index.ts +14 -0
- package/src/types.ts +3 -1
|
@@ -63,6 +63,27 @@
|
|
|
63
63
|
z-index: 1;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/* ── The face ring sits in the SAME place on every screen ────────────────────
|
|
67
|
+
* Figma puts `ring-uploading` at (27, 188) on every frame that draws it —
|
|
68
|
+
* `8.x`, `9.x`, the four head turns and the wave — and never moves it. Left
|
|
69
|
+
* to the flex band the ring drifted instead, because what sits UNDER it
|
|
70
|
+
* changes from screen to screen: an instruction on `8.0`, a spinner and a
|
|
71
|
+
* label on `8.1`, an arrow chip plus a line on the turns, nothing at all
|
|
72
|
+
* behind the wave's card. Centring a taller stack pushes the ring up
|
|
73
|
+
* (measured 150 / 165 / 185 across three screens where the frames say 188),
|
|
74
|
+
* and a capture window that moves between steps reads as a glitch.
|
|
75
|
+
*
|
|
76
|
+
* So the ring band is TOP-aligned and the offset is named once. 188 is
|
|
77
|
+
* Figma's; 84 is where the content column starts here (the status-bar reserve
|
|
78
|
+
* plus the 37 stepper band); 40 is Core's own padding above the stage inside
|
|
79
|
+
* `PrizmaSelfieCapture`. What is left is the air this rule owns. Everything
|
|
80
|
+
* under the ring then flows below it, which is what the frames draw.
|
|
81
|
+
* DEVIATIONS #VS-24. */
|
|
82
|
+
.video-selfie-band--ring {
|
|
83
|
+
justify-content: flex-start;
|
|
84
|
+
padding-top: calc(188px - var(--pcs-status-bar, 47px) - 37px - var(--prizma-spacing-40));
|
|
85
|
+
}
|
|
86
|
+
|
|
66
87
|
/* ── Recording indicator (10.1 / 11.1 / 11.3 / 11.5) ─────────────────────────
|
|
67
88
|
* A sound-wave glyph over the "Recording…" HUD line. The glyph ink is Core's
|
|
68
89
|
* own Static camera ink — the token `PrizmaCaptureManual` uses for the shutter
|
|
@@ -106,6 +127,33 @@
|
|
|
106
127
|
align-items: center;
|
|
107
128
|
}
|
|
108
129
|
|
|
130
|
+
/* The two verdict cards (`10.2` processing, `10.3` success) carry no buttons,
|
|
131
|
+
* so their content stack is two rows where the prompt's is three — left alone
|
|
132
|
+
* they came out ~70px shorter and the sheet jumped as the step advanced. The
|
|
133
|
+
* owner's call (2026-09-30) is that every card in the step is the same size,
|
|
134
|
+
* so the card takes the prompt's measure and centres its content in it.
|
|
135
|
+
*
|
|
136
|
+
* 184 is Figma's own `modal-light` height on `10.0` / `10.1`, which is what
|
|
137
|
+
* the prompt renders at; it is the module's geometry, not a token, and it is
|
|
138
|
+
* a MINIMUM — the card still grows if a translation needs more room.
|
|
139
|
+
* DEVIATIONS #VS-25. */
|
|
140
|
+
/* ⚠ The card, not the wrapper, has to do the centring. `height: 100%` on the
|
|
141
|
+
* wrapper resolved to `auto` — Core's `.prizma-modal` is not a flex container
|
|
142
|
+
* and its height is content-driven, so a percentage height had nothing to
|
|
143
|
+
* measure against and the block stayed pinned to the top (owner, 2026-09-30:
|
|
144
|
+
* "these are aligned to the top instead of centered"). Making the CARD the
|
|
145
|
+
* flex column is what actually distributes the extra height. */
|
|
146
|
+
.video-selfie-sheet-panel--status .prizma-modal {
|
|
147
|
+
min-height: 184px;
|
|
148
|
+
display: flex;
|
|
149
|
+
flex-direction: column;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.video-selfie-sheet-panel--status .prizma-modal__title {
|
|
154
|
+
text-align: center;
|
|
155
|
+
}
|
|
156
|
+
|
|
109
157
|
/* The card's verdict mark (10.3 / 11.x success). The glyph itself is the
|
|
110
158
|
* canonical bank icon Core's own `PrizmaTitle` uses for `status: success`
|
|
111
159
|
* (`IconStatusPositive` at Title/Icon/Size); only the INK is set here, because
|
|
@@ -155,10 +203,49 @@
|
|
|
155
203
|
color: var(--prizma-text-body-500-secondary);
|
|
156
204
|
}
|
|
157
205
|
|
|
206
|
+
/* ── The document frame's own measure ────────────────────────────────────────
|
|
207
|
+
* `3.x` / `5.x` / `6.x` all draw `ID Capture Frame` at **286 × 455**, centred
|
|
208
|
+
* in the 390 column (52 either side) — and they keep that measure on every
|
|
209
|
+
* device section in the file: 320, 368, 390, 430 and the 892-wide Pixel Fold
|
|
210
|
+
* all show the same 286-wide frame, only the air around it changes.
|
|
211
|
+
*
|
|
212
|
+
* Core's `PrizmaIdCapture` is `width: 100%` + `aspect-ratio: 288/456`, so left
|
|
213
|
+
* alone it takes the whole content column: 342 × 541 at 390, which ran the
|
|
214
|
+
* frame down into the verified tag (owner-reported in the 320 sweep,
|
|
215
|
+
* 2026-09-28, and the reason the stage read broken at every width, 2026-09-30).
|
|
216
|
+
*
|
|
217
|
+
* So the MODULE caps it, exactly as the frames do. `min()` keeps the cap from
|
|
218
|
+
* turning into a floor: on a column narrower than the frame it gives ground
|
|
219
|
+
* instead of spilling, which is what the 320 sections show. The aspect ratio
|
|
220
|
+
* stays Core's. DEVIATIONS #VS-18. */
|
|
221
|
+
.video-selfie-stage {
|
|
222
|
+
/* Figma geometry, not a token: `ID Capture Frame` is 286 wide on every
|
|
223
|
+
device section of this module. */
|
|
224
|
+
width: min(286px, 100%);
|
|
225
|
+
/* The viewfinder column stretches its children, so a child that names its
|
|
226
|
+
own width lands flush LEFT — which put the frame 16px off the phone's
|
|
227
|
+
centre line while the status card floating on it stayed centred. */
|
|
228
|
+
margin-inline: auto;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* `IdCaptureLayout.css` gives every vertical stage inside a viewfinder a
|
|
232
|
+
* `flex-basis` of "the full column over the aspect", so the frame can give up
|
|
233
|
+
* height on a short screen and the aspect then narrows it back (Camila,
|
|
234
|
+
* 2026-09-28). That machinery is right and stays — it just has to start from
|
|
235
|
+
* THIS module's capped width, not the whole column: fed 342 it computed a
|
|
236
|
+
* 541-tall frame and then clamped the width back to 286, which is how the
|
|
237
|
+
* stage ended up 286 × 541 instead of 286 × 455 and grew into the verified
|
|
238
|
+
* tag. Same formula, capped input. */
|
|
239
|
+
.prizma-viewfinder__content
|
|
240
|
+
> .video-selfie-stage.prizma-id-capture--vertical:not(.prizma-id-capture--poa)
|
|
241
|
+
.prizma-id-capture__frame {
|
|
242
|
+
flex-basis: calc(min(286px, 100cqw - 2 * var(--prizma-spacing-24)) * 456 / 288);
|
|
243
|
+
}
|
|
244
|
+
|
|
158
245
|
/* ── The captured document inside the vertical frame ─────────────────────────
|
|
159
246
|
* Groups 3 and 5 draw the ID UPRIGHT in a portrait frame: the card is rotated a
|
|
160
|
-
* quarter turn so its long edge runs down the screen, exactly as `3.2`
|
|
161
|
-
* `5.2` show it (the licence's text reads bottom-to-top).
|
|
247
|
+
* quarter turn so its long edge runs down the screen, exactly as `3.1`/`3.2`
|
|
248
|
+
* and `5.1`/`5.2` show it (the licence's text reads bottom-to-top).
|
|
162
249
|
*
|
|
163
250
|
* The sample scans are landscape (328×202). Dropped into a portrait frame with
|
|
164
251
|
* Core's `object-fit: cover` they scaled 4.2× to fill the height and lost their
|
|
@@ -166,40 +253,41 @@
|
|
|
166
253
|
* zoomed in to fit height, when it should be 90 degrees to fit the frame").
|
|
167
254
|
* Rotating instead of cropping keeps the whole document legible.
|
|
168
255
|
*
|
|
169
|
-
*
|
|
170
|
-
* still lays out on its pre-transform box: the image is sized to the frame's
|
|
171
|
-
* height × width, then turned, so it lands exactly frame-sized. No literal —
|
|
172
|
-
* both are percentages of whatever box Core gives it. DEVIATIONS #VS-6.
|
|
173
|
-
*/
|
|
174
|
-
/* The captured document is landscape (a card scan); the VERTICAL frame is
|
|
175
|
-
* portrait. `object-fit: cover` cropped its sides away and blew it up 4.2× —
|
|
176
|
-
* the band of static the owner reported. Figma turns the card a quarter turn
|
|
177
|
-
* instead, so the whole document stays legible with its text running up the
|
|
178
|
-
* side, exactly as `3.2` / `5.2` draw it.
|
|
179
|
-
*
|
|
180
|
-
* The image is sized to the frame's height × width BEFORE the turn, because a
|
|
256
|
+
* The image is sized to the frame's HEIGHT × WIDTH before the turn, because a
|
|
181
257
|
* rotated element still lays out on its pre-transform box — so after the 90°
|
|
182
258
|
* it lands exactly frame-sized. Both values are percentages of whatever box
|
|
183
|
-
* Core gives it, so nothing is hardcoded.
|
|
259
|
+
* Core gives it, so nothing is hardcoded.
|
|
260
|
+
*
|
|
261
|
+
* ⚠ The first pass centred that oversized box with `display: grid;
|
|
262
|
+
* place-items: center`, which does NOT centre it: a grid item WIDER than its
|
|
263
|
+
* area is clamped to the start edge (the browsers' overflow-safety rule). The
|
|
264
|
+
* card therefore laid out at the frame's top-left, and turning it around ITS
|
|
265
|
+
* centre threw it 75px right and 75px up — hanging off two sides of the frame
|
|
266
|
+
* and across the camera. That is the "ID shown in a very weird way" report
|
|
267
|
+
* (owner, 2026-09-30). Absolute centring has no such clamp, so the turn
|
|
268
|
+
* happens around the frame's real middle. DEVIATIONS #VS-6. */
|
|
184
269
|
.prizma-id-capture--vertical .prizma-id-capture__still {
|
|
185
270
|
/* A size container, so the image can be measured against the frame's OWN
|
|
186
271
|
box — `cqh`/`cqw` below are the only way to swap width and height without
|
|
187
272
|
writing either number down. The frame has definite dimensions, which is
|
|
188
273
|
what `container-type: size` requires. */
|
|
189
274
|
container-type: size;
|
|
190
|
-
display: grid;
|
|
191
|
-
place-items: center;
|
|
192
275
|
overflow: hidden;
|
|
193
276
|
}
|
|
194
277
|
|
|
195
278
|
.prizma-id-capture--vertical .prizma-id-capture__still > img {
|
|
196
279
|
/* Swapped on purpose: the image is laid out at the frame's HEIGHT × WIDTH,
|
|
197
|
-
then turned
|
|
280
|
+
then turned, so it lands exactly frame-sized. */
|
|
281
|
+
position: absolute;
|
|
282
|
+
inset: auto;
|
|
283
|
+
top: 50%;
|
|
284
|
+
left: 50%;
|
|
198
285
|
width: 100cqh;
|
|
199
286
|
height: 100cqw;
|
|
200
287
|
max-width: none;
|
|
201
288
|
object-fit: cover;
|
|
202
|
-
|
|
289
|
+
/* Centre first, THEN turn — around the frame's own middle. */
|
|
290
|
+
transform: translate(-50%, -50%) rotate(90deg);
|
|
203
291
|
transform-origin: center;
|
|
204
292
|
}
|
|
205
293
|
|
|
@@ -222,26 +310,69 @@
|
|
|
222
310
|
pointer-events: none;
|
|
223
311
|
}
|
|
224
312
|
|
|
225
|
-
|
|
226
|
-
|
|
313
|
+
/* The card itself: hugs its content, never wider than the document frame it
|
|
314
|
+
* floats on (Figma's widest, `6.1.3`'s three-line rejection, is 260 inside the
|
|
315
|
+
* 286 frame), and always dark — the camera stage never mode-flips. The mark
|
|
316
|
+
* stacks above the label because the content is Core's `PrizmaTitle`; the card
|
|
317
|
+
* only supplies the surface and the air around it. */
|
|
318
|
+
.video-selfie-stage-status__card {
|
|
319
|
+
box-sizing: border-box;
|
|
320
|
+
max-width: min(260px, 100%);
|
|
321
|
+
padding: var(--prizma-spacing-24);
|
|
227
322
|
border-radius: var(--prizma-border-radius-medium);
|
|
228
|
-
background: var(--prizma-surface-neutral-900-
|
|
323
|
+
background: var(--prizma-surface-neutral-900-static);
|
|
229
324
|
}
|
|
230
325
|
|
|
231
|
-
/*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
326
|
+
/* Both pieces inside the card sit ON that dark surface, so their ink is the
|
|
327
|
+
* camera HUD's own Static white rather than the theme's primary — the same
|
|
328
|
+
* re-seat `SelfieCapture.css` does for its overlay status band. */
|
|
329
|
+
.video-selfie-stage-status__card .prizma-title__heading,
|
|
330
|
+
.video-selfie-stage-status__card .prizma-loading-spinner__title {
|
|
331
|
+
color: var(--prizma-text-body-0-static);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* ── The coaching bubble above the document frame ────────────────────────────
|
|
335
|
+
* The five condition frames put `Capture feedback` at the TOP of the content,
|
|
336
|
+
* over the camera and above the frame. The band it sits in is already the
|
|
337
|
+
* flexible one; this only pins it to the band's top edge and lifts it over
|
|
338
|
+
* the stage's scrim (the trap `ViewfinderScreen.css` documents on `__intro`:
|
|
339
|
+
* the scrim is an absolutely positioned sibling with an escaping box-shadow,
|
|
340
|
+
* so unlifted content renders under an 80% veil). The bubble's own width,
|
|
341
|
+
* radius, ink and illustration are Core's. DEVIATIONS #VS-28. */
|
|
342
|
+
.video-selfie-feedback {
|
|
343
|
+
position: relative;
|
|
344
|
+
z-index: 1;
|
|
345
|
+
width: 100%;
|
|
346
|
+
/* Top of the band, which is directly under the stepper — where the five
|
|
347
|
+
condition frames seat it (bubble top y≈88 of 844). */
|
|
348
|
+
margin-block: 0 auto;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* ── The head-turn mark and its instruction ──────────────────────────────────
|
|
352
|
+
* The `🙂↔️ Turn Head` band puts a 48 circled arrow between the ring and the
|
|
353
|
+
* line that says which way to turn (`element container` 342×48, the glyph
|
|
354
|
+
* centred, then 16 to the text). The gap is Core's own Spacing/16; the copy is
|
|
355
|
+
* the foundation's `.prizma-viewfinder__title`, unchanged. Lifted over the
|
|
356
|
+
* stage's scrim for the reason `ViewfinderScreen.css` documents on `__intro`.
|
|
357
|
+
* DEVIATIONS #VS-20. */
|
|
358
|
+
.video-selfie-turn {
|
|
359
|
+
position: relative;
|
|
360
|
+
z-index: 1;
|
|
237
361
|
display: flex;
|
|
238
362
|
flex-direction: column;
|
|
239
|
-
align-items:
|
|
363
|
+
align-items: center;
|
|
364
|
+
gap: var(--prizma-spacing-16);
|
|
240
365
|
width: 100%;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* The mark is ONE bank glyph at 2xl — a filled disc with the arrow knocked
|
|
369
|
+
* out — so this only inks it. The first pass wrapped a 24 bare arrow in a
|
|
370
|
+
* hand-made disc and read as a thin chevron on the camera. */
|
|
371
|
+
.video-selfie-turn__mark {
|
|
372
|
+
display: flex;
|
|
373
|
+
align-items: center;
|
|
374
|
+
justify-content: center;
|
|
375
|
+
color: var(--prizma-icon-neutral-50-static);
|
|
245
376
|
}
|
|
246
377
|
|
|
247
378
|
/* Step rows — the <ol> `PrizmaList` would supply, composed here only because
|
|
@@ -1,41 +1,61 @@
|
|
|
1
1
|
import React, { forwardRef, type ReactNode } from 'react';
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
PrizmaSelfieCapture,
|
|
4
|
+
type PrizmaSelfieCaptureState,
|
|
5
|
+
} from '@incodetech/prizma-core-web/selfie-capture';
|
|
6
|
+
import { PrizmaStepper } from '@incodetech/prizma-core-web/stepper';
|
|
7
|
+
import { PrizmaScreenShell, type PrizmaScreenShellProps } from './ScreenShell';
|
|
4
8
|
import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
|
|
5
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
VideoSelfieDocumentType,
|
|
11
|
+
VideoSelfieFeedbackKind,
|
|
12
|
+
VideoSelfieStrings,
|
|
13
|
+
} from './VideoSelfieStrings';
|
|
6
14
|
import './ScreenLayout.css';
|
|
15
|
+
import './ViewfinderScreen.css';
|
|
16
|
+
import './VideoSelfieLayout.css';
|
|
7
17
|
|
|
8
18
|
/** Which purple liveness section to render (group 8). */
|
|
9
19
|
export type VideoSelfieSelfieState = 'start' | 'processing' | 'success' | 'failed';
|
|
10
20
|
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
21
|
+
/**
|
|
22
|
+
* Purple state → Core `PrizmaSelfieCapture` state, in the OVERLAY recipe.
|
|
23
|
+
*
|
|
24
|
+
* `start` is `filled`, not `empty`: `empty` is Core's *searching* beat and
|
|
25
|
+
* draws the dashed silhouette guide, which `8.0` and every `9.x` message
|
|
26
|
+
* deliberately hide (`silhouette hidden=true` on the frames — the guide
|
|
27
|
+
* belongs to the head-turn steps, which do draw it). `filled` is Core's
|
|
28
|
+
* "window open, neutral ring, no guide" beat, and in overlay mode there is no
|
|
29
|
+
* still to fill it with: the host's feed shows straight through the ring.
|
|
30
|
+
*/
|
|
15
31
|
const CAPTURE_STATE: Record<VideoSelfieSelfieState, PrizmaSelfieCaptureState> = {
|
|
16
|
-
start: '
|
|
32
|
+
start: 'filled',
|
|
17
33
|
processing: 'uploading',
|
|
18
34
|
success: 'success',
|
|
19
35
|
failed: 'error',
|
|
20
36
|
};
|
|
21
37
|
|
|
38
|
+
/** Which beat of the seven-step journey the liveness ring occupies — the
|
|
39
|
+
* microphone grant is 1, the two document sides 2 and 3. */
|
|
40
|
+
const STEP = 4;
|
|
41
|
+
|
|
22
42
|
export interface VideoSelfieSelfieCaptureScreenProps
|
|
23
|
-
extends Omit<
|
|
24
|
-
PrizmaCaptureScreenProps,
|
|
25
|
-
'state' | 'feedbackText' | 'statusLabels' | 'verifiedLabel' | 'navLabel'
|
|
26
|
-
> {
|
|
43
|
+
extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
|
|
27
44
|
/** Module copy — this screen reads the `selfieCapture` slice. */
|
|
28
45
|
strings: VideoSelfieStrings;
|
|
29
46
|
/** Which purple section of group 8 to render. Default `start`. */
|
|
30
47
|
state?: VideoSelfieSelfieState;
|
|
31
48
|
/**
|
|
32
|
-
* Which of group 9's
|
|
33
|
-
*
|
|
49
|
+
* Which of group 9's live-feedback lines to show under the ring. Only
|
|
50
|
+
* meaningful while the ring is live (`state="start"`): the sections of
|
|
34
51
|
* group 9 are that same frame with a different instruction. Omit for `8.0`,
|
|
35
52
|
* which shows the plain "Look at your front camera" line.
|
|
36
53
|
*/
|
|
37
54
|
feedback?: VideoSelfieFeedbackKind;
|
|
38
|
-
/**
|
|
55
|
+
/** Which document the journey collects — the Stepper's own `documentType`. */
|
|
56
|
+
documentType?: VideoSelfieDocumentType;
|
|
57
|
+
/** Live camera feed (<video>) or sample still (<img>), full-bleed behind the
|
|
58
|
+
* ring — NOT inside it. The overlay recipe makes the ring a window. */
|
|
39
59
|
camera?: ReactNode;
|
|
40
60
|
screenId?: string;
|
|
41
61
|
overrides?: PrizmaScreenOverrides;
|
|
@@ -43,29 +63,41 @@ export interface VideoSelfieSelfieCaptureScreenProps
|
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
/**
|
|
46
|
-
* Video Selfie — the liveness ring (Archetype
|
|
47
|
-
* ONE screen for
|
|
48
|
-
* `selfie capture feedback 9.
|
|
49
|
-
* variants of Core's `PrizmaSelfieCapture`, which
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
66
|
+
* Video Selfie — the liveness ring (Archetype D, GUIDELINES.md §6-D; always
|
|
67
|
+
* dark). ONE screen for every purple section of groups 8 and 9: `selfie
|
|
68
|
+
* capture 8.0–8.3` and every `selfie capture feedback 9.x` message. They are
|
|
69
|
+
* `state` × `feedbackText` variants of Core's `PrizmaSelfieCapture`, which is
|
|
70
|
+
* exactly how the component is axed.
|
|
71
|
+
*
|
|
72
|
+
* **Rebuilt 2026-09-30 to follow the frames.** The first pass shipped Core's
|
|
73
|
+
* INLINE recipe — the feed inside a circular window on a WHITE surface, the
|
|
74
|
+
* verdicts dissolving the ring into a centred mark — and logged the gap. On
|
|
75
|
+
* the module page that read as a different product from the rest of the flow:
|
|
76
|
+
* a small ring on white between two full-bleed dark camera screens, with the
|
|
77
|
+
* ring vanishing on the very beats Figma keeps it (owner, 2026-09-30: "they
|
|
78
|
+
* are looking very broken… a broken experience"). Rather than hand-roll the
|
|
79
|
+
* full-bleed composition here, Core's `PrizmaSelfieCapture` grew the
|
|
80
|
+
* `mode="overlay"` + `scrim` axis `PrizmaIdCapture` already carries — it is
|
|
81
|
+
* the Figma library's own `Capture / Selfie overlay` component — and this
|
|
82
|
+
* screen composes it. Nothing about the ring, the scrim, the glow sweeps, the
|
|
83
|
+
* verdict marks or the instruction typography is re-implemented (§7).
|
|
84
|
+
* DEVIATIONS #VS-22.
|
|
85
|
+
*
|
|
86
|
+
* The composition, measured off `8.0` (390×844):
|
|
53
87
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* Face Capture pilot settled it.
|
|
88
|
+
* - the camera runs FULL-BLEED on `.prizma-viewfinder__surface` (`selfie bg
|
|
89
|
+
* image`, 390×844 — it passes under the browser chrome too);
|
|
90
|
+
* - the journey stepper sits in the band the logo nav normally occupies
|
|
91
|
+
* (`top content` y=56, the Stepper 342 wide at x=24), the module-wide
|
|
92
|
+
* treatment `VideoSelfieLayout.css` owns;
|
|
93
|
+
* - the ring is 335 centred in the remaining air (Figma 27,188 → 362,523
|
|
94
|
+
* against a 708-high content area), with the instruction stacked under it.
|
|
95
|
+
* Core's own Spacing/32 gap replaces Figma's 25.5 (the token wins, §4).
|
|
63
96
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* instruction copy.
|
|
97
|
+
* NO verified tag: `8.x` and `9.x` draw nothing between the instruction and
|
|
98
|
+
* the bottom of the screen (GUIDELINES §1 — follow the module's section, not
|
|
99
|
+
* the default). The document stage keeps its tag because its own sections
|
|
100
|
+
* draw one (#VS-3).
|
|
69
101
|
*/
|
|
70
102
|
export const VideoSelfieSelfieCaptureScreen = forwardRef<
|
|
71
103
|
HTMLDivElement,
|
|
@@ -76,10 +108,12 @@ export const VideoSelfieSelfieCaptureScreen = forwardRef<
|
|
|
76
108
|
strings,
|
|
77
109
|
state = 'start',
|
|
78
110
|
feedback,
|
|
111
|
+
documentType = 'id',
|
|
79
112
|
camera,
|
|
80
113
|
screenId = `video-selfie-selfie-capture-${feedback ?? state}`,
|
|
81
114
|
overrides,
|
|
82
115
|
onAction,
|
|
116
|
+
className,
|
|
83
117
|
...shell
|
|
84
118
|
},
|
|
85
119
|
ref,
|
|
@@ -89,31 +123,42 @@ export const VideoSelfieSelfieCaptureScreen = forwardRef<
|
|
|
89
123
|
carry Core's own status copy instead (its `statusLabels` slot), which is
|
|
90
124
|
why the feedback line is scoped to the live state. */
|
|
91
125
|
const feedbackText =
|
|
92
|
-
|
|
93
|
-
? feedback
|
|
94
|
-
? slice.feedback[feedback]
|
|
95
|
-
: slice.startLabel
|
|
96
|
-
: undefined;
|
|
126
|
+
state === 'start' ? (feedback ? slice.feedback[feedback] : slice.startLabel) : undefined;
|
|
97
127
|
|
|
98
128
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
<PrizmaScreenShell ref={ref} dark className={className} {...shell}>
|
|
130
|
+
<div className="prizma-viewfinder__surface">{camera}</div>
|
|
131
|
+
<div className="video-selfie-stepper">
|
|
132
|
+
<PrizmaStepper
|
|
133
|
+
steps={7}
|
|
134
|
+
currentState={STEP}
|
|
135
|
+
documentType={documentType}
|
|
136
|
+
completedValueText={strings.stepperCompletedLabel}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
<div
|
|
140
|
+
className={[
|
|
141
|
+
'prizma-viewfinder__content',
|
|
142
|
+
overrides?.align === 'left' ? 'prizma-viewfinder__content--left' : '',
|
|
143
|
+
]
|
|
144
|
+
.filter(Boolean)
|
|
145
|
+
.join(' ')}
|
|
146
|
+
>
|
|
147
|
+
<div className="video-selfie-band video-selfie-band--ring">
|
|
148
|
+
<PrizmaSelfieCapture
|
|
149
|
+
mode="overlay"
|
|
150
|
+
scrim
|
|
151
|
+
state={CAPTURE_STATE[state]}
|
|
152
|
+
feedbackText={feedbackText}
|
|
153
|
+
statusLabels={{
|
|
154
|
+
uploading: slice.processingLabel,
|
|
155
|
+
success: slice.successLabel,
|
|
156
|
+
error: slice.failedLabel,
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</PrizmaScreenShell>
|
|
117
162
|
);
|
|
118
163
|
},
|
|
119
164
|
);
|
|
@@ -120,23 +120,31 @@ export const VideoSelfieSpeakScreen = forwardRef<
|
|
|
120
120
|
onSecondaryClick: () => onAction?.({ type: 'secondary', screenId }),
|
|
121
121
|
}}
|
|
122
122
|
/>
|
|
123
|
-
) : stage === 'processing' ? (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
123
|
+
) : stage === 'processing' || stage === 'success' ? (
|
|
124
|
+
/* The two verdict cards. They carry no buttons, so left alone they came
|
|
125
|
+
out shorter than the prompt they replace and the sheet jumped between
|
|
126
|
+
steps; and Core's Modal renders `body` AFTER the heading, which put
|
|
127
|
+
the spinner and the check UNDER their own label. Both are fixed in
|
|
128
|
+
the module: `--status` gives the card the prompt's measure and centres
|
|
129
|
+
its content, and the mark is passed as the LEADING slot so it stacks
|
|
130
|
+
above the copy the way `10.2` / `10.3` draw it.
|
|
131
|
+
|
|
132
|
+
No ✕ either. Figma draws one, but it is the same call #VS-5 already
|
|
133
|
+
made on the prompt: closing mid-step strands the user with no way
|
|
134
|
+
back, and a dismiss that appears on two of five cards and not the
|
|
135
|
+
other three is the inconsistency, not the fix (owner, 2026-09-30).
|
|
136
|
+
DEVIATIONS #VS-25. */
|
|
132
137
|
<PrizmaModalContent
|
|
133
|
-
title={slice.successLabel}
|
|
138
|
+
title={stage === 'processing' ? slice.processingLabel : slice.successLabel}
|
|
139
|
+
showCloseIcon={false}
|
|
134
140
|
showButtonGroup={false}
|
|
135
|
-
|
|
136
|
-
onClose={() => onAction?.({ type: 'tertiary', screenId })}
|
|
137
|
-
body={
|
|
141
|
+
leading={
|
|
138
142
|
<span className="video-selfie-sheet-status">
|
|
139
|
-
|
|
143
|
+
{stage === 'processing' ? (
|
|
144
|
+
<PrizmaLoadingSpinner loadingLabel={slice.processingLabel} />
|
|
145
|
+
) : (
|
|
146
|
+
<IconStatusPositive size="xl" />
|
|
147
|
+
)}
|
|
140
148
|
</span>
|
|
141
149
|
}
|
|
142
150
|
/>
|
|
@@ -214,6 +222,9 @@ export const VideoSelfieSpeakScreen = forwardRef<
|
|
|
214
222
|
className={[
|
|
215
223
|
'video-selfie-sheet-panel',
|
|
216
224
|
isConfirm ? 'video-selfie-sheet-panel--centered' : '',
|
|
225
|
+
stage === 'processing' || stage === 'success'
|
|
226
|
+
? 'video-selfie-sheet-panel--status'
|
|
227
|
+
: '',
|
|
217
228
|
]
|
|
218
229
|
.filter(Boolean)
|
|
219
230
|
.join(' ')}
|