@smileid/web-components 10.0.6 → 11.0.1

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 (142) hide show
  1. package/README.md +15 -15
  2. package/dist/README.md +15 -0
  3. package/dist/components/README.md +14 -0
  4. package/dist/components/document/src/README.md +111 -0
  5. package/dist/components/document/src/document-capture/README.md +90 -0
  6. package/dist/components/document/src/document-capture-instructions/README.md +56 -0
  7. package/dist/components/document/src/document-capture-review/README.md +79 -0
  8. package/dist/components/selfie/README.md +225 -0
  9. package/dist/components/smart-camera-web/src/README.md +207 -0
  10. package/dist/domain/camera/src/README.md +38 -0
  11. package/dist/domain/file-upload/README.md +35 -0
  12. package/dist/esm/{DocumentCaptureScreens-BjATTDqu.js → DocumentCaptureScreens-DmH2JZDA.js} +3 -3
  13. package/dist/esm/DocumentCaptureScreens-DmH2JZDA.js.map +1 -0
  14. package/dist/esm/EndUserConsent-D4fd1ovG.js.map +1 -1
  15. package/dist/esm/Navigation-CTjK6tLU.js.map +1 -1
  16. package/dist/esm/PoweredBySmileId-CxbaihMu.js.map +1 -1
  17. package/dist/esm/SelfieCaptureScreens-DbdN2zNk.js +7901 -0
  18. package/dist/esm/SelfieCaptureScreens-DbdN2zNk.js.map +1 -0
  19. package/dist/esm/SignaturePad-C7MtmT8m.js.map +1 -1
  20. package/dist/esm/TotpConsent-CQU5jQi4.js.map +1 -1
  21. package/dist/esm/combobox.js.map +1 -1
  22. package/dist/esm/document.js +1 -1
  23. package/dist/esm/main.js +2 -2
  24. package/dist/esm/{package-CZlW6BZn.js → package-bgeQiff6.js} +2 -2
  25. package/dist/esm/package-bgeQiff6.js.map +1 -0
  26. package/dist/esm/selfie.js +1 -1
  27. package/dist/esm/smart-camera-web.js +3 -3
  28. package/dist/esm/smart-camera-web.js.map +1 -1
  29. package/dist/esm/styles-BOEZtbuc.js.map +1 -1
  30. package/dist/package-lock.json +4948 -0
  31. package/dist/package.json +59 -0
  32. package/dist/smart-camera-web.js +72 -98
  33. package/dist/smart-camera-web.js.gz +0 -0
  34. package/dist/smart-camera-web.js.map +1 -1
  35. package/dist/src/components/combobox/src/index.js +2 -0
  36. package/dist/src/components/combobox/src/index.js.map +7 -0
  37. package/dist/src/components/document/src/index.js +2 -0
  38. package/dist/src/components/document/src/index.js.map +7 -0
  39. package/dist/src/components/end-user-consent/src/index.js +14 -0
  40. package/dist/src/components/end-user-consent/src/index.js.map +7 -0
  41. package/dist/src/components/selfie/src/index.js +2 -0
  42. package/dist/src/components/selfie/src/index.js.map +7 -0
  43. package/dist/src/components/signature-pad/src/index.js +10 -0
  44. package/dist/src/components/signature-pad/src/index.js.map +7 -0
  45. package/dist/src/components/smart-camera-web/src/SmartCameraWeb.js +2 -0
  46. package/dist/src/components/smart-camera-web/src/SmartCameraWeb.js.map +7 -0
  47. package/dist/src/components/totp-consent/src/index.js +14 -0
  48. package/dist/src/components/totp-consent/src/index.js.map +7 -0
  49. package/dist/src/index.js.map +7 -0
  50. package/dist/styles/README.md +3 -0
  51. package/dist/types/combobox.d.ts +19 -19
  52. package/dist/types/document.d.ts +19 -19
  53. package/dist/types/end-user-consent.d.ts +19 -19
  54. package/dist/types/main.d.ts +24 -20
  55. package/dist/types/navigation.d.ts +19 -19
  56. package/dist/types/selfie.d.ts +19 -19
  57. package/dist/types/signature-pad.d.ts +19 -19
  58. package/dist/types/smart-camera-web.d.ts +19 -19
  59. package/dist/types/totp-consent.d.ts +19 -19
  60. package/lib/components/README.md +14 -14
  61. package/lib/components/attribution/PoweredBySmileId.js +42 -42
  62. package/lib/components/camera-permission/CameraPermission.js +139 -139
  63. package/lib/components/camera-permission/CameraPermission.stories.js +27 -27
  64. package/lib/components/combobox/src/Combobox.js +589 -589
  65. package/lib/components/combobox/src/index.js +1 -1
  66. package/lib/components/document/src/DocumentCaptureScreens.js +410 -409
  67. package/lib/components/document/src/DocumentCaptureScreens.stories.js +57 -57
  68. package/lib/components/document/src/README.md +111 -111
  69. package/lib/components/document/src/document-capture/DocumentCapture.js +760 -760
  70. package/lib/components/document/src/document-capture/DocumentCapture.stories.js +78 -78
  71. package/lib/components/document/src/document-capture/README.md +90 -90
  72. package/lib/components/document/src/document-capture/index.js +3 -3
  73. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +545 -545
  74. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +24 -24
  75. package/lib/components/document/src/document-capture-instructions/README.md +56 -56
  76. package/lib/components/document/src/document-capture-instructions/index.js +3 -3
  77. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +360 -360
  78. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +24 -24
  79. package/lib/components/document/src/document-capture-review/README.md +79 -79
  80. package/lib/components/document/src/document-capture-review/index.js +3 -3
  81. package/lib/components/document/src/index.js +3 -3
  82. package/lib/components/end-user-consent/src/EndUserConsent.js +795 -795
  83. package/lib/components/end-user-consent/src/EndUserConsent.stories.js +29 -29
  84. package/lib/components/end-user-consent/src/index.js +4 -4
  85. package/lib/components/navigation/src/Navigation.js +171 -171
  86. package/lib/components/navigation/src/Navigation.stories.js +24 -24
  87. package/lib/components/navigation/src/index.js +3 -3
  88. package/lib/components/selfie/README.md +225 -225
  89. package/lib/components/selfie/src/SelfieCaptureScreens.js +420 -431
  90. package/lib/components/selfie/src/SelfieCaptureScreens.stories.js +29 -29
  91. package/lib/components/selfie/src/index.js +3 -3
  92. package/lib/components/selfie/src/selfie-capture/SelfieCapture.js +1099 -1084
  93. package/lib/components/selfie/src/selfie-capture/SelfieCapture.stories.js +36 -36
  94. package/lib/components/selfie/src/selfie-capture/index.js +3 -3
  95. package/lib/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +689 -689
  96. package/lib/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +23 -23
  97. package/lib/components/selfie/src/selfie-capture-instructions/index.js +3 -3
  98. package/lib/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +209 -209
  99. package/lib/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +24 -24
  100. package/lib/components/selfie/src/selfie-capture-review/index.js +3 -3
  101. package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +256 -239
  102. package/lib/components/selfie/src/selfie-capture-wrapper/index.ts +1 -1
  103. package/lib/components/selfie/src/smartselfie-capture/OvalProgress.tsx +81 -81
  104. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +265 -283
  105. package/lib/components/selfie/src/smartselfie-capture/components/AlertDisplay.tsx +34 -34
  106. package/lib/components/selfie/src/smartselfie-capture/components/CameraPreview.tsx +97 -97
  107. package/lib/components/selfie/src/smartselfie-capture/components/CaptureControls.tsx +78 -76
  108. package/lib/components/selfie/src/smartselfie-capture/components/index.ts +3 -3
  109. package/lib/components/selfie/src/smartselfie-capture/constants.ts +23 -23
  110. package/lib/components/selfie/src/smartselfie-capture/hooks/index.ts +2 -2
  111. package/lib/components/selfie/src/smartselfie-capture/hooks/useCamera.ts +238 -238
  112. package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +618 -617
  113. package/lib/components/selfie/src/smartselfie-capture/index.ts +1 -1
  114. package/lib/components/selfie/src/smartselfie-capture/utils/alertMessages.ts +13 -13
  115. package/lib/components/selfie/src/smartselfie-capture/utils/canvas.ts +105 -105
  116. package/lib/components/selfie/src/smartselfie-capture/utils/faceDetection.ts +129 -129
  117. package/lib/components/selfie/src/smartselfie-capture/utils/imageCapture.ts +64 -64
  118. package/lib/components/selfie/src/smartselfie-capture/utils/index.ts +4 -4
  119. package/lib/components/selfie/src/smartselfie-capture/utils/mediapipeManager.ts +77 -77
  120. package/lib/components/signature-pad/package-lock.json +3009 -3009
  121. package/lib/components/signature-pad/package.json +30 -30
  122. package/lib/components/signature-pad/src/SignaturePad.js +484 -484
  123. package/lib/components/signature-pad/src/SignaturePad.stories.js +32 -32
  124. package/lib/components/signature-pad/src/index.js +3 -3
  125. package/lib/components/smart-camera-web/src/README.md +206 -206
  126. package/lib/components/smart-camera-web/src/SmartCameraWeb.js +305 -305
  127. package/lib/components/smart-camera-web/src/SmartCameraWeb.stories.js +57 -57
  128. package/lib/components/totp-consent/src/TotpConsent.js +949 -949
  129. package/lib/components/totp-consent/src/index.js +4 -4
  130. package/lib/domain/camera/src/README.md +38 -38
  131. package/lib/domain/camera/src/SmartCamera.js +109 -109
  132. package/lib/domain/constants/src/Constants.js +27 -27
  133. package/lib/domain/file-upload/README.md +35 -35
  134. package/lib/domain/file-upload/src/SmartFileUpload.js +65 -65
  135. package/lib/styles/README.md +3 -3
  136. package/lib/styles/src/styles.js +372 -372
  137. package/lib/styles/src/typography.js +52 -52
  138. package/package.json +111 -112
  139. package/dist/esm/DocumentCaptureScreens-BjATTDqu.js.map +0 -1
  140. package/dist/esm/SelfieCaptureScreens-UUzZzl1A.js +0 -11361
  141. package/dist/esm/SelfieCaptureScreens-UUzZzl1A.js.map +0 -1
  142. package/dist/esm/package-CZlW6BZn.js.map +0 -1
