@smileid/web-components 10.0.2 → 10.0.5

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 (45) hide show
  1. package/dist/esm/{DocumentCaptureScreens-ChpBVvNE.js → DocumentCaptureScreens-BIJUlWLB.js} +98 -56
  2. package/dist/esm/DocumentCaptureScreens-BIJUlWLB.js.map +1 -0
  3. package/dist/esm/{EndUserConsent-CMHp-34-.js → EndUserConsent-D4fd1ovG.js} +2 -2
  4. package/dist/esm/{EndUserConsent-CMHp-34-.js.map → EndUserConsent-D4fd1ovG.js.map} +1 -1
  5. package/dist/esm/{Navigation-juBE4qOw.js → Navigation-CTjK6tLU.js} +6 -6
  6. package/dist/esm/{Navigation-juBE4qOw.js.map → Navigation-CTjK6tLU.js.map} +1 -1
  7. package/dist/esm/SelfieCaptureScreens-CnMaKUmP.js +11361 -0
  8. package/dist/esm/SelfieCaptureScreens-CnMaKUmP.js.map +1 -0
  9. package/dist/esm/document.js +1 -1
  10. package/dist/esm/end-user-consent.js +1 -1
  11. package/dist/esm/main.js +4 -4
  12. package/dist/esm/navigation.js +1 -1
  13. package/dist/esm/{package-D_4zJ9ck.js → package-PZvRbm5J.js} +2 -2
  14. package/dist/esm/{package-D_4zJ9ck.js.map → package-PZvRbm5J.js.map} +1 -1
  15. package/dist/esm/selfie.js +1 -1
  16. package/dist/esm/smart-camera-web.js +12 -6
  17. package/dist/esm/smart-camera-web.js.map +1 -1
  18. package/dist/esm/{styles-D2i3GFLK.js → styles-BOEZtbuc.js} +19 -5
  19. package/dist/esm/{styles-D2i3GFLK.js.map → styles-BOEZtbuc.js.map} +1 -1
  20. package/dist/smart-camera-web.js +278 -289
  21. package/dist/smart-camera-web.js.map +1 -1
  22. package/lib/components/document/src/DocumentCaptureScreens.js +1 -1
  23. package/lib/components/document/src/document-capture/DocumentCapture.js +2 -1
  24. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +54 -8
  25. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +2 -3
  26. package/lib/components/navigation/src/Navigation.js +5 -5
  27. package/lib/components/selfie/src/SelfieCaptureScreens.js +116 -118
  28. package/lib/components/selfie/src/selfie-capture/SelfieCapture.js +54 -10
  29. package/lib/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +91 -58
  30. package/lib/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +23 -153
  31. package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +13 -0
  32. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +66 -6
  33. package/lib/components/selfie/src/smartselfie-capture/components/CaptureControls.tsx +2 -0
  34. package/lib/components/selfie/src/smartselfie-capture/hooks/useCamera.ts +165 -21
  35. package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +82 -23
  36. package/lib/components/selfie/src/smartselfie-capture/utils/alertMessages.ts +2 -1
  37. package/lib/components/selfie/src/smartselfie-capture/utils/imageCapture.ts +2 -3
  38. package/lib/components/selfie/src/smartselfie-capture/utils/mediapipeManager.ts +18 -1
  39. package/lib/components/signature-pad/package.json +1 -1
  40. package/lib/components/smart-camera-web/src/SmartCameraWeb.js +7 -1
  41. package/lib/styles/src/styles.js +18 -4
  42. package/package.json +3 -1
  43. package/dist/esm/DocumentCaptureScreens-ChpBVvNE.js.map +0 -1
  44. package/dist/esm/SelfieCaptureScreens-DMo-z-32.js +0 -7618
  45. package/dist/esm/SelfieCaptureScreens-DMo-z-32.js.map +0 -1
