@smileid/web-components 11.0.3 → 11.1.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.
Files changed (92) hide show
  1. package/README.md +61 -0
  2. package/dist/components/smart-camera-web/src/README.md +0 -1
  3. package/dist/esm/{DocumentCaptureScreens-C5BhNB-0.js → DocumentCaptureScreens-DHAfE8VG.js} +191 -185
  4. package/dist/esm/DocumentCaptureScreens-DHAfE8VG.js.map +1 -0
  5. package/dist/esm/{EndUserConsent-D4fd1ovG.js → EndUserConsent-DE6OBSGG.js} +65 -63
  6. package/dist/esm/EndUserConsent-DE6OBSGG.js.map +1 -0
  7. package/dist/esm/{Navigation-CTjK6tLU.js → Navigation-kjll5SgE.js} +17 -9
  8. package/dist/esm/Navigation-kjll5SgE.js.map +1 -0
  9. package/dist/esm/{SelfieCaptureScreens-KoQpCxtc.js → SelfieCaptureScreens-DaREKFi4.js} +3087 -3159
  10. package/dist/esm/SelfieCaptureScreens-DaREKFi4.js.map +1 -0
  11. package/dist/esm/{TotpConsent-CQU5jQi4.js → TotpConsent-DsMJhXD8.js} +13 -9
  12. package/dist/esm/TotpConsent-DsMJhXD8.js.map +1 -0
  13. package/dist/esm/combobox.js +20 -19
  14. package/dist/esm/combobox.js.map +1 -1
  15. package/dist/esm/document.js +1 -1
  16. package/dist/esm/end-user-consent.js +1 -1
  17. package/dist/esm/index-LJce6hoU.js +1360 -0
  18. package/dist/esm/index-LJce6hoU.js.map +1 -0
  19. package/dist/esm/localisation.js +21 -0
  20. package/dist/esm/localisation.js.map +1 -0
  21. package/dist/esm/main.js +34 -17
  22. package/dist/esm/main.js.map +1 -1
  23. package/dist/esm/navigation.js +1 -1
  24. package/dist/esm/{package-B-UwEdv7.js → package-BoRHLVSv.js} +25 -40
  25. package/dist/esm/package-BoRHLVSv.js.map +1 -0
  26. package/dist/esm/selfie.js +1 -1
  27. package/dist/esm/smart-camera-web.js +22 -18
  28. package/dist/esm/smart-camera-web.js.map +1 -1
  29. package/dist/esm/totp-consent.js +1 -1
  30. package/dist/smart-camera-web.js +136 -152
  31. package/dist/smart-camera-web.js.map +1 -1
  32. package/dist/src/components/combobox/src/index.js +424 -1
  33. package/dist/src/components/document/src/index.js +1422 -1
  34. package/dist/src/components/end-user-consent/src/index.js +1573 -1
  35. package/dist/src/components/selfie/src/index.js +1220 -1
  36. package/dist/src/components/signature-pad/src/index.js +787 -1
  37. package/dist/src/components/smart-camera-web/src/SmartCameraWeb.js +2753 -1
  38. package/dist/src/components/totp-consent/src/index.js +1292 -1
  39. package/dist/types/combobox.d.ts +2 -2
  40. package/dist/types/document.d.ts +2 -2
  41. package/dist/types/end-user-consent.d.ts +2 -2
  42. package/dist/types/locale.d.ts +19 -0
  43. package/dist/types/localisation.d.ts +21 -0
  44. package/dist/types/main.d.ts +35 -26
  45. package/dist/types/navigation.d.ts +2 -2
  46. package/dist/types/selfie.d.ts +2 -2
  47. package/dist/types/signature-pad.d.ts +2 -2
  48. package/dist/types/smart-camera-web.d.ts +2 -2
  49. package/dist/types/totp-consent.d.ts +2 -2
  50. package/lib/components/camera-permission/CameraPermission.js +8 -3
  51. package/lib/components/combobox/src/Combobox.js +4 -2
  52. package/lib/components/document/src/DocumentCaptureScreens.js +4 -3
  53. package/lib/components/document/src/DocumentCaptureScreens.stories.js +37 -13
  54. package/lib/components/document/src/document-capture/DocumentCapture.js +20 -14
  55. package/lib/components/document/src/document-capture/DocumentCapture.stories.js +11 -2
  56. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +16 -11
  57. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +14 -5
  58. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +11 -7
  59. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +14 -5
  60. package/lib/components/end-user-consent/src/EndUserConsent.js +30 -29
  61. package/lib/components/end-user-consent/src/EndUserConsent.stories.js +12 -2
  62. package/lib/components/navigation/src/Navigation.js +15 -2
  63. package/lib/components/navigation/src/Navigation.stories.js +20 -4
  64. package/lib/components/selfie/src/SelfieCaptureScreens.js +3 -7
  65. package/lib/components/selfie/src/SelfieCaptureScreens.stories.js +16 -4
  66. package/lib/components/selfie/src/selfie-capture/SelfieCapture.js +23 -16
  67. package/lib/components/selfie/src/selfie-capture/SelfieCapture.stories.js +19 -7
  68. package/lib/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +16 -11
  69. package/lib/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +14 -5
  70. package/lib/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +10 -5
  71. package/lib/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +14 -5
  72. package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +2 -2
  73. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.stories.js +47 -0
  74. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +2 -2
  75. package/lib/components/selfie/src/smartselfie-capture/components/CaptureControls.tsx +5 -2
  76. package/lib/components/selfie/src/smartselfie-capture/hooks/useCamera.ts +4 -4
  77. package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +6 -5
  78. package/lib/components/selfie/src/smartselfie-capture/utils/alertMessages.ts +11 -9
  79. package/lib/components/selfie/src/smartselfie-capture/utils/imageCapture.ts +3 -1
  80. package/lib/components/signature-pad/package.json +1 -1
  81. package/lib/components/totp-consent/src/TotpConsent.js +8 -3
  82. package/lib/domain/camera/src/SmartCamera.js +7 -22
  83. package/lib/domain/constants/src/Constants.js +28 -0
  84. package/lib/domain/file-upload/src/SmartFileUpload.js +9 -10
  85. package/lib/domain/localisation/index.js +456 -0
  86. package/package.json +12 -6
  87. package/dist/esm/DocumentCaptureScreens-C5BhNB-0.js.map +0 -1
  88. package/dist/esm/EndUserConsent-D4fd1ovG.js.map +0 -1
  89. package/dist/esm/Navigation-CTjK6tLU.js.map +0 -1
  90. package/dist/esm/SelfieCaptureScreens-KoQpCxtc.js.map +0 -1
  91. package/dist/esm/TotpConsent-CQU5jQi4.js.map +0 -1
  92. package/dist/esm/package-B-UwEdv7.js.map +0 -1