@@ -1,484 +1,484 @@
1
- import SignaturePadCore from 'signature_pad';
2
-
3
- class SmartFileUpload {
4
- static memoryLimit = 2048000;
5
-
6
- static supportedTypes = ['image/jpeg', 'image/png', 'image/svg+xml'];
7
-
8
- static getHumanSize(numberOfBytes) {
9
- // Approximate to the closest prefixed unit
10
- const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
11
- const exponent = Math.min(
12
- Math.floor(Math.log(numberOfBytes) / Math.log(1024)),
13
- units.length - 1,
14
- );
15
- const approx = numberOfBytes / 1024 ** exponent;
16
- const output =
17
- exponent === 0
18
- ? `${numberOfBytes} bytes`
19
- : `${approx.toFixed(0)} ${units[exponent]}`;
20
-
21
- return output;
22
- }
23
-
24
- static getData(file) {
25
- return new Promise((resolve, reject) => {
26
- const reader = new FileReader();
27
-
28
- reader.onload = (e) => {
29
- resolve(e.target.result);
30
- };
31
- reader.onerror = () => {
32
- reject(
33
- new Error(
34
- 'An error occurred reading the file. Please check the file, and try again',
35
- ),
36
- );
37
- };
38
- reader.readAsDataURL(file);
39
- });
40
- }
41
-
42
- static async retrieve(files) {
43
- if (files.length > 1) {
44
- throw new Error('Only one file upload is permitted at a time');
45
- }
46
-
47
- const file = files[0];
48
-
49
- if (!SmartFileUpload.supportedTypes.includes(file.type)) {
50
- throw new Error(
51
- 'Unsupported file format. Please ensure that you are providing a JPG, PNG or SVG image',
52
- );
53
- }
54
-
55
- if (file.size > SmartFileUpload.memoryLimit) {
56
- throw new Error(
57
- `${
58
- file.name
59
- } is too large. Please ensure that the file is less than ${SmartFileUpload.getHumanSize(
60
- SmartFileUpload.memoryLimit,
61
- )}.`,
62
- );
63
- }
64
-
65
- const imageAsDataUrl = await SmartFileUpload.getData(file);
66
-
67
- return imageAsDataUrl;
68
- }
69
- }
70
-
71
- class SignaturePad extends HTMLElement {
72
- connectedCallback() {
73
- const shadow = this.attachShadow({ mode: 'open' });
74
-
75
- const style = document.createElement('style');
76
- style.textContent = `
77
- :host {
78
- display: block;
79
- block-size: auto;
80
- inline-size: 30rem;
81
- max-inline-size: 100%;
82
- position: relative;
83
- --color-active: #2D2B2A;
84
- --color-default: #001096;
85
- --color-disabled: #848282;
86
- }
87
-
88
- :host::part(upload) {
89
- text-align: center;
90
- }
91
-
92
- :host::part(signature-controls) {
93
- display: inline-flex;
94
- position: absolute;
95
- top: 1rem;
96
- right: 1rem;
97
- }
98
-
99
- :host::part(upload) svg + * {
100
- margin-inline-start: .5rem;
101
- }
102
-
103
- :host::part(canvas) {
104
- background-color: #F9F0E7;
105
- --dot-bg: #F9F0E7;
106
- --dot-color: black;
107
- --dot-size: 1px;
108
- --dot-space: 22px;
109
- background:
110
- linear-gradient(90deg, var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space),
111
- linear-gradient(var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space),
112
- var(--dot-color);
113
- border-radius: 2rem;
114
- inline-size: 30rem;
115
- max-inline-size: 100%;
116
- aspect-ratio: 2 / 1;
117
- }
118
-
119
- :host::part(upload-preview-image) {
120
- max-inline-size: 10rem;
121
- margin-inline: auto;
122
- }
123
-
124
- .visually-hidden {
125
- clip: rect(0 0 0 0);
126
- clip-path: inset(50%);
127
- height: 1px;
128
- overflow: hidden;
129
- position: absolute;
130
- white-space: nowrap;
131
- width: 1px;
132
- }
133
-
134
- button,
135
- label {
136
- font: inherit;
137
- cursor: pointer;
138
- }
139
-
140
- label {
141
- display: inline-flex;
142
- text-decoration: underline;
143
- }
144
-
145
- label svg + * {
146
- margin-inline-start: .5rem;
147
- }
148
-
149
- [type="file"] {
150
- display: none;
151
- }
152
-
153
- .center {
154
- text-align: center;
155
- margin-inline: auto;
156
- }
157
-
158
- .color-red {
159
- color: red;
160
- }
161
-
162
- button[data-variant="icon"] {
163
- appearance: none;
164
- -webkit-appearance: none;
165
- background-color: transparent;
166
- border: 0px;
167
- }
168
-
169
- button[data-variant="text"] {
170
- appearance: none;
171
- -webkit-appearance: none;
172
- background-color: transparent;
173
- border: 0px;
174
- text-decoration: underline;
175
- display: inline-flex;
176
- align-items: baseline;
177
- }
178
-
179
- button[data-variant="solid"] {
180
- --button-color: ${this.themeColor};
181
- border-radius: 2.5rem;
182
- border: 0;
183
- background-color: transparent;
184
- color: #fff;
185
- cursor: pointer;
186
- inline-size: 100%;
187
- display: inline-flex;
188
- align-items: center;
189
- justify-content: center;
190
- font-size: 18px;
191
- font-weight: 600;
192
- padding: .75rem 1.5rem;
193
- text-align: center;
194
- background-color: var(--button-color);
195
- border: 2px solid var(--button-color);
196
- }
197
-
198
- button:hover,
199
- button:focus,
200
- button:active {
201
- --button-color: var(--color-active);
202
- }
203
-
204
- button:disabled {
205
- --button-color: var(--color-disabled);
206
- }
207
- `;
208
-
209
- const wrapper = document.createElement('div');
210
- const errorMessage = document.createElement('div');
211
- errorMessage.innerHTML = `
212
- <p id="error" class="color-red | center"><p>
213
- `;
214
-
215
- const signatureControls = document.createElement('div');
216
- signatureControls.setAttribute('id', 'controls');
217
- signatureControls.setAttribute('part', 'signature-controls');
218
- signatureControls.innerHTML = `
219
- <button data-variant="icon" type="button" name="clear" id="clear">
220
- <span class="visually-hidden">
221
- Clear Signature
222
- </span>
223
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" width="18" viewBox="0 0 17 18">
224
- <path d="M3.314 15.646a8.004 8.004 0 01-2.217-4.257 8.06 8.06 0 01.545-4.655l1.789.788a6.062 6.062 0 001.264 6.737 6.033 6.033 0 008.551 0c2.358-2.37 2.358-6.224 0-8.592a5.996 5.996 0 00-4.405-1.782l.662 2.354-3.128-.796-3.127-.796 2.25-2.324L7.748 0l.55 1.953a7.966 7.966 0 016.33 2.326 8.004 8.004 0 012.342 5.684 8.005 8.005 0 01-2.343 5.683A7.928 7.928 0 018.97 18a7.928 7.928 0 01-5.656-2.354z" fill="currentColor" />
225
- </svg>
226
- </button>
227
- `;
228
-
229
- const canvas = document.createElement('canvas');
230
-
231
- /**
232
- * NOTE: In order to make this responsive, we need to calculate the width
233
- * / height of the canvas element relative to the closest visible element
234
- *
235
- * Within our consuming context, we do follow these steps:
236
- * - Find the closest ancestor that is visible
237
- * - Find the visible child of that ancestor
238
- * - Get the reference width / inline-size of the element
239
- * - Compute the canvas.height as the lesser of the reference width or the
240
- * inline size of the canvas set in CSS
241
- * - Compute the canvas.height as half the size of the canvas.width or the
242
- * block size computed by the aspect-ratio property
243
- */
244
-
245
- const closestVisibleAncestor = this.parentElement.closest(':not([hidden])');
246
- const visibleChild = closestVisibleAncestor.querySelector(':not([hidden])');
247
- const containerWidth = visibleChild.offsetWidth;
248
-
249
- const remInPx = getComputedStyle(document.documentElement).fontSize.split(
250
- 'px',
251
- )[0];
252
- const componentMaxInlineSize = 30 * remInPx; // 30rem from the style declaration
253
- const aspectRatio = 2; // 2 from the canvas style aspect ratio declaration
254
-
255
- canvas.width =
256
- containerWidth < componentMaxInlineSize
257
- ? containerWidth
258
- : componentMaxInlineSize;
259
- canvas.height =
260
- (containerWidth < componentMaxInlineSize
261
- ? containerWidth
262
- : componentMaxInlineSize) / aspectRatio;
263
-
264
- canvas.setAttribute('id', 'signature-canvas');
265
- canvas.setAttribute('part', 'canvas');
266
-
267
- const uploadControl = document.createElement('div');
268
- uploadControl.setAttribute('id', 'signature-upload-wrapper');
269
- uploadControl.innerHTML = `
270
- <p part="upload">
271
- <strong>or</strong>
272
- <label>
273
- <input type='file' onclick='this.value=null;' id='upload-signature' accept='image/jpeg, image/png, image/svg+xml' />
274
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
275
- <rect width="16" height="16" fill="#F9F0E7" rx="2"/>
276
- <mask id="sign" width="16" height="16" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha">
277
- <rect width="16" height="16" fill="#D9D9D9" rx="2"/>
278
- </mask>
279
- <g mask="url(#sign)">
280
- <path fill="#2D2B2A" d="M0 13.333h16V16H0z"/>
281
- </g>
282
- <path fill="#2D2B2A" fill-rule="evenodd" d="M2.69 7.346a.23.23 0 0 1 .059-.073.275.275 0 0 1 .284-.034c.07.036.146.064.224.084h.12c0-.012.105-.048.105-.395a.796.796 0 0 1 .211-.61.667.667 0 0 1 .607-.096c.202.061.39.154.555.275.194.138.38.286.555.443.146.134.31.25.489.347l.092.047c.119.06.238-.023.185-.143l-.04-.12a.817.817 0 0 1-.066-.694.675.675 0 0 1 .244-.273.774.774 0 0 1 .364-.12c.324-.028.651 0 .964.083h.026c.027-.861.225-1.83.82-2.523.593-.694 1.478-.993 2.205-.837.726.155 1.307.73 1.307 1.602 0 1.34-.872 2.26-1.915 2.93.471.374.85.835 1.11 1.351.027.046.05.094.065.144h.014a.55.55 0 0 0 .079.203.44.44 0 0 0 .04.18c.002.025.011.05.026.07h-.013c.037.17.041.344.013.515a.955.955 0 0 1-.188.493 1.097 1.097 0 0 1-.433.344 1.346 1.346 0 0 1-1.122.012 2.194 2.194 0 0 1-.846-.67 5.024 5.024 0 0 1-.462-.681h-.026a.502.502 0 0 0-.106-.144.014.014 0 0 1-.01-.003.011.011 0 0 1-.003-.009.035.035 0 0 1-.013-.023.047.047 0 0 1-.011-.017.626.626 0 0 0-.055-.163.24.24 0 0 0-.014-.095h.014a3.477 3.477 0 0 1-.198-.49 6.318 6.318 0 0 1-.278-1.699L7.51 6.51c-.243-.07-.5-.094-.753-.071-.158.024-.198.071-.211.107-.014.036-.04.168.053.359.092.191.171.478.118.658a.45.45 0 0 1-.21.25.66.66 0 0 1-.305.06 1.521 1.521 0 0 1-.568-.19 3.533 3.533 0 0 1-.58-.42 3.54 3.54 0 0 0-.49-.394 1.49 1.49 0 0 0-.409-.203c-.053-.024-.079-.024-.092-.012H4.05c-.014.012-.066.072-.066.275 0 .395-.12.705-.423.813a.813.813 0 0 1-.41.024 1.646 1.646 0 0 1-.343-.12.237.237 0 0 1-.126-.127.208.208 0 0 1 .007-.172Zm5.731.766c.068.204.152.404.251.598.011.053.03.105.053.155.036.068.08.132.132.191.02.05.047.099.08.144.085.152.186.296.303.43.167.22.389.4.647.526a.847.847 0 0 0 .687-.011.655.655 0 0 0 .247-.204.573.573 0 0 0 .11-.287 1.435 1.435 0 0 0-.04-.454h-.014a.496.496 0 0 0-.079-.335.491.491 0 0 0-.119-.191 3.842 3.842 0 0 0-1.017-1.16l-.356.18a.253.253 0 0 1-.193.027.248.248 0 0 1-.088-.041.22.22 0 0 1-.063-.07.219.219 0 0 1-.02-.172.242.242 0 0 1 .113-.14l.172-.083a3.388 3.388 0 0 0-.463-.251l-.58-.24c.022.47.101.935.237 1.388Zm.568-1.555c.24.12.474.252.7.395 1.017-.634 1.81-1.459 1.81-2.63 0-.67-.41-1.053-.912-1.16-.502-.108-1.189.083-1.704.669-.515.586-.7 1.47-.713 2.32v.083c.251.084.515.192.819.323Z" clip-rule="evenodd"/>
283
- </svg>
284
- <span>upload a signature</span>
285
- </label>
286
- </p>
287
- `;
288
-
289
- const publishSignatureContainer = document.createElement('p');
290
- publishSignatureContainer.innerHTML = `
291
- <button data-variant="solid" type="button" name="publish" id="publish">
292
- <span>
293
- Continue
294
- </span>
295
- <svg
296
- aria-hidden="true"
297
- width="25"
298
- height="24"
299
- fill="none"
300
- xmlns="http://www.w3.org/2000/svg"
301
- >
302
- <path
303
- d="M7 12h11m0 0-4.588-4M18 12l-4.588 4"
304
- stroke="#fff"
305
- stroke-width="1.5"
306
- stroke-linecap="round"
307
- stroke-linejoin="round"
308
- />
309
- </svg>
310
- </button>
311
- `;
312
-
313
- wrapper.appendChild(errorMessage);
314
- wrapper.appendChild(signatureControls);
315
- wrapper.appendChild(canvas);
316
- if (this.allowUpload) wrapper.appendChild(uploadControl);
317
- wrapper.appendChild(publishSignatureContainer);
318
-
319
- shadow.appendChild(style);
320
- shadow.appendChild(wrapper);
321
-
322
- this.core = new SignaturePadCore(canvas);
323
-
324
- // Error Message
325
- this.errorMessage = errorMessage.querySelector('#error');
326
-
327
- // Canvas Resize / Sizing
328
- if (window) {
329
- window.onresize = this.resizeCanvas();
330
- }
331
-
332
- // Signature Pad Controls
333
- this.clearSignatureButton = signatureControls.querySelector('#clear');
334
- this.clearSignatureButton.addEventListener('click', () =>
335
- this.clearSignature(),
336
- );
337
-
338
- // Upload Controls
339
- this.uploadSignatureButton =
340
- uploadControl.querySelector('#upload-signature');
341
- this.uploadSignatureButton.addEventListener('change', (event) =>
342
- this.uploadSignature(event),
343
- );
344
-
345
- // Publish Signature
346
- this.publishSignatureButton =
347
- publishSignatureContainer.querySelector('#publish');
348
- this.publishSignatureButton.addEventListener('click', () =>
349
- this.publishSignature(),
350
- );
351
- }
352
-
353
- disconnectedCallback() {
354
- this.publishSignatureButton.removeEventListener('click', () =>
355
- this.publishSignature(),
356
- );
357
- this.clearSignatureButton.removeEventListener('click', () =>
358
- this.clearSignature(),
359
- );
360
- this.uploadSignatureButton.removeEventListener('change', (event) =>
361
- this.uploadSignature(event),
362
- );
363
- }
364
-
365
- // Adjust canvas coordinate space taking into account pixel ratio,
366
- // to make it look crisp on mobile devices.
367
- // This also causes canvas to be cleared.
368
- resizeCanvas() {
369
- // When zoomed out to less than 100%, for some very strange reason,
370
- // some browsers report devicePixelRatio as less than 1
371
- // and only part of the canvas is cleared then.
372
- const canvas = this.shadowRoot.querySelector('canvas');
373
- const ratio = Math.max(window.devicePixelRatio || 1, 1);
374
-
375
- // This part causes the canvas to be cleared
376
- canvas.width = (canvas.offsetWidth || canvas.width) * ratio;
377
- canvas.height = (canvas.offsetHeight || canvas.height) * ratio;
378
- canvas.getContext('2d').scale(ratio, ratio);
379
-
380
- // This library does not listen for canvas changes, so after the canvas is automatically
381
- // cleared by the browser, SignaturePad#isEmpty might still return false, even though the
382
- // canvas looks empty, because the internal data of this library wasn't cleared. To make sure
383
- // that the state of this library is consistent with visual state of the canvas, you
384
- // have to clear it manually.
385
- // signaturePad.clear();
386
-
387
- // If you want to keep the drawing on resize instead of clearing it you can reset the data.
388
- this.core.fromData(this.core.toData());
389
- }
390
-
391
- publishSignature() {
392
- try {
393
- this.resetErrorMessage();
394
- const previewImage = this.shadowRoot.querySelector('img');
395
- let image = previewImage ? previewImage.src : undefined;
396
- if (!image && !this.core.isEmpty()) {
397
- image = this.core.toDataURL('image/svg+xml');
398
- }
399
-
400
- if (image) {
401
- this.dispatchEvent(
402
- new CustomEvent('signature-pad.publish', {
403
- detail: image,
404
- }),
405
- );
406
- } else {
407
- throw new Error(
408
- `No signature present. ${
409
- this.allowUpload ? 'Draw or upload' : 'Draw'
410
- } a signature`,
411
- );
412
- }
413
- } catch (error) {
414
- this.handleError(error.message);
415
- }
416
- }
417
-
418
- resetErrorMessage() {
419
- this.errorMessage.textContent = '';
420
- }
421
-
422
- handleError(error) {
423
- this.errorMessage.textContent = error;
424
- }
425
-
426
- clearSignature() {
427
- this.resetErrorMessage();
428
- const canvas = this.shadowRoot.querySelector('canvas');
429
- const img = this.shadowRoot.querySelector('img');
430
-
431
- if (img) {
432
- img.remove();
433
- canvas.removeAttribute('hidden');
434
- }
435
-
436
- this.core.clear();
437
- }
438
-
439
- previewUpload(fileData) {
440
- const canvas = this.shadowRoot.querySelector('canvas');
441
- let img = this.shadowRoot.querySelector('img');
442
-
443
- if (!img) {
444
- img = document.createElement('img');
445
- }
446
-
447
- img.src = fileData;
448
- img.setAttribute('part', 'upload-preview-image');
449
- canvas.setAttribute('hidden', true);
450
- canvas.insertAdjacentElement('afterend', img);
451
- }
452
-
453
- async uploadSignature(event) {
454
- try {
455
- this.resetErrorMessage();
456
- const { files } = event.target;
457
-
458
- // validate file, and convert file to data url
459
- const fileData = await SmartFileUpload.retrieve(files);
460
-
461
- // swap out canvas for an image for preview
462
- this.previewUpload(fileData);
463
- } catch (error) {
464
- this.handleError(error.message);
465
- }
466
- }
467
-
468
- get allowUpload() {
469
- return this.hasAttribute('allow-upload');
470
- }
471
-
472
- get themeColor() {
473
- return this.getAttribute('theme-color') || '#001096';
474
- }
475
- }
476
-
477
- if (
478
- 'customElements' in window &&
479
- !window.customElements.get('smileid-signature-pad')
480
- ) {
481
- window.customElements.define('smileid-signature-pad', SignaturePad);
482
- }
483
-
484
- export default SignaturePad;
1
+ import SignaturePadCore from 'signature_pad';
2
+
3
+ class SmartFileUpload {
4
+ static memoryLimit = 2048000;
5
+
6
+ static supportedTypes = ['image/jpeg', 'image/png', 'image/svg+xml'];
7
+
8
+ static getHumanSize(numberOfBytes) {
9
+ // Approximate to the closest prefixed unit
10
+ const units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
11
+ const exponent = Math.min(
12
+ Math.floor(Math.log(numberOfBytes) / Math.log(1024)),
13
+ units.length - 1,
14
+ );
15
+ const approx = numberOfBytes / 1024 ** exponent;
16
+ const output =
17
+ exponent === 0
18
+ ? `${numberOfBytes} bytes`
19
+ : `${approx.toFixed(0)} ${units[exponent]}`;
20
+
21
+ return output;
22
+ }
23
+
24
+ static getData(file) {
25
+ return new Promise((resolve, reject) => {
26
+ const reader = new FileReader();
27
+
28
+ reader.onload = (e) => {
29
+ resolve(e.target.result);
30
+ };
31
+ reader.onerror = () => {
32
+ reject(
33
+ new Error(
34
+ 'An error occurred reading the file. Please check the file, and try again',
35
+ ),
36
+ );
37
+ };
38
+ reader.readAsDataURL(file);
39
+ });
40
+ }
41
+
42
+ static async retrieve(files) {
43
+ if (files.length > 1) {
44
+ throw new Error('Only one file upload is permitted at a time');
45
+ }
46
+
47
+ const file = files[0];
48
+
49
+ if (!SmartFileUpload.supportedTypes.includes(file.type)) {
50
+ throw new Error(
51
+ 'Unsupported file format. Please ensure that you are providing a JPG, PNG or SVG image',
52
+ );
53
+ }
54
+
55
+ if (file.size > SmartFileUpload.memoryLimit) {
56
+ throw new Error(
57
+ `${
58
+ file.name
59
+ } is too large. Please ensure that the file is less than ${SmartFileUpload.getHumanSize(
60
+ SmartFileUpload.memoryLimit,
61
+ )}.`,
62
+ );
63
+ }
64
+
65
+ const imageAsDataUrl = await SmartFileUpload.getData(file);
66
+
67
+ return imageAsDataUrl;
68
+ }
69
+ }
70
+
71
+ class SignaturePad extends HTMLElement {
72
+ connectedCallback() {
73
+ const shadow = this.attachShadow({ mode: 'open' });
74
+
75
+ const style = document.createElement('style');
76
+ style.textContent = `
77
+ :host {
78
+ display: block;
79
+ block-size: auto;
80
+ inline-size: 30rem;
81
+ max-inline-size: 100%;
82
+ position: relative;
83
+ --color-active: #2D2B2A;
84
+ --color-default: #001096;
85
+ --color-disabled: #848282;
86
+ }
87
+
88
+ :host::part(upload) {
89
+ text-align: center;
90
+ }
91
+
92
+ :host::part(signature-controls) {
93
+ display: inline-flex;
94
+ position: absolute;
95
+ top: 1rem;
96
+ right: 1rem;
97
+ }
98
+
99
+ :host::part(upload) svg + * {
100
+ margin-inline-start: .5rem;
101
+ }
102
+
103
+ :host::part(canvas) {
104
+ background-color: #F9F0E7;
105
+ --dot-bg: #F9F0E7;
106
+ --dot-color: black;
107
+ --dot-size: 1px;
108
+ --dot-space: 22px;
109
+ background:
110
+ linear-gradient(90deg, var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space),
111
+ linear-gradient(var(--dot-bg) calc(var(--dot-space) - var(--dot-size)), transparent 1%) center / var(--dot-space) var(--dot-space),
112
+ var(--dot-color);
113
+ border-radius: 2rem;
114
+ inline-size: 30rem;
115
+ max-inline-size: 100%;
116
+ aspect-ratio: 2 / 1;
117
+ }
118
+
119
+ :host::part(upload-preview-image) {
120
+ max-inline-size: 10rem;
121
+ margin-inline: auto;
122
+ }
123
+
124
+ .visually-hidden {
125
+ clip: rect(0 0 0 0);
126
+ clip-path: inset(50%);
127
+ height: 1px;
128
+ overflow: hidden;
129
+ position: absolute;
130
+ white-space: nowrap;
131
+ width: 1px;
132
+ }
133
+
134
+ button,
135
+ label {
136
+ font: inherit;
137
+ cursor: pointer;
138
+ }
139
+
140
+ label {
141
+ display: inline-flex;
142
+ text-decoration: underline;
143
+ }
144
+
145
+ label svg + * {
146
+ margin-inline-start: .5rem;
147
+ }
148
+
149
+ [type="file"] {
150
+ display: none;
151
+ }
152
+
153
+ .center {
154
+ text-align: center;
155
+ margin-inline: auto;
156
+ }
157
+
158
+ .color-red {
159
+ color: red;
160
+ }
161
+
162
+ button[data-variant="icon"] {
163
+ appearance: none;
164
+ -webkit-appearance: none;
165
+ background-color: transparent;
166
+ border: 0px;
167
+ }
168
+
169
+ button[data-variant="text"] {
170
+ appearance: none;
171
+ -webkit-appearance: none;
172
+ background-color: transparent;
173
+ border: 0px;
174
+ text-decoration: underline;
175
+ display: inline-flex;
176
+ align-items: baseline;
177
+ }
178
+
179
+ button[data-variant="solid"] {
180
+ --button-color: ${this.themeColor};
181
+ border-radius: 2.5rem;
182
+ border: 0;
183
+ background-color: transparent;
184
+ color: #fff;
185
+ cursor: pointer;
186
+ inline-size: 100%;
187
+ display: inline-flex;
188
+ align-items: center;
189
+ justify-content: center;
190
+ font-size: 18px;
191
+ font-weight: 600;
192
+ padding: .75rem 1.5rem;
193
+ text-align: center;
194
+ background-color: var(--button-color);
195
+ border: 2px solid var(--button-color);
196
+ }
197
+
198
+ button:hover,
199
+ button:focus,
200
+ button:active {
201
+ --button-color: var(--color-active);
202
+ }
203
+
204
+ button:disabled {
205
+ --button-color: var(--color-disabled);
206
+ }
207
+ `;
208
+
209
+ const wrapper = document.createElement('div');
210
+ const errorMessage = document.createElement('div');
211
+ errorMessage.innerHTML = `
212
+ <p id="error" class="color-red | center"><p>
213
+ `;
214
+
215
+ const signatureControls = document.createElement('div');
216
+ signatureControls.setAttribute('id', 'controls');
217
+ signatureControls.setAttribute('part', 'signature-controls');
218
+ signatureControls.innerHTML = `
219
+ <button data-variant="icon" type="button" name="clear" id="clear">
220
+ <span class="visually-hidden">
221
+ Clear Signature
222
+ </span>
223
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" width="18" viewBox="0 0 17 18">
224
+ <path d="M3.314 15.646a8.004 8.004 0 01-2.217-4.257 8.06 8.06 0 01.545-4.655l1.789.788a6.062 6.062 0 001.264 6.737 6.033 6.033 0 008.551 0c2.358-2.37 2.358-6.224 0-8.592a5.996 5.996 0 00-4.405-1.782l.662 2.354-3.128-.796-3.127-.796 2.25-2.324L7.748 0l.55 1.953a7.966 7.966 0 016.33 2.326 8.004 8.004 0 012.342 5.684 8.005 8.005 0 01-2.343 5.683A7.928 7.928 0 018.97 18a7.928 7.928 0 01-5.656-2.354z" fill="currentColor" />
225
+ </svg>
226
+ </button>
227
+ `;
228
+
229
+ const canvas = document.createElement('canvas');
230
+
231
+ /**
232
+ * NOTE: In order to make this responsive, we need to calculate the width
233
+ * / height of the canvas element relative to the closest visible element
234
+ *
235
+ * Within our consuming context, we do follow these steps:
236
+ * - Find the closest ancestor that is visible
237
+ * - Find the visible child of that ancestor
238
+ * - Get the reference width / inline-size of the element
239
+ * - Compute the canvas.height as the lesser of the reference width or the
240
+ * inline size of the canvas set in CSS
241
+ * - Compute the canvas.height as half the size of the canvas.width or the
242
+ * block size computed by the aspect-ratio property
243
+ */
244
+
245
+ const closestVisibleAncestor = this.parentElement.closest(':not([hidden])');
246
+ const visibleChild = closestVisibleAncestor.querySelector(':not([hidden])');
247
+ const containerWidth = visibleChild.offsetWidth;
248
+
249
+ const remInPx = getComputedStyle(document.documentElement).fontSize.split(
250
+ 'px',
251
+ )[0];
252
+ const componentMaxInlineSize = 30 * remInPx; // 30rem from the style declaration
253
+ const aspectRatio = 2; // 2 from the canvas style aspect ratio declaration
254
+
255
+ canvas.width =
256
+ containerWidth < componentMaxInlineSize
257
+ ? containerWidth
258
+ : componentMaxInlineSize;
259
+ canvas.height =
260
+ (containerWidth < componentMaxInlineSize
261
+ ? containerWidth
262
+ : componentMaxInlineSize) / aspectRatio;
263
+
264
+ canvas.setAttribute('id', 'signature-canvas');
265
+ canvas.setAttribute('part', 'canvas');
266
+
267
+ const uploadControl = document.createElement('div');
268
+ uploadControl.setAttribute('id', 'signature-upload-wrapper');
269
+ uploadControl.innerHTML = `
270
+ <p part="upload">
271
+ <strong>or</strong>
272
+ <label>
273
+ <input type='file' onclick='this.value=null;' id='upload-signature' accept='image/jpeg, image/png, image/svg+xml' />
274
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
275
+ <rect width="16" height="16" fill="#F9F0E7" rx="2"/>
276
+ <mask id="sign" width="16" height="16" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha">
277
+ <rect width="16" height="16" fill="#D9D9D9" rx="2"/>
278
+ </mask>
279
+ <g mask="url(#sign)">
280
+ <path fill="#2D2B2A" d="M0 13.333h16V16H0z"/>
281
+ </g>
282
+ <path fill="#2D2B2A" fill-rule="evenodd" d="M2.69 7.346a.23.23 0 0 1 .059-.073.275.275 0 0 1 .284-.034c.07.036.146.064.224.084h.12c0-.012.105-.048.105-.395a.796.796 0 0 1 .211-.61.667.667 0 0 1 .607-.096c.202.061.39.154.555.275.194.138.38.286.555.443.146.134.31.25.489.347l.092.047c.119.06.238-.023.185-.143l-.04-.12a.817.817 0 0 1-.066-.694.675.675 0 0 1 .244-.273.774.774 0 0 1 .364-.12c.324-.028.651 0 .964.083h.026c.027-.861.225-1.83.82-2.523.593-.694 1.478-.993 2.205-.837.726.155 1.307.73 1.307 1.602 0 1.34-.872 2.26-1.915 2.93.471.374.85.835 1.11 1.351.027.046.05.094.065.144h.014a.55.55 0 0 0 .079.203.44.44 0 0 0 .04.18c.002.025.011.05.026.07h-.013c.037.17.041.344.013.515a.955.955 0 0 1-.188.493 1.097 1.097 0 0 1-.433.344 1.346 1.346 0 0 1-1.122.012 2.194 2.194 0 0 1-.846-.67 5.024 5.024 0 0 1-.462-.681h-.026a.502.502 0 0 0-.106-.144.014.014 0 0 1-.01-.003.011.011 0 0 1-.003-.009.035.035 0 0 1-.013-.023.047.047 0 0 1-.011-.017.626.626 0 0 0-.055-.163.24.24 0 0 0-.014-.095h.014a3.477 3.477 0 0 1-.198-.49 6.318 6.318 0 0 1-.278-1.699L7.51 6.51c-.243-.07-.5-.094-.753-.071-.158.024-.198.071-.211.107-.014.036-.04.168.053.359.092.191.171.478.118.658a.45.45 0 0 1-.21.25.66.66 0 0 1-.305.06 1.521 1.521 0 0 1-.568-.19 3.533 3.533 0 0 1-.58-.42 3.54 3.54 0 0 0-.49-.394 1.49 1.49 0 0 0-.409-.203c-.053-.024-.079-.024-.092-.012H4.05c-.014.012-.066.072-.066.275 0 .395-.12.705-.423.813a.813.813 0 0 1-.41.024 1.646 1.646 0 0 1-.343-.12.237.237 0 0 1-.126-.127.208.208 0 0 1 .007-.172Zm5.731.766c.068.204.152.404.251.598.011.053.03.105.053.155.036.068.08.132.132.191.02.05.047.099.08.144.085.152.186.296.303.43.167.22.389.4.647.526a.847.847 0 0 0 .687-.011.655.655 0 0 0 .247-.204.573.573 0 0 0 .11-.287 1.435 1.435 0 0 0-.04-.454h-.014a.496.496 0 0 0-.079-.335.491.491 0 0 0-.119-.191 3.842 3.842 0 0 0-1.017-1.16l-.356.18a.253.253 0 0 1-.193.027.248.248 0 0 1-.088-.041.22.22 0 0 1-.063-.07.219.219 0 0 1-.02-.172.242.242 0 0 1 .113-.14l.172-.083a3.388 3.388 0 0 0-.463-.251l-.58-.24c.022.47.101.935.237 1.388Zm.568-1.555c.24.12.474.252.7.395 1.017-.634 1.81-1.459 1.81-2.63 0-.67-.41-1.053-.912-1.16-.502-.108-1.189.083-1.704.669-.515.586-.7 1.47-.713 2.32v.083c.251.084.515.192.819.323Z" clip-rule="evenodd"/>
283
+ </svg>
284
+ <span>upload a signature</span>
285
+ </label>
286
+ </p>
287
+ `;
288
+
289
+ const publishSignatureContainer = document.createElement('p');
290
+ publishSignatureContainer.innerHTML = `
291
+ <button data-variant="solid" type="button" name="publish" id="publish">
292
+ <span>
293
+ Continue
294
+ </span>
295
+ <svg
296
+ aria-hidden="true"
297
+ width="25"
298
+ height="24"
299
+ fill="none"
300
+ xmlns="http://www.w3.org/2000/svg"
301
+ >
302
+ <path
303
+ d="M7 12h11m0 0-4.588-4M18 12l-4.588 4"
304
+ stroke="#fff"
305
+ stroke-width="1.5"
306
+ stroke-linecap="round"
307
+ stroke-linejoin="round"
308
+ />
309
+ </svg>
310
+ </button>
311
+ `;
312
+
313
+ wrapper.appendChild(errorMessage);
314
+ wrapper.appendChild(signatureControls);
315
+ wrapper.appendChild(canvas);
316
+ if (this.allowUpload) wrapper.appendChild(uploadControl);
317
+ wrapper.appendChild(publishSignatureContainer);
318
+
319
+ shadow.appendChild(style);
320
+ shadow.appendChild(wrapper);
321
+
322
+ this.core = new SignaturePadCore(canvas);
323
+
324
+ // Error Message
325
+ this.errorMessage = errorMessage.querySelector('#error');
326
+
327
+ // Canvas Resize / Sizing
328
+ if (window) {
329
+ window.onresize = this.resizeCanvas();
330
+ }
331
+
332
+ // Signature Pad Controls
333
+ this.clearSignatureButton = signatureControls.querySelector('#clear');
334
+ this.clearSignatureButton.addEventListener('click', () =>
335
+ this.clearSignature(),
336
+ );
337
+
338
+ // Upload Controls
339
+ this.uploadSignatureButton =
340
+ uploadControl.querySelector('#upload-signature');
341
+ this.uploadSignatureButton.addEventListener('change', (event) =>
342
+ this.uploadSignature(event),
343
+ );
344
+
345
+ // Publish Signature
346
+ this.publishSignatureButton =
347
+ publishSignatureContainer.querySelector('#publish');
348
+ this.publishSignatureButton.addEventListener('click', () =>
349
+ this.publishSignature(),
350
+ );
351
+ }
352
+
353
+ disconnectedCallback() {
354
+ this.publishSignatureButton.removeEventListener('click', () =>
355
+ this.publishSignature(),
356
+ );
357
+ this.clearSignatureButton.removeEventListener('click', () =>
358
+ this.clearSignature(),
359
+ );
360
+ this.uploadSignatureButton.removeEventListener('change', (event) =>
361
+ this.uploadSignature(event),
362
+ );
363
+ }
364
+
365
+ // Adjust canvas coordinate space taking into account pixel ratio,
366
+ // to make it look crisp on mobile devices.
367
+ // This also causes canvas to be cleared.
368
+ resizeCanvas() {
369
+ // When zoomed out to less than 100%, for some very strange reason,
370
+ // some browsers report devicePixelRatio as less than 1
371
+ // and only part of the canvas is cleared then.
372
+ const canvas = this.shadowRoot.querySelector('canvas');
373
+ const ratio = Math.max(window.devicePixelRatio || 1, 1);
374
+
375
+ // This part causes the canvas to be cleared
376
+ canvas.width = (canvas.offsetWidth || canvas.width) * ratio;
377
+ canvas.height = (canvas.offsetHeight || canvas.height) * ratio;
378
+ canvas.getContext('2d').scale(ratio, ratio);
379
+
380
+ // This library does not listen for canvas changes, so after the canvas is automatically
381
+ // cleared by the browser, SignaturePad#isEmpty might still return false, even though the
382
+ // canvas looks empty, because the internal data of this library wasn't cleared. To make sure
383
+ // that the state of this library is consistent with visual state of the canvas, you
384
+ // have to clear it manually.
385
+ // signaturePad.clear();
386
+
387
+ // If you want to keep the drawing on resize instead of clearing it you can reset the data.
388
+ this.core.fromData(this.core.toData());
389
+ }
390
+
391
+ publishSignature() {
392
+ try {
393
+ this.resetErrorMessage();
394
+ const previewImage = this.shadowRoot.querySelector('img');
395
+ let image = previewImage ? previewImage.src : undefined;
396
+ if (!image && !this.core.isEmpty()) {
397
+ image = this.core.toDataURL('image/svg+xml');
398
+ }
399
+
400
+ if (image) {
401
+ this.dispatchEvent(
402
+ new CustomEvent('signature-pad.publish', {
403
+ detail: image,
404
+ }),
405
+ );
406
+ } else {
407
+ throw new Error(
408
+ `No signature present. ${
409
+ this.allowUpload ? 'Draw or upload' : 'Draw'
410
+ } a signature`,
411
+ );
412
+ }
413
+ } catch (error) {
414
+ this.handleError(error.message);
415
+ }
416
+ }
417
+
418
+ resetErrorMessage() {
419
+ this.errorMessage.textContent = '';
420
+ }
421
+
422
+ handleError(error) {
423
+ this.errorMessage.textContent = error;
424
+ }
425
+
426
+ clearSignature() {
427
+ this.resetErrorMessage();
428
+ const canvas = this.shadowRoot.querySelector('canvas');
429
+ const img = this.shadowRoot.querySelector('img');
430
+
431
+ if (img) {
432
+ img.remove();
433
+ canvas.removeAttribute('hidden');
434
+ }
435
+
436
+ this.core.clear();
437
+ }
438
+
439
+ previewUpload(fileData) {
440
+ const canvas = this.shadowRoot.querySelector('canvas');
441
+ let img = this.shadowRoot.querySelector('img');
442
+
443
+ if (!img) {
444
+ img = document.createElement('img');
445
+ }
446
+
447
+ img.src = fileData;
448
+ img.setAttribute('part', 'upload-preview-image');
449
+ canvas.setAttribute('hidden', true);
450
+ canvas.insertAdjacentElement('afterend', img);
451
+ }
452
+
453
+ async uploadSignature(event) {
454
+ try {
455
+ this.resetErrorMessage();
456
+ const { files } = event.target;
457
+
458
+ // validate file, and convert file to data url
459
+ const fileData = await SmartFileUpload.retrieve(files);
460
+
461
+ // swap out canvas for an image for preview
462
+ this.previewUpload(fileData);
463
+ } catch (error) {
464
+ this.handleError(error.message);
465
+ }
466
+ }
467
+
468
+ get allowUpload() {
469
+ return this.hasAttribute('allow-upload');
470
+ }
471
+
472
+ get themeColor() {
473
+ return this.getAttribute('theme-color') || '#001096';
474
+ }
475
+ }
476
+
477
+ if (
478
+ 'customElements' in window &&
479
+ !window.customElements.get('smileid-signature-pad')
480
+ ) {
481
+ window.customElements.define('smileid-signature-pad', SignaturePad);
482
+ }
483
+
484
+ export default SignaturePad;