@smileid/web-components 1.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +72 -0
- package/components/README.md +14 -0
- package/components/attribution/PoweredBySmileId.js +42 -0
- package/components/camera-permission/CameraPermission.js +136 -0
- package/components/camera-permission/CameraPermission.stories.js +21 -0
- package/components/combobox/src/Combobox.js +586 -0
- package/components/combobox/src/index.js +1 -0
- package/components/document/src/DocumentCaptureScreens.js +317 -0
- package/components/document/src/DocumentCaptureScreens.stories.js +51 -0
- package/components/document/src/README.md +108 -0
- package/components/document/src/document-capture/DocumentCapture.js +677 -0
- package/components/document/src/document-capture/DocumentCapture.stories.js +71 -0
- package/components/document/src/document-capture/README.md +89 -0
- package/components/document/src/document-capture/index.js +3 -0
- package/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +499 -0
- package/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +17 -0
- package/components/document/src/document-capture-instructions/README.md +56 -0
- package/components/document/src/document-capture-instructions/index.js +3 -0
- package/components/document/src/document-capture-review/DocumentCaptureReview.js +378 -0
- package/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +14 -0
- package/components/document/src/document-capture-review/README.md +79 -0
- package/components/document/src/document-capture-review/index.js +3 -0
- package/components/document/src/index.js +3 -0
- package/components/end-user-consent/src/EndUserConsent.js +809 -0
- package/components/end-user-consent/src/EndUserConsent.stories.js +23 -0
- package/components/end-user-consent/src/index.js +4 -0
- package/components/navigation/src/Navigation.js +160 -0
- package/components/navigation/src/Navigation.stories.js +24 -0
- package/components/navigation/src/index.js +3 -0
- package/components/selfie/README.md +201 -0
- package/components/selfie/src/SelfieCaptureScreens.js +224 -0
- package/components/selfie/src/SelfieCaptureScreens.stories.js +21 -0
- package/components/selfie/src/index.js +5 -0
- package/components/selfie/src/selfie-capture/SelfieCapture.js +878 -0
- package/components/selfie/src/selfie-capture/SelfieCapture.stories.js +23 -0
- package/components/selfie/src/selfie-capture/index.js +3 -0
- package/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +638 -0
- package/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +17 -0
- package/components/selfie/src/selfie-capture-instructions/index.js +3 -0
- package/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +348 -0
- package/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +17 -0
- package/components/selfie/src/selfie-capture-review/index.js +3 -0
- package/components/signature-pad/package.json +30 -0
- package/components/signature-pad/src/SignaturePad.js +477 -0
- package/components/signature-pad/src/SignaturePad.stories.js +24 -0
- package/components/signature-pad/src/index.js +3 -0
- package/components/smart-camera-web/src/SmartCameraWeb.js +246 -0
- package/components/smart-camera-web/src/SmartCameraWeb.stories.js +35 -0
- package/components/totp-consent/src/TotpConsent.js +935 -0
- package/components/totp-consent/src/index.js +4 -0
- package/cypress/e2e/smart-camera-web-attribution.cy.js +21 -0
- package/cypress/e2e/smart-camera-web-back-press.cy.js +481 -0
- package/cypress/e2e/smart-camera-web-hide-instructions.cy.js +334 -0
- package/cypress/e2e/smart-camera-web.cy.js +309 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/pages/capture-back-of-id-hide-attribution.html +44 -0
- package/cypress/pages/capture-back-of-id-navigation.html +72 -0
- package/cypress/pages/smart-camera-web-hide-instructions.html +38 -0
- package/cypress/pages/smart-camera-web.html +38 -0
- package/cypress/support/commands.js +144 -0
- package/cypress/support/e2e.js +20 -0
- package/cypress.config.js +11 -0
- package/domain/camera/src/README.md +38 -0
- package/domain/camera/src/SmartCamera.js +81 -0
- package/domain/constants/src/Constants.js +27 -0
- package/domain/file-upload/README.md +35 -0
- package/domain/file-upload/src/SmartFileUpload.js +65 -0
- package/esbuild.js +119 -0
- package/index.js +5 -0
- package/package.json +46 -0
- package/styles/README.md +3 -0
- package/styles/src/styles.js +348 -0
- package/styles/src/typography.js +52 -0
|
@@ -0,0 +1,477 @@
|
|
|
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: var(--color-default);
|
|
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
|
+
|
|
473
|
+
if ('customElements' in window) {
|
|
474
|
+
window.customElements.define('smileid-signature-pad', SignaturePad);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export default SignaturePad;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import './SignaturePad';
|
|
2
|
+
|
|
3
|
+
const meta = {
|
|
4
|
+
component: 'smileid-signature-pad',
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export default meta;
|
|
8
|
+
|
|
9
|
+
export const SignaturePad = {
|
|
10
|
+
render: () => `
|
|
11
|
+
<smileid-signature-pad
|
|
12
|
+
>
|
|
13
|
+
</smileid-signature-pad>
|
|
14
|
+
`,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const SignaturePadWithUploads = {
|
|
18
|
+
render: () => `
|
|
19
|
+
<smileid-signature-pad
|
|
20
|
+
allow-upload
|
|
21
|
+
>
|
|
22
|
+
</smileid-signature-pad>
|
|
23
|
+
`,
|
|
24
|
+
};
|