@@ -1,8 +1,12 @@
1
- import { IMAGE_TYPE } from '../../../../domain/constants/src/Constants';
1
+ import {
2
+ IMAGE_TYPE,
3
+ JPEG_QUALITY,
4
+ } from '../../../../domain/constants/src/Constants';
2
5
  import SmartCamera from '../../../../domain/camera/src/SmartCamera';
3
6
  import styles from '../../../../styles/src/styles';
4
7
  import packageJson from '../../../../../package.json';
5
8
  import '../../../navigation/src';
9
+ import { t, tHtml, getDirection } from '../../../../domain/localisation';
6
10
 
7
11
  const COMPONENTS_VERSION = packageJson.version;
8
12
 
@@ -28,8 +32,9 @@ function getLivenessFramesIndices(
28
32
 
29
33
  if (totalNoOfFrames < numberOfFramesRequired) {
30
34
  throw new Error(
31
- 'SmartCameraWeb: Minimum required no of frames is ',
32
- numberOfFramesRequired,
35
+ tHtml('selfie.capture.error.minFrames', {
36
+ count: numberOfFramesRequired,
37
+ }),
33
38
  );
34
39
  }
35
40
 
@@ -146,7 +151,7 @@ function templateString() {
146
151
  .title-color {
147
152
  color: ${this.themeColor};
148
153
  }
149
-
154
+
150
155
  .theme-color {
151
156
  color: ${this.themeColor};
152
157
  }
@@ -509,10 +514,10 @@ function templateString() {
509
514
  margin-inline-start; 1em;
510
515
  }
511
516
  </style>
512
- <div id='selfie-capture-screen' class='flow center'>
517
+ <div id='selfie-capture-screen' class='flow center' dir='${this.direction}'>
513
518
  <smileid-navigation theme-color='${this.themeColor}' ${this.showNavigation ? 'show-navigation' : ''} ${this.hideBack ? 'hide-back' : ''}></smileid-navigation>
514
519
 
515
- <div class='tips'>Fit your head inside the oval frame</div>
520
+ <div class='tips'>${t('selfie.capture.tip.fitHead')}</div>
516
521
 
517
522
  <div className="error">
518
523
  ${this.cameraError ? `<p class="color-red">${this.cameraError}</p>` : ''}
@@ -544,15 +549,15 @@ function templateString() {
544
549
  </svg>
545
550
  </div>
546
551
  <div>
547
- <p id='smile-cta'>SMILE</p>
552
+ <p id='smile-cta'>${t('selfie.capture.cta.smile')}</p>
548
553
  </div>
549
554
  </div>
550
555
  </div>
551
556
 
552
- ${this.allowAgentMode ? `<button data-variant='outline small' id='switch-camera' class='button | center' type='button'>${this.inAgentMode ? 'Agent Mode On' : 'Agent Mode Off'}</button>` : ''}
557
+ ${this.allowAgentMode ? `<button data-variant='outline small' id='switch-camera' class='button | center' type='button'>${this.inAgentMode ? t('selfie.capture.agentMode.on') : t('selfie.capture.agentMode.off')}</button>` : ''}
553
558
 
554
559
  <button data-variant='solid' id='start-image-capture' class='button | center' type='button'>
555
- Take Selfie
560
+ ${t('selfie.capture.button.takeSelfie')}
556
561
  </button>
557
562
 
558
563
  ${this.hideAttribution ? '' : '<powered-by-smile-id></powered-by-smile-id>'}
@@ -687,7 +692,7 @@ class SelfieCaptureScreen extends HTMLElement {
687
692
 
688
693
  setTimeout(() => {
689
694
  this.smileCTABox.style.opacity = 1;
690
- this.smileCTA.textContent = 'WIDER SMILE';
695
+ this.smileCTA.textContent = t('selfie.capture.cta.widerSmile');
691
696
  this.mouth.setAttribute(
692
697
  'd',
693
698
  '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',
@@ -773,7 +778,7 @@ class SelfieCaptureScreen extends HTMLElement {
773
778
  // NOTE: we do not want to test POL images
774
779
  this._drawImage(canvas, false);
775
780
 
776
- this._rawImages.push(canvas.toDataURL('image/jpeg'));
781
+ this._rawImages.push(canvas.toDataURL('image/jpeg', JPEG_QUALITY));
777
782
  }
778
783
 
779
784
  _captureReferencePhoto() {
@@ -801,7 +806,7 @@ class SelfieCaptureScreen extends HTMLElement {
801
806
  // NOTE: we want to test the image quality of the reference photo
802
807
  this._drawImage(canvas, !this.disableImageTests);
803
808
 
804
- const image = canvas.toDataURL('image/jpeg');
809
+ const image = canvas.toDataURL('image/jpeg', JPEG_QUALITY);
805
810
 
806
811
  this._referenceImage = image;
807
812
 
@@ -852,9 +857,7 @@ class SelfieCaptureScreen extends HTMLElement {
852
857
  if (hasEnoughColors) {
853
858
  return context;
854
859
  }
855
- throw new Error(
856
- 'Unable to capture webcam images - Please try another device',
857
- );
860
+ throw new Error(t('selfie.capture.error.webcamCapture'));
858
861
  } else {
859
862
  return context;
860
863
  }
@@ -994,7 +997,11 @@ class SelfieCaptureScreen extends HTMLElement {
994
997
  }
995
998
 
996
999
  get title() {
997
- return this.getAttribute('title') || 'Submit Front of ID';
1000
+ return this.getAttribute('title') || t('selfie.instructions.title');
1001
+ }
1002
+
1003
+ get direction() {
1004
+ return this.getAttribute('dir') || getDirection() || 'ltr';
998
1005
  }
999
1006
 
1000
1007
  get hidden() {
@@ -1,8 +1,17 @@
1
1
  import SmartCamera from '../../../../domain/camera/src/SmartCamera';
2
+ import { setCurrentLocale } from '../../../../domain/localisation';
2
3
  import './SelfieCapture';
3
4
 
4
5
  const meta = {
6
+ args: {
7
+ language: 'en',
8
+ 'theme-color': '#001096',
9
+ },
5
10
  argTypes: {
11
+ language: {
12
+ control: 'select',
13
+ options: ['en', 'ar'],
14
+ },
6
15
  'theme-color': { control: 'color' },
7
16
  },
8
17
  component: 'selfie-capture',
@@ -11,9 +20,6 @@ const meta = {
11
20
  export default meta;
12
21
 
13
22
  export const SelfieCapture = {
14
- args: {
15
- 'theme-color': '#001096',
16
- },
17
23
  loaders: [
18
24
  async () => ({
19
25
  permissionGranted: await SmartCamera.getMedia({
@@ -22,15 +28,21 @@ export const SelfieCapture = {
22
28
  }),
23
29
  }),
24
30
  ],
25
- render: (args) => `
31
+ render: (args) => {
32
+ setCurrentLocale(args.language);
33
+ return `
26
34
  <selfie-capture theme-color='${args['theme-color']}'>
27
35
  </selfie-capture>
28
- `,
36
+ `;
37
+ },
29
38
  };
30
39
 
31
40
  export const SelfieCaptureAgentMode = {
32
- render: () => `
41
+ render: (args) => {
42
+ setCurrentLocale(args.language);
43
+ return `
33
44
  <selfie-capture allow-agent-mode='true' data-camera-ready show-agent-mode-for-tests>
34
45
  </selfie-capture>
35
- `,
46
+ `;
47
+ },
36
48
  };
@@ -1,5 +1,6 @@
1
1
  import styles from '../../../../styles/src/styles';
2
2
  import '../../../navigation/src';
3
+ import { t, getDirection } from '../../../../domain/localisation';
3
4
 
4
5
  function templateString() {
5
6
  return `
@@ -339,7 +340,7 @@ function templateString() {
339
340
  }
340
341
  </style>
341
342
  ${styles(this.themeColor)}
342
- <div id="selfie-capture-instruction-screen" class="center">
343
+ <div id="selfie-capture-instruction-screen" class="center" dir="${this.direction}">
343
344
  <div class="content-root">
344
345
  <div class="content-header">
345
346
  <smileid-navigation theme-color=${this.themeColor} ${this.hideBack ? 'hide-back' : ''} ${this.showNavigation ? '' : 'hidden'}></smileid-navigation>
@@ -370,7 +371,7 @@ function templateString() {
370
371
  </clipPath>
371
372
  </defs>
372
373
  </svg>
373
- <h1 class='title-color font-bold'>Next, we'll take a quick selfie</h1>
374
+ <h1 class='title-color font-bold'>${t('selfie.instructions.title')}</h1>
374
375
  </header>
375
376
  <div class="instructions-wrapper">
376
377
  <div class="instructions">
@@ -449,9 +450,9 @@ function templateString() {
449
450
  </defs>
450
451
  </svg>
451
452
  <div class="instruction">
452
- <p class="text-base title-color font-bold tip-header">Good Light</p>
453
+ <p class="text-base title-color font-bold tip-header">${t('selfie.instructions.tips.goodLight.header')}</p>
453
454
  <p class="tip-body">
454
- Make sure you are in a well-lit environment.
455
+ ${t('selfie.instructions.tips.goodLight.body')}
455
456
  </p>
456
457
  </div>
457
458
  </div>
@@ -537,9 +538,9 @@ function templateString() {
537
538
  </defs>
538
539
  </svg>
539
540
  <div class="instruction">
540
- <p class="text-base title-color font-bold tip-header">Clear Image</p>
541
+ <p class="text-base title-color font-bold tip-header">${t('selfie.instructions.tips.clearImage.header')}</p>
541
542
  <p class="tip-body">
542
- Hold your phone steady so the selfie is clear and sharp.
543
+ ${t('selfie.instructions.tips.clearImage.body')}
543
544
  </p>
544
545
  </div>
545
546
  </div>
@@ -569,25 +570,25 @@ function templateString() {
569
570
  />
570
571
  </svg>
571
572
  <div class="instruction">
572
- <p class="text-base title-color font-bold tip-header">Remove Obstructions</p>
573
+ <p class="text-base title-color font-bold tip-header">${t('selfie.instructions.tips.removeObstructions.header')}</p>
573
574
  <p class="tip-body">
574
- Remove anything that covers your face, such as glasses, masks, and hats.
575
+ ${t('selfie.instructions.tips.removeObstructions.body')}
575
576
  </p>
576
577
  </div>
577
578
  </div>
578
579
  <div class="instructions">
579
580
  <svg xmlns="http://www.w3.org/2000/svg" width="38" height="38" viewBox="0 0 24 24" fill="none" stroke="${this.themeColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-mood-happy"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M9 9l.01 0" /><path d="M15 9l.01 0" /><path d="M8 13a4 4 0 1 0 8 0h-8" /></svg>
580
581
  <div class="instruction">
581
- <p class="text-base title-color font-bold tip-header">Wide Smile</p>
582
+ <p class="text-base title-color font-bold tip-header">${t('selfie.instructions.tips.wideSmile.header')}</p>
582
583
  <p class="tip-body">
583
- When asked to smile, give a wide smile, showing your teeth.
584
+ ${t('selfie.instructions.tips.wideSmile.body')}
584
585
  </p>
585
586
  </div>
586
587
  </div>
587
588
  </div>
588
589
  <section class="controls">
589
590
  <button id='allow' data-variant='solid full-width' class='button theme-background'>
590
- Allow
591
+ ${t('selfie.instructions.allowButton')}
591
592
  </button>
592
593
  </section>
593
594
  ${
@@ -667,6 +668,10 @@ class SelfieCaptureInstructions extends HTMLElement {
667
668
  return this.hasAttribute('show-navigation');
668
669
  }
669
670
 
671
+ get direction() {
672
+ return this.getAttribute('dir') || getDirection() || 'ltr';
673
+ }
674
+
670
675
  handleCloseEvents() {
671
676
  this.dispatchEvent(new CustomEvent('selfie-capture-instructions.close'));
672
677
  }
@@ -1,7 +1,16 @@
1
+ import { setCurrentLocale } from '../../../../domain/localisation';
1
2
  import './index';
2
3
 
3
4
  const meta = {
5
+ args: {
6
+ language: 'en',
7
+ 'theme-color': '#001096',
8
+ },
4
9
  argTypes: {
10
+ language: {
11
+ control: 'select',
12
+ options: ['en', 'ar'],
13
+ },
5
14
  'theme-color': { control: 'color' },
6
15
  },
7
16
  component: 'selfie-capture-instructions',
@@ -10,14 +19,14 @@ const meta = {
10
19
  export default meta;
11
20
 
12
21
  export const SelfieInstruction = {
13
- args: {
14
- 'theme-color': '#001096',
15
- },
16
- render: (args) => `
22
+ render: (args) => {
23
+ setCurrentLocale(args.language);
24
+ return `
17
25
  <selfie-capture-instructions
18
26
  show-navigation
19
27
  theme-color='${args['theme-color']}'
20
28
  >
21
29
  </selfie-capture-instructions>
22
- `,
30
+ `;
31
+ },
23
32
  };
@@ -1,5 +1,6 @@
1
1
  import styles from '../../../../styles/src/styles';
2
2
  import '../../../navigation/src';
3
+ import { t, getDirection } from '../../../../domain/localisation';
3
4
 
4
5
  function templateString() {
5
6
  return `
@@ -55,10 +56,10 @@ function templateString() {
55
56
  }
56
57
  </style>
57
58
  ${styles(this.themeColor)}
58
- <div id='selfie-capture-review-screen' class='center'>
59
+ <div id='selfie-capture-review-screen' class='center' dir='${this.direction}'>
59
60
  <smileid-navigation ${this.showNavigation ? 'show-navigation' : ''} hide-back></smileid-navigation>
60
61
  <h1 class="header-title title-color font-bold">
61
- Is your whole face clear?
62
+ ${t('selfie.review.question')}
62
63
  </h1>
63
64
  <div class='section | flow'>
64
65
  <div class='selfie-review-container ${this.isPortraitCaptureView ? 'portrait' : 'landscape'}'>
@@ -76,10 +77,10 @@ function templateString() {
76
77
  </div>
77
78
  <div class='flow action-buttons'>
78
79
  <button data-variant='solid full-width' class='button' type='button' id='select-id-image'>
79
- Yes, use this
80
+ ${t('selfie.review.acceptButton')}
80
81
  </button>
81
82
  <button data-variant='ghost full-width' class='button retake-photo' type='button' id='re-capture-image'>
82
- No, Retake Selfie
83
+ ${t('selfie.review.retakeButton')}
83
84
  </button>
84
85
  </div>
85
86
  ${
@@ -145,7 +146,11 @@ class SelfieCaptureReview extends HTMLElement {
145
146
  }
146
147
 
147
148
  get title() {
148
- return this.getAttribute('title') || 'Submit Front of ID';
149
+ return this.getAttribute('title') || t('selfie.review.title');
150
+ }
151
+
152
+ get direction() {
153
+ return this.getAttribute('dir') || getDirection() || 'ltr';
149
154
  }
150
155
 
151
156
  handleBackEvents() {
@@ -1,7 +1,16 @@
1
+ import { setCurrentLocale } from '../../../../domain/localisation';
1
2
  import './SelfieCaptureReview';
2
3
 
3
4
  const meta = {
5
+ args: {
6
+ language: 'en',
7
+ 'theme-color': '#001096',
8
+ },
4
9
  argTypes: {
10
+ language: {
11
+ control: 'select',
12
+ options: ['en', 'ar'],
13
+ },
5
14
  'theme-color': { control: 'color' },
6
15
  },
7
16
  component: 'selfie-capture-review',
@@ -10,15 +19,15 @@ const meta = {
10
19
  export default meta;
11
20
 
12
21
  export const SelfieCaptureReview = {
13
- args: {
14
- 'theme-color': '#001096',
15
- },
16
- render: (args) => `
22
+ render: (args) => {
23
+ setCurrentLocale(args.language);
24
+ return `
17
25
  <selfie-capture-review
18
26
  show-navigation
19
27
  data-image="https://placehold.co/600x400"
20
28
  theme-color='${args['theme-color']}'
21
29
  >
22
30
  </selfie-capture-review>
23
- `,
31
+ `;
32
+ },
24
33
  };
@@ -3,7 +3,7 @@ import { IconLoader2 } from '@tabler/icons-preact';
3
3
  import register from 'preact-custom-element';
4
4
  import type { FunctionComponent } from 'preact';
5
5
 
6
- import { getBoolProp } from '@/utils/props';
6
+ import { getBoolProp } from '../../../../utils/props';
7
7
  import SmartSelfieCapture from '../smartselfie-capture/SmartSelfieCapture';
8
8
  // Legacy web component fallback (used when Mediapipe isn't available)
9
9
  import '../selfie-capture/SelfieCapture';
@@ -196,7 +196,7 @@ const SelfieCaptureWrapper: FunctionComponent<Props> = ({
196
196
  delete (propsWithoutHidden as any).hidden;
197
197
 
198
198
  return (
199
- // @ts-ignore
199
+ // @ts-expect-error --- preact-custom-element doesn't have proper types for refs
200
200
  <selfie-capture
201
201
  {...propsWithoutHidden}
202
202
  ref={(el: HTMLElement) => {
@@ -0,0 +1,47 @@
1
+ import { setCurrentLocale } from '../../../../domain/localisation';
2
+ import './index';
3
+
4
+ const meta = {
5
+ args: {
6
+ language: 'en',
7
+ 'theme-color': '#001096',
8
+ },
9
+ argTypes: {
10
+ language: {
11
+ control: 'select',
12
+ options: ['en', 'ar'],
13
+ },
14
+ 'theme-color': { control: 'color' },
15
+ },
16
+ component: 'smart-selfie-capture',
17
+ };
18
+
19
+ export default meta;
20
+
21
+ export const SmartSelfieCapture = {
22
+ render: (args) => {
23
+ setCurrentLocale(args.language);
24
+ return `
25
+ <smart-selfie-capture
26
+ show-navigation
27
+ theme-color='${args['theme-color']}'
28
+ >
29
+ </smart-selfie-capture>
30
+ `;
31
+ },
32
+ };
33
+
34
+ export const SmartSelfieCaptureAgentMode = {
35
+ render: (args) => {
36
+ setCurrentLocale(args.language);
37
+ return `
38
+ <smart-selfie-capture
39
+ show-navigation
40
+ allow-agent-mode
41
+ show-agent-mode-for-tests
42
+ theme-color='${args['theme-color']}'
43
+ >
44
+ </smart-selfie-capture>
45
+ `;
46
+ },
47
+ };
@@ -152,7 +152,7 @@ const SmartSelfieCapture: FunctionComponent<Props> = ({
152
152
  return (
153
153
  <div className="smartselfie-capture">
154
154
  {showNavigation && (
155
- // @ts-ignore
155
+ // @ts-expect-error --- preact-custom-element doesn't have proper types for refs
156
156
  <smileid-navigation ref={navigationRef} theme-color={themeColor} />
157
157
  )}
158
158
 
@@ -188,7 +188,7 @@ const SmartSelfieCapture: FunctionComponent<Props> = ({
188
188
  />
189
189
  )}
190
190
 
191
- {/* @ts-ignore */}
191
+ {/* @ts-expect-error -- preact-custom-element doesn't have proper types for refs */}
192
192
  {!hideAttribution && <powered-by-smile-id />}
193
193
 
194
194
  <style>{`
@@ -1,4 +1,5 @@
1
1
  import type { FunctionComponent } from 'preact';
2
+ import { t } from '../../../../../domain/localisation';
2
3
 
3
4
  interface CaptureControlsProps {
4
5
  isCapturing: boolean;
@@ -33,7 +34,7 @@ export const CaptureControls: FunctionComponent<CaptureControlsProps> = ({
33
34
  onClick={onStartCapture}
34
35
  disabled={isCapturing || hasFinishedCapture || !isReadyToCapture}
35
36
  >
36
- Start Capture
37
+ {t('selfie.capture.button.startCapture')}
37
38
  </button>
38
39
 
39
40
  {allowAgentMode && (agentSupported || showAgentModeForTests) && (
@@ -43,7 +44,9 @@ export const CaptureControls: FunctionComponent<CaptureControlsProps> = ({
43
44
  className="agent-mode-btn"
44
45
  disabled={isCapturing || hasFinishedCapture}
45
46
  >
46
- {facingMode === 'user' ? 'Agent Mode Off' : 'Agent Mode On'}
47
+ {facingMode === 'user'
48
+ ? t('selfie.capture.agentMode.off')
49
+ : t('selfie.capture.agentMode.on')}
47
50
  </button>
48
51
  )}
49
52
  </div>
@@ -110,7 +110,7 @@ export const useCamera = (initialFacingMode: CameraFacingMode = 'user') => {
110
110
  }
111
111
 
112
112
  await startCamera(newFacingMode);
113
- } catch (error) {
113
+ } catch {
114
114
  setFacingMode(previousFacingMode);
115
115
  isSwitchingCameraRef.current = false;
116
116
 
@@ -180,7 +180,7 @@ export const useCamera = (initialFacingMode: CameraFacingMode = 'user') => {
180
180
  userCameraId =
181
181
  userStream.getVideoTracks()[0].getSettings().deviceId ?? null;
182
182
  userStream.getTracks().forEach((track) => track.stop());
183
- } catch (error) {
183
+ } catch {
184
184
  // no user-facing camera available
185
185
  }
186
186
 
@@ -192,7 +192,7 @@ export const useCamera = (initialFacingMode: CameraFacingMode = 'user') => {
192
192
  environmentCameraId =
193
193
  envStream.getVideoTracks()[0].getSettings().deviceId ?? null;
194
194
  envStream.getTracks().forEach((track) => track.stop());
195
- } catch (error) {
195
+ } catch {
196
196
  // no environment-facing camera available
197
197
  }
198
198
 
@@ -207,7 +207,7 @@ export const useCamera = (initialFacingMode: CameraFacingMode = 'user') => {
207
207
  }
208
208
 
209
209
  setAgentSupported(!isGecko);
210
- } catch (error) {
210
+ } catch {
211
211
  setAgentSupported(false);
212
212
  }
213
213
  };
@@ -17,6 +17,7 @@ import { captureImageFromVideo } from '../utils/imageCapture';
17
17
  import { ImageType } from '../constants';
18
18
  import { MESSAGES, type MessageKey } from '../utils/alertMessages';
19
19
  import { getMediapipeInstance } from '../utils/mediapipeManager';
20
+ import { t } from '../../../../../domain/localisation';
20
21
  import packageJson from '../../../../../../package.json';
21
22
 
22
23
  const COMPONENTS_VERSION = packageJson.version;
@@ -88,7 +89,7 @@ export const useFaceCapture = ({
88
89
 
89
90
  const updateAlertImmediate = (messageKey: MessageKey | null) => {
90
91
  if (messageKey && MESSAGES[messageKey]) {
91
- alertTitle.value = MESSAGES[messageKey];
92
+ alertTitle.value = MESSAGES[messageKey]?.();
92
93
  } else {
93
94
  alertTitle.value = '';
94
95
  }
@@ -141,7 +142,7 @@ export const useFaceCapture = ({
141
142
  const isInSmileZone = capturesTaken.value >= smileCheckpoint.value;
142
143
 
143
144
  if (isInNeutralZone) {
144
- alertTitle.value = 'Capturing...';
145
+ alertTitle.value = t('selfie.smart.status.capturing');
145
146
  } else if (isInSmileZone) {
146
147
  const timeSinceSmile = Date.now() - lastSmileTime.value;
147
148
  if (timeSinceSmile > smileCooldown) {
@@ -154,7 +155,7 @@ export const useFaceCapture = ({
154
155
  updateAlert('smile-required');
155
156
  }
156
157
  } else {
157
- alertTitle.value = 'Keep smiling!';
158
+ alertTitle.value = t('selfie.smart.status.keepSmiling');
158
159
  }
159
160
  } else {
160
161
  updateAlert(null);
@@ -177,7 +178,7 @@ export const useFaceCapture = ({
177
178
  } else if (isCapturing.value) {
178
179
  updateCaptureAlerts();
179
180
  } else {
180
- alertTitle.value = 'Ready to capture';
181
+ alertTitle.value = t('selfie.smart.status.readyToCapture');
181
182
  }
182
183
  };
183
184
 
@@ -330,7 +331,7 @@ export const useFaceCapture = ({
330
331
  }
331
332
 
332
333
  updateAlerts();
333
- } catch (error) {
334
+ } catch {
334
335
  faceDetected.value = false;
335
336
  faceInBounds.value = false;
336
337
  multipleFaces.value = false;
@@ -1,13 +1,15 @@
1
+ import { t } from '../../../../../domain/localisation';
2
+
1
3
  export const MESSAGES = {
2
- 'multiple-faces': 'Ensure only one face is visible',
3
- 'no-face': 'Position your face in the oval',
4
- 'out-of-bounds': 'Position your face in the oval',
5
- 'too-close': 'Move farther away',
6
- 'too-far': 'Move closer',
7
- 'neutral-expression': 'Neutral expression',
8
- 'smile-required': 'Smile!',
9
- 'open-mouth-smile': 'Wider smile - teeth visible',
10
- initializing: 'Initializing...',
4
+ 'multiple-faces': () => t('selfie.smart.alert.multipleFaces'),
5
+ 'no-face': () => t('selfie.smart.alert.noFace'),
6
+ 'out-of-bounds': () => t('selfie.smart.alert.outOfBounds'),
7
+ 'too-close': () => t('selfie.smart.alert.tooClose'),
8
+ 'too-far': () => t('selfie.smart.alert.tooFar'),
9
+ 'neutral-expression': () => t('selfie.smart.alert.neutralExpression'),
10
+ 'smile-required': () => t('selfie.smart.alert.smileRequired'),
11
+ 'open-mouth-smile': () => t('selfie.smart.alert.openMouthSmile'),
12
+ initializing: () => t('selfie.smart.alert.initializing'),
11
13
  };
12
14
 
13
15
  export type MessageKey = keyof typeof MESSAGES;
@@ -1,3 +1,5 @@
1
+ import { JPEG_QUALITY } from '../../../../../domain/constants/src/Constants';
2
+
1
3
  export const captureImageFromVideo = (
2
4
  videoElement: HTMLVideoElement,
3
5
  isReference: boolean = false,
@@ -60,5 +62,5 @@ export const captureImageFromVideo = (
60
62
  canvas.height,
61
63
  );
62
64
 
63
- return canvas.toDataURL('image/jpeg');
65
+ return canvas.toDataURL('image/jpeg', JPEG_QUALITY);
64
66
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smileid/signature-pad",
3
- "version": "11.0.3",
3
+ "version": "11.1.0",
4
4
  "private": "true",
5
5
  "exports": {
6
6
  ".": "./index.js"