@incodetech/prizma-core-screens 0.2.7 → 0.2.12
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/BiometricConsentScreen.d.ts +95 -0
- package/dist/BiometricConsentScreen.js +91 -0
- package/dist/BiometricConsentStrings.d.ts +84 -0
- package/dist/BiometricConsentStrings.js +31 -0
- package/dist/CaptureScreen.d.ts +13 -0
- package/dist/CaptureScreen.js +2 -2
- package/dist/DemoScreensLayout.css +16 -0
- package/dist/DemoScreensRedirectScreen.d.ts +6 -1
- package/dist/DemoScreensRedirectScreen.js +3 -2
- package/dist/DemoScreensStrings.d.ts +5 -0
- package/dist/DigitalIdUploadErrorScreen.d.ts +31 -0
- package/dist/DigitalIdUploadErrorScreen.js +26 -0
- package/dist/DigitalIdUploadFileTooLargeScreen.d.ts +27 -0
- package/dist/DigitalIdUploadFileTooLargeScreen.js +21 -0
- package/dist/DigitalIdUploadReviewScreen.d.ts +57 -0
- package/dist/DigitalIdUploadReviewScreen.js +44 -0
- package/dist/DigitalIdUploadStrings.d.ts +64 -0
- package/dist/DigitalIdUploadStrings.js +19 -0
- package/dist/DigitalIdUploadTutorialScreen.d.ts +28 -0
- package/dist/DigitalIdUploadTutorialScreen.js +23 -0
- package/dist/DocumentCaptureIllustration.d.ts +1 -1
- package/dist/EkybCountryScreen.d.ts +6 -6
- package/dist/EkybCountryScreen.js +20 -7
- package/dist/EkybStrings.d.ts +6 -1
- package/dist/ErrorScreensScreen.d.ts +5 -2
- package/dist/ErrorScreensScreen.js +15 -4
- package/dist/ErrorScreensStrings.d.ts +17 -2
- package/dist/ErrorScreensStrings.js +18 -10
- package/dist/FaceCaptureCapturingScreen.d.ts +63 -0
- package/dist/FaceCaptureCapturingScreen.js +62 -2
- package/dist/FaceCaptureLayout.css +69 -4
- package/dist/FaceCaptureStrings.d.ts +23 -0
- package/dist/FormScreen.css +14 -0
- package/dist/FormScreen.d.ts +28 -0
- package/dist/FormScreen.js +3 -2
- package/dist/IdCaptureLayout.css +165 -2
- package/dist/IdCaptureStageScreen.d.ts +47 -0
- package/dist/IdCaptureStageScreen.js +39 -3
- package/dist/IdCaptureStrings.d.ts +19 -0
- package/dist/ListScreen.d.ts +19 -0
- package/dist/ListScreen.js +7 -2
- package/dist/LoaderScreen.d.ts +7 -5
- package/dist/LoaderScreen.js +7 -5
- package/dist/LoadersStrings.d.ts +18 -5
- package/dist/OcrReviewErrorScreen.d.ts +30 -0
- package/dist/OcrReviewErrorScreen.js +24 -0
- package/dist/OcrReviewLayout.css +24 -0
- package/dist/OcrReviewLoadingScreen.d.ts +25 -0
- package/dist/OcrReviewLoadingScreen.js +19 -0
- package/dist/OcrReviewScreen.d.ts +76 -0
- package/dist/OcrReviewScreen.js +65 -0
- package/dist/OcrReviewStrings.d.ts +47 -0
- package/dist/OcrReviewStrings.js +17 -0
- package/dist/OcrReviewSuccessScreen.d.ts +30 -0
- package/dist/OcrReviewSuccessScreen.js +24 -0
- package/dist/PermissionsAllowScreen.d.ts +8 -11
- package/dist/PermissionsAllowScreen.js +8 -11
- package/dist/PermissionsAskScreen.d.ts +41 -0
- package/dist/PermissionsAskScreen.js +43 -0
- package/dist/PermissionsBrowserStepsScreen.d.ts +9 -2
- package/dist/PermissionsBrowserStepsScreen.js +12 -3
- package/dist/PermissionsLayout.css +47 -0
- package/dist/PermissionsStrings.d.ts +20 -9
- package/dist/PermissionsStrings.js +10 -4
- package/dist/PhoneInputOtpScreen.d.ts +8 -0
- package/dist/PhoneInputOtpScreen.js +26 -19
- package/dist/PhoneInputStrings.d.ts +11 -3
- package/dist/PhoneInputVerifyScreen.js +21 -5
- package/dist/QualifiedSignatureDocumentScreen.d.ts +8 -5
- package/dist/QualifiedSignatureDocumentScreen.js +9 -6
- package/dist/QualifiedSignatureResultScreen.d.ts +16 -13
- package/dist/QualifiedSignatureResultScreen.js +17 -18
- package/dist/QualifiedSignatureStrings.d.ts +11 -4
- package/dist/ResultScreen.d.ts +8 -0
- package/dist/ResultScreen.js +14 -3
- package/dist/ReusabilityCredentialSelectionScreen.d.ts +95 -0
- package/dist/ReusabilityCredentialSelectionScreen.js +94 -0
- package/dist/ReusabilityLayout.css +241 -0
- package/dist/ReusabilityPrivacyPolicyScreen.d.ts +55 -0
- package/dist/ReusabilityPrivacyPolicyScreen.js +44 -0
- package/dist/ReusabilityStrings.d.ts +85 -0
- package/dist/ReusabilityStrings.js +37 -0
- package/dist/ReviewScreen.d.ts +38 -12
- package/dist/ReviewScreen.js +41 -8
- package/dist/ScreenLayout.css +382 -6
- package/dist/VideoSelfieErrorScreen.js +1 -1
- package/dist/VideoSelfieIdCaptureScreen.js +1 -1
- package/dist/VideoSelfieLayout.css +304 -2
- package/dist/VideoSelfieSelfieCaptureScreen.js +1 -1
- package/dist/VideoSelfieSpeakScreen.js +1 -1
- package/dist/VideoSelfieStrings.d.ts +4 -1
- package/dist/VideoSelfieTurnHeadScreen.js +14 -11
- package/dist/VideoSelfieTutorialScreen.d.ts +9 -0
- package/dist/VideoSelfieTutorialScreen.js +10 -1
- package/dist/VideoSelfieWaveScreen.js +1 -1
- package/dist/ViewfinderScreen.css +36 -0
- package/dist/flows.d.ts +8 -0
- package/dist/flows.js +17 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +12 -0
- package/dist/styles.css +3 -0
- package/package.json +68 -3
- package/samples/video-selfie-camera-wide.jpg +0 -0
- package/samples/video-selfie-room-wide.jpg +0 -0
- package/src/BiometricConsentScreen.tsx +192 -0
- package/src/BiometricConsentStrings.ts +119 -0
- package/src/CaptureScreen.tsx +17 -0
- package/src/DemoScreensLayout.css +16 -0
- package/src/DemoScreensRedirectScreen.tsx +14 -6
- package/src/DemoScreensStrings.ts +5 -0
- package/src/DigitalIdUploadErrorScreen.tsx +83 -0
- package/src/DigitalIdUploadFileTooLargeScreen.tsx +67 -0
- package/src/DigitalIdUploadReviewScreen.tsx +119 -0
- package/src/DigitalIdUploadStrings.ts +88 -0
- package/src/DigitalIdUploadTutorialScreen.tsx +59 -0
- package/src/DocumentCaptureIllustration.tsx +1 -1
- package/src/EkybCountryScreen.tsx +23 -6
- package/src/EkybStrings.ts +6 -1
- package/src/ErrorScreensScreen.tsx +15 -4
- package/src/ErrorScreensStrings.ts +37 -12
- package/src/FaceCaptureCapturingScreen.tsx +102 -13
- package/src/FaceCaptureLayout.css +69 -4
- package/src/FaceCaptureStrings.ts +24 -0
- package/src/FormScreen.css +14 -0
- package/src/FormScreen.tsx +34 -0
- package/src/IdCaptureLayout.css +165 -2
- package/src/IdCaptureStageScreen.tsx +75 -0
- package/src/IdCaptureStrings.ts +19 -0
- package/src/ListScreen.tsx +48 -9
- package/src/LoaderScreen.tsx +7 -5
- package/src/LoadersStrings.ts +18 -5
- package/src/OcrReviewErrorScreen.tsx +53 -0
- package/src/OcrReviewLayout.css +24 -0
- package/src/OcrReviewLoadingScreen.tsx +48 -0
- package/src/OcrReviewScreen.tsx +190 -0
- package/src/OcrReviewStrings.ts +69 -0
- package/src/OcrReviewSuccessScreen.tsx +52 -0
- package/src/PermissionsAllowScreen.tsx +9 -10
- package/src/PermissionsAskScreen.tsx +105 -0
- package/src/PermissionsBrowserStepsScreen.tsx +11 -3
- package/src/PermissionsLayout.css +47 -0
- package/src/PermissionsStrings.ts +42 -12
- package/src/PhoneInputOtpScreen.tsx +23 -3
- package/src/PhoneInputStrings.ts +11 -3
- package/src/PhoneInputVerifyScreen.tsx +24 -4
- package/src/QualifiedSignatureDocumentScreen.tsx +9 -7
- package/src/QualifiedSignatureResultScreen.tsx +28 -26
- package/src/QualifiedSignatureStrings.ts +11 -4
- package/src/ResultScreen.tsx +33 -2
- package/src/ReusabilityCredentialSelectionScreen.tsx +246 -0
- package/src/ReusabilityLayout.css +241 -0
- package/src/ReusabilityPrivacyPolicyScreen.tsx +109 -0
- package/src/ReusabilityStrings.ts +127 -0
- package/src/ReviewScreen.tsx +96 -27
- package/src/ScreenLayout.css +382 -6
- package/src/VideoSelfieErrorScreen.tsx +1 -1
- package/src/VideoSelfieIdCaptureScreen.tsx +9 -1
- package/src/VideoSelfieLayout.css +304 -2
- package/src/VideoSelfieSelfieCaptureScreen.tsx +9 -1
- package/src/VideoSelfieSpeakScreen.tsx +9 -1
- package/src/VideoSelfieStrings.ts +4 -2
- package/src/VideoSelfieTurnHeadScreen.tsx +25 -14
- package/src/VideoSelfieTutorialScreen.tsx +11 -0
- package/src/VideoSelfieWaveScreen.tsx +9 -1
- package/src/ViewfinderScreen.css +36 -0
- package/src/flows.ts +25 -0
- package/src/index.ts +74 -3
- package/src/styles.css +3 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { PrizmaCaptureDigitalId } from '@incodetech/prizma-core-web/capture-digital-id';
|
|
5
|
+
import { PrizmaReviewScreen } from './ReviewScreen';
|
|
6
|
+
import './ScreenLayout.css';
|
|
7
|
+
/**
|
|
8
|
+
* Digital ID Upload — review / analyzing / success, built on the
|
|
9
|
+
* `PrizmaReviewScreen` archetype (GUIDELINES.md §6-H). Figma: `Upload Digital
|
|
10
|
+
* ID` file (wxFsHRvllern4VnjRqM4JO), "Web Happy Path - Light Mode" section —
|
|
11
|
+
* `Review Document` / `Analyzing` / `Success` (nodes 25351:14725/6/7).
|
|
12
|
+
*
|
|
13
|
+
* The media stage is Core's `PrizmaCaptureDigitalId` across all three stages
|
|
14
|
+
* — its own state enum (`review` / `analyzing` / `success`) maps directly
|
|
15
|
+
* onto the archetype's stage axis, so it already owns the border treatment
|
|
16
|
+
* (neutral on review, green 4px on analyzing/success), the scan-flash sweep
|
|
17
|
+
* and the filename row. Nothing is hand-built here, unlike
|
|
18
|
+
* `DocumentCaptureConfirmUploadScreen`'s bespoke file-card — Core ships a
|
|
19
|
+
* purpose-built component for this module.
|
|
20
|
+
*
|
|
21
|
+
* Header/footer visibility is genuinely per-stage in this Figma (logo nav +
|
|
22
|
+
* verified tag show on `review`, both are absent on `analyzing` and
|
|
23
|
+
* `success`) — not a layout choice this file invents, just the existing
|
|
24
|
+
* `showHeader`/`showFooter` chrome knobs (owner rule 2026-08-18: header and
|
|
25
|
+
* footer are independently controlled) applied per stage.
|
|
26
|
+
*
|
|
27
|
+
* Always opts into the archetype's `centeredWhenNoNav` — it only takes effect
|
|
28
|
+
* while the nav is hidden (`analyzing`/`success` here), where this Figma
|
|
29
|
+
* draws the header + media as one group centred with equal space above and
|
|
30
|
+
* below it, not a pinned header (see ReviewScreen.tsx for the mechanics).
|
|
31
|
+
* `review` always shows its own nav, so the flag is a no-op there, and the
|
|
32
|
+
* default `false` leaves Document Capture's existing analyzing/success
|
|
33
|
+
* screens byte-for-byte unchanged (neither ever hides the nav here).
|
|
34
|
+
*
|
|
35
|
+
* GAP (report, not workaround — README rule 2): Figma's `Success` section
|
|
36
|
+
* also shows a subtitle ("Let's continue") under the success title, but the
|
|
37
|
+
* shared `PrizmaReviewScreen` archetype only renders a subtitle on
|
|
38
|
+
* `stage="review"` — every other current consumer's success state has none.
|
|
39
|
+
* Left unset here pending an owner call on whether to extend the shared
|
|
40
|
+
* archetype with a `successSubtitle` slot.
|
|
41
|
+
*/
|
|
42
|
+
export const DigitalIdUploadReviewScreen = forwardRef(({ strings, stage = 'review', filename, progress = 45, showHeader = stage === 'review', showFooter = stage === 'review', screenId = `digital-id-upload-${stage}`, overrides, onAction, ...shell }, ref) => (_jsx(PrizmaReviewScreen, { ref: ref, stage: stage, title: strings.review.title, subtitle: strings.review.subtitle, successTitle: strings.review.successTitle, analyzingLabel: strings.review.analyzingLabel, centeredWhenNoNav: true, progress: progress, primaryLabel: strings.review.ctaLabel, secondaryLabel: strings.review.replaceLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, showHeader: showHeader, showFooter: showFooter, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx(PrizmaCaptureDigitalId, { state: stage, filename: filename }) })));
|
|
43
|
+
DigitalIdUploadReviewScreen.displayName = 'DigitalIdUploadReviewScreen';
|
|
44
|
+
export default DigitalIdUploadReviewScreen;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** The three stages the confirm flow's screen shares — matches
|
|
2
|
+
* `PrizmaReviewScreen`'s own `PrizmaReviewStage` (ReviewScreen.tsx). Kept as
|
|
3
|
+
* a separate module-scoped type rather than importing it, same convention as
|
|
4
|
+
* `DocumentCaptureConfirmStage` (DocumentCaptureStrings.ts). */
|
|
5
|
+
export type DigitalIdUploadConfirmStage = 'review' | 'analyzing' | 'success';
|
|
6
|
+
export interface DigitalIdUploadStrings {
|
|
7
|
+
/** Accessible name for the logo nav region (i18n — no baked-in English). */
|
|
8
|
+
navLabel?: string;
|
|
9
|
+
/** "verified by" attribution copy for the footer tag. */
|
|
10
|
+
verifiedLabel: string;
|
|
11
|
+
/** `Tutorial Screen` (node 25351:14722). */
|
|
12
|
+
tutorial: {
|
|
13
|
+
title: string;
|
|
14
|
+
subtitle?: string;
|
|
15
|
+
/** "Upload PDF" */
|
|
16
|
+
ctaLabel: string;
|
|
17
|
+
};
|
|
18
|
+
/** `Review Document` / `Analyzing` / `Success` (nodes
|
|
19
|
+
* 25351:14725/14726/14727) — one screen, three stages. */
|
|
20
|
+
review: {
|
|
21
|
+
/** `review` title ("Review document"). */
|
|
22
|
+
title: string;
|
|
23
|
+
/** `review` description. Figma draws no subtitle on `analyzing` or
|
|
24
|
+
* `success` — the shared `PrizmaReviewScreen` archetype already only
|
|
25
|
+
* shows one on `review`, so this needs no per-stage handling here. */
|
|
26
|
+
subtitle?: string;
|
|
27
|
+
/** Loading-bar caption on `analyzing` ("Analyzing.."). */
|
|
28
|
+
analyzingLabel: string;
|
|
29
|
+
/** H2 on `success` ("Successfully processed!"). */
|
|
30
|
+
successTitle: string;
|
|
31
|
+
/** Secondary CTA on `review` ("Replace file"). */
|
|
32
|
+
replaceLabel: string;
|
|
33
|
+
/** Primary CTA on `review` / `success` ("Continue"). */
|
|
34
|
+
ctaLabel: string;
|
|
35
|
+
};
|
|
36
|
+
/** `Errors` section, `Error` frame (node 25874:11957) — the uploaded file
|
|
37
|
+
* itself was rejected (wrong document type). Built on the SAME
|
|
38
|
+
* `PrizmaReviewScreen` archetype as `review`, stage `"error"`: the card
|
|
39
|
+
* reuses `PrizmaCaptureDigitalId state="error"` (red 4px border), not a
|
|
40
|
+
* generic archetype-F verdict screen (DigitalIdUploadErrorScreen.tsx). */
|
|
41
|
+
error: {
|
|
42
|
+
/** "ID type is not accepted" */
|
|
43
|
+
title: string;
|
|
44
|
+
/** "Please try scanning a different document" */
|
|
45
|
+
subtitle?: string;
|
|
46
|
+
/** "Scan your ID" */
|
|
47
|
+
ctaLabel: string;
|
|
48
|
+
};
|
|
49
|
+
/** `Errors` section, `File Limit Error` frame (node 26083:3981) — the file
|
|
50
|
+
* was never uploaded at all (client-side size check), so there is no
|
|
51
|
+
* rejected document to show. Built on Archetype F (`PrizmaResultScreen`,
|
|
52
|
+
* ResultScreen.tsx) rather than the review archetype `error` uses above —
|
|
53
|
+
* Figma centres the status + title + subtitle in the full available
|
|
54
|
+
* height with no media stage at all, exactly F's shape, not H's
|
|
55
|
+
* header-above-a-card one (DigitalIdUploadFileTooLargeScreen.tsx). */
|
|
56
|
+
fileTooLarge: {
|
|
57
|
+
/** "File is too large" */
|
|
58
|
+
title: string;
|
|
59
|
+
/** "The selected file is larger than 5 MB, please upload a smaller one" */
|
|
60
|
+
subtitle?: string;
|
|
61
|
+
/** "Choose another file" */
|
|
62
|
+
ctaLabel: string;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Digital ID Upload module — copy contract.
|
|
2
|
+
//
|
|
3
|
+
// Every Digital ID Upload screen receives the SAME `strings:
|
|
4
|
+
// DigitalIdUploadStrings` object (rule: all copy via props, no hardcoded
|
|
5
|
+
// English — GUIDELINES.md §9).
|
|
6
|
+
//
|
|
7
|
+
// Authoritative screens: the "Web Happy Path - Light Mode" purple section of
|
|
8
|
+
// the Upload Digital ID Figma file (wxFsHRvllern4VnjRqM4JO, node 25351:14721)
|
|
9
|
+
// — covers `tutorial` and the `review` / `analyzing` / `success` stages of
|
|
10
|
+
// the confirm flow — plus TWO separate `Errors` sections: node 25874:11955
|
|
11
|
+
// (`ID type is not accepted`) and node 26083:3981 (`File Limit Error`, "File
|
|
12
|
+
// is too large"). Three frames of the happy-path section are NOT part of
|
|
13
|
+
// this contract: `1-1-1-how to verify` (a verification-method chooser shared
|
|
14
|
+
// with ID Capture — see IdCaptureDocumentSelectionScreen.tsx, which is one
|
|
15
|
+
// row short of this Figma's version), a native iOS file-browser frame (OS
|
|
16
|
+
// chrome, DEVIATIONS.md #DC-20 precedent), and `1-18-1-id-hold-on` (ID
|
|
17
|
+
// Capture's own server-side-wait screen — dropped from this module by
|
|
18
|
+
// product decision, not a gap to fill).
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type PrizmaTutorialScreenProps } from './TutorialScreen';
|
|
3
|
+
import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
|
|
4
|
+
import type { DigitalIdUploadStrings } from './DigitalIdUploadStrings';
|
|
5
|
+
import './ScreenLayout.css';
|
|
6
|
+
export interface DigitalIdUploadTutorialScreenProps extends Omit<PrizmaTutorialScreenProps, 'title' | 'subtitle' | 'ctaLabel' | 'illustration' | 'verifiedLabel' | 'navLabel'> {
|
|
7
|
+
/** Module copy — this screen reads the `tutorial` slice (+ shared labels). */
|
|
8
|
+
strings: DigitalIdUploadStrings;
|
|
9
|
+
screenId?: string;
|
|
10
|
+
overrides?: PrizmaScreenOverrides;
|
|
11
|
+
onAction?: PrizmaScreenActionHandler;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Digital ID Upload — tutorial, built on the `PrizmaTutorialScreen` archetype
|
|
15
|
+
* (GUIDELINES.md §6-A). Figma: `Upload Digital ID` file
|
|
16
|
+
* (wxFsHRvllern4VnjRqM4JO), "Web Happy Path - Light Mode" section, `Tutorial
|
|
17
|
+
* Screen` (node 25351:14722).
|
|
18
|
+
*
|
|
19
|
+
* The illustration slot is Core's own `PrizmaCaptureDigitalId` in its
|
|
20
|
+
* `tutorial` state — the Figma file names this exact instance "Capture /
|
|
21
|
+
* Digital ID" (node 26161:10686, Figma description: "Incode specific
|
|
22
|
+
* component for Digital ID module experience"), the same managed component
|
|
23
|
+
* already shipped as `core/platforms/web/src/CaptureDigitalId.tsx`. No module
|
|
24
|
+
* illustration is drawn here — the component owns its own tutorial artwork
|
|
25
|
+
* (`PrizmaIllustrationDigitalId`).
|
|
26
|
+
*/
|
|
27
|
+
export declare const DigitalIdUploadTutorialScreen: React.ForwardRefExoticComponent<DigitalIdUploadTutorialScreenProps & React.RefAttributes<HTMLDivElement>>;
|
|
28
|
+
export default DigitalIdUploadTutorialScreen;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { PrizmaCaptureDigitalId } from '@incodetech/prizma-core-web/capture-digital-id';
|
|
5
|
+
import { PrizmaTutorialScreen } from './TutorialScreen';
|
|
6
|
+
import './ScreenLayout.css';
|
|
7
|
+
/**
|
|
8
|
+
* Digital ID Upload — tutorial, built on the `PrizmaTutorialScreen` archetype
|
|
9
|
+
* (GUIDELINES.md §6-A). Figma: `Upload Digital ID` file
|
|
10
|
+
* (wxFsHRvllern4VnjRqM4JO), "Web Happy Path - Light Mode" section, `Tutorial
|
|
11
|
+
* Screen` (node 25351:14722).
|
|
12
|
+
*
|
|
13
|
+
* The illustration slot is Core's own `PrizmaCaptureDigitalId` in its
|
|
14
|
+
* `tutorial` state — the Figma file names this exact instance "Capture /
|
|
15
|
+
* Digital ID" (node 26161:10686, Figma description: "Incode specific
|
|
16
|
+
* component for Digital ID module experience"), the same managed component
|
|
17
|
+
* already shipped as `core/platforms/web/src/CaptureDigitalId.tsx`. No module
|
|
18
|
+
* illustration is drawn here — the component owns its own tutorial artwork
|
|
19
|
+
* (`PrizmaIllustrationDigitalId`).
|
|
20
|
+
*/
|
|
21
|
+
export const DigitalIdUploadTutorialScreen = forwardRef(({ strings, screenId = 'digital-id-upload-tutorial', overrides, onAction, ...shell }, ref) => (_jsx(PrizmaTutorialScreen, { ref: ref, title: strings.tutorial.title, subtitle: strings.tutorial.subtitle, ctaLabel: strings.tutorial.ctaLabel, illustration: _jsx(PrizmaCaptureDigitalId, { state: "tutorial" }), verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell })));
|
|
22
|
+
DigitalIdUploadTutorialScreen.displayName = 'DigitalIdUploadTutorialScreen';
|
|
23
|
+
export default DigitalIdUploadTutorialScreen;
|
|
@@ -6,7 +6,7 @@ import './DocumentCaptureLayout.css';
|
|
|
6
6
|
*
|
|
7
7
|
* WHY IT LIVES IN THE MODULE LAYER: `prizma-core-web` ships
|
|
8
8
|
* `PrizmaIllustrationSelfie`, `…IdTutorial`, `…IdIntro`, `…IdFlip`,
|
|
9
|
-
* `…IdHint`, `…IdFeedback`, `…NfcScan`, `…
|
|
9
|
+
* `…IdHint`, `…IdFeedback`, `…NfcScan`, `…Geolocation`,
|
|
10
10
|
* `…DigitalId` and `…Avatar` — there is NO document / proof-of-address
|
|
11
11
|
* illustration. GUIDELINES.md §10 is the standing asset rule for that gap
|
|
12
12
|
* ("Illustrations…: inline SVG React components (Figma-sourced), tintable via
|
|
@@ -39,12 +39,12 @@ export interface EkybCountryScreenProps extends Omit<PrizmaFormScreenProps, 'tit
|
|
|
39
39
|
* live interaction) — the same mechanism Phone Input's `countryCode` and
|
|
40
40
|
* CURP's `Gene.dropdown` sections use. The search bar Figma draws inside the
|
|
41
41
|
* menu is Core's own `searchable`.
|
|
42
|
-
* 3. Menu rows and the closed field read the same "
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
42
|
+
* 3. Menu rows and the closed field read the same flag + "United States",
|
|
43
|
+
* no dial code. Figma 5707:54174 draws the rows with Phone Input's "+1"
|
|
44
|
+
* prefixes, dropped by the owner (Elena, 2026-09-28) because a country of
|
|
45
|
+
* registration has no phone prefix. The flag is Core's raster in the
|
|
46
|
+
* dropdown's leading slot, not an emoji in the label (Elena, 2026-10-23;
|
|
47
|
+
* #PI-3, the WatchlistBusinessFormScreen precedent).
|
|
48
48
|
*
|
|
49
49
|
* This page is the only eKYB form page with NO "Back" secondary and NO Stepper —
|
|
50
50
|
* it is step 0, before the three-step wizard starts.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { PrizmaDropdown } from '@incodetech/prizma-core-web/dropdown';
|
|
5
|
+
import { PrizmaCountryFlag } from '@incodetech/prizma-core-web/combined';
|
|
5
6
|
import { PrizmaFormScreen } from './FormScreen';
|
|
6
7
|
import './ScreenLayout.css';
|
|
7
8
|
/**
|
|
@@ -26,12 +27,12 @@ import './ScreenLayout.css';
|
|
|
26
27
|
* live interaction) — the same mechanism Phone Input's `countryCode` and
|
|
27
28
|
* CURP's `Gene.dropdown` sections use. The search bar Figma draws inside the
|
|
28
29
|
* menu is Core's own `searchable`.
|
|
29
|
-
* 3. Menu rows and the closed field read the same "
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* 3. Menu rows and the closed field read the same flag + "United States",
|
|
31
|
+
* no dial code. Figma 5707:54174 draws the rows with Phone Input's "+1"
|
|
32
|
+
* prefixes, dropped by the owner (Elena, 2026-09-28) because a country of
|
|
33
|
+
* registration has no phone prefix. The flag is Core's raster in the
|
|
34
|
+
* dropdown's leading slot, not an emoji in the label (Elena, 2026-10-23;
|
|
35
|
+
* #PI-3, the WatchlistBusinessFormScreen precedent).
|
|
35
36
|
*
|
|
36
37
|
* This page is the only eKYB form page with NO "Back" secondary and NO Stepper —
|
|
37
38
|
* it is step 0, before the three-step wizard starts.
|
|
@@ -70,7 +71,19 @@ export const EkybCountryScreen = forwardRef(({ strings, state = 'empty', countri
|
|
|
70
71
|
(DEVIATIONS #PI-6). */
|
|
71
72
|
defaultValue: selected, searchable: true, searchPlaceholder: copy.searchPlaceholder, clearSearchLabel: copy.clearSearchLabel,
|
|
72
73
|
/* The `dropdown` section IS the open-menu spec state. */
|
|
73
|
-
forceOpen: state === 'dropdown'
|
|
74
|
+
forceOpen: state === 'dropdown',
|
|
75
|
+
/* Core's canonical flag raster in the closed field and every menu
|
|
76
|
+
row (see note 3). A country outside the bank falls back to its
|
|
77
|
+
`flag` emoji. */
|
|
78
|
+
renderLeading: (option) => {
|
|
79
|
+
const match = countries.find((c) => c.value === option.value);
|
|
80
|
+
return match ? (_jsx(PrizmaCountryFlag, { country: {
|
|
81
|
+
code: match.value,
|
|
82
|
+
name: match.label,
|
|
83
|
+
dialCode: '',
|
|
84
|
+
flag: match.flag ?? '',
|
|
85
|
+
} })) : null;
|
|
86
|
+
} }) }));
|
|
74
87
|
});
|
|
75
88
|
EkybCountryScreen.displayName = 'EkybCountryScreen';
|
|
76
89
|
export default EkybCountryScreen;
|
package/dist/EkybStrings.d.ts
CHANGED
|
@@ -59,8 +59,13 @@ export interface EkybOption {
|
|
|
59
59
|
label: string;
|
|
60
60
|
/** Compact face for the CLOSED field when it differs from the menu row —
|
|
61
61
|
* Core's own `PrizmaDropdownOption.fieldLabel`. The eKYB country list
|
|
62
|
-
* does not need it: rows and field both read "
|
|
62
|
+
* does not need it: rows and field both read "United States" after the
|
|
63
|
+
* flag. */
|
|
63
64
|
fieldLabel?: string;
|
|
65
|
+
/** Country menus only: the emoji fallback for a `value` outside Core's
|
|
66
|
+
* flag raster bank. The country screen draws the bank's flag by `value`,
|
|
67
|
+
* so this never travels inside `label`. */
|
|
68
|
+
flag?: string;
|
|
64
69
|
}
|
|
65
70
|
/** The values the business-identity page carries. Runtime data, never copy. */
|
|
66
71
|
export interface EkybBusinessValues {
|
|
@@ -21,7 +21,7 @@ export interface ErrorScreensScreenProps extends Omit<PrizmaResultScreenProps, '
|
|
|
21
21
|
* Error screens — the session-level failures, built on the Result archetype
|
|
22
22
|
* (GUIDELINES.md §6-F, `PrizmaResultScreen`).
|
|
23
23
|
*
|
|
24
|
-
* ONE screen for all
|
|
24
|
+
* ONE screen for all four generic sections. They draw the identical skeleton —
|
|
25
25
|
* a centred mark + title + supporting line over a CTA band — and differ only in
|
|
26
26
|
* copy, in which mark leads, and in what the band carries:
|
|
27
27
|
*
|
|
@@ -29,8 +29,11 @@ export interface ErrorScreensScreenProps extends Omit<PrizmaResultScreenProps, '
|
|
|
29
29
|
* - `noConnection` — Wi-Fi glyph, "Refresh"
|
|
30
30
|
* - `unsupportedBrowser` — canonical negative mark, the session URL above
|
|
31
31
|
* "Copy link", with "Continue anyways" under it
|
|
32
|
+
* - `somethingWentWrong` — canonical negative mark, "Try again" when the copy
|
|
33
|
+
* carries a CTA, "Quit process" under it when it
|
|
34
|
+
* carries a quit label; a dead end when it has neither
|
|
32
35
|
*
|
|
33
|
-
* Chrome is CONSTANT. All
|
|
36
|
+
* Chrome is CONSTANT. All the frames are drawn without the logo header and
|
|
34
37
|
* without the verified tag; both are kept here, because the owner rule of
|
|
35
38
|
* 2026-08-18 (#F-30, #F-34) makes the chrome the same on every screen so
|
|
36
39
|
* nothing shifts between states, and every module's connectivity screen must
|
|
@@ -9,7 +9,7 @@ import './ScreenLayout.css';
|
|
|
9
9
|
* Error screens — the session-level failures, built on the Result archetype
|
|
10
10
|
* (GUIDELINES.md §6-F, `PrizmaResultScreen`).
|
|
11
11
|
*
|
|
12
|
-
* ONE screen for all
|
|
12
|
+
* ONE screen for all four generic sections. They draw the identical skeleton —
|
|
13
13
|
* a centred mark + title + supporting line over a CTA band — and differ only in
|
|
14
14
|
* copy, in which mark leads, and in what the band carries:
|
|
15
15
|
*
|
|
@@ -17,8 +17,11 @@ import './ScreenLayout.css';
|
|
|
17
17
|
* - `noConnection` — Wi-Fi glyph, "Refresh"
|
|
18
18
|
* - `unsupportedBrowser` — canonical negative mark, the session URL above
|
|
19
19
|
* "Copy link", with "Continue anyways" under it
|
|
20
|
+
* - `somethingWentWrong` — canonical negative mark, "Try again" when the copy
|
|
21
|
+
* carries a CTA, "Quit process" under it when it
|
|
22
|
+
* carries a quit label; a dead end when it has neither
|
|
20
23
|
*
|
|
21
|
-
* Chrome is CONSTANT. All
|
|
24
|
+
* Chrome is CONSTANT. All the frames are drawn without the logo header and
|
|
22
25
|
* without the verified tag; both are kept here, because the owner rule of
|
|
23
26
|
* 2026-08-18 (#F-30, #F-34) makes the chrome the same on every screen so
|
|
24
27
|
* nothing shifts between states, and every module's connectivity screen must
|
|
@@ -49,11 +52,19 @@ import './ScreenLayout.css';
|
|
|
49
52
|
export const ErrorScreensScreen = forwardRef(({ strings, variant = 'sessionExpired', sessionUrl, copied = false, screenId = `error-screens-${variant}`, overrides, onAction, ...shell }, ref) => {
|
|
50
53
|
const unsupported = variant === 'unsupportedBrowser';
|
|
51
54
|
const noConnection = variant === 'noConnection';
|
|
55
|
+
const generic = variant === 'somethingWentWrong';
|
|
52
56
|
const copy = unsupported
|
|
53
57
|
? strings.unsupportedBrowser
|
|
54
58
|
: noConnection
|
|
55
59
|
? strings.noConnection
|
|
56
|
-
:
|
|
60
|
+
: generic
|
|
61
|
+
? strings.somethingWentWrong
|
|
62
|
+
: strings.sessionExpired;
|
|
63
|
+
const linkLabel = unsupported
|
|
64
|
+
? strings.unsupportedBrowser.continueLabel
|
|
65
|
+
: generic
|
|
66
|
+
? strings.somethingWentWrong.quitLabel
|
|
67
|
+
: undefined;
|
|
57
68
|
return (_jsx(PrizmaResultScreen, { ref: ref,
|
|
58
69
|
/* The connectivity screen leads with the Wi-Fi glyph instead of a
|
|
59
70
|
status mark; the other two carry the canonical negative mark. */
|
|
@@ -65,7 +76,7 @@ export const ErrorScreensScreen = forwardRef(({ strings, variant = 'sessionExpir
|
|
|
65
76
|
.filter(Boolean)
|
|
66
77
|
.join(' '), children: _jsx(IconWifiOff, { size: "l" }) })) : undefined, progress: unsupported && copied ? (_jsx(PrizmaSnackbar, { state: "success", contentResizing: "fill", children: strings.unsupportedBrowser.copiedLabel })) : undefined, title: copy.title, subtitle: copy.subtitle, disclaimer: unsupported
|
|
67
78
|
? sessionUrl ?? strings.unsupportedBrowser.urlPlaceholder
|
|
68
|
-
: undefined, ctaLabel: copy.ctaLabel, linkLabel:
|
|
79
|
+
: undefined, ctaLabel: copy.ctaLabel, linkLabel: linkLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell }));
|
|
69
80
|
});
|
|
70
81
|
ErrorScreensScreen.displayName = 'ErrorScreensScreen';
|
|
71
82
|
export default ErrorScreensScreen;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The four sections.
|
|
3
3
|
* - `sessionExpired` — the link is dead; the user must go back to the provider
|
|
4
4
|
* - `noConnection` — the network went away; retry when it returns
|
|
5
5
|
* - `unsupportedBrowser` — the SDK cannot run here; copy the link and move
|
|
6
|
+
* - `somethingWentWrong` — the generic failure; try again, or nothing, or quit
|
|
6
7
|
*/
|
|
7
|
-
export type ErrorScreensVariant = 'sessionExpired' | 'noConnection' | 'unsupportedBrowser';
|
|
8
|
+
export type ErrorScreensVariant = 'sessionExpired' | 'noConnection' | 'unsupportedBrowser' | 'somethingWentWrong';
|
|
8
9
|
export interface ErrorScreensCopy {
|
|
9
10
|
title: string;
|
|
10
11
|
subtitle?: string;
|
|
@@ -28,4 +29,18 @@ export interface ErrorScreensStrings {
|
|
|
28
29
|
/** Snackbar copy after the link is copied — the frame's second state. */
|
|
29
30
|
copiedLabel: string;
|
|
30
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* The generic failure. Three ways the modules fill it, all the same screen:
|
|
34
|
+
* `ctaLabel` "Try again" (eKYC, eKYB, Forms, Email Input);
|
|
35
|
+
* no `ctaLabel`, a dead end (Certificate Issuance, Qualified Signature);
|
|
36
|
+
* `ctaLabel` plus `quitLabel`, the way out under the retry (Video Selfie).
|
|
37
|
+
* Phone Input's `unknown` fills it with "Continue" instead, because a retry
|
|
38
|
+
* may not be possible there (owner, 2026-10-09) — the CTA is the module's
|
|
39
|
+
* call; the shape and the title are what the family fixes.
|
|
40
|
+
* The title never carries an ellipsis — a failure is not in progress.
|
|
41
|
+
*/
|
|
42
|
+
somethingWentWrong: ErrorScreensCopy & {
|
|
43
|
+
/** Tertiary link under the CTA — "Quit process". Omit for no way out. */
|
|
44
|
+
quitLabel?: string;
|
|
45
|
+
};
|
|
31
46
|
}
|
|
@@ -6,19 +6,27 @@
|
|
|
6
6
|
// 214:3357 Geolocation.error.noNetwork → `noConnection`
|
|
7
7
|
// 240:154 Error screen - Unsupported browser → `unsupportedBrowser`
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
9
|
+
// plus the one failure every module draws for itself and the file never did:
|
|
10
|
+
//
|
|
11
|
+
// — <module>.error "Something went wrong" → `somethingWentWrong`
|
|
12
|
+
//
|
|
13
|
+
// The failures that belong to the SESSION rather than to any module: the link
|
|
14
|
+
// has expired, the network went away, the browser cannot run the SDK, the
|
|
15
|
+
// server said no for a reason the user cannot act on. Every module can hit all
|
|
16
|
+
// four, which is why they are recorded once in the generic family — a user who
|
|
17
|
+
// sees three different "no internet" screens in one journey reads them as three
|
|
18
|
+
// different products.
|
|
14
19
|
//
|
|
15
20
|
// `noConnection` is deliberately the same screen every module already ships
|
|
16
21
|
// (`FaceCaptureNoConnectionScreen`, `IdCaptureNoConnectionScreen`,
|
|
17
|
-
// `DocumentCaptureNoConnectionScreen`, `GeolocationErrorScreen`'s `noNetwork
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
+
// `DocumentCaptureNoConnectionScreen`, `GeolocationErrorScreen`'s `noNetwork`,
|
|
23
|
+
// Phone Input's and Video Selfie's `noConnection`). `somethingWentWrong` is the
|
|
24
|
+
// same screen eKYC, eKYB, Forms, Email Input, Phone Input, Certificate
|
|
25
|
+
// Issuance, both signatures and Video Selfie already ship. The generic entry is
|
|
26
|
+
// where the shape and the copy are DOCUMENTED; the module copies stay put until
|
|
27
|
+
// someone retires them deliberately, because deleting exported components to
|
|
28
|
+
// save one screen is a breaking change for every host that imports them.
|
|
29
|
+
// Flagged for the owner (DEVIATIONS #GEN-7, #GEN-18).
|
|
22
30
|
//
|
|
23
31
|
// The family has no flow of its own: each of these is entered from wherever the
|
|
24
32
|
// session broke and exits to a retry or to nothing. Core Lab lists it as a
|
|
@@ -9,6 +9,19 @@ export interface FaceCaptureCapturingScreenProps extends Omit<PrizmaCaptureScree
|
|
|
9
9
|
state?: FaceCaptureCapturingState;
|
|
10
10
|
/** Live camera feed (<video>) or sample still (<img>) for the ring window. */
|
|
11
11
|
camera?: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Raise the "Need more time?" offer over the feed — the accessibility
|
|
14
|
+
* notification shown 20 s before auto capture falls back to manual.
|
|
15
|
+
*
|
|
16
|
+
* It is a MODIFIER of whatever capturing state is already on screen, not a
|
|
17
|
+
* sixteenth state: the timer runs underneath the guidance, so the user can be
|
|
18
|
+
* told to move closer and offered more time in the same frame. Needs
|
|
19
|
+
* `strings.extendTime`; without it the flag is a no-op, which is what a host
|
|
20
|
+
* that has not localized the copy should get rather than English.
|
|
21
|
+
*/
|
|
22
|
+
extendTime?: boolean;
|
|
23
|
+
/** Tapping "Extend auto-capture". The host resets its own fallback timer. */
|
|
24
|
+
onExtendTime?: () => void;
|
|
12
25
|
}
|
|
13
26
|
/**
|
|
14
27
|
* Face Capture — auto-capture stage (Archetype B, GUIDELINES.md §6-B).
|
|
@@ -27,6 +40,56 @@ export interface FaceCaptureCapturingScreenProps extends Omit<PrizmaCaptureScree
|
|
|
27
40
|
*
|
|
28
41
|
* The archetype adds no offsets of its own — the component owns that spacing
|
|
29
42
|
* (same deviation row).
|
|
43
|
+
*
|
|
44
|
+
* ── The "Need more time?" offer (`extendTime`) ──────────────────────────────
|
|
45
|
+
*
|
|
46
|
+
* Auto capture gives up after a configured timeout and drops the user into
|
|
47
|
+
* manual capture. Someone who is struggling — a tremor, low vision, bad light,
|
|
48
|
+
* an unfamiliar phone — gets moved off the EASIER path precisely because they
|
|
49
|
+
* were slow, which is backwards. So 20 s before the switch, the screen offers
|
|
50
|
+
* to stay on auto. A client turns it on per flow from the dashboard; with it
|
|
51
|
+
* off, nothing about today's capture changes.
|
|
52
|
+
*
|
|
53
|
+
* NOT a nicety — it is the conformance route for WCAG 2.2.1 Timing Adjustable
|
|
54
|
+
* via the *Extend* clause (warn before the limit, ≥20 s to act, ≥10 extensions).
|
|
55
|
+
* Two things follow that the visuals cannot settle: the notice must be able to
|
|
56
|
+
* come back ten times, and it must be ANNOUNCED early enough to be acted on.
|
|
57
|
+
*
|
|
58
|
+
* ── Live-region politeness: `role="alert"`, not the default ────────────────
|
|
59
|
+
*
|
|
60
|
+
* SETTLED 2026-10-05 (Camila). The Snackbar computes `role="status"` (polite)
|
|
61
|
+
* for every state but error, and this instance overrides it to `alert`.
|
|
62
|
+
*
|
|
63
|
+
* A polite region is allowed to wait until the screen reader finishes whatever
|
|
64
|
+
* it is already saying — and on this screen it always IS saying something: the
|
|
65
|
+
* capture instruction under the ring changes as the face moves, so the queue
|
|
66
|
+
* is rarely empty. The offer has a 20 s life. Queued behind two or three
|
|
67
|
+
* guidance announcements it can be read out after it has already expired,
|
|
68
|
+
* which turns the one affordance a struggling user needs into an
|
|
69
|
+
* announcement that the limit has passed.
|
|
70
|
+
*
|
|
71
|
+
* That is not a polish issue, it is the conformance failing: WCAG 2.2.1's
|
|
72
|
+
* Extend clause requires the warning to be given in time to ACT on it. An
|
|
73
|
+
* announcement the user cannot act on does not satisfy it, however correct the
|
|
74
|
+
* visuals. `alert` is assertive, so it interrupts the guidance and is heard
|
|
75
|
+
* while the 20 s is still running.
|
|
76
|
+
*
|
|
77
|
+
* The usual objection to `alert` is that interrupting is rude. It applies to
|
|
78
|
+
* notices that can wait; this one cannot, and it is strictly better to
|
|
79
|
+
* interrupt "Move closer" than to let the window close in silence. It is also
|
|
80
|
+
* why the offer must not be re-raised on a timer — each new `alert` interrupts
|
|
81
|
+
* again. It appears once per countdown, and WCAG allows that up to ten times.
|
|
82
|
+
*
|
|
83
|
+
* ⚠ NEW, not yet authored in Core. Two parts of this notice have no Figma
|
|
84
|
+
* variant behind them and are drawn from the exploration, not from the SoT:
|
|
85
|
+
* • the bold title line — the Snackbar has no `title` slot, so it rides
|
|
86
|
+
* inside `children` as a <strong>. Core to decide whether to adopt a real
|
|
87
|
+
* slot, at which point this moves onto it.
|
|
88
|
+
* • the state — it is `neutral` (blue surface, ⓘ glyph), Camila's call
|
|
89
|
+
* 2026-10-05. `warning` would signal attention more sharply but turns the
|
|
90
|
+
* surface amber, and the exploration's open question is whether amber
|
|
91
|
+
* reads as "hurry up" to someone who was already struggling. The icon
|
|
92
|
+
* cannot be chosen independently: `ICON_BY_STATE` binds it to `state`.
|
|
30
93
|
*/
|
|
31
94
|
export declare const FaceCaptureCapturingScreen: React.ForwardRefExoticComponent<FaceCaptureCapturingScreenProps & React.RefAttributes<HTMLDivElement>>;
|
|
32
95
|
export default FaceCaptureCapturingScreen;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
|
+
import { PrizmaSnackbar } from '@incodetech/prizma-core-web/snackbar';
|
|
4
5
|
import { PrizmaCaptureScreen } from './CaptureScreen';
|
|
5
6
|
import './FaceCaptureLayout.css';
|
|
6
7
|
/**
|
|
@@ -47,7 +48,66 @@ const SELFIE_STATE = {
|
|
|
47
48
|
*
|
|
48
49
|
* The archetype adds no offsets of its own — the component owns that spacing
|
|
49
50
|
* (same deviation row).
|
|
51
|
+
*
|
|
52
|
+
* ── The "Need more time?" offer (`extendTime`) ──────────────────────────────
|
|
53
|
+
*
|
|
54
|
+
* Auto capture gives up after a configured timeout and drops the user into
|
|
55
|
+
* manual capture. Someone who is struggling — a tremor, low vision, bad light,
|
|
56
|
+
* an unfamiliar phone — gets moved off the EASIER path precisely because they
|
|
57
|
+
* were slow, which is backwards. So 20 s before the switch, the screen offers
|
|
58
|
+
* to stay on auto. A client turns it on per flow from the dashboard; with it
|
|
59
|
+
* off, nothing about today's capture changes.
|
|
60
|
+
*
|
|
61
|
+
* NOT a nicety — it is the conformance route for WCAG 2.2.1 Timing Adjustable
|
|
62
|
+
* via the *Extend* clause (warn before the limit, ≥20 s to act, ≥10 extensions).
|
|
63
|
+
* Two things follow that the visuals cannot settle: the notice must be able to
|
|
64
|
+
* come back ten times, and it must be ANNOUNCED early enough to be acted on.
|
|
65
|
+
*
|
|
66
|
+
* ── Live-region politeness: `role="alert"`, not the default ────────────────
|
|
67
|
+
*
|
|
68
|
+
* SETTLED 2026-10-05 (Camila). The Snackbar computes `role="status"` (polite)
|
|
69
|
+
* for every state but error, and this instance overrides it to `alert`.
|
|
70
|
+
*
|
|
71
|
+
* A polite region is allowed to wait until the screen reader finishes whatever
|
|
72
|
+
* it is already saying — and on this screen it always IS saying something: the
|
|
73
|
+
* capture instruction under the ring changes as the face moves, so the queue
|
|
74
|
+
* is rarely empty. The offer has a 20 s life. Queued behind two or three
|
|
75
|
+
* guidance announcements it can be read out after it has already expired,
|
|
76
|
+
* which turns the one affordance a struggling user needs into an
|
|
77
|
+
* announcement that the limit has passed.
|
|
78
|
+
*
|
|
79
|
+
* That is not a polish issue, it is the conformance failing: WCAG 2.2.1's
|
|
80
|
+
* Extend clause requires the warning to be given in time to ACT on it. An
|
|
81
|
+
* announcement the user cannot act on does not satisfy it, however correct the
|
|
82
|
+
* visuals. `alert` is assertive, so it interrupts the guidance and is heard
|
|
83
|
+
* while the 20 s is still running.
|
|
84
|
+
*
|
|
85
|
+
* The usual objection to `alert` is that interrupting is rude. It applies to
|
|
86
|
+
* notices that can wait; this one cannot, and it is strictly better to
|
|
87
|
+
* interrupt "Move closer" than to let the window close in silence. It is also
|
|
88
|
+
* why the offer must not be re-raised on a timer — each new `alert` interrupts
|
|
89
|
+
* again. It appears once per countdown, and WCAG allows that up to ten times.
|
|
90
|
+
*
|
|
91
|
+
* ⚠ NEW, not yet authored in Core. Two parts of this notice have no Figma
|
|
92
|
+
* variant behind them and are drawn from the exploration, not from the SoT:
|
|
93
|
+
* • the bold title line — the Snackbar has no `title` slot, so it rides
|
|
94
|
+
* inside `children` as a <strong>. Core to decide whether to adopt a real
|
|
95
|
+
* slot, at which point this moves onto it.
|
|
96
|
+
* • the state — it is `neutral` (blue surface, ⓘ glyph), Camila's call
|
|
97
|
+
* 2026-10-05. `warning` would signal attention more sharply but turns the
|
|
98
|
+
* surface amber, and the exploration's open question is whether amber
|
|
99
|
+
* reads as "hurry up" to someone who was already struggling. The icon
|
|
100
|
+
* cannot be chosen independently: `ICON_BY_STATE` binds it to `state`.
|
|
50
101
|
*/
|
|
51
|
-
export const FaceCaptureCapturingScreen = forwardRef(({ strings, state = 'initiating', camera, screenId = 'face-capture-capturing', className, ...rest }, ref) =>
|
|
102
|
+
export const FaceCaptureCapturingScreen = forwardRef(({ strings, state = 'initiating', camera, extendTime, onExtendTime, screenId = 'face-capture-capturing', className, ...rest }, ref) => {
|
|
103
|
+
const offer = strings.extendTime;
|
|
104
|
+
// No copy, no notice: a host that has not localized this must not get
|
|
105
|
+
// English, and the flag alone is not permission to invent it.
|
|
106
|
+
const notice = extendTime && offer ? (_jsxs(PrizmaSnackbar, { state: "neutral", isMultiline: true,
|
|
107
|
+
// Assertive on purpose — see the live-region note in this file's
|
|
108
|
+
// header. The component default here would be `status`.
|
|
109
|
+
role: "alert", linkLabel: offer.linkLabel, onLinkClick: onExtendTime, children: [_jsx("strong", { children: offer.title }), _jsx("br", {}), offer.description] })) : undefined;
|
|
110
|
+
return (_jsx(PrizmaCaptureScreen, { ref: ref, className: className, state: SELFIE_STATE[state], camera: camera, feedbackText: strings.capturing[state], notice: notice, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, ...rest }));
|
|
111
|
+
});
|
|
52
112
|
FaceCaptureCapturingScreen.displayName = 'FaceCaptureCapturingScreen';
|
|
53
113
|
export default FaceCaptureCapturingScreen;
|