@smileid/web-components 11.4.5 → 11.6.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/dist/esm/DocumentCaptureScreens-DjSTdVP-.js +5398 -0
- package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js.map +1 -0
- package/dist/esm/{Navigation-Bb7MPLE8.js → Navigation-6DH3vF4-.js} +28 -22
- package/dist/esm/Navigation-6DH3vF4-.js.map +1 -0
- package/dist/esm/{PoweredBySmileId-CxbaihMu.js → PoweredBySmileId-DoKwoPUd.js} +424 -6
- package/dist/esm/PoweredBySmileId-DoKwoPUd.js.map +1 -0
- package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js +11470 -0
- package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js.map +1 -0
- package/dist/esm/combobox.js +1 -1
- package/dist/esm/document.js +1 -1
- package/dist/esm/end-user-consent.js +713 -2
- package/dist/esm/end-user-consent.js.map +1 -1
- package/dist/esm/index-BqyuTk9f.js +1366 -0
- package/dist/esm/{index-C4RTMbgw.js.map → index-BqyuTk9f.js.map} +1 -1
- package/dist/esm/localisation.js +1 -1
- package/dist/esm/main.js +14 -14
- package/dist/esm/navigation.js +1 -1
- package/dist/esm/package-CjZI-cNQ.js +2540 -0
- package/dist/esm/package-CjZI-cNQ.js.map +1 -0
- package/dist/esm/selfie.js +1 -1
- package/dist/esm/smart-camera-web.js +81 -37
- package/dist/esm/smart-camera-web.js.map +1 -1
- package/dist/esm/totp-consent.js +731 -2
- package/dist/esm/totp-consent.js.map +1 -1
- package/dist/esm/validate.js +31 -0
- package/dist/esm/validate.js.map +1 -0
- package/dist/smart-camera-web.js +1513 -383
- package/dist/smart-camera-web.js.map +1 -1
- package/dist/types/main.d.ts +18 -1
- package/dist/types/validate.d.ts +21 -0
- package/lib/components/document/src/DocumentCaptureScreens.js +97 -18
- package/lib/components/document/src/assets/lottie.d.ts +12 -0
- package/lib/components/document/src/assets/svg-inline.d.ts +8 -0
- package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.stories.js +75 -0
- package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.tsx +1458 -0
- package/lib/components/document/src/document-auto-capture/README.md +73 -0
- package/lib/components/document/src/document-auto-capture/assets/Greenbook_Shimmer.svg +42 -0
- package/lib/components/document/src/document-auto-capture/assets/ID_Back_Shimmer.svg +8 -0
- package/lib/components/document/src/document-auto-capture/assets/ID_Front_Shimmer.svg +20 -0
- package/lib/components/document/src/document-auto-capture/assets/Passport-Shimmer.svg +143 -0
- package/lib/components/document/src/document-auto-capture/assets/shimmers.ts +21 -0
- package/lib/components/document/src/document-auto-capture/assets/svg-raw.d.ts +4 -0
- package/lib/components/document/src/document-auto-capture/components/CaptureButton.tsx +122 -0
- package/lib/components/document/src/document-auto-capture/components/Overlay.tsx +167 -0
- package/lib/components/document/src/document-auto-capture/components/TuningPanel.tsx +856 -0
- package/lib/components/document/src/document-auto-capture/constants/captureLayout.ts +58 -0
- package/lib/components/document/src/document-auto-capture/detection/cvErrorRecovery.ts +40 -0
- package/lib/components/document/src/document-auto-capture/detection/documentAspect.ts +20 -0
- package/lib/components/document/src/document-auto-capture/detection/qualityScoring.ts +35 -0
- package/lib/components/document/src/document-auto-capture/detection/seamRejection.ts +209 -0
- package/lib/components/document/src/document-auto-capture/detection/synthesisTiming.ts +10 -0
- package/lib/components/document/src/document-auto-capture/hooks/useCamera.ts +117 -0
- package/lib/components/document/src/document-auto-capture/hooks/useCardDetection.ts +3059 -0
- package/lib/components/document/src/document-auto-capture/index.ts +4 -0
- package/lib/components/document/src/document-auto-capture/theme.ts +40 -0
- package/lib/components/document/src/document-auto-capture/utils/debug.ts +25 -0
- package/lib/components/document/src/document-auto-capture/utils/opencvLoader.ts +86 -0
- package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.tsx +327 -244
- package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +153 -189
- package/lib/components/document/src/document-capture-submission/DocumentCaptureSubmission.tsx +432 -0
- package/lib/components/document/src/document-capture-submission/index.js +3 -0
- package/lib/components/navigation/src/Navigation.js +27 -8
- package/lib/components/selfie/README.md +13 -0
- package/lib/components/selfie/src/SelfieCaptureScreens.js +56 -8
- package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieCapture.tsx +684 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieConsent.tsx +71 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieSubmission.tsx +181 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/OvalProgress.tsx +87 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/Icon.svg +8 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/accessories.svg +77 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/active_liveness_animation.lottie +0 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/device.svg +12 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/device_orientation.lottie +0 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/good.svg +52 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/id-card.svg +9 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/illustrations.tsx +852 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/instructions-img.svg +3 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/multiple-faces.svg +69 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/person.svg +6 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/phone.svg +8 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/poor-lighting.svg +53 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/too_dark_animation.lottie +0 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/ActiveLivenessOverlay.tsx +226 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/AlertDisplay.tsx +38 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/BackNavigation.tsx +45 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CameraPreview.tsx +96 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CaptureControls.tsx +97 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CaptureGuidelines.tsx +374 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/ConsentView.tsx +460 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/SubmissionView.tsx +426 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/components/index.ts +3 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/constants.ts +23 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/index.ts +2 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/useCamera.ts +238 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/useFaceCapture.ts +1075 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/index.ts +1 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/alertMessages.ts +20 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/canvas.ts +108 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/faceDetection.ts +545 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/imageCapture.ts +66 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/imageQuality.ts +151 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/index.ts +5 -0
- package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/mediapipeManager.ts +215 -0
- package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +24 -1
- package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +2 -2
- package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +15 -7
- package/lib/components/selfie/src/smartselfie-capture/utils/canvas.ts +4 -6
- package/lib/components/signature-pad/package.json +1 -1
- package/lib/components/smart-camera-web/src/README.md +11 -0
- package/lib/components/smart-camera-web/src/SmartCameraWeb.js +89 -8
- package/lib/components/totp-consent/src/TotpConsent.js +1 -1
- package/lib/domain/localisation/index.js +2 -2
- package/package.json +9 -5
- package/dist/esm/DocumentCaptureScreens-D2G0NOQr.js +0 -4147
- package/dist/esm/DocumentCaptureScreens-D2G0NOQr.js.map +0 -1
- package/dist/esm/EndUserConsent-uHfA3txP.js +0 -717
- package/dist/esm/EndUserConsent-uHfA3txP.js.map +0 -1
- package/dist/esm/Navigation-Bb7MPLE8.js.map +0 -1
- package/dist/esm/PoweredBySmileId-CxbaihMu.js.map +0 -1
- package/dist/esm/SelfieCaptureScreens-Dr7VzON7.js +0 -7651
- package/dist/esm/SelfieCaptureScreens-Dr7VzON7.js.map +0 -1
- package/dist/esm/TotpConsent-Depzg0ti.js +0 -734
- package/dist/esm/TotpConsent-Depzg0ti.js.map +0 -1
- package/dist/esm/index-C4RTMbgw.js +0 -1360
- package/dist/esm/package-D6YrpMcO.js +0 -565
- package/dist/esm/package-D6YrpMcO.js.map +0 -1
- package/dist/esm/styles-BTEClL7R.js +0 -419
- package/dist/esm/styles-BTEClL7R.js.map +0 -1
- /package/lib/components/document/src/assets/lottie/{taking photo of green book passport.lottie → greenbook.lottie} +0 -0
- /package/lib/components/document/src/assets/lottie/{taking photo of ID FLIP 2D.lottie → id-card-flip.lottie} +0 -0
- /package/lib/components/document/src/assets/lottie/{taking photo of ID.lottie → id-card.lottie} +0 -0
- /package/lib/components/document/src/assets/lottie/{taking photo of passport 2.lottie → passport.lottie} +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'preact/hooks';
|
|
2
|
+
import register from 'preact-custom-element';
|
|
3
|
+
import type { FunctionComponent } from 'preact';
|
|
4
|
+
|
|
5
|
+
import { getBoolProp } from '../../../../utils/props';
|
|
6
|
+
import { t } from '../../../../domain/localisation';
|
|
7
|
+
|
|
8
|
+
import '../../../attribution/PoweredBySmileId';
|
|
9
|
+
import '../../../navigation/src';
|
|
10
|
+
|
|
11
|
+
export type SubmissionState = 'submitting' | 'success' | 'error';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
'theme-color'?: string;
|
|
15
|
+
'hide-attribution'?: string | boolean;
|
|
16
|
+
/** When set, render the close affordance (smileid-navigation). */
|
|
17
|
+
'show-navigation'?: string | boolean;
|
|
18
|
+
/** Captured document data URI shown behind the status card. */
|
|
19
|
+
'image-src'?: string;
|
|
20
|
+
/** Initial submission state. Can be overridden at runtime via attribute or event. */
|
|
21
|
+
'submission-state'?: SubmissionState;
|
|
22
|
+
/** Optional supporting copy under the title (e.g. failure reason). */
|
|
23
|
+
'submission-message'?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Status overlays (match the Enhanced SmartSelfie visual language) ─────────
|
|
27
|
+
|
|
28
|
+
function Spinner() {
|
|
29
|
+
return (
|
|
30
|
+
<div class="doc-submit-overlay" aria-hidden="true">
|
|
31
|
+
<svg
|
|
32
|
+
class="doc-submit-spinner-svg"
|
|
33
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
34
|
+
width="64"
|
|
35
|
+
height="64"
|
|
36
|
+
viewBox="0 0 64 64"
|
|
37
|
+
fill="none"
|
|
38
|
+
>
|
|
39
|
+
<g clip-path="url(#doc-submit-loader-clip)">
|
|
40
|
+
<foreignObject
|
|
41
|
+
x="-1031.25"
|
|
42
|
+
y="-1031.25"
|
|
43
|
+
width="2062.5"
|
|
44
|
+
height="2062.5"
|
|
45
|
+
transform="matrix(0.032 0 0 0.032 32 32)"
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
{...{ xmlns: 'http://www.w3.org/1999/xhtml' }}
|
|
49
|
+
style="background:conic-gradient(from 90deg,rgba(39,174,96,0) 0deg,rgba(58,225,128,0) 0.036deg,rgba(58,225,128,1) 360deg);height:100%;width:100%;opacity:1"
|
|
50
|
+
/>
|
|
51
|
+
</foreignObject>
|
|
52
|
+
</g>
|
|
53
|
+
<path
|
|
54
|
+
fill-rule="evenodd"
|
|
55
|
+
clip-rule="evenodd"
|
|
56
|
+
d="M60.751 25.6018C62.2117 25.4134 63.5486 26.4447 63.737 27.9053C63.9122 29.2632 64 30.6309 64 31.9999C64 33.4727 62.8061 34.6666 61.3334 34.6666C59.8606 34.6666 58.6667 33.4727 58.6667 31.9999C58.6667 30.859 58.5935 29.7193 58.4475 28.5878C58.2591 27.1271 59.2904 25.7903 60.751 25.6018Z"
|
|
57
|
+
fill="#2CC05C"
|
|
58
|
+
/>
|
|
59
|
+
<defs>
|
|
60
|
+
<clipPath id="doc-submit-loader-clip">
|
|
61
|
+
<path
|
|
62
|
+
fill-rule="evenodd"
|
|
63
|
+
clip-rule="evenodd"
|
|
64
|
+
d="M32 64C49.6731 64 64 49.6731 64 32C64 14.3269 49.6731 0 32 0C14.3269 0 0 14.3269 0 32C0 49.6731 14.3269 64 32 64ZM32 58.6667C46.7276 58.6667 58.6667 46.7276 58.6667 32C58.6667 17.2724 46.7276 5.33333 32 5.33333C17.2724 5.33333 5.33333 17.2724 5.33333 32C5.33333 46.7276 17.2724 58.6667 32 58.6667Z"
|
|
65
|
+
/>
|
|
66
|
+
</clipPath>
|
|
67
|
+
</defs>
|
|
68
|
+
</svg>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function TickBadge() {
|
|
74
|
+
return (
|
|
75
|
+
<div class="doc-submit-overlay" aria-hidden="true">
|
|
76
|
+
<svg
|
|
77
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
78
|
+
width="64"
|
|
79
|
+
height="64"
|
|
80
|
+
viewBox="0 0 64 64"
|
|
81
|
+
fill="none"
|
|
82
|
+
>
|
|
83
|
+
<rect width="64" height="64" rx="32" fill="#2CC05C" />
|
|
84
|
+
<path
|
|
85
|
+
d="M27.1566 42.6663C26.4724 42.6663 25.7882 42.4088 25.2481 41.8568L19.4503 35.9324C18.9481 35.4131 18.6664 34.7123 18.6664 33.9821C18.6664 33.252 18.9481 32.5512 19.4503 32.0319C20.4946 30.9647 22.2232 30.9647 23.2675 32.0319L27.1566 36.006L40.7327 22.1334C41.777 21.0662 43.5055 21.0662 44.5498 22.1334C45.5941 23.2005 45.5941 24.9668 44.5498 26.0339L29.0652 41.8568C28.525 42.4088 27.8408 42.6663 27.1566 42.6663Z"
|
|
86
|
+
fill="white"
|
|
87
|
+
/>
|
|
88
|
+
</svg>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function CrossBadge() {
|
|
94
|
+
return (
|
|
95
|
+
<div class="doc-submit-overlay" aria-hidden="true">
|
|
96
|
+
<svg
|
|
97
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
98
|
+
width="64"
|
|
99
|
+
height="64"
|
|
100
|
+
viewBox="0 0 64 64"
|
|
101
|
+
fill="none"
|
|
102
|
+
>
|
|
103
|
+
<rect width="64" height="64" rx="32" fill="#EC221F" />
|
|
104
|
+
<path
|
|
105
|
+
d="M36.1146 31.9953L45.0425 22.6828C46.1682 21.5086 46.1682 19.5651 45.0425 18.3909C44.4947 17.8261 43.7555 17.5094 42.9852 17.5094C42.215 17.5094 41.4758 17.8261 40.9279 18.3909L32 27.7035L23.0721 18.3909C22.5242 17.8261 21.785 17.5094 21.0148 17.5094C20.2445 17.5094 19.5053 17.8261 18.9575 18.3909C17.8318 19.5651 17.8318 21.5086 18.9575 22.6828L27.8854 31.9953L18.9575 41.3079C17.8318 42.4821 17.8318 44.4256 18.9575 45.5998C19.5397 46.2071 20.2773 46.4906 21.0148 46.4906C21.7523 46.4906 22.4898 46.2071 23.0721 45.5998L32 36.2872L40.9279 45.5998C41.5102 46.2071 42.2477 46.4906 42.9852 46.4906C43.7227 46.4906 44.4603 46.2071 45.0425 45.5998C46.1682 44.4256 46.1682 42.4821 45.0425 41.3079L36.1146 31.9953Z"
|
|
106
|
+
fill="white"
|
|
107
|
+
/>
|
|
108
|
+
</svg>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* `<document-capture-submission>` — standalone post-capture submission UI for
|
|
115
|
+
* document flows where the host takes the captured image, uploads it, and
|
|
116
|
+
* wants to show submitting / success / failure cards in our visual language.
|
|
117
|
+
*
|
|
118
|
+
* Mirrors `<enhanced-smart-selfie-submission>`: the host knows the upload
|
|
119
|
+
* outcome and drives this element accordingly.
|
|
120
|
+
*
|
|
121
|
+
* Drive it via either:
|
|
122
|
+
* - attribute updates: `submission-state="submitting|success|error"`,
|
|
123
|
+
* `submission-message`
|
|
124
|
+
* - or window events: `document-capture-submission.set-state`
|
|
125
|
+
* detail: { state, message? }
|
|
126
|
+
*
|
|
127
|
+
* Emits:
|
|
128
|
+
* - `document-capture-submission.continue` (detail: { success }) when the
|
|
129
|
+
* submission resolves (success or error). Hosts can listen to navigate on.
|
|
130
|
+
*/
|
|
131
|
+
const DocumentCaptureSubmission: FunctionComponent<Props> = ({
|
|
132
|
+
'theme-color': themeColor = '#001096',
|
|
133
|
+
'hide-attribution': hideAttributionProp = false,
|
|
134
|
+
'show-navigation': showNavigationProp = false,
|
|
135
|
+
'image-src': imageSrc = '',
|
|
136
|
+
'submission-state': submissionStateProp = 'submitting',
|
|
137
|
+
'submission-message': submissionMessage,
|
|
138
|
+
}) => {
|
|
139
|
+
const hideAttribution = getBoolProp(hideAttributionProp);
|
|
140
|
+
const showNavigation = getBoolProp(showNavigationProp);
|
|
141
|
+
const navRef = useRef<HTMLElement | null>(null);
|
|
142
|
+
|
|
143
|
+
const [state, setState] = useState<SubmissionState>(submissionStateProp);
|
|
144
|
+
const [message, setMessage] = useState<string | undefined>(submissionMessage);
|
|
145
|
+
|
|
146
|
+
// Bridge the navigation web component's `navigation.close` (which fires on
|
|
147
|
+
// the element, not bubbling) out to a window event the host can act on.
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
const el = navRef.current;
|
|
150
|
+
if (!el) return undefined;
|
|
151
|
+
const onClose = () => {
|
|
152
|
+
window.dispatchEvent(
|
|
153
|
+
new CustomEvent('document-capture-submission.close'),
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
el.addEventListener('navigation.close', onClose);
|
|
157
|
+
return () => el.removeEventListener('navigation.close', onClose);
|
|
158
|
+
}, [showNavigation, state]);
|
|
159
|
+
|
|
160
|
+
// Sync attribute changes from the host through to local state so partners
|
|
161
|
+
// can update the card by setAttribute alone (no event required).
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
setState(submissionStateProp);
|
|
164
|
+
}, [submissionStateProp]);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
setMessage(submissionMessage);
|
|
167
|
+
}, [submissionMessage]);
|
|
168
|
+
|
|
169
|
+
// Event-driven path: avoids partners having to reach into the shadow DOM
|
|
170
|
+
// to flip attributes. Detail mirrors the attribute names.
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
const handler = (e: Event) => {
|
|
173
|
+
const ce = e as CustomEvent<{
|
|
174
|
+
state?: SubmissionState;
|
|
175
|
+
message?: string;
|
|
176
|
+
}>;
|
|
177
|
+
if (ce.detail?.state) setState(ce.detail.state);
|
|
178
|
+
if (ce.detail?.message !== undefined) setMessage(ce.detail.message);
|
|
179
|
+
};
|
|
180
|
+
window.addEventListener('document-capture-submission.set-state', handler);
|
|
181
|
+
return () => {
|
|
182
|
+
window.removeEventListener(
|
|
183
|
+
'document-capture-submission.set-state',
|
|
184
|
+
handler,
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
// Emit a resolution event when submission settles so hosts can navigate on.
|
|
190
|
+
// Only fire on a transition INTO a resolved state — never on initial mount
|
|
191
|
+
// (a host may render the element already `success`/`error`) and never on a
|
|
192
|
+
// re-render where the state is unchanged — so the host gets exactly one
|
|
193
|
+
// `.continue` per submission.
|
|
194
|
+
const isResolved = state === 'success' || state === 'error';
|
|
195
|
+
const prevStateRef = useRef<SubmissionState>(submissionStateProp);
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
const prevResolved =
|
|
198
|
+
prevStateRef.current === 'success' || prevStateRef.current === 'error';
|
|
199
|
+
prevStateRef.current = state;
|
|
200
|
+
if (!isResolved || prevResolved) return;
|
|
201
|
+
window.dispatchEvent(
|
|
202
|
+
new CustomEvent('document-capture-submission.continue', {
|
|
203
|
+
detail: { success: state === 'success' },
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
}, [isResolved, state]);
|
|
207
|
+
|
|
208
|
+
const isSubmitting = state === 'submitting';
|
|
209
|
+
const isSuccess = state === 'success';
|
|
210
|
+
const isError = state === 'error';
|
|
211
|
+
|
|
212
|
+
let title = '';
|
|
213
|
+
let body = '';
|
|
214
|
+
if (isSubmitting) {
|
|
215
|
+
title = t('document.submission.submitting.title');
|
|
216
|
+
body = t('document.submission.submitting.body');
|
|
217
|
+
} else if (isSuccess) {
|
|
218
|
+
// Success shows only the title (matches the design). Never surface a
|
|
219
|
+
// leftover `submission-message` from a prior error state.
|
|
220
|
+
title = t('document.submission.success.title');
|
|
221
|
+
body = '';
|
|
222
|
+
} else {
|
|
223
|
+
title = t('document.submission.error.title');
|
|
224
|
+
body = message || '';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<div class="doc-submit-root">
|
|
229
|
+
{showNavigation && (
|
|
230
|
+
<div class="doc-submit-nav">
|
|
231
|
+
{/* @ts-expect-error preact-custom-element types */}
|
|
232
|
+
<smileid-navigation
|
|
233
|
+
ref={navRef}
|
|
234
|
+
theme-color={themeColor}
|
|
235
|
+
show-navigation
|
|
236
|
+
hide-back
|
|
237
|
+
/>
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
<div class="doc-submit-image-area">
|
|
241
|
+
<div class="doc-submit-image-wrap">
|
|
242
|
+
{imageSrc && (
|
|
243
|
+
<img
|
|
244
|
+
class={`doc-submit-image ${isSubmitting ? 'is-dimmed' : ''}`}
|
|
245
|
+
src={imageSrc}
|
|
246
|
+
alt={t('document.submission.imageAlt')}
|
|
247
|
+
/>
|
|
248
|
+
)}
|
|
249
|
+
{isSubmitting && <Spinner />}
|
|
250
|
+
{isSuccess && <TickBadge />}
|
|
251
|
+
{isError && <CrossBadge />}
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div class="doc-submit-footer">
|
|
256
|
+
<div class="doc-submit-card">
|
|
257
|
+
<h1 class="doc-submit-title" style={{ color: themeColor }}>
|
|
258
|
+
{title}
|
|
259
|
+
</h1>
|
|
260
|
+
{isSubmitting ? (
|
|
261
|
+
<p class="doc-submit-body">
|
|
262
|
+
{body.split('\n').map((line, i, arr) => (
|
|
263
|
+
<>
|
|
264
|
+
{line}
|
|
265
|
+
{i < arr.length - 1 && <br />}
|
|
266
|
+
</>
|
|
267
|
+
))}
|
|
268
|
+
</p>
|
|
269
|
+
) : (
|
|
270
|
+
body && <p class="doc-submit-body">{body}</p>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
{!hideAttribution && (
|
|
275
|
+
<div class="doc-submit-attribution">
|
|
276
|
+
{/* @ts-expect-error preact-custom-element types */}
|
|
277
|
+
<powered-by-smile-id />
|
|
278
|
+
</div>
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<style>{`
|
|
283
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
284
|
+
:host { display: block; width: 100%; height: 100%; }
|
|
285
|
+
|
|
286
|
+
.doc-submit-root {
|
|
287
|
+
font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
|
|
288
|
+
background: #f8fafc;
|
|
289
|
+
display: flex;
|
|
290
|
+
flex-direction: column;
|
|
291
|
+
width: 100%;
|
|
292
|
+
height: 100%;
|
|
293
|
+
min-height: 100%;
|
|
294
|
+
position: relative;
|
|
295
|
+
overflow: hidden;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.doc-submit-nav {
|
|
299
|
+
position: absolute;
|
|
300
|
+
top: 16px;
|
|
301
|
+
left: 16px;
|
|
302
|
+
right: 16px;
|
|
303
|
+
z-index: 10;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.doc-submit-image-area {
|
|
307
|
+
flex: 1;
|
|
308
|
+
display: flex;
|
|
309
|
+
align-items: center;
|
|
310
|
+
justify-content: center;
|
|
311
|
+
padding: 72px 24px 16px;
|
|
312
|
+
min-height: 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.doc-submit-image-wrap {
|
|
316
|
+
position: relative;
|
|
317
|
+
display: flex;
|
|
318
|
+
align-items: center;
|
|
319
|
+
justify-content: center;
|
|
320
|
+
max-width: 100%;
|
|
321
|
+
max-height: 100%;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.doc-submit-image {
|
|
325
|
+
max-width: 100%;
|
|
326
|
+
max-height: 100%;
|
|
327
|
+
width: auto;
|
|
328
|
+
height: auto;
|
|
329
|
+
border-radius: 12px;
|
|
330
|
+
display: block;
|
|
331
|
+
object-fit: contain;
|
|
332
|
+
transition: filter 0.2s ease;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.doc-submit-image.is-dimmed {
|
|
336
|
+
filter: brightness(0.55);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Centered status overlay (spinner / tick / cross) */
|
|
340
|
+
.doc-submit-overlay {
|
|
341
|
+
position: absolute;
|
|
342
|
+
top: 50%;
|
|
343
|
+
left: 50%;
|
|
344
|
+
transform: translate(-50%, -50%);
|
|
345
|
+
width: 64px;
|
|
346
|
+
height: 64px;
|
|
347
|
+
display: flex;
|
|
348
|
+
align-items: center;
|
|
349
|
+
justify-content: center;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.doc-submit-spinner-svg {
|
|
353
|
+
animation: doc-submit-spin 1s linear infinite;
|
|
354
|
+
transform-origin: center;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@keyframes doc-submit-spin {
|
|
358
|
+
to { transform: rotate(360deg); }
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.doc-submit-footer {
|
|
362
|
+
padding: 0 20px 16px;
|
|
363
|
+
display: flex;
|
|
364
|
+
flex-direction: column;
|
|
365
|
+
align-items: center;
|
|
366
|
+
gap: 16px;
|
|
367
|
+
flex-shrink: 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.doc-submit-card {
|
|
371
|
+
width: 100%;
|
|
372
|
+
max-width: 420px;
|
|
373
|
+
background: #ffffff;
|
|
374
|
+
border-radius: 16px;
|
|
375
|
+
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08),
|
|
376
|
+
0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
|
377
|
+
padding: 28px 20px;
|
|
378
|
+
display: flex;
|
|
379
|
+
flex-direction: column;
|
|
380
|
+
align-items: center;
|
|
381
|
+
gap: 8px;
|
|
382
|
+
text-align: center;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.doc-submit-title {
|
|
386
|
+
margin: 0;
|
|
387
|
+
font-size: 1.125rem;
|
|
388
|
+
font-weight: 700;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.doc-submit-body {
|
|
392
|
+
margin: 0;
|
|
393
|
+
font-size: 0.8125rem;
|
|
394
|
+
font-weight: 400;
|
|
395
|
+
line-height: 1.35;
|
|
396
|
+
color: #5b6b7b;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.doc-submit-attribution {
|
|
400
|
+
display: flex;
|
|
401
|
+
align-items: center;
|
|
402
|
+
justify-content: center;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@media (min-width: 640px) {
|
|
406
|
+
.doc-submit-image-area { padding-top: 80px; }
|
|
407
|
+
}
|
|
408
|
+
`}</style>
|
|
409
|
+
</div>
|
|
410
|
+
);
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
if (
|
|
414
|
+
typeof customElements !== 'undefined' &&
|
|
415
|
+
!customElements.get('document-capture-submission')
|
|
416
|
+
) {
|
|
417
|
+
register(
|
|
418
|
+
DocumentCaptureSubmission,
|
|
419
|
+
'document-capture-submission',
|
|
420
|
+
[
|
|
421
|
+
'theme-color',
|
|
422
|
+
'hide-attribution',
|
|
423
|
+
'show-navigation',
|
|
424
|
+
'image-src',
|
|
425
|
+
'submission-state',
|
|
426
|
+
'submission-message',
|
|
427
|
+
],
|
|
428
|
+
{ shadow: true },
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export default DocumentCaptureSubmission;
|
|
@@ -12,12 +12,19 @@ class Navigation extends HTMLElement {
|
|
|
12
12
|
const iconColor = this.hasThemeColor ? this.themeColor : '#FFFFFF';
|
|
13
13
|
const focusColor = '#FFFFFF';
|
|
14
14
|
|
|
15
|
+
let justifyContent = 'flex-end';
|
|
16
|
+
if (this.showBackButton && this.showCloseButton) {
|
|
17
|
+
justifyContent = 'space-between';
|
|
18
|
+
} else if (this.showBackButton) {
|
|
19
|
+
justifyContent = 'flex-start';
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
const style = document.createElement('style');
|
|
16
23
|
style.textContent = `
|
|
17
24
|
:host {
|
|
18
25
|
display: flex;
|
|
19
26
|
max-inline-size: 100%;
|
|
20
|
-
justify-content: ${
|
|
27
|
+
justify-content: ${justifyContent};
|
|
21
28
|
direction: ${direction};
|
|
22
29
|
padding: var(--smileid-navigation-padding, ${hostPadding});
|
|
23
30
|
gap: 1rem;
|
|
@@ -135,23 +142,31 @@ button svg {
|
|
|
135
142
|
|
|
136
143
|
shadow.appendChild(style);
|
|
137
144
|
if (this.showBackButton) shadow.appendChild(backButton);
|
|
138
|
-
shadow.appendChild(closeButton);
|
|
145
|
+
if (this.showCloseButton) shadow.appendChild(closeButton);
|
|
139
146
|
|
|
140
147
|
// Set language direction attribute on host for CSS selectors
|
|
141
148
|
this.setAttribute('dir', direction);
|
|
142
149
|
|
|
143
150
|
// Back Button Controls
|
|
144
|
-
this.
|
|
145
|
-
|
|
151
|
+
if (this.showBackButton) {
|
|
152
|
+
this.backButton = backButton;
|
|
153
|
+
this.backButton.addEventListener('click', () => this.handleBack());
|
|
154
|
+
}
|
|
146
155
|
|
|
147
156
|
// Close Button Controls
|
|
148
|
-
this.
|
|
149
|
-
|
|
157
|
+
if (this.showCloseButton) {
|
|
158
|
+
this.closeButton = closeButton;
|
|
159
|
+
this.closeButton.addEventListener('click', () => this.handleClose());
|
|
160
|
+
}
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
disconnectedCallback() {
|
|
153
|
-
this.backButton
|
|
154
|
-
|
|
164
|
+
if (this.backButton) {
|
|
165
|
+
this.backButton.removeEventListener('click', () => this.handleBack());
|
|
166
|
+
}
|
|
167
|
+
if (this.closeButton) {
|
|
168
|
+
this.closeButton.removeEventListener('click', () => this.handleClose());
|
|
169
|
+
}
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
handleBack() {
|
|
@@ -166,6 +181,10 @@ button svg {
|
|
|
166
181
|
return !this.hasAttribute('hide-back');
|
|
167
182
|
}
|
|
168
183
|
|
|
184
|
+
get showCloseButton() {
|
|
185
|
+
return !this.hasAttribute('hide-close');
|
|
186
|
+
}
|
|
187
|
+
|
|
169
188
|
get themeColor() {
|
|
170
189
|
return this.getAttribute('theme-color') || '#001096';
|
|
171
190
|
}
|
|
@@ -44,6 +44,19 @@ Usage:
|
|
|
44
44
|
<selfie-capture-screens show-navigation></selfie-capture-screens>
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
#### use-strict-mode
|
|
48
|
+
|
|
49
|
+
This attribute enables Enhanced SmartSelfie (strict-mode capture flow).
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<selfie-capture-screens use-strict-mode="true"></selfie-capture-screens>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`allow-legacy-selfie-fallback` is also available as an optional compatibility
|
|
58
|
+
attribute.
|
|
59
|
+
|
|
47
60
|
### Permissions
|
|
48
61
|
|
|
49
62
|
The `SelfieCaptureScreens` component requires camera permissions to function. It will automatically request these permissions when used. If the permissions are granted, it will remove the `data-camera-error` attribute from the capture screen and set the `data-camera-ready` attribute to true. If the permissions are denied, it will remove the `data-camera-ready` attribute and set the `data-camera-error` attribute with the error message.
|
|
@@ -11,6 +11,15 @@ const COMPONENTS_VERSION = packageJson.version;
|
|
|
11
11
|
|
|
12
12
|
const smartCameraWeb = document.querySelector('smart-camera-web');
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Minimum-correct HTML attribute escape. `&` must be replaced first so the
|
|
16
|
+
* subsequent `"` -> `"` substitution isn't double-encoded. Used by every
|
|
17
|
+
* getter that interpolates a partner-supplied value into the `innerHTML`
|
|
18
|
+
* template in `connectedCallback` — without this, a value containing `"`
|
|
19
|
+
* (or `&`) would break out of the attribute and inject markup.
|
|
20
|
+
*/
|
|
21
|
+
const escAttr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"');
|
|
22
|
+
|
|
14
23
|
const cropImageFromDataUri = (dataUri, cropPercentX = 0, cropPercentY = 0) =>
|
|
15
24
|
new Promise((resolve, reject) => {
|
|
16
25
|
if (!dataUri || typeof dataUri !== 'string') {
|
|
@@ -83,9 +92,9 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
83
92
|
this.innerHTML = `
|
|
84
93
|
${styles(this.themeColor)}
|
|
85
94
|
<div style="height: 100%;">
|
|
86
|
-
<selfie-capture-instructions theme-color=
|
|
87
|
-
<selfie-capture-wrapper theme-color=
|
|
88
|
-
<selfie-capture-review theme-color=
|
|
95
|
+
<selfie-capture-instructions theme-color="${escAttr(this.themeColor)}" ${this.showNavigation} ${this.hideAttribution} ${this.hideBack} hidden></selfie-capture-instructions>
|
|
96
|
+
<selfie-capture-wrapper theme-color="${escAttr(this.themeColor)}" ${this.showNavigation} ${this.allowAgentMode} ${this.allowAgentModeTests} ${this.hideAttribution} ${this.disableImageTests} ${this.allowLegacySelfieFallback} ${this.useStrictMode} ${this.showBackOnGuidelines} key="${this._remountKey}" start-countdown="false" hidden></selfie-capture-wrapper>
|
|
97
|
+
<selfie-capture-review theme-color="${escAttr(this.themeColor)}" ${this.showNavigation} ${this.hideAttribution} hidden></selfie-capture-review>
|
|
89
98
|
</div>
|
|
90
99
|
`;
|
|
91
100
|
|
|
@@ -101,7 +110,11 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
101
110
|
|
|
102
111
|
if (
|
|
103
112
|
this.getAttribute('initial-screen') === 'selfie-capture' ||
|
|
104
|
-
this.hideInstructions
|
|
113
|
+
this.hideInstructions ||
|
|
114
|
+
// In strict mode the modern `enhanced-smartselfie-capture` element
|
|
115
|
+
// renders its own guidelines screen, so we skip the legacy
|
|
116
|
+
// `selfie-capture-instructions` element entirely.
|
|
117
|
+
this.isStrictMode
|
|
105
118
|
) {
|
|
106
119
|
this.setActiveScreen(this.selfieCapture);
|
|
107
120
|
} else {
|
|
@@ -346,7 +359,7 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
346
359
|
// Force remount of selfie-capture-wrapper for clean state on next visit
|
|
347
360
|
await this.forceWrapperRemount();
|
|
348
361
|
|
|
349
|
-
if (this.hideInstructions) {
|
|
362
|
+
if (this.hideInstructions || this.isStrictMode) {
|
|
350
363
|
this.handleBackEvents();
|
|
351
364
|
return;
|
|
352
365
|
}
|
|
@@ -367,6 +380,18 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
367
380
|
smartCameraWeb?.dispatchEvent(
|
|
368
381
|
new CustomEvent('metadata.selfie-capture-end'),
|
|
369
382
|
);
|
|
383
|
+
this._data.images = event.detail.images;
|
|
384
|
+
SmartCamera.stopMedia();
|
|
385
|
+
|
|
386
|
+
// In strict mode (Enhanced SmartSelfie), the ESS component already
|
|
387
|
+
// shows its own review screen and only re-dispatches `publish` after
|
|
388
|
+
// the user confirms. Skip the legacy `selfie-capture-review` step and
|
|
389
|
+
// publish straight up to the host page.
|
|
390
|
+
if (this.isStrictMode) {
|
|
391
|
+
this._publishSelectedImages();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
370
395
|
this.selfieReview.setAttribute(
|
|
371
396
|
'data-image',
|
|
372
397
|
await cropImageFromDataUri(event.detail.referenceImage, 20, 20),
|
|
@@ -376,8 +401,6 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
376
401
|
'mirror-image',
|
|
377
402
|
shouldMirror ? 'true' : 'false',
|
|
378
403
|
);
|
|
379
|
-
this._data.images = event.detail.images;
|
|
380
|
-
SmartCamera.stopMedia();
|
|
381
404
|
this.setActiveScreen(this.selfieReview);
|
|
382
405
|
};
|
|
383
406
|
|
|
@@ -450,10 +473,31 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
450
473
|
|
|
451
474
|
get allowLegacySelfieFallback() {
|
|
452
475
|
return this.hasAttribute('allow-legacy-selfie-fallback')
|
|
453
|
-
? `allow-legacy-selfie-fallback=
|
|
476
|
+
? `allow-legacy-selfie-fallback="${escAttr(this.getAttribute('allow-legacy-selfie-fallback'))}"`
|
|
454
477
|
: '';
|
|
455
478
|
}
|
|
456
479
|
|
|
480
|
+
get useStrictMode() {
|
|
481
|
+
return this.hasAttribute('use-strict-mode') &&
|
|
482
|
+
this.getAttribute('use-strict-mode') !== 'false'
|
|
483
|
+
? 'use-strict-mode="true"'
|
|
484
|
+
: '';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
get showBackOnGuidelines() {
|
|
488
|
+
return this.hasAttribute('show-back-on-guidelines')
|
|
489
|
+
? 'show-back-on-guidelines="true"'
|
|
490
|
+
: '';
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** Boolean form of `use-strict-mode` for runtime checks. */
|
|
494
|
+
get isStrictMode() {
|
|
495
|
+
return (
|
|
496
|
+
this.hasAttribute('use-strict-mode') &&
|
|
497
|
+
this.getAttribute('use-strict-mode') !== 'false'
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
457
501
|
get themeColor() {
|
|
458
502
|
return this.getAttribute('theme-color') || '#001096';
|
|
459
503
|
}
|
|
@@ -477,6 +521,8 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
477
521
|
'allow-legacy-selfie-fallback',
|
|
478
522
|
'show-agent-mode-for-tests',
|
|
479
523
|
'disable-image-tests',
|
|
524
|
+
'use-strict-mode',
|
|
525
|
+
'show-back-on-guidelines',
|
|
480
526
|
];
|
|
481
527
|
}
|
|
482
528
|
|
|
@@ -489,6 +535,8 @@ class SelfieCaptureScreens extends HTMLElement {
|
|
|
489
535
|
case 'allow-legacy-selfie-fallback':
|
|
490
536
|
case 'show-agent-mode-for-tests':
|
|
491
537
|
case 'disable-image-tests':
|
|
538
|
+
case 'use-strict-mode':
|
|
539
|
+
case 'show-back-on-guidelines':
|
|
492
540
|
this.connectedCallback();
|
|
493
541
|
break;
|
|
494
542
|
default:
|