@@ -50,7 +50,7 @@ class DocumentCaptureScreens extends HTMLElement {
50
50
  connectedCallback() {
51
51
  this.innerHTML = `
52
52
  ${styles(this.themeColor)}
53
- <div>
53
+ <div style="height: 100%;">
54
54
  <document-capture-instructions theme-color='${this.themeColor}' id='document-capture-instructions-front' ${this.title}
55
55
  ${this.documentCaptureModes} ${this.showNavigation} ${this.hideInstructions ? 'hidden' : ''}
56
56
  ${this.hideAttribution}
@@ -60,11 +60,11 @@ function templateString() {
60
60
 
61
61
  #document-capture-screen,
62
62
  #back-of-document-capture-screen {
63
- block-size: 45rem;
64
63
  display: flex;
65
64
  flex-direction: column;
66
65
  max-block-size: 100%;
67
66
  max-inline-size: 40ch;
67
+ padding: 1rem;
68
68
  }
69
69
 
70
70
  #document-capture-screen header p {
@@ -121,6 +121,7 @@ function templateString() {
121
121
  inset: -1px;
122
122
  }
123
123
  canvas {
124
+ width: 100%;
124
125
  border-width: 0.25rem;
125
126
  border-color: #9394ab;
126
127
  border-style: solid;
@@ -155,12 +155,51 @@ function backDocumentIcon() {
155
155
 
156
156
  function templateString() {
157
157
  return `
158
+ <style>
159
+ h1 {
160
+ font-size: 1.25rem;
161
+ }
162
+
163
+ .controls {
164
+ width: 100%;
165
+ margin-top: 1rem;
166
+ display: flex;
167
+ flex-direction: column;
168
+ gap: 0.5rem;
169
+ }
170
+
171
+ .content-root {
172
+ height: 100%;
173
+ display: flex;
174
+ align-items: center;
175
+ flex-direction: column;
176
+ }
177
+
178
+ .content-header, .content-body, .content-footer {
179
+ width: 100%;
180
+ }
181
+
182
+ .content-body {
183
+ height: 100%;
184
+ display: flex;
185
+ flex-direction: column;
186
+ align-items: center;
187
+ justify-content: space-between;
188
+ }
189
+
190
+ .content-body header {
191
+ margin-top: 1rem;
192
+ }
193
+ </style>
158
194
  <div id="document-capture-instructions-screen" class="flow center">
159
- <section className="main">
195
+ <div class="content-root">
196
+ <div class="content-header">
160
197
  <smileid-navigation theme-color='${this.themeColor}' ${this.showNavigation ? 'show-navigation' : ''} ${this.hideBack ? 'hide-back' : ''}></smileid-navigation>
198
+ </div>
199
+ <div class="content-body">
161
200
  <header>
162
201
  ${this.isFrontOfId ? frontDocumentIcon() : backDocumentIcon()}
163
- <h1 class='text-2xl title-color font-bold'>${this.title}</h1>
202
+ <h1 class='title-color font-bold'>${this.title}</h1>
164
203
  <p class="description text-sm font-normal">
165
204
  We'll use it to verify your identity.
166
205
  </p>
@@ -168,7 +207,7 @@ function templateString() {
168
207
  Please follow the instructions below.
169
208
  </p>
170
209
  </header>
171
- <div class="flow instructions-wrapper">
210
+ <div class="instructions-wrapper">
172
211
  <div class="instructions">
173
212
  <svg
174
213
  xmlns="http://www.w3.org/2000/svg"
@@ -342,9 +381,7 @@ function templateString() {
342
381
  <div id="error" class='color-red'>
343
382
  </div>
344
383
  </div>
345
- </section>
346
- <section className="footer">
347
- <div class='flow'>
384
+ <div class='controls'>
348
385
  ${
349
386
  this.supportBothCaptureModes || this.documentCaptureModes === 'camera'
350
387
  ? `
@@ -367,9 +404,18 @@ function templateString() {
367
404
  : ''
368
405
  }
369
406
  </div>
370
- ${this.hideAttribution ? '' : '<powered-by-smile-id></powered-by-smile-id>'}
371
- </section>
407
+ ${
408
+ this.hideAttribution
409
+ ? ''
410
+ : `
411
+ <div class="content-footer">
412
+ <powered-by-smile-id></powered-by-smile-id>
372
413
  </div>
414
+ `
415
+ }
416
+ </div>
417
+ </div>
418
+ </div>
373
419
  ${styles(this.themeColor)}
374
420
  `;
375
421
  }
@@ -17,7 +17,6 @@ function templateString() {
17
17
 
18
18
  .section {
19
19
  width: 100%;
20
- height: 100vh;
21
20
  justify-content: center;
22
21
  }
23
22
  }
@@ -125,11 +124,11 @@ function templateString() {
125
124
  }
126
125
 
127
126
  #document-capture-review-screen {
128
- block-size: 45rem;
129
127
  display: flex;
130
128
  flex-direction: column;
131
129
  max-block-size: 100%;
132
130
  max-inline-size: 40ch;
131
+ padding: 1rem;
133
132
  }
134
133
 
135
134
  #document-capture-review-screen .id-image-container.landscape {
@@ -194,7 +193,7 @@ function templateString() {
194
193
  text-align: center;
195
194
 
196
195
  /* h1 */
197
- font-size: 1.5rem;
196
+ font-size: 1.25rem;
198
197
  font-style: normal;
199
198
  font-weight: 700;
200
199
  line-height: 36px; /* 150% */
@@ -23,7 +23,7 @@ button {
23
23
  color: #ffffff;
24
24
  cursor: pointer;
25
25
  display: inline-flex;
26
- font-size: 20px;
26
+ font-size: 1rem;
27
27
  font-weight: 500;
28
28
  inline-size: 100%;
29
29
  justify-content: center;
@@ -76,8 +76,8 @@ button[data-type="icon"] {
76
76
  backButton.innerHTML = `
77
77
  <svg
78
78
  xmlns="http://www.w3.org/2000/svg"
79
- width="40"
80
- height="40"
79
+ width="24"
80
+ height="24"
81
81
  viewBox="0 0 24 24"
82
82
  fill="none"
83
83
  >
@@ -103,8 +103,8 @@ button[data-type="icon"] {
103
103
  <svg
104
104
  xmlns="http://www.w3.org/2000/svg"
105
105
  viewBox="0 0 24 24"
106
- width="40"
107
- height="40"
106
+ width="24"
107
+ height="24"
108
108
  fill="none"
109
109
  >
110
110
  <path
@@ -9,34 +9,6 @@ const COMPONENTS_VERSION = packageJson.version;
9
9
 
10
10
  const smartCameraWeb = document.querySelector('smart-camera-web');
11
11
 
12
- async function getPermissions(captureScreen, facingMode = 'user') {
13
- try {
14
- const stream = await SmartCamera.getMedia({
15
- audio: false,
16
- video: { facingMode },
17
- });
18
- const devices = await navigator.mediaDevices.enumerateDevices();
19
- const videoDevice = devices.find(
20
- (device) =>
21
- device.kind === 'videoinput' &&
22
- stream.getVideoTracks()[0].getSettings().deviceId === device.deviceId,
23
- );
24
- smartCameraWeb?.dispatchEvent(
25
- new CustomEvent('metadata.camera-name', {
26
- detail: { cameraName: videoDevice?.label },
27
- }),
28
- );
29
- captureScreen.removeAttribute('data-camera-error');
30
- captureScreen.setAttribute('data-camera-ready', true);
31
- } catch (error) {
32
- captureScreen.removeAttribute('data-camera-ready');
33
- captureScreen.setAttribute(
34
- 'data-camera-error',
35
- SmartCamera.handleCameraError(error),
36
- );
37
- }
38
- }
39
-
40
12
  const cropImageFromDataUri = (
41
13
  dataUri,
42
14
  cropPercentX = 0,
@@ -113,7 +85,7 @@ class SelfieCaptureScreens extends HTMLElement {
113
85
  connectedCallback() {
114
86
  this.innerHTML = `
115
87
  ${styles(this.themeColor)}
116
- <div>
88
+ <div style="height: 100%;">
117
89
  <selfie-capture-instructions theme-color='${this.themeColor}' ${this.showNavigation} ${this.hideAttribution} ${this.hideBack} hidden></selfie-capture-instructions>
118
90
  <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>
119
91
  <selfie-capture-review theme-color='${this.themeColor}' ${this.showNavigation} ${this.hideAttribution} hidden></selfie-capture-review>
@@ -130,16 +102,10 @@ class SelfieCaptureScreens extends HTMLElement {
130
102
  this.selfieCapture = this.querySelector('selfie-capture-wrapper');
131
103
  this.selfieReview = this.querySelector('selfie-capture-review');
132
104
 
133
- if (this.hideInstructions && !this.hasAttribute('hidden')) {
134
- getPermissions(this.selfieCapture, this.getAgentMode());
135
- }
136
-
137
- // If the initial screen is selfie-capture, we need to get permissions
138
- if (this.getAttribute('initial-screen') === 'selfie-capture') {
139
- getPermissions(this.selfieCapture, this.getAgentMode()).then(() =>
140
- this.setActiveScreen(this.selfieCapture),
141
- );
142
- } else if (this.hideInstructions) {
105
+ if (
106
+ this.getAttribute('initial-screen') === 'selfie-capture' ||
107
+ this.hideInstructions
108
+ ) {
143
109
  this.setActiveScreen(this.selfieCapture);
144
110
  } else {
145
111
  this.setActiveScreen(this.selfieInstruction);
@@ -154,6 +120,14 @@ class SelfieCaptureScreens extends HTMLElement {
154
120
 
155
121
  disconnectedCallback() {
156
122
  SmartCamera.stopMedia();
123
+
124
+ if (this._selfieWrapperListeners) {
125
+ this._selfieWrapperListeners.forEach(({ event, handler }) => {
126
+ window.removeEventListener(event, handler);
127
+ });
128
+ this._selfieWrapperListeners = null;
129
+ }
130
+
157
131
  if (this.activeScreen) {
158
132
  this.activeScreen.removeAttribute('hidden');
159
133
  }
@@ -165,22 +139,25 @@ class SelfieCaptureScreens extends HTMLElement {
165
139
  this.selfieInstruction.addEventListener(
166
140
  'selfie-capture-instructions.capture',
167
141
  async () => {
168
- await getPermissions(this.selfieCapture, this.getAgentMode()).then(() =>
169
- this.setActiveScreen(this.selfieCapture),
170
- );
171
- smartCameraWeb?.dispatchEvent(
172
- new CustomEvent('metadata.selfie-capture-start'),
173
- );
174
- smartCameraWeb?.dispatchEvent(
175
- new CustomEvent('metadata.selfie-origin', {
176
- detail: {
177
- imageOrigin: {
178
- environment: 'back_camera',
179
- user: 'front_camera',
180
- }[this.getAgentMode()],
181
- },
182
- }),
183
- );
142
+ this.setActiveScreen(this.selfieCapture);
143
+
144
+ const selfieCapture =
145
+ this.selfieCapture.querySelector('selfie-capture');
146
+ if (selfieCapture) {
147
+ smartCameraWeb?.dispatchEvent(
148
+ new CustomEvent('metadata.selfie-capture-start'),
149
+ );
150
+ smartCameraWeb?.dispatchEvent(
151
+ new CustomEvent('metadata.selfie-origin', {
152
+ detail: {
153
+ imageOrigin: {
154
+ environment: 'back_camera',
155
+ user: 'front_camera',
156
+ }[this.getAgentMode()],
157
+ },
158
+ }),
159
+ );
160
+ }
184
161
  },
185
162
  );
186
163
  this.selfieInstruction.addEventListener(
@@ -189,7 +166,6 @@ class SelfieCaptureScreens extends HTMLElement {
189
166
  this.handleBackEvents();
190
167
  },
191
168
  );
192
- // Setup selfie-wrapper event listeners
193
169
  this.setupSelfieWrapperEventListeners();
194
170
 
195
171
  this.selfieReview.addEventListener(
@@ -199,17 +175,12 @@ class SelfieCaptureScreens extends HTMLElement {
199
175
  new CustomEvent('metadata.selfie-capture-retry'),
200
176
  );
201
177
  this.selfieReview.removeAttribute('data-image');
178
+ this.selfieReview.removeAttribute('mirror-image');
202
179
  this._data.images = [];
203
180
 
204
- // Force remount by incrementing key
205
- this.forceWrapperRemount();
181
+ await this.forceWrapperRemount();
206
182
 
207
- if (this.hideInstructions) {
208
- this.setActiveScreen(this.selfieCapture);
209
- await getPermissions(this.selfieCapture, this.getAgentMode());
210
- } else {
211
- this.setActiveScreen(this.selfieInstruction);
212
- }
183
+ this.setActiveScreen(this.selfieCapture);
213
184
  },
214
185
  );
215
186
 
@@ -233,52 +204,66 @@ class SelfieCaptureScreens extends HTMLElement {
233
204
  }
234
205
 
235
206
  // Force remount of selfie-capture-wrapper component for clean state
236
- forceWrapperRemount() {
207
+ async forceWrapperRemount() {
208
+ SmartCamera.stopMedia();
209
+
237
210
  this._remountKey++;
211
+
238
212
  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();
213
+ const oldWrapper = this.selfieCapture;
214
+
215
+ if (oldWrapper && container) {
216
+ // recreate wrapper element
217
+ const newWrapper = document.createElement('selfie-capture-wrapper');
218
+
219
+ // copy attributes from old wrapper, but skip key and start-countdown
220
+ const attributesToCopy = Array.from(oldWrapper.attributes);
221
+
222
+ attributesToCopy.forEach((attr) => {
223
+ newWrapper.setAttribute(attr.name, attr.value);
224
+ });
225
+ oldWrapper.remove();
226
+ await new Promise((resolve) => {
227
+ setTimeout(resolve, 50);
228
+ });
229
+
230
+ newWrapper.setAttribute('key', this._remountKey.toString());
231
+ newWrapper.setAttribute('start-countdown', 'false');
232
+ newWrapper.setAttribute('hidden', '');
233
+
234
+ const reviewElement = container.querySelector('selfie-capture-review');
235
+ if (reviewElement) {
236
+ container.insertBefore(newWrapper, reviewElement);
237
+ } else {
238
+ container.appendChild(newWrapper);
239
+ }
240
+
241
+ this.selfieCapture = newWrapper;
242
+
243
+ this.setupSelfieWrapperEventListeners();
244
+ }
245
+
246
+ // give time for the new component to initialize
247
+ return new Promise((resolve) => {
248
+ setTimeout(() => {
249
+ resolve();
250
+ }, 200);
251
+ });
270
252
  }
271
253
 
272
254
  // Override setActiveScreen to enable countdown when selfie-capture is active
273
255
  setActiveScreen(screen) {
256
+ if (this.activeScreen === screen) {
257
+ return;
258
+ }
259
+
274
260
  this.activeScreen?.setAttribute('hidden', '');
275
261
  screen.removeAttribute('hidden');
276
262
  this.activeScreen = screen;
277
263
 
278
- // If activating selfie-capture-wrapper, enable the countdown and ensure permissions
264
+ // If activating selfie-capture-wrapper, enable the countdown
279
265
  if (screen === this.selfieCapture) {
280
266
  screen.setAttribute('start-countdown', 'true');
281
- getPermissions(this.selfieCapture, this.getAgentMode());
282
267
  } else if (this.selfieCapture) {
283
268
  // Disable countdown when not on capture screen
284
269
  this.selfieCapture.setAttribute('start-countdown', 'false');
@@ -286,11 +271,19 @@ class SelfieCaptureScreens extends HTMLElement {
286
271
  }
287
272
 
288
273
  setupSelfieWrapperEventListeners() {
289
- window.addEventListener('selfie-capture.cancelled', () => {
274
+ // Remove existing event listeners if they exist
275
+ if (this._selfieWrapperListeners) {
276
+ this._selfieWrapperListeners.forEach(({ event, handler }) => {
277
+ window.removeEventListener(event, handler);
278
+ });
279
+ }
280
+
281
+ // Create new event handlers
282
+ const cancelledHandler = async () => {
290
283
  SmartCamera.stopMedia();
291
284
 
292
285
  // Force remount of selfie-capture-wrapper for clean state on next visit
293
- this.forceWrapperRemount();
286
+ await this.forceWrapperRemount();
294
287
 
295
288
  if (this.hideInstructions) {
296
289
  this.handleBackEvents();
@@ -298,19 +291,18 @@ class SelfieCaptureScreens extends HTMLElement {
298
291
  }
299
292
 
300
293
  this.setActiveScreen(this.selfieInstruction);
301
- });
294
+ };
302
295
 
303
- // Add specific close event handler for selfie-capture-wrapper
304
- window.addEventListener('selfie-capture.close', () => {
296
+ const closeHandler = async () => {
305
297
  SmartCamera.stopMedia();
306
298
 
307
299
  // Force remount of selfie-capture-wrapper for clean state on next visit
308
- this.forceWrapperRemount();
300
+ await this.forceWrapperRemount();
309
301
 
310
302
  this.handleCloseEvent();
311
- });
303
+ };
312
304
 
313
- window.addEventListener('selfie-capture.publish', async (event) => {
305
+ const publishHandler = async (event) => {
314
306
  smartCameraWeb?.dispatchEvent(
315
307
  new CustomEvent('metadata.selfie-capture-end'),
316
308
  );
@@ -318,25 +310,31 @@ class SelfieCaptureScreens extends HTMLElement {
318
310
  'data-image',
319
311
  await cropImageFromDataUri(event.detail.referenceImage, 20, 20),
320
312
  );
313
+ const shouldMirror = event.detail.facingMode === 'user';
314
+ this.selfieReview.setAttribute(
315
+ 'mirror-image',
316
+ shouldMirror ? 'true' : 'false',
317
+ );
321
318
  this._data.images = event.detail.images;
322
319
  SmartCamera.stopMedia();
323
320
  this.setActiveScreen(this.selfieReview);
321
+ };
322
+
323
+ // Store references to remove them later
324
+ this._selfieWrapperListeners = [
325
+ { event: 'selfie-capture.cancelled', handler: cancelledHandler },
326
+ { event: 'selfie-capture.close', handler: closeHandler },
327
+ { event: 'selfie-capture.publish', handler: publishHandler },
328
+ ];
329
+
330
+ // Add event listeners
331
+ this._selfieWrapperListeners.forEach(({ event, handler }) => {
332
+ window.addEventListener(event, handler);
324
333
  });
325
334
 
326
335
  // Also listen for the publish event on the parent SelfieCaptureScreens element
327
336
  // 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
- });
337
+ this.addEventListener('selfie-capture.publish', publishHandler);
340
338
  }
341
339
 
342
340
  _publishSelectedImages() {
@@ -351,6 +351,7 @@ function templateString() {
351
351
  position: relative;
352
352
  z-index: 1;
353
353
  width: 100%;
354
+ overflow: hidden;
354
355
  }
355
356
 
356
357
  .video-container video,
@@ -467,11 +468,12 @@ function templateString() {
467
468
 
468
469
  #selfie-capture-screen,
469
470
  #back-of-id-entry-screen {
470
- block-size: 45rem;
471
+ box-sizing: border-box;
471
472
  display: flex;
472
473
  flex-direction: column;
473
474
  max-block-size: 100%;
474
475
  max-inline-size: 40ch;
476
+ padding: 1rem;
475
477
  }
476
478
 
477
479
  #selfie-capture-screen header p {
@@ -574,7 +576,8 @@ async function getPermissions(
574
576
  device.kind === 'videoinput' &&
575
577
  stream.getVideoTracks()[0].getSettings().deviceId === device.deviceId,
576
578
  );
577
- window.dispatchEvent(
579
+ const smartCameraWeb = document.querySelector('smart-camera-web');
580
+ smartCameraWeb?.dispatchEvent(
578
581
  new CustomEvent('metadata.camera-name', {
579
582
  detail: { cameraName: videoDevice?.label },
580
583
  }),
@@ -669,7 +672,7 @@ class SelfieCaptureScreen extends HTMLElement {
669
672
 
670
673
  setTimeout(() => {
671
674
  this.smileCTABox.style.opacity = 1;
672
- this.smileCTA.textContent = 'BIGGER SMILE';
675
+ this.smileCTA.textContent = 'WIDER SMILE';
673
676
  this.mouth.setAttribute(
674
677
  'd',
675
678
  'm 213.88314,319.4551 c -1.58,0.97 -0.35309,9.33393 1.50671,9.30586 6.05679,-0.0914 16.11631,0.17227 34.57066,0.13346 18.45435,-0.0388 28.15778,-0.0418 31.09964,-0.79956 1.80122,-0.46394 2.75061,-7.48365 1.16061,-8.45365 -1.6,-1.74874 -2.96432,-0.94348 -6.77747,-1.56441 -12.83012,0.04 -36.52534,0.50197 -41.29469,0.43262 -2.51525,-0.0713 -18.41588,-0.61 -20.01588,0.35 z m 57.29363,1.36599 c -9.24417,-2.23757 -8.08363,-2.42362 -20.78363,-2.42362 -12.7,0 -17.77931,2.69528 -26.84042,5.36549 12.57883,3.28731 33.57775,-4.29887 49.70067,2.24964 z',
@@ -794,9 +797,14 @@ class SelfieCaptureScreen extends HTMLElement {
794
797
  }
795
798
 
796
799
  _publishImages() {
800
+ const eventDetail = {
801
+ ...this._data,
802
+ facingMode: this.facingMode,
803
+ };
804
+
797
805
  this.dispatchEvent(
798
806
  new CustomEvent('selfie-capture.publish', {
799
- detail: this._data,
807
+ detail: eventDetail,
800
808
  }),
801
809
  );
802
810
  }
@@ -839,6 +847,11 @@ class SelfieCaptureScreen extends HTMLElement {
839
847
 
840
848
  handleStream(stream) {
841
849
  try {
850
+ const videoContainer = this.shadowRoot.querySelector('.video');
851
+ if (!videoContainer) {
852
+ return;
853
+ }
854
+
842
855
  const videoExists = this.shadowRoot.querySelector('video');
843
856
  let video = null;
844
857
  if (videoExists) {
@@ -862,9 +875,6 @@ class SelfieCaptureScreen extends HTMLElement {
862
875
  };
863
876
 
864
877
  this._video = video;
865
- const videoContainer = this.shadowRoot.querySelector(
866
- '.video-container > .video',
867
- );
868
878
  this._data.permissionGranted = true;
869
879
 
870
880
  if (!videoExists) {
@@ -914,7 +924,10 @@ class SelfieCaptureScreen extends HTMLElement {
914
924
 
915
925
  if (SmartCamera.stream) {
916
926
  this.handleStream(SmartCamera.stream);
917
- } else if (this.hasAttribute('data-camera-ready')) {
927
+ } else if (
928
+ this.hasAttribute('data-camera-ready') ||
929
+ !this.hasAttribute('data-camera-error')
930
+ ) {
918
931
  getPermissions(this, { facingMode: this.facingMode });
919
932
  }
920
933
 
@@ -1005,13 +1018,44 @@ class SelfieCaptureScreen extends HTMLElement {
1005
1018
  attributeChangedCallback(name) {
1006
1019
  switch (name) {
1007
1020
  case 'data-camera-error':
1008
- case 'data-camera-ready':
1009
1021
  case 'hidden':
1010
1022
  case 'title':
1011
- case 'allow-agent-mode':
1012
1023
  this.shadowRoot.innerHTML = this.render();
1013
1024
  this.init();
1014
1025
  break;
1026
+ case 'allow-agent-mode':
1027
+ // only re-render if the shadowRoot is empty or not initialized
1028
+ if (!this.shadowRoot.innerHTML.trim()) {
1029
+ this.shadowRoot.innerHTML = this.render();
1030
+ this.init();
1031
+ } else {
1032
+ // update the setupAgentMode
1033
+ this.setupAgentMode();
1034
+ }
1035
+ break;
1036
+ case 'show-navigation':
1037
+ // update the navigation element if it exists
1038
+ if (this.shadowRoot.innerHTML.trim()) {
1039
+ const navigation =
1040
+ this.shadowRoot.querySelector('smileid-navigation');
1041
+ if (navigation) {
1042
+ if (this.showNavigation) {
1043
+ navigation.setAttribute('show-navigation', '');
1044
+ } else {
1045
+ navigation.removeAttribute('show-navigation');
1046
+ }
1047
+ }
1048
+ } else {
1049
+ this.shadowRoot.innerHTML = this.render();
1050
+ this.init();
1051
+ }
1052
+ break;
1053
+ case 'data-camera-ready':
1054
+ // don't re-render, just handle the stream
1055
+ if (this.hasAttribute('data-camera-ready') && SmartCamera.stream) {
1056
+ this.handleStream(SmartCamera.stream);
1057
+ }
1058
+ break;
1015
1059
  default:
1016
1060
  break;
1017
1061
  }