@smileid/web-components 2.0.2 → 10.0.2

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.
Files changed (127) hide show
  1. package/dist/esm/DocumentCaptureScreens-ChpBVvNE.js +1532 -0
  2. package/dist/esm/DocumentCaptureScreens-ChpBVvNE.js.map +1 -0
  3. package/dist/esm/EndUserConsent-CMHp-34-.js +715 -0
  4. package/dist/esm/EndUserConsent-CMHp-34-.js.map +1 -0
  5. package/dist/esm/Navigation-juBE4qOw.js +136 -0
  6. package/dist/esm/Navigation-juBE4qOw.js.map +1 -0
  7. package/dist/esm/PoweredBySmileId-CxbaihMu.js +33 -0
  8. package/dist/esm/PoweredBySmileId-CxbaihMu.js.map +1 -0
  9. package/dist/esm/SelfieCaptureScreens-DMo-z-32.js +7618 -0
  10. package/dist/esm/SelfieCaptureScreens-DMo-z-32.js.map +1 -0
  11. package/dist/esm/SignaturePad-C7MtmT8m.js +324 -0
  12. package/dist/esm/SignaturePad-C7MtmT8m.js.map +1 -0
  13. package/dist/esm/TotpConsent-CQU5jQi4.js +730 -0
  14. package/dist/esm/TotpConsent-CQU5jQi4.js.map +1 -0
  15. package/dist/esm/combobox.js +300 -0
  16. package/dist/esm/combobox.js.map +1 -0
  17. package/dist/esm/document.js +5 -0
  18. package/dist/esm/document.js.map +1 -0
  19. package/dist/esm/end-user-consent.js +5 -0
  20. package/dist/esm/end-user-consent.js.map +1 -0
  21. package/dist/esm/main.js +22 -0
  22. package/dist/esm/main.js.map +1 -0
  23. package/dist/esm/navigation.js +5 -0
  24. package/dist/esm/navigation.js.map +1 -0
  25. package/dist/esm/package-D_4zJ9ck.js +105 -0
  26. package/dist/esm/package-D_4zJ9ck.js.map +1 -0
  27. package/dist/esm/selfie.js +5 -0
  28. package/dist/esm/selfie.js.map +1 -0
  29. package/dist/esm/signature-pad.js +5 -0
  30. package/dist/esm/signature-pad.js.map +1 -0
  31. package/dist/esm/smart-camera-web.js +302 -0
  32. package/dist/esm/smart-camera-web.js.map +1 -0
  33. package/dist/esm/styles-D2i3GFLK.js +405 -0
  34. package/dist/esm/styles-D2i3GFLK.js.map +1 -0
  35. package/dist/esm/totp-consent.js +5 -0
  36. package/dist/esm/totp-consent.js.map +1 -0
  37. package/dist/smart-camera-web.js +4130 -0
  38. package/dist/smart-camera-web.js.map +1 -0
  39. package/dist/types/combobox.d.ts +21 -0
  40. package/dist/types/document.d.ts +21 -0
  41. package/dist/types/end-user-consent.d.ts +21 -0
  42. package/dist/types/main.d.ts +331 -0
  43. package/dist/types/navigation.d.ts +21 -0
  44. package/dist/types/selfie.d.ts +21 -0
  45. package/dist/types/signature-pad.d.ts +21 -0
  46. package/dist/types/smart-camera-web.d.ts +21 -0
  47. package/dist/types/totp-consent.d.ts +21 -0
  48. package/{src → lib}/components/camera-permission/CameraPermission.js +0 -1
  49. package/{src → lib}/components/document/src/document-capture/DocumentCapture.js +0 -1
  50. package/{src → lib}/components/document/src/document-capture-review/DocumentCaptureReview.js +0 -1
  51. package/{src → lib}/components/selfie/src/SelfieCaptureScreens.js +188 -37
  52. package/{src → lib}/components/selfie/src/index.js +0 -2
  53. package/{src → lib}/components/selfie/src/selfie-capture/SelfieCapture.js +3 -4
  54. package/{src → lib}/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +0 -1
  55. package/{src → lib}/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +0 -8
  56. package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +226 -0
  57. package/lib/components/selfie/src/selfie-capture-wrapper/index.ts +1 -0
  58. package/lib/components/selfie/src/smartselfie-capture/OvalProgress.tsx +81 -0
  59. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +223 -0
  60. package/lib/components/selfie/src/smartselfie-capture/components/AlertDisplay.tsx +34 -0
  61. package/lib/components/selfie/src/smartselfie-capture/components/CameraPreview.tsx +97 -0
  62. package/lib/components/selfie/src/smartselfie-capture/components/CaptureControls.tsx +74 -0
  63. package/lib/components/selfie/src/smartselfie-capture/components/index.ts +3 -0
  64. package/lib/components/selfie/src/smartselfie-capture/constants.ts +23 -0
  65. package/lib/components/selfie/src/smartselfie-capture/hooks/index.ts +2 -0
  66. package/lib/components/selfie/src/smartselfie-capture/hooks/useCamera.ts +94 -0
  67. package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +558 -0
  68. package/lib/components/selfie/src/smartselfie-capture/index.ts +1 -0
  69. package/lib/components/selfie/src/smartselfie-capture/utils/alertMessages.ts +12 -0
  70. package/lib/components/selfie/src/smartselfie-capture/utils/canvas.ts +105 -0
  71. package/lib/components/selfie/src/smartselfie-capture/utils/faceDetection.ts +129 -0
  72. package/lib/components/selfie/src/smartselfie-capture/utils/imageCapture.ts +65 -0
  73. package/lib/components/selfie/src/smartselfie-capture/utils/index.ts +4 -0
  74. package/lib/components/selfie/src/smartselfie-capture/utils/mediapipeManager.ts +60 -0
  75. package/{src → lib}/components/signature-pad/package.json +1 -1
  76. package/{src → lib}/components/smart-camera-web/src/README.md +0 -1
  77. package/{src → lib}/styles/src/styles.js +0 -1
  78. package/package.json +76 -24
  79. package/src/index.js +0 -5
  80. /package/{src → lib}/components/README.md +0 -0
  81. /package/{src → lib}/components/attribution/PoweredBySmileId.js +0 -0
  82. /package/{src → lib}/components/camera-permission/CameraPermission.stories.js +0 -0
  83. /package/{src → lib}/components/combobox/src/Combobox.js +0 -0
  84. /package/{src → lib}/components/combobox/src/index.js +0 -0
  85. /package/{src → lib}/components/document/src/DocumentCaptureScreens.js +0 -0
  86. /package/{src → lib}/components/document/src/DocumentCaptureScreens.stories.js +0 -0
  87. /package/{src → lib}/components/document/src/README.md +0 -0
  88. /package/{src → lib}/components/document/src/document-capture/DocumentCapture.stories.js +0 -0
  89. /package/{src → lib}/components/document/src/document-capture/README.md +0 -0
  90. /package/{src → lib}/components/document/src/document-capture/index.js +0 -0
  91. /package/{src → lib}/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +0 -0
  92. /package/{src → lib}/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +0 -0
  93. /package/{src → lib}/components/document/src/document-capture-instructions/README.md +0 -0
  94. /package/{src → lib}/components/document/src/document-capture-instructions/index.js +0 -0
  95. /package/{src → lib}/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +0 -0
  96. /package/{src → lib}/components/document/src/document-capture-review/README.md +0 -0
  97. /package/{src → lib}/components/document/src/document-capture-review/index.js +0 -0
  98. /package/{src → lib}/components/document/src/index.js +0 -0
  99. /package/{src → lib}/components/end-user-consent/src/EndUserConsent.js +0 -0
  100. /package/{src → lib}/components/end-user-consent/src/EndUserConsent.stories.js +0 -0
  101. /package/{src → lib}/components/end-user-consent/src/index.js +0 -0
  102. /package/{src → lib}/components/navigation/src/Navigation.js +0 -0
  103. /package/{src → lib}/components/navigation/src/Navigation.stories.js +0 -0
  104. /package/{src → lib}/components/navigation/src/index.js +0 -0
  105. /package/{src → lib}/components/selfie/README.md +0 -0
  106. /package/{src → lib}/components/selfie/src/SelfieCaptureScreens.stories.js +0 -0
  107. /package/{src → lib}/components/selfie/src/selfie-capture/SelfieCapture.stories.js +0 -0
  108. /package/{src → lib}/components/selfie/src/selfie-capture/index.js +0 -0
  109. /package/{src → lib}/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +0 -0
  110. /package/{src → lib}/components/selfie/src/selfie-capture-instructions/index.js +0 -0
  111. /package/{src → lib}/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +0 -0
  112. /package/{src → lib}/components/selfie/src/selfie-capture-review/index.js +0 -0
  113. /package/{src → lib}/components/signature-pad/package-lock.json +0 -0
  114. /package/{src → lib}/components/signature-pad/src/SignaturePad.js +0 -0
  115. /package/{src → lib}/components/signature-pad/src/SignaturePad.stories.js +0 -0
  116. /package/{src → lib}/components/signature-pad/src/index.js +0 -0
  117. /package/{src → lib}/components/smart-camera-web/src/SmartCameraWeb.js +0 -0
  118. /package/{src → lib}/components/smart-camera-web/src/SmartCameraWeb.stories.js +0 -0
  119. /package/{src → lib}/components/totp-consent/src/TotpConsent.js +0 -0
  120. /package/{src → lib}/components/totp-consent/src/index.js +0 -0
  121. /package/{src → lib}/domain/camera/src/README.md +0 -0
  122. /package/{src → lib}/domain/camera/src/SmartCamera.js +0 -0
  123. /package/{src → lib}/domain/constants/src/Constants.js +0 -0
  124. /package/{src → lib}/domain/file-upload/README.md +0 -0
  125. /package/{src → lib}/domain/file-upload/src/SmartFileUpload.js +0 -0
  126. /package/{src → lib}/styles/README.md +0 -0
  127. /package/{src → lib}/styles/src/typography.js +0 -0
@@ -1,6 +1,6 @@
1
- import './selfie-capture';
2
1
  import './selfie-capture-instructions';
3
2
  import './selfie-capture-review';
3
+ import './selfie-capture-wrapper/index.ts';
4
4
  import SmartCamera from '../../../domain/camera/src/SmartCamera';
5
5
  import styles from '../../../styles/src/styles';
6
6
  import packageJson from '../../../../package.json';
@@ -37,10 +37,76 @@ async function getPermissions(captureScreen, facingMode = 'user') {
37
37
  }
38
38
  }
39
39
 
40
+ const cropImageFromDataUri = (
41
+ dataUri,
42
+ cropPercentX = 0,
43
+ cropPercentY = 0,
44
+ quality = 0.9,
45
+ ) =>
46
+ new Promise((resolve, reject) => {
47
+ if (!dataUri || typeof dataUri !== 'string') {
48
+ reject(new Error('Invalid data URI provided'));
49
+ return;
50
+ }
51
+
52
+ if (
53
+ cropPercentX < 0 ||
54
+ cropPercentX >= 99 ||
55
+ cropPercentY < 0 ||
56
+ cropPercentY >= 99
57
+ ) {
58
+ reject(new Error('Crop percentages must be between 0 and 99'));
59
+ return;
60
+ }
61
+
62
+ const img = new Image();
63
+
64
+ img.onload = () => {
65
+ try {
66
+ const canvas = document.createElement('canvas');
67
+ const ctx = canvas.getContext('2d');
68
+
69
+ const originalWidth = img.width;
70
+ const originalHeight = img.height;
71
+ const cropAmountX = (originalWidth * cropPercentX) / 100;
72
+ const cropAmountY = (originalHeight * cropPercentY) / 100;
73
+
74
+ const newWidth = originalWidth - cropAmountX * 2;
75
+ const newHeight = originalHeight - cropAmountY * 2;
76
+ canvas.width = newWidth;
77
+ canvas.height = newHeight;
78
+
79
+ ctx.drawImage(
80
+ img,
81
+ cropAmountX,
82
+ cropAmountY,
83
+ newWidth,
84
+ newHeight,
85
+ 0,
86
+ 0,
87
+ newWidth,
88
+ newHeight,
89
+ );
90
+
91
+ const croppedDataUri = canvas.toDataURL('image/jpeg', quality);
92
+ resolve(croppedDataUri);
93
+ } catch (error) {
94
+ reject(new Error(`Failed to process image: ${error.message}`));
95
+ }
96
+ };
97
+
98
+ img.onerror = () => {
99
+ reject(new Error('Failed to load image from data URI'));
100
+ };
101
+
102
+ img.src = dataUri;
103
+ });
104
+
40
105
  class SelfieCaptureScreens extends HTMLElement {
41
106
  constructor() {
42
107
  super();
43
108
  this.activeScreen = null;
109
+ this._remountKey = 0; // Counter for forcing wrapper remounts
44
110
  smartCameraWeb?.dispatchEvent(new CustomEvent('metadata.initialize'));
45
111
  }
46
112
 
@@ -49,7 +115,7 @@ class SelfieCaptureScreens extends HTMLElement {
49
115
  ${styles(this.themeColor)}
50
116
  <div>
51
117
  <selfie-capture-instructions theme-color='${this.themeColor}' ${this.showNavigation} ${this.hideAttribution} ${this.hideBack} hidden></selfie-capture-instructions>
52
- <selfie-capture theme-color='${this.themeColor}' ${this.showNavigation} ${this.allowAgentMode} ${this.allowAgentModeTests} ${this.hideAttribution} ${this.disableImageTests} hidden></selfie-capture>
118
+ <selfie-capture-wrapper theme-color='${this.themeColor}' ${this.showNavigation} ${this.allowAgentMode} ${this.allowAgentModeTests} ${this.hideAttribution} ${this.disableImageTests} key="${this._remountKey}" start-countdown="false" hidden></selfie-capture-wrapper>
53
119
  <selfie-capture-review theme-color='${this.themeColor}' ${this.showNavigation} ${this.hideAttribution} hidden></selfie-capture-review>
54
120
  </div>
55
121
  `;
@@ -60,9 +126,8 @@ class SelfieCaptureScreens extends HTMLElement {
60
126
  libraryVersion: COMPONENTS_VERSION,
61
127
  },
62
128
  };
63
-
64
129
  this.selfieInstruction = this.querySelector('selfie-capture-instructions');
65
- this.selfieCapture = this.querySelector('selfie-capture');
130
+ this.selfieCapture = this.querySelector('selfie-capture-wrapper');
66
131
  this.selfieReview = this.querySelector('selfie-capture-review');
67
132
 
68
133
  if (this.hideInstructions && !this.hasAttribute('hidden')) {
@@ -124,27 +189,8 @@ class SelfieCaptureScreens extends HTMLElement {
124
189
  this.handleBackEvents();
125
190
  },
126
191
  );
127
-
128
- this.selfieCapture.addEventListener('selfie-capture.cancelled', () => {
129
- this.selfieCapture.reset();
130
- SmartCamera.stopMedia();
131
- if (this.hideInstructions) {
132
- this.handleBackEvents();
133
- return;
134
- }
135
-
136
- this.setActiveScreen(this.selfieInstruction);
137
- });
138
-
139
- this.selfieCapture.addEventListener('selfie-capture.publish', (event) => {
140
- smartCameraWeb?.dispatchEvent(
141
- new CustomEvent('metadata.selfie-capture-end'),
142
- );
143
- this.selfieReview.setAttribute('data-image', event.detail.referenceImage);
144
- this._data.images = event.detail.images;
145
- SmartCamera.stopMedia();
146
- this.setActiveScreen(this.selfieReview);
147
- });
192
+ // Setup selfie-wrapper event listeners
193
+ this.setupSelfieWrapperEventListeners();
148
194
 
149
195
  this.selfieReview.addEventListener(
150
196
  'selfie-capture-review.rejected',
@@ -154,6 +200,10 @@ class SelfieCaptureScreens extends HTMLElement {
154
200
  );
155
201
  this.selfieReview.removeAttribute('data-image');
156
202
  this._data.images = [];
203
+
204
+ // Force remount by incrementing key
205
+ this.forceWrapperRemount();
206
+
157
207
  if (this.hideInstructions) {
158
208
  this.setActiveScreen(this.selfieCapture);
159
209
  await getPermissions(this.selfieCapture, this.getAgentMode());
@@ -182,6 +232,113 @@ class SelfieCaptureScreens extends HTMLElement {
182
232
  );
183
233
  }
184
234
 
235
+ // Force remount of selfie-capture-wrapper component for clean state
236
+ forceWrapperRemount() {
237
+ this._remountKey++;
238
+ const container = this.querySelector('div');
239
+ const oldSelfieCapture = this.selfieCapture;
240
+
241
+ // Create new selfie-capture-wrapper element with same attributes
242
+ const newSelfieCapture = document.createElement('selfie-capture-wrapper');
243
+ newSelfieCapture.setAttribute('theme-color', this.themeColor);
244
+ newSelfieCapture.setAttribute('key', this._remountKey.toString());
245
+ newSelfieCapture.setAttribute('start-countdown', 'false');
246
+
247
+ // Copy all the correct attributes based on the getter logic
248
+ const showNav = this.hasAttribute('show-navigation');
249
+ const allowAgent = this.getAttribute('allow-agent-mode') === 'true';
250
+ const allowAgentTests = this.hasAttribute('show-agent-mode-for-tests');
251
+ const hideAttr = this.hasAttribute('hide-attribution');
252
+ const disableTests = this.hasAttribute('disable-image-tests');
253
+
254
+ if (showNav) newSelfieCapture.setAttribute('show-navigation', '');
255
+ if (allowAgent) newSelfieCapture.setAttribute('allow-agent-mode', 'true');
256
+ if (allowAgentTests)
257
+ newSelfieCapture.setAttribute('show-agent-mode-for-tests', '');
258
+ if (hideAttr) newSelfieCapture.setAttribute('hide-attribution', '');
259
+ if (disableTests) newSelfieCapture.setAttribute('disable-image-tests', '');
260
+ newSelfieCapture.setAttribute('hidden', '');
261
+
262
+ // Replace old element with new one
263
+ container.replaceChild(newSelfieCapture, oldSelfieCapture);
264
+
265
+ // Update reference
266
+ this.selfieCapture = newSelfieCapture;
267
+
268
+ // Re-add event listeners for the new element
269
+ this.setupSelfieWrapperEventListeners();
270
+ }
271
+
272
+ // Override setActiveScreen to enable countdown when selfie-capture is active
273
+ setActiveScreen(screen) {
274
+ this.activeScreen?.setAttribute('hidden', '');
275
+ screen.removeAttribute('hidden');
276
+ this.activeScreen = screen;
277
+
278
+ // If activating selfie-capture-wrapper, enable the countdown and ensure permissions
279
+ if (screen === this.selfieCapture) {
280
+ screen.setAttribute('start-countdown', 'true');
281
+ getPermissions(this.selfieCapture, this.getAgentMode());
282
+ } else if (this.selfieCapture) {
283
+ // Disable countdown when not on capture screen
284
+ this.selfieCapture.setAttribute('start-countdown', 'false');
285
+ }
286
+ }
287
+
288
+ setupSelfieWrapperEventListeners() {
289
+ window.addEventListener('selfie-capture.cancelled', () => {
290
+ SmartCamera.stopMedia();
291
+
292
+ // Force remount of selfie-capture-wrapper for clean state on next visit
293
+ this.forceWrapperRemount();
294
+
295
+ if (this.hideInstructions) {
296
+ this.handleBackEvents();
297
+ return;
298
+ }
299
+
300
+ this.setActiveScreen(this.selfieInstruction);
301
+ });
302
+
303
+ // Add specific close event handler for selfie-capture-wrapper
304
+ window.addEventListener('selfie-capture.close', () => {
305
+ SmartCamera.stopMedia();
306
+
307
+ // Force remount of selfie-capture-wrapper for clean state on next visit
308
+ this.forceWrapperRemount();
309
+
310
+ this.handleCloseEvent();
311
+ });
312
+
313
+ window.addEventListener('selfie-capture.publish', async (event) => {
314
+ smartCameraWeb?.dispatchEvent(
315
+ new CustomEvent('metadata.selfie-capture-end'),
316
+ );
317
+ this.selfieReview.setAttribute(
318
+ 'data-image',
319
+ await cropImageFromDataUri(event.detail.referenceImage, 20, 20),
320
+ );
321
+ this._data.images = event.detail.images;
322
+ SmartCamera.stopMedia();
323
+ this.setActiveScreen(this.selfieReview);
324
+ });
325
+
326
+ // Also listen for the publish event on the parent SelfieCaptureScreens element
327
+ // in case smartselfie-capture dispatches it there
328
+ this.addEventListener('selfie-capture.publish', async (event) => {
329
+ smartCameraWeb?.dispatchEvent(
330
+ new CustomEvent('metadata.selfie-capture-end'),
331
+ );
332
+ this.selfieReview.setAttribute(
333
+ 'data-image',
334
+ await cropImageFromDataUri(event.detail.referenceImage, 20, 20),
335
+ );
336
+ this._data.images = event.detail.images;
337
+ SmartCamera.stopMedia();
338
+ this.setActiveScreen(this.selfieReview);
339
+ });
340
+ }
341
+
185
342
  _publishSelectedImages() {
186
343
  this.dispatchEvent(
187
344
  new CustomEvent('selfie-capture-screens.publish', { detail: this._data }),
@@ -193,7 +350,7 @@ class SelfieCaptureScreens extends HTMLElement {
193
350
  }
194
351
 
195
352
  get hideAttribution() {
196
- return this.hasAttribute('hide-attribution') ? 'hide-attribution' : '';
353
+ return this.hasAttribute('hide-attribution') ? 'hide-attribution=""' : '';
197
354
  }
198
355
 
199
356
  get hideBackOfId() {
@@ -201,7 +358,7 @@ class SelfieCaptureScreens extends HTMLElement {
201
358
  }
202
359
 
203
360
  get showNavigation() {
204
- return this.hasAttribute('show-navigation') ? 'show-navigation' : '';
361
+ return this.hasAttribute('show-navigation') ? 'show-navigation=""' : '';
205
362
  }
206
363
 
207
364
  get inAgentMode() {
@@ -209,25 +366,25 @@ class SelfieCaptureScreens extends HTMLElement {
209
366
  }
210
367
 
211
368
  get allowAgentMode() {
212
- return this.inAgentMode ? "allow-agent-mode='true'" : '';
369
+ return this.inAgentMode ? 'allow-agent-mode="true"' : '';
213
370
  }
214
371
 
215
372
  get allowAgentModeTests() {
216
373
  return this.hasAttribute('show-agent-mode-for-tests')
217
- ? 'show-agent-mode-for-tests'
374
+ ? 'show-agent-mode-for-tests=""'
218
375
  : '';
219
376
  }
220
377
 
221
378
  get hideBack() {
222
379
  return this.hasAttribute('hide-back-to-host') ||
223
380
  this.hasAttribute('hide-back')
224
- ? 'hide-back'
381
+ ? 'hide-back=""'
225
382
  : '';
226
383
  }
227
384
 
228
385
  get disableImageTests() {
229
386
  return this.hasAttribute('disable-image-tests')
230
- ? 'disable-image-tests'
387
+ ? 'disable-image-tests=""'
231
388
  : '';
232
389
  }
233
390
 
@@ -235,12 +392,6 @@ class SelfieCaptureScreens extends HTMLElement {
235
392
  return this.getAttribute('theme-color') || '#001096';
236
393
  }
237
394
 
238
- setActiveScreen(screen) {
239
- this.activeScreen?.setAttribute('hidden', '');
240
- screen.removeAttribute('hidden');
241
- this.activeScreen = screen;
242
- }
243
-
244
395
  handleBackEvents() {
245
396
  this.dispatchEvent(new CustomEvent('selfie-capture-screens.cancelled'));
246
397
  }
@@ -1,5 +1,3 @@
1
1
  import SelfieCaptureScreens from './SelfieCaptureScreens';
2
- import SelfieCapture from './selfie-capture/SelfieCapture';
3
2
 
4
3
  export default SelfieCaptureScreens;
5
- export { SelfieCapture };
@@ -358,7 +358,7 @@ function templateString() {
358
358
  left: 50%;
359
359
  min-width: auto;
360
360
  position: absolute;
361
- top: calc(50% - 3px);
361
+ top: 50%;
362
362
  transform: translateX(-50%) translateY(50%);
363
363
  }
364
364
 
@@ -381,7 +381,7 @@ function templateString() {
381
381
  }
382
382
 
383
383
  .video-container video {
384
- min-height: 100%;
384
+ height: 150%;
385
385
  transform: scaleX(-1) translateX(50%) translateY(-50%);
386
386
  }
387
387
 
@@ -468,7 +468,6 @@ function templateString() {
468
468
  #selfie-capture-screen,
469
469
  #back-of-id-entry-screen {
470
470
  block-size: 45rem;
471
- padding-block: 2rem;
472
471
  display: flex;
473
472
  flex-direction: column;
474
473
  max-block-size: 100%;
@@ -520,7 +519,7 @@ function templateString() {
520
519
  <div class='video-container'>
521
520
  <div class='video'>
522
521
  </div>
523
- <svg id="image-outline" width="215" height="245" viewBox="0 0 215 245" fill="none" xmlns="http://www.w3.org/2000/svg">
522
+ <svg id="image-outline" width="215" height="245" viewBox="0 0 215 245" fill="none" xmlns="http://www.w3.org/2000/svg" style="position: relative;">
524
523
  <path d="M210.981 122.838C210.981 188.699 164.248 241.268 107.55 241.268C50.853 241.268 4.12018 188.699 4.12018 122.838C4.12018 56.9763 50.853 4.40771 107.55 4.40771C164.248 4.40771 210.981 56.9763 210.981 122.838Z" stroke="${this.themeColor}" stroke-width="7.13965"/>
525
524
  </svg>
526
525
  <div id="smile-cta-box">
@@ -260,7 +260,6 @@ function templateString() {
260
260
  }
261
261
 
262
262
  #selfie-capture-instruction-screen {
263
- padding-block: 2rem;
264
263
  display: flex;
265
264
  flex-direction: column;
266
265
  max-block-size: 100%;
@@ -94,7 +94,6 @@ function templateString() {
94
94
  }
95
95
  #selfie-capture-review-screen {
96
96
  block-size: 45rem;
97
- padding-block: 2rem;
98
97
  display: flex;
99
98
  flex-direction: column;
100
99
  max-block-size: 100%;
@@ -126,13 +125,6 @@ function templateString() {
126
125
  z-index: 1;
127
126
  block-size: 100%;
128
127
  }
129
-
130
- .selfie-container,
131
- .selfie-review-container.landscape {
132
- height: auto;
133
- clip-path: inset(15% 20% 20% 20% round 10px);
134
- overflow: hidden;
135
- }
136
128
 
137
129
  .selfie-container img {
138
130
  background-color: black;
@@ -0,0 +1,226 @@
1
+ import { useState, useEffect } from 'preact/hooks';
2
+ import { IconLoader2 } from '@tabler/icons-preact';
3
+ import register from 'preact-custom-element';
4
+ import type { FunctionComponent } from 'preact';
5
+
6
+ import { getBoolProp } from '@/utils/props';
7
+ import SmartSelfieCapture from '../smartselfie-capture/SmartSelfieCapture';
8
+ import '../selfie-capture/SelfieCapture';
9
+ import { getMediapipeInstance } from '../smartselfie-capture/utils/mediapipeManager';
10
+
11
+ interface Props {
12
+ timeout?: number;
13
+ interval?: number;
14
+ duration?: number;
15
+ 'theme-color'?: string;
16
+ 'show-navigation'?: string | boolean;
17
+ 'allow-agent-mode'?: string | boolean;
18
+ 'show-agent-mode-for-tests'?: string | boolean;
19
+ 'hide-attribution'?: string | boolean;
20
+ 'disable-image-tests'?: string | boolean;
21
+ key?: string;
22
+ 'start-countdown'?: string | boolean;
23
+ hidden?: string | boolean;
24
+ }
25
+
26
+ const SelfieCaptureWrapper: FunctionComponent<Props> = ({
27
+ timeout = 20000,
28
+ 'start-countdown': startCountdownProp = false,
29
+ hidden: hiddenProp = false,
30
+ ...props
31
+ }) => {
32
+ const hidden = getBoolProp(hiddenProp);
33
+ const startCountdown = getBoolProp(startCountdownProp);
34
+ const [mediapipeReady, setMediapipeReady] = useState(false);
35
+ const [loadingProgress, setLoadingProgress] = useState(0);
36
+ const [initialSessionCompleted, setInitialSessionCompleted] = useState(false);
37
+ const [mediapipeLoading, setMediapipeLoading] = useState(false);
38
+ const [usingSelfieCapture, setUsingSelfieCapture] = useState(false);
39
+
40
+ useEffect(() => {
41
+ if (mediapipeReady || mediapipeLoading) return;
42
+
43
+ const loadMediapipe = async () => {
44
+ setMediapipeLoading(true);
45
+ try {
46
+ await getMediapipeInstance();
47
+ setMediapipeReady(true);
48
+ } catch (error) {
49
+ console.error('Failed to load Mediapipe:', error);
50
+ }
51
+ setMediapipeLoading(false);
52
+ };
53
+
54
+ loadMediapipe();
55
+ }, [mediapipeReady, mediapipeLoading]);
56
+
57
+ useEffect(() => {
58
+ if (hidden || !startCountdown || mediapipeReady) return undefined;
59
+
60
+ const timer = setInterval(() => {
61
+ setLoadingProgress((prev: number) => Math.min(prev + 1, 100));
62
+ }, timeout / 100);
63
+
64
+ return () => {
65
+ clearInterval(timer);
66
+ };
67
+ }, [hidden, startCountdown, timeout, mediapipeReady]);
68
+
69
+ useEffect(() => {
70
+ if (hidden || mediapipeReady || loadingProgress < 100) return undefined;
71
+
72
+ const setupEventForwarding = () => {
73
+ const selfieCapture = document.querySelector('selfie-capture');
74
+ if (
75
+ selfieCapture &&
76
+ !selfieCapture.hasAttribute('data-backup-events-setup')
77
+ ) {
78
+ selfieCapture.setAttribute('data-backup-events-setup', 'true');
79
+
80
+ const forwardEvent = (event: Event) => {
81
+ const customEvent = event as CustomEvent;
82
+ window.dispatchEvent(
83
+ new CustomEvent(customEvent.type, {
84
+ detail: customEvent.detail,
85
+ bubbles: true,
86
+ }),
87
+ );
88
+ };
89
+
90
+ selfieCapture.addEventListener('selfie-capture.publish', forwardEvent);
91
+ selfieCapture.addEventListener(
92
+ 'selfie-capture.cancelled',
93
+ forwardEvent,
94
+ );
95
+ selfieCapture.addEventListener('selfie-capture.close', forwardEvent);
96
+
97
+ return () => {
98
+ selfieCapture.removeEventListener(
99
+ 'selfie-capture.publish',
100
+ forwardEvent,
101
+ );
102
+ selfieCapture.removeEventListener(
103
+ 'selfie-capture.cancelled',
104
+ forwardEvent,
105
+ );
106
+ selfieCapture.removeEventListener(
107
+ 'selfie-capture.close',
108
+ forwardEvent,
109
+ );
110
+ };
111
+ }
112
+ return undefined;
113
+ };
114
+
115
+ const timeoutId = setTimeout(setupEventForwarding, 200);
116
+ return () => {
117
+ clearTimeout(timeoutId);
118
+ };
119
+ }, [hidden, mediapipeReady, loadingProgress]);
120
+
121
+ if (hidden) {
122
+ return null;
123
+ }
124
+
125
+ // on retakes, prefer SmartSelfieCapture if Mediapipe is ready
126
+ if (initialSessionCompleted && mediapipeReady && !usingSelfieCapture) {
127
+ return <SmartSelfieCapture {...props} />;
128
+ }
129
+
130
+ // use SmartSelfieCapture if mediapipe loads
131
+ if (!initialSessionCompleted && mediapipeReady && !usingSelfieCapture) {
132
+ return <SmartSelfieCapture {...props} />;
133
+ }
134
+
135
+ if (loadingProgress >= 100) {
136
+ if (!usingSelfieCapture) {
137
+ setUsingSelfieCapture(true);
138
+ }
139
+
140
+ const propsWithoutHidden = { ...props };
141
+ delete (propsWithoutHidden as any).hidden;
142
+
143
+ return (
144
+ // @ts-ignore
145
+ <selfie-capture
146
+ {...propsWithoutHidden}
147
+ ref={(el: HTMLElement) => {
148
+ if (el && !el.hasAttribute('data-events-setup')) {
149
+ el.setAttribute('data-events-setup', 'true');
150
+
151
+ const forwardEvent = (event: Event) => {
152
+ const customEvent = event as CustomEvent;
153
+
154
+ if (
155
+ customEvent.type === 'selfie-capture.publish' ||
156
+ customEvent.type === 'selfie-capture.cancelled' ||
157
+ customEvent.type === 'selfie-capture.close'
158
+ ) {
159
+ setInitialSessionCompleted(true);
160
+ }
161
+
162
+ window.dispatchEvent(
163
+ new CustomEvent(customEvent.type, {
164
+ detail: customEvent.detail,
165
+ bubbles: true,
166
+ }),
167
+ );
168
+ };
169
+
170
+ el.addEventListener('selfie-capture.publish', forwardEvent);
171
+ el.addEventListener('selfie-capture.cancelled', forwardEvent);
172
+ el.addEventListener('selfie-capture.close', forwardEvent);
173
+ }
174
+ }}
175
+ />
176
+ );
177
+ }
178
+
179
+ return (
180
+ <div style={{ textAlign: 'center', marginTop: '20%' }}>
181
+ <div
182
+ style={{
183
+ marginBottom: '16px',
184
+ display: 'flex',
185
+ justifyContent: 'center',
186
+ }}
187
+ >
188
+ <IconLoader2
189
+ size={48}
190
+ style={{ animation: 'spin 1s linear infinite' }}
191
+ />
192
+ </div>
193
+ <p>Loading... {loadingProgress}%</p>
194
+ <style>{`
195
+ @keyframes spin {
196
+ from { transform: rotate(0deg); }
197
+ to { transform: rotate(360deg); }
198
+ }
199
+ `}</style>
200
+ </div>
201
+ );
202
+ };
203
+
204
+ if (!customElements.get('selfie-capture-wrapper')) {
205
+ register(
206
+ SelfieCaptureWrapper,
207
+ 'selfie-capture-wrapper',
208
+ [
209
+ 'timeout',
210
+ 'interval',
211
+ 'duration',
212
+ 'theme-color',
213
+ 'show-navigation',
214
+ 'allow-agent-mode',
215
+ 'show-agent-mode-for-tests',
216
+ 'hide-attribution',
217
+ 'disable-image-tests',
218
+ 'key',
219
+ 'start-countdown',
220
+ 'hidden',
221
+ ],
222
+ { shadow: true },
223
+ );
224
+ }
225
+
226
+ export default SelfieCaptureWrapper;
@@ -0,0 +1 @@
1
+ export { default as SelfieCaptureWrapper } from './SelfieCaptureWrapper';