@signosoft/signpad-js 0.2.3 → 0.3.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.
- package/README.md +544 -142
- package/dist/index.d.ts +79 -44
- package/dist/signosoft-signpad.js +790 -779
- package/dist/signosoft-signpad.umd.cjs +39 -39
- package/package.json +2 -12
- package/src/styles/signosoft-signpad.css +9 -5
- package/src/styles/styles-variables.css +11 -9
- package/src/scripts/generate-theme.js +0 -340
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## 📍 Table of Contents
|
|
4
4
|
|
|
5
|
-
| Core Concepts & Overview | Getting Started & Usage
|
|
6
|
-
| :---------------------------------------- |
|
|
7
|
-
| 📜 [Description](#-description) | 📦 [Installation](#-installation)
|
|
8
|
-
| 🎬 [Demo](#-demo) | 🔐 [Licensing](#-
|
|
9
|
-
| ⚙️ [Tech stack](#-tech-stack--built-with) | 🚀 [Quick Start](#-quick-start)
|
|
10
|
-
| | 📋 [Properties](#-properties)
|
|
11
|
-
| | 🧩 [Methods](#-methods)
|
|
12
|
-
| | 🎨 [Styling](#-styling--theming)
|
|
5
|
+
| Core Concepts & Overview | Getting Started & Usage | Advanced & Support |
|
|
6
|
+
| :---------------------------------------- | :-------------------------------- | :--------------------------------- |
|
|
7
|
+
| 📜 [Description](#-description) | 📦 [Installation](#-installation) | 💬 [Feedback](#-feedback--support) |
|
|
8
|
+
| 🎬 [Demo](#-demo) | 🔐 [Licensing](#-lease-setup) | 🛠️ [Contributing](#-contributing) |
|
|
9
|
+
| ⚙️ [Tech stack](#-tech-stack--built-with) | 🚀 [Quick Start](#-quick-start) | 📄 [License](#-license) |
|
|
10
|
+
| | 📋 [Properties](#-properties) | |
|
|
11
|
+
| | 🧩 [Methods](#-methods) | |
|
|
12
|
+
| | 🎨 [Styling](#-styling--theming) | |
|
|
13
13
|
|
|
14
14
|
## 📜 Description
|
|
15
15
|
|
|
@@ -38,33 +38,61 @@ It expertly handles the complexities of **WebHID device connections**, signature
|
|
|
38
38
|
- [](https://vitejs.dev/)
|
|
39
39
|
<br/>
|
|
40
40
|
|
|
41
|
-
## 🔐
|
|
41
|
+
## 🔐 Lease Setup
|
|
42
42
|
|
|
43
|
-
To use the Signosoft Signpad component and its hardware drivers, a valid
|
|
43
|
+
To use the Signosoft Signpad component and its hardware drivers, a valid `lease` is **required**.
|
|
44
44
|
|
|
45
45
|
#### 🛑 CRITICAL: Mandatory Initialization
|
|
46
46
|
|
|
47
|
-
Without a valid
|
|
47
|
+
Without a valid lease, the component <b>WILL NOT INITIALIZE</b> and hardware communication features will be <b>disabled</b>.
|
|
48
48
|
|
|
49
|
-
### 1.
|
|
49
|
+
### 1. Lease flow
|
|
50
50
|
|
|
51
|
-
1.
|
|
52
|
-
2.
|
|
53
|
-
3.
|
|
54
|
-
4. Click **Generate API Key** (License Key).
|
|
55
|
-
5. Copy your key and add it to your configuration.
|
|
51
|
+
1. Generate/get your license key in Signosoft portal.
|
|
52
|
+
2. Request a lease from the Signosoft lease API.
|
|
53
|
+
3. Put the returned lease into `config.lease`.
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
> Security note: In production, call the lease API from your backend and return only the lease to frontend.
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
### 2. Frontend implementation (plain JavaScript reference)
|
|
60
58
|
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
```javascript
|
|
60
|
+
async function fetchLease() {
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetch(
|
|
63
|
+
"https://test.signosoft.com/api/v1/driver/leases",
|
|
64
|
+
{
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
licenceKey: "YOUR-LICENSE-KEY",
|
|
69
|
+
validityHours: 24,
|
|
70
|
+
port: 4204,
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
);
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const errorText = await response.text();
|
|
77
|
+
throw new Error(
|
|
78
|
+
`License server error: ${response.statusText} - ${errorText}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return await response.json();
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error("Signosoft Signpad: License initialization failed", error);
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function applyLeaseToConfig(currentConfig) {
|
|
90
|
+
const lease = await fetchLease();
|
|
91
|
+
return {
|
|
92
|
+
...currentConfig,
|
|
93
|
+
lease,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
68
96
|
```
|
|
69
97
|
|
|
70
98
|
<br/>
|
|
@@ -169,7 +197,13 @@ export class SignosoftSignpadDirective implements OnChanges {
|
|
|
169
197
|
Import the library, the directive, and add the **`CUSTOM_ELEMENTS_SCHEMA`**.
|
|
170
198
|
|
|
171
199
|
```typescript
|
|
172
|
-
import {
|
|
200
|
+
import {
|
|
201
|
+
Component,
|
|
202
|
+
ViewChild,
|
|
203
|
+
CUSTOM_ELEMENTS_SCHEMA,
|
|
204
|
+
AfterViewInit,
|
|
205
|
+
signal,
|
|
206
|
+
} from "@angular/core";
|
|
173
207
|
import "@signosoft/signpad-js"; // Import the Web Component registration
|
|
174
208
|
import {
|
|
175
209
|
type SignosoftSignpad,
|
|
@@ -184,14 +218,54 @@ import { SignosoftSignpadDirective } from "./directives/signosoft-signpad.direct
|
|
|
184
218
|
templateUrl: "./app.component.html",
|
|
185
219
|
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Mandatory for custom HTML tags
|
|
186
220
|
})
|
|
187
|
-
export class AppComponent {
|
|
221
|
+
export class AppComponent implements AfterViewInit {
|
|
188
222
|
@ViewChild(SignosoftSignpadDirective)
|
|
189
223
|
signpadDirective!: SignosoftSignpadDirective;
|
|
190
224
|
|
|
191
|
-
config
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
225
|
+
public config = signal<SignpadConfig>({
|
|
226
|
+
lease: undefined,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
async ngAfterViewInit() {
|
|
230
|
+
await this.applyLeaseToConfig();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public async fetchLease(): Promise<any> {
|
|
234
|
+
try {
|
|
235
|
+
const response = await fetch(
|
|
236
|
+
"https://test.signosoft.com/api/v1/driver/leases",
|
|
237
|
+
{
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: { "Content-Type": "application/json" },
|
|
240
|
+
body: JSON.stringify({
|
|
241
|
+
licenceKey: "YOUR-LICENSE-KEY",
|
|
242
|
+
validityHours: 24,
|
|
243
|
+
port: 4204,
|
|
244
|
+
}),
|
|
245
|
+
},
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
if (!response.ok) {
|
|
249
|
+
const errorText = await response.text();
|
|
250
|
+
throw new Error(
|
|
251
|
+
`License server error: ${response.statusText} - ${errorText}`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return await response.json();
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error("Signosoft Signpad: License initialization failed", error);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
public async applyLeaseToConfig() {
|
|
263
|
+
const lease = await this.fetchLease();
|
|
264
|
+
this.config.update((currentConfig) => ({
|
|
265
|
+
...currentConfig,
|
|
266
|
+
lease,
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
195
269
|
|
|
196
270
|
// Helper to access methods easily (ok, clear, cancel, etc.)
|
|
197
271
|
public get signpad(): SignosoftSignpad | undefined {
|
|
@@ -207,7 +281,7 @@ Use the custom tag in your HTML and bind the configuration object.
|
|
|
207
281
|
```html
|
|
208
282
|
<div>
|
|
209
283
|
<div>
|
|
210
|
-
<signosoft-signpad [config]="config"></signosoft-signpad>
|
|
284
|
+
<signosoft-signpad [config]="config()"></signosoft-signpad>
|
|
211
285
|
</div>
|
|
212
286
|
</div>
|
|
213
287
|
```
|
|
@@ -235,21 +309,50 @@ npm install @signosoft/signpad-js
|
|
|
235
309
|
|
|
236
310
|
## 2. Implementation
|
|
237
311
|
|
|
238
|
-
In React, we use
|
|
312
|
+
In React, we use `useRef` for methods and `useEffect` to fetch a lease before updating config.
|
|
239
313
|
|
|
240
314
|
```tsx
|
|
241
|
-
import { useRef } from "react";
|
|
315
|
+
import { useEffect, useRef, useState } from "react";
|
|
242
316
|
import "@signosoft/signpad-js"; // Registers the web component
|
|
243
317
|
import type { SignpadConfig } from "@signosoft/signpad-js";
|
|
244
318
|
|
|
319
|
+
async function fetchLease() {
|
|
320
|
+
try {
|
|
321
|
+
const response = await fetch(
|
|
322
|
+
"https://test.signosoft.com/api/v1/driver/leases",
|
|
323
|
+
{
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: { "Content-Type": "application/json" },
|
|
326
|
+
body: JSON.stringify({
|
|
327
|
+
licenceKey: "YOUR-LICENSE-KEY",
|
|
328
|
+
validityHours: 24,
|
|
329
|
+
port: 4204,
|
|
330
|
+
}),
|
|
331
|
+
},
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
if (!response.ok) {
|
|
335
|
+
const errorText = await response.text();
|
|
336
|
+
throw new Error(
|
|
337
|
+
`License server error: ${response.statusText} - ${errorText}`,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return await response.json();
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error("Signosoft Signpad: License initialization failed", error);
|
|
344
|
+
throw error;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
245
348
|
function App() {
|
|
246
349
|
// Use ref to access component methods (ok, clear, connect, etc. by signpadRef.current)
|
|
247
350
|
const signpadRef = useRef<any>(null);
|
|
351
|
+
const [config, setConfig] = useState<SignpadConfig>({ lease: undefined });
|
|
248
352
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
};
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
fetchLease().then((lease) => setConfig((prev) => ({ ...prev, lease })));
|
|
355
|
+
}, []);
|
|
253
356
|
|
|
254
357
|
return (
|
|
255
358
|
<div>
|
|
@@ -309,7 +412,7 @@ export default defineConfig({
|
|
|
309
412
|
|
|
310
413
|
## 3. Implementation
|
|
311
414
|
|
|
312
|
-
In Vue 3, we use `ref`
|
|
415
|
+
In Vue 3, we use `ref` for both the element and config, then fetch the lease and assign it to config.
|
|
313
416
|
|
|
314
417
|
```typescript
|
|
315
418
|
<script setup lang="ts">
|
|
@@ -320,11 +423,40 @@ import type { SignpadConfig, SignosoftSignpad, IPenData } from "@signosoft/signp
|
|
|
320
423
|
// Use ref to access component methods (ok, clear, connect, etc. by signpadRef.value)
|
|
321
424
|
const signpadRef = ref<SignosoftSignpad | null>(null);
|
|
322
425
|
|
|
323
|
-
const signpadConfig
|
|
324
|
-
|
|
325
|
-
|
|
426
|
+
const signpadConfig = ref<SignpadConfig>({ lease: undefined });
|
|
427
|
+
|
|
428
|
+
const fetchLease = async () => {
|
|
429
|
+
try {
|
|
430
|
+
const response = await fetch("https://test.signosoft.com/api/v1/driver/leases", {
|
|
431
|
+
method: "POST",
|
|
432
|
+
headers: { "Content-Type": "application/json" },
|
|
433
|
+
body: JSON.stringify({
|
|
434
|
+
licenceKey: "YOUR-LICENSE-KEY",
|
|
435
|
+
validityHours: 24,
|
|
436
|
+
port: 4204,
|
|
437
|
+
}),
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
if (!response.ok) {
|
|
441
|
+
const errorText = await response.text();
|
|
442
|
+
throw new Error(`License server error: ${response.statusText} - ${errorText}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return await response.json();
|
|
446
|
+
} catch (error) {
|
|
447
|
+
console.error("Signosoft Signpad: License initialization failed", error);
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
326
450
|
};
|
|
327
451
|
|
|
452
|
+
fetchLease()
|
|
453
|
+
.then((lease) => {
|
|
454
|
+
signpadConfig.value = {
|
|
455
|
+
...signpadConfig.value,
|
|
456
|
+
lease,
|
|
457
|
+
};
|
|
458
|
+
});
|
|
459
|
+
|
|
328
460
|
</script>
|
|
329
461
|
|
|
330
462
|
<template>
|
|
@@ -376,8 +508,41 @@ const pad = document.querySelector("signosoft-signpad");
|
|
|
376
508
|
|
|
377
509
|
// 1. Initial Configuration
|
|
378
510
|
pad.config = {
|
|
379
|
-
|
|
511
|
+
lease: undefined,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const fetchLease = async () => {
|
|
515
|
+
try {
|
|
516
|
+
const response = await fetch(
|
|
517
|
+
"https://test.signosoft.com/api/v1/driver/leases",
|
|
518
|
+
{
|
|
519
|
+
method: "POST",
|
|
520
|
+
headers: { "Content-Type": "application/json" },
|
|
521
|
+
body: JSON.stringify({
|
|
522
|
+
licenceKey: "YOUR-LICENSE-KEY",
|
|
523
|
+
validityHours: 24,
|
|
524
|
+
port: 4204,
|
|
525
|
+
}),
|
|
526
|
+
},
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
if (!response.ok) {
|
|
530
|
+
const errorText = await response.text();
|
|
531
|
+
throw new Error(
|
|
532
|
+
`License server error: ${response.statusText} - ${errorText}`,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return await response.json();
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.error("Signosoft Signpad: License initialization failed", error);
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
380
541
|
};
|
|
542
|
+
|
|
543
|
+
fetchLease().then((lease) => {
|
|
544
|
+
pad.config = { ...pad.config, lease };
|
|
545
|
+
});
|
|
381
546
|
```
|
|
382
547
|
|
|
383
548
|
## 3. HTML Structure
|
|
@@ -421,10 +586,9 @@ The component is primarily configured through a single `config` property. This o
|
|
|
421
586
|
|
|
422
587
|
### 🔑 Core Options
|
|
423
588
|
|
|
424
|
-
| Option
|
|
425
|
-
|
|
|
426
|
-
| **`
|
|
427
|
-
| `customCssVariables` | `object` | No | Custom theme object generated via the `getSignpadTheme` utility. |
|
|
589
|
+
| Option | Type | Required | Description |
|
|
590
|
+
| :---------- | :------- | :------- | :---------------------------------------------------------------------------- |
|
|
591
|
+
| **`lease`** | `object` | **Yes** | Lease payload returned from the lease API call. Mandatory for initialization. |
|
|
428
592
|
|
|
429
593
|
### 🔄 autoconnectOptions
|
|
430
594
|
|
|
@@ -482,25 +646,27 @@ The component provides two ways to interact with internal processes.
|
|
|
482
646
|
|
|
483
647
|
These allow you to **inject custom logic** directly into the internal button flows. Your code runs alongside the component's internal logic.
|
|
484
648
|
|
|
485
|
-
| Handler | Arguments
|
|
486
|
-
| :------------- |
|
|
487
|
-
| `handleOk` | `data?:
|
|
488
|
-
| `handleClear` | —
|
|
489
|
-
| `handleCancel` | —
|
|
649
|
+
| Handler | Arguments | Description |
|
|
650
|
+
| :------------- | :---------------------------------- | :---------------------------------------------------------------------- |
|
|
651
|
+
| `handleOk` | `data?: ISignatureConfirmationData` | Extends the internal OK button logic. Receives captured signature data. |
|
|
652
|
+
| `handleClear` | — | Extends the internal Clear button logic. |
|
|
653
|
+
| `handleCancel` | — | Extends the internal Cancel button logic. |
|
|
490
654
|
|
|
491
655
|
#### 🔔 Event Callbacks (`eventCallbacks`)
|
|
492
656
|
|
|
493
657
|
Standard event listeners triggered _after_ specific actions. Useful for observing the component from your application state.
|
|
494
658
|
|
|
495
|
-
| Callback | Payload
|
|
496
|
-
| :------------- |
|
|
497
|
-
| `onPen` | `event: CustomEvent<IPenData> `
|
|
498
|
-
| `onConnect` | `event: CustomEvent`
|
|
499
|
-
| `onDisconnect` | —
|
|
500
|
-
| `onOk` | `
|
|
501
|
-
| `onClear` | —
|
|
502
|
-
| `onCancel` | —
|
|
503
|
-
| `onError` | `error: Error`
|
|
659
|
+
| Callback | Payload | Description |
|
|
660
|
+
| :------------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------- |
|
|
661
|
+
| `onPen` | `event: CustomEvent<IPenData> ` | Dispatched when a device or mouse fallback are in contact with component or signature pad display. |
|
|
662
|
+
| `onConnect` | `event: CustomEvent` | Dispatched when a device or mouse fallback connects. |
|
|
663
|
+
| `onDisconnect` | — | Dispatched when the hardware connection is closed. |
|
|
664
|
+
| `onOk` | `event: CustomEvent<ISignatureConfirmationData>` | Dispatched when signature is confirmed. |
|
|
665
|
+
| `onClear` | — | Dispatched when the canvas has been cleared. |
|
|
666
|
+
| `onCancel` | — | Dispatched when the user aborts the session. |
|
|
667
|
+
| `onError` | `error: Error` | Dispatched on critical failures (Lease, Driver, etc.). |
|
|
668
|
+
|
|
669
|
+
> `actionHandlers` and `eventCallbacks` are separate hooks and can both be configured.
|
|
504
670
|
|
|
505
671
|
---
|
|
506
672
|
|
|
@@ -508,7 +674,7 @@ Standard event listeners triggered _after_ specific actions. Useful for observin
|
|
|
508
674
|
|
|
509
675
|
```typescript
|
|
510
676
|
this.config = {
|
|
511
|
-
|
|
677
|
+
lease: leasePayloadFromApi,
|
|
512
678
|
autoconnectOptions: {
|
|
513
679
|
autoConnect: true,
|
|
514
680
|
},
|
|
@@ -532,15 +698,15 @@ this.config = {
|
|
|
532
698
|
|
|
533
699
|
The following methods are available on the component instance to control the signing process programmatically.
|
|
534
700
|
|
|
535
|
-
| Method | Returns
|
|
536
|
-
| :--------------- |
|
|
537
|
-
| `connect()` | `Promise<boolean>`
|
|
538
|
-
| `disconnect()` | `Promise<void>`
|
|
539
|
-
| `startSigning()` | `Promise<void>`
|
|
540
|
-
| `stopSigning()` | `Promise<any>`
|
|
541
|
-
| `ok()` | `Promise<
|
|
542
|
-
| `clear()` | `Promise<void>`
|
|
543
|
-
| `cancel()` | `Promise<void>`
|
|
701
|
+
| Method | Returns | Description |
|
|
702
|
+
| :--------------- | :------------------------------------------------- | :------------------------------------------------------------------------------- |
|
|
703
|
+
| `connect()` | `Promise<boolean>` | Connects device to component |
|
|
704
|
+
| `disconnect()` | `Promise<void>` | Disconnects device from component |
|
|
705
|
+
| `startSigning()` | `Promise<void>` | Initializes a new signing session on the canvas and hardware. |
|
|
706
|
+
| `stopSigning()` | `Promise<any>` | Immediately ends the session and returns the captured signature data. |
|
|
707
|
+
| `ok()` | `Promise<ISignatureConfirmationData \| undefined>` | Finalizes the session, cleans the device screen, and returns the signature data. |
|
|
708
|
+
| `clear()` | `Promise<void>` | Wipes the signature from the UI and hardware without ending the session. |
|
|
709
|
+
| `cancel()` | `Promise<void>` | Aborts the current session and resets the component state. |
|
|
544
710
|
|
|
545
711
|
---
|
|
546
712
|
|
|
@@ -599,6 +765,87 @@ This is the standard way to confirm a signature. It:
|
|
|
599
765
|
3. Dispatches the `SIGN_OK` event.
|
|
600
766
|
4. Returns the final signature payload (coordinates, pressure, metadata).
|
|
601
767
|
|
|
768
|
+
**Returned payload shape (`ISignatureConfirmationData`)**
|
|
769
|
+
|
|
770
|
+
```js
|
|
771
|
+
[
|
|
772
|
+
points, // index 0: Data about each point
|
|
773
|
+
imageBase64, // index 1: Image in base64
|
|
774
|
+
metadata, // index 2: Information about tablet and canvas
|
|
775
|
+
];
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
```js
|
|
779
|
+
// [0] Points structure
|
|
780
|
+
{
|
|
781
|
+
absoluteX: number,
|
|
782
|
+
absoluteY: number,
|
|
783
|
+
inContact: boolean,
|
|
784
|
+
pressure: number,
|
|
785
|
+
ready: boolean,
|
|
786
|
+
relativeX: number, // normalized 0..1
|
|
787
|
+
relativeY: number, // normalized 0..1
|
|
788
|
+
sequence: number,
|
|
789
|
+
timestamp: number | bigint
|
|
790
|
+
}
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
```js
|
|
794
|
+
// [1] Image structure
|
|
795
|
+
{
|
|
796
|
+
data: image / png;
|
|
797
|
+
(base64, Some - long - string);
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
```js
|
|
802
|
+
// [2] Metadata structure
|
|
803
|
+
{
|
|
804
|
+
canvas: {
|
|
805
|
+
top: number,
|
|
806
|
+
left: number,
|
|
807
|
+
right: number,
|
|
808
|
+
bottom: number,
|
|
809
|
+
width: number,
|
|
810
|
+
height: number,
|
|
811
|
+
cssWidth: number,
|
|
812
|
+
cssHeight: number,
|
|
813
|
+
dpr: number
|
|
814
|
+
},
|
|
815
|
+
tablet: {
|
|
816
|
+
name: string,
|
|
817
|
+
aspectRatio: number,
|
|
818
|
+
isCanvasStyleTablet: boolean,
|
|
819
|
+
screenWidth: number,
|
|
820
|
+
screenHeight: number,
|
|
821
|
+
drawingArea: { x: number, y: number, width: number, height: number },
|
|
822
|
+
serialNumber: Promise<string> | string,
|
|
823
|
+
vendorId: number | null,
|
|
824
|
+
productId: number | null
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
`handleOk(data)` receives the same structure. `onOk` receives it in `event.detail`.
|
|
830
|
+
|
|
831
|
+
#### 📜 `onPen` payload
|
|
832
|
+
|
|
833
|
+
`onPen` is emitted continuously while writing and provides a single `IPenData` object in `event.detail`.
|
|
834
|
+
|
|
835
|
+
```js
|
|
836
|
+
{
|
|
837
|
+
absoluteX: number,
|
|
838
|
+
absoluteY: number,
|
|
839
|
+
inContact: boolean,
|
|
840
|
+
pressure: number,
|
|
841
|
+
ready: boolean,
|
|
842
|
+
relativeX: number, // normalized 0..1
|
|
843
|
+
relativeY: number, // normalized 0..1
|
|
844
|
+
sequence: number,
|
|
845
|
+
timestamp: number | bigint
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
602
849
|
#### 📜 `stopSigning()`
|
|
603
850
|
|
|
604
851
|
A low-level method that forces the driver to stop capturing data and returns the raw signature object. Unlike `ok()`, it does not trigger the full "Finalization" flow (UI transitions or device screen clearing).
|
|
@@ -613,44 +860,97 @@ Stops the session immediately. It does not collect any signature data and resets
|
|
|
613
860
|
|
|
614
861
|
## 🎨 Styling & Theming
|
|
615
862
|
|
|
616
|
-
The component
|
|
617
|
-
|
|
618
|
-
### 🛠️ Built-in Theme Generator
|
|
619
|
-
|
|
620
|
-
The easiest way to create a consistent theme is to use our CLI utility. It generates a configuration object that you can pass directly to the component.
|
|
621
|
-
|
|
622
|
-
**1. Run the generator in your terminal:**
|
|
623
|
-
|
|
624
|
-
```bash
|
|
625
|
-
npx getSignpadTheme
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
The CLI will guide you through:
|
|
629
|
-
|
|
630
|
-
- **Output path:** Where to save the file.
|
|
631
|
-
- **Filename:** What to call the file.
|
|
632
|
-
- **Format:** Choose between `.js` or `.ts`.
|
|
863
|
+
The component uses a hybrid theming model:
|
|
633
864
|
|
|
634
|
-
|
|
635
|
-
|
|
865
|
+
- It ships with complete default styles (works out of the box).
|
|
866
|
+
- You can override the visual design through CSS variables.
|
|
636
867
|
|
|
637
|
-
|
|
638
|
-
import { myCustomTheme } from "./styles/myCustomTheme";
|
|
868
|
+
## ✍️ Override via CSS
|
|
639
869
|
|
|
640
|
-
|
|
641
|
-
licenseKey: "...",
|
|
642
|
-
customCssVariables: myCustomTheme,
|
|
643
|
-
};
|
|
644
|
-
```
|
|
870
|
+
You can override variables in your stylesheet
|
|
645
871
|
|
|
646
|
-
###
|
|
872
|
+
### 📎 Copy-Paste Starter
|
|
647
873
|
|
|
648
|
-
|
|
874
|
+
Paste this directly into your app stylesheet and adjust values:
|
|
649
875
|
|
|
650
876
|
```css
|
|
651
877
|
signosoft-signpad {
|
|
652
|
-
|
|
653
|
-
--
|
|
878
|
+
/* Color Variables */
|
|
879
|
+
--primary-color-0: #4e56ea;
|
|
880
|
+
--primary-color-10: #7178ee;
|
|
881
|
+
--background-color-0: #f1f2fd;
|
|
882
|
+
--background-color-10: #e3e4fc;
|
|
883
|
+
--text-color-0: #333e4a;
|
|
884
|
+
--white-color: #ffffff;
|
|
885
|
+
--grey-color: #b5b9be;
|
|
886
|
+
|
|
887
|
+
/* Constraints */
|
|
888
|
+
--min-height: 48px;
|
|
889
|
+
--spacing-constraints: 16px 24px;
|
|
890
|
+
|
|
891
|
+
/* CSS Variables */
|
|
892
|
+
--sign-font-family: Arial, Helvetica, sans-serif;
|
|
893
|
+
|
|
894
|
+
/* Top Bar */
|
|
895
|
+
--sign-top-bar-bg-base: var(--background-color-10);
|
|
896
|
+
--sign-top-bar-text-base: var(--primary-color-0);
|
|
897
|
+
--sign-top-bar-padding: var(--spacing-constraints);
|
|
898
|
+
--sign-top-bar-min-height: var(--min-height);
|
|
899
|
+
|
|
900
|
+
/* Canvas Area */
|
|
901
|
+
--sign-canvas-bg-base: var(--background-color-0);
|
|
902
|
+
|
|
903
|
+
/* Line */
|
|
904
|
+
--sign-line-height: 22px;
|
|
905
|
+
--sign-line-margin-bottom: 16px;
|
|
906
|
+
--sign-line-border-base: var(--primary-color-10);
|
|
907
|
+
--sign-line-additional-text-color: var(--text-color-0);
|
|
908
|
+
--sign-line-margin: 0px 24px var(--sign-line-margin-bottom) 24px;
|
|
909
|
+
--sign-canvas-line-text-font-size: 12px;
|
|
910
|
+
--sign-canvas-height-offset: var(--sign-line-height);
|
|
911
|
+
|
|
912
|
+
/* Bottom Bar */
|
|
913
|
+
--sign-bottom-bar-bg-base: var(--background-color-10);
|
|
914
|
+
--sign-bottom-bar-padding: var(--spacing-constraints);
|
|
915
|
+
--sign-bottom-bar-min-height: var(--min-height);
|
|
916
|
+
--sign-bottom-bar-gap: 12px;
|
|
917
|
+
|
|
918
|
+
/* Button general settings */
|
|
919
|
+
--sign-button-font-size: 16px;
|
|
920
|
+
--sign-button-padding: 14px 16px;
|
|
921
|
+
--sign-button-min-height: var(--min-height);
|
|
922
|
+
--sign-button-border-radius: 8px;
|
|
923
|
+
--sign-button-font-weight: 500;
|
|
924
|
+
|
|
925
|
+
/* Primary Buttons (OK, Clear, Cancel) */
|
|
926
|
+
--sign-button-primary-bg-base: var(--primary-color-0);
|
|
927
|
+
--sign-button-primary-bg-hover: var(--primary-color-10);
|
|
928
|
+
--sign-button-primary-bg-disabled: var(--grey-color);
|
|
929
|
+
--sign-button-primary-text-base: var(--white-color);
|
|
930
|
+
--sign-button-primary-text-hover: var(--white-color);
|
|
931
|
+
--sign-button-primary-text-disabled: var(--white-color);
|
|
932
|
+
|
|
933
|
+
/* Link Buttons (Connect signpad) */
|
|
934
|
+
--sign-button-link-bg-base: transparent;
|
|
935
|
+
--sign-button-link-bg-hover: var(--background-color-10);
|
|
936
|
+
--sign-button-link-bg-disabled: transparent;
|
|
937
|
+
--sign-button-link-text-base: var(--primary-color-0);
|
|
938
|
+
--sign-button-link-text-hover: var(--primary-color-10);
|
|
939
|
+
--sign-button-link-text-disabled: var(--grey-color);
|
|
940
|
+
--sign-button-link-border-base: 1px solid var(--primary-color-0);
|
|
941
|
+
--sign-button-link-border-hover: 1px solid var(--primary-color-10);
|
|
942
|
+
--sign-button-link-border-disabled: 1px solid var(--grey-color);
|
|
943
|
+
|
|
944
|
+
/* Device State Text Colors */
|
|
945
|
+
--sign-device-state-text-color-connected: green;
|
|
946
|
+
--sign-device-state-text-color-disconnected: red;
|
|
947
|
+
|
|
948
|
+
/* Loading Overlay */
|
|
949
|
+
--sign-loading-overlay-bg-color: rgba(255, 255, 255, 0.8);
|
|
950
|
+
--sign-loading-overlay-text-color: var(--text-color-0);
|
|
951
|
+
--sign-loading-overlay-spinner-color: var(--primary-color-0);
|
|
952
|
+
--sign-loading-overlay-spinner-border-color: rgba(78, 86, 234, 0.1);
|
|
953
|
+
--sign-loading-overlay-spinner-size: 30px;
|
|
654
954
|
}
|
|
655
955
|
```
|
|
656
956
|
|
|
@@ -678,41 +978,44 @@ signosoft-signpad {
|
|
|
678
978
|
|
|
679
979
|
#### Top Bar
|
|
680
980
|
|
|
681
|
-
| Variable | Description |
|
|
682
|
-
| :-------------------------- | :------------------------------- |
|
|
683
|
-
| `--sign-top-bar-bg-base` | Background color of the top bar. |
|
|
684
|
-
| `--sign-top-bar-text-base` | Text color in the top bar. |
|
|
685
|
-
| `--sign-top-bar-padding` | Inner padding of the top bar. |
|
|
686
|
-
| `--sign-top-bar-min-height` | Minimum height of the top bar. |
|
|
981
|
+
| Variable | Default | Description |
|
|
982
|
+
| :-------------------------- | :--------------------------- | :------------------------------- |
|
|
983
|
+
| `--sign-top-bar-bg-base` | `var(--background-color-10)` | Background color of the top bar. |
|
|
984
|
+
| `--sign-top-bar-text-base` | `var(--primary-color-0)` | Text color in the top bar. |
|
|
985
|
+
| `--sign-top-bar-padding` | `var(--spacing-constraints)` | Inner padding of the top bar. |
|
|
986
|
+
| `--sign-top-bar-min-height` | `var(--min-height)` | Minimum height of the top bar. |
|
|
687
987
|
|
|
688
988
|
#### Canvas & Signature Line
|
|
689
989
|
|
|
690
|
-
| Variable | Description |
|
|
691
|
-
| :---------------------------------- | :-------------------------------------------- |
|
|
692
|
-
| `--sign-canvas-bg-base` | Background color of the drawing area. |
|
|
693
|
-
| `--sign-line-height` | Vertical offset/height for the guide line. |
|
|
694
|
-
| `--sign-line-
|
|
695
|
-
| `--sign-line-
|
|
696
|
-
| `--sign-line-
|
|
697
|
-
| `--sign-
|
|
698
|
-
| `--sign-canvas-
|
|
990
|
+
| Variable | Default | Description |
|
|
991
|
+
| :---------------------------------- | :--------------------------------------------- | :-------------------------------------------- |
|
|
992
|
+
| `--sign-canvas-bg-base` | `var(--background-color-0)` | Background color of the drawing area. |
|
|
993
|
+
| `--sign-line-height` | `22px` | Vertical offset/height for the guide line. |
|
|
994
|
+
| `--sign-line-margin-bottom` | `16px` | Bottom spacing used in canvas line layout. |
|
|
995
|
+
| `--sign-line-border-base` | `var(--primary-color-10)` | Color of the signature guide line. |
|
|
996
|
+
| `--sign-line-additional-text-color` | `var(--text-color-0)` | Color of the helper text below the line. |
|
|
997
|
+
| `--sign-line-margin` | `0px 24px var(--sign-line-margin-bottom) 24px` | Spacing around the guide line. |
|
|
998
|
+
| `--sign-canvas-line-text-font-size` | `12px` | Font size for guide line labels. |
|
|
999
|
+
| `--sign-canvas-height-offset` | `var(--sign-line-height)` | Canvas offset calculation for the guide line. |
|
|
699
1000
|
|
|
700
1001
|
#### Bottom Bar
|
|
701
1002
|
|
|
702
|
-
| Variable | Description |
|
|
703
|
-
| :----------------------------- | :--------------------------------------- |
|
|
704
|
-
| `--sign-bottom-bar-bg-base` | Background color of the bottom bar. |
|
|
705
|
-
| `--sign-bottom-bar-padding` | Inner padding of the bottom bar. |
|
|
706
|
-
| `--sign-bottom-bar-min-height` | Minimum height of the bottom bar. |
|
|
707
|
-
| `--sign-bottom-bar-gap` | Space between buttons in the bottom bar. |
|
|
1003
|
+
| Variable | Default | Description |
|
|
1004
|
+
| :----------------------------- | :--------------------------- | :--------------------------------------- |
|
|
1005
|
+
| `--sign-bottom-bar-bg-base` | `var(--background-color-10)` | Background color of the bottom bar. |
|
|
1006
|
+
| `--sign-bottom-bar-padding` | `var(--spacing-constraints)` | Inner padding of the bottom bar. |
|
|
1007
|
+
| `--sign-bottom-bar-min-height` | `var(--min-height)` | Minimum height of the bottom bar. |
|
|
1008
|
+
| `--sign-bottom-bar-gap` | `12px` | Space between buttons in the bottom bar. |
|
|
708
1009
|
|
|
709
1010
|
#### Buttons (General)
|
|
710
1011
|
|
|
711
|
-
| Variable
|
|
712
|
-
|
|
|
713
|
-
| `--sign-button-font-size`
|
|
714
|
-
| `--sign-button-padding`
|
|
715
|
-
| `--sign-button-min-height`
|
|
1012
|
+
| Variable | Default | Description |
|
|
1013
|
+
| :---------------------------- | :------------------ | :----------------------------- |
|
|
1014
|
+
| `--sign-button-font-size` | `16px` | Font size for all buttons. |
|
|
1015
|
+
| `--sign-button-padding` | `14px 16px` | Inner padding for all buttons. |
|
|
1016
|
+
| `--sign-button-min-height` | `var(--min-height)` | Minimum height for buttons. |
|
|
1017
|
+
| `--sign-button-border-radius` | `8px` | Border radius for all buttons. |
|
|
1018
|
+
| `--sign-button-font-weight` | `500` | Font weight for all buttons. |
|
|
716
1019
|
|
|
717
1020
|
#### Primary Buttons (OK, Clear, Cancel)
|
|
718
1021
|
|
|
@@ -727,31 +1030,130 @@ signosoft-signpad {
|
|
|
727
1030
|
|
|
728
1031
|
#### Link/Connect Buttons
|
|
729
1032
|
|
|
730
|
-
| Variable
|
|
731
|
-
|
|
|
732
|
-
| `--sign-button-link-bg-base`
|
|
733
|
-
| `--sign-button-link-
|
|
734
|
-
| `--sign-button-link-
|
|
735
|
-
| `--sign-button-link-text-
|
|
736
|
-
| `--sign-button-link-
|
|
1033
|
+
| Variable | Default | Description |
|
|
1034
|
+
| :----------------------------------- | :---------------------------------- | :------------------------------------------- |
|
|
1035
|
+
| `--sign-button-link-bg-base` | `transparent` | Background color for secondary/link buttons. |
|
|
1036
|
+
| `--sign-button-link-bg-hover` | `var(--background-color-10)` | Hover background color. |
|
|
1037
|
+
| `--sign-button-link-bg-disabled` | `transparent` | Disabled background color. |
|
|
1038
|
+
| `--sign-button-link-text-base` | `var(--primary-color-0)` | Text color for secondary/link buttons. |
|
|
1039
|
+
| `--sign-button-link-text-hover` | `var(--primary-color-10)` | Hover text color. |
|
|
1040
|
+
| `--sign-button-link-text-disabled` | `var(--grey-color)` | Disabled text color. |
|
|
1041
|
+
| `--sign-button-link-border-base` | `1px solid var(--primary-color-0)` | Border for secondary/link buttons. |
|
|
1042
|
+
| `--sign-button-link-border-hover` | `1px solid var(--primary-color-10)` | Hover border color. |
|
|
1043
|
+
| `--sign-button-link-border-disabled` | `1px solid var(--grey-color)` | Disabled border color. |
|
|
737
1044
|
|
|
738
1045
|
#### Status & Overlays
|
|
739
1046
|
|
|
740
|
-
| Variable | Default
|
|
741
|
-
| :-------------------------------------------- |
|
|
742
|
-
| `--sign-device-state-text-color-connected` | `green`
|
|
743
|
-
| `--sign-device-state-text-color-disconnected` | `red`
|
|
744
|
-
| `--sign-loading-overlay-bg-color` | `rgba(
|
|
745
|
-
| `--sign-loading-overlay-
|
|
746
|
-
| `--sign-loading-overlay-spinner-
|
|
1047
|
+
| Variable | Default | Description |
|
|
1048
|
+
| :-------------------------------------------- | :------------------------- | :--------------------------------- |
|
|
1049
|
+
| `--sign-device-state-text-color-connected` | `green` | Text color when device is ready. |
|
|
1050
|
+
| `--sign-device-state-text-color-disconnected` | `red` | Text color when disconnected. |
|
|
1051
|
+
| `--sign-loading-overlay-bg-color` | `rgba(255, 255, 255, 0.8)` | Background of the loading spinner. |
|
|
1052
|
+
| `--sign-loading-overlay-text-color` | `var(--text-color-0)` | Loading overlay text color. |
|
|
1053
|
+
| `--sign-loading-overlay-spinner-color` | `var(--primary-color-0)` | Color of the animated spinner. |
|
|
1054
|
+
| `--sign-loading-overlay-spinner-border-color` | `rgba(78, 86, 234, 0.1)` | Base border color of spinner ring. |
|
|
1055
|
+
| `--sign-loading-overlay-spinner-size` | `30px` | Size of the spinner icon. |
|
|
747
1056
|
|
|
748
|
-
##
|
|
1057
|
+
## 💬 Feedback & Support
|
|
749
1058
|
|
|
750
1059
|
We are constantly working to improve our components and drivers. Your feedback is essential to us. Whether you found a bug, have a feature request, or need technical assistance, please reach out to us:
|
|
751
1060
|
|
|
752
1061
|
- 📧 **Support Email:** [esign@signosoft.com](mailto:esign@signosoft.com)
|
|
753
1062
|
- 🌐 **Official Website:** [www.signosoft.com](https://www.signosoft.com)
|
|
754
1063
|
|
|
1064
|
+
## 🛠️ Contributing
|
|
1065
|
+
|
|
1066
|
+
### Commit naming convention
|
|
1067
|
+
|
|
1068
|
+
- Every commit message must start with Jira task id and category:
|
|
1069
|
+
- `MAIN-000/<category>: <summary>`
|
|
1070
|
+
- For work in progress:
|
|
1071
|
+
- `MAIN-000/<category>: WIP - <summary>`
|
|
1072
|
+
|
|
1073
|
+
Allowed categories:
|
|
1074
|
+
|
|
1075
|
+
- `feat`: adding a new feature
|
|
1076
|
+
- `fix`: fixing a bug
|
|
1077
|
+
- `refactor`: performance/readability/maintainability change
|
|
1078
|
+
- `chore`: docs, formatting, tests, cleanup, tooling
|
|
1079
|
+
- `merge`: branch merge into `main`
|
|
1080
|
+
|
|
1081
|
+
Use infinitive verb form in commit summaries:
|
|
1082
|
+
|
|
1083
|
+
- <span style="color: #16a34a;"><strong>RIGHT:</strong></span> `add`, `fix`, `remove`, `update`, `refactor`
|
|
1084
|
+
- <span style="color: #dc2626;"><strong>WRONG:</strong></span> Continuous tense: `adding`, `removing` Past tense: `added`, `fixed`
|
|
1085
|
+
|
|
1086
|
+
Examples:
|
|
1087
|
+
|
|
1088
|
+
```bash
|
|
1089
|
+
git commit -m "MAIN-000/feat: add new button component; add button to templates"
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
```bash
|
|
1093
|
+
git commit -m "MAIN-000/fix: add stop directive to button component to prevent propagation"
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
```bash
|
|
1097
|
+
git commit -m "MAIN-000/refactor: rewrite button component in TypeScript"
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
git commit -m "MAIN-000/chore: write button documentation"
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
```bash
|
|
1105
|
+
git commit -m "MAIN-000/merge: new button"
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
```bash
|
|
1109
|
+
git commit -m "MAIN-000/feat: WIP - basic signing functionality"
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
### Versioning and release commits
|
|
1113
|
+
|
|
1114
|
+
Follow Semantic Versioning:
|
|
1115
|
+
|
|
1116
|
+
- `MAJOR` (`x.0.0`): breaking changes
|
|
1117
|
+
- `MINOR` (`x.y.0`): backward-compatible feature set
|
|
1118
|
+
- `PATCH` (`x.y.z`): backward-compatible bug fixes
|
|
1119
|
+
|
|
1120
|
+
Commit naming for version bumps:
|
|
1121
|
+
|
|
1122
|
+
- Version-only change: use `chore`
|
|
1123
|
+
- Version bump with actual code/features in same commit: use `feat`/`fix`/`refactor` based on dominant change
|
|
1124
|
+
- For release commits, include both versions in message: `release from <old> to <new>`
|
|
1125
|
+
|
|
1126
|
+
Example (version bump with feature changes):
|
|
1127
|
+
|
|
1128
|
+
```bash
|
|
1129
|
+
git commit -m "MAIN-000/feat: release from 0.2.4 to 0.3.0 - add signature method"
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
Example (version-only commit):
|
|
1133
|
+
|
|
1134
|
+
```bash
|
|
1135
|
+
git commit -m "MAIN-000/chore: release from 0.2.4 to 0.3.0"
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
How version increment works:
|
|
1139
|
+
|
|
1140
|
+
- `PATCH` (`x.y.Z`): increase patch only (`1.4.2` -> `1.4.3`)
|
|
1141
|
+
- `MINOR` (`x.Y.0`): increase minor, reset patch (`1.4.9` -> `1.5.0`)
|
|
1142
|
+
- `MAJOR` (`X.0.0`): increase major, reset minor and patch (`1.9.7` -> `2.0.0`)
|
|
1143
|
+
|
|
1144
|
+
Examples:
|
|
1145
|
+
|
|
1146
|
+
```bash
|
|
1147
|
+
# patch
|
|
1148
|
+
git commit -m "MAIN-000/fix: release from 1.0.0 to 1.0.1 - fix onPen throttle edge case"
|
|
1149
|
+
|
|
1150
|
+
# minor
|
|
1151
|
+
git commit -m "MAIN-000/feat: release from 1.1.1 to 1.2.0 - add lease refresh helper"
|
|
1152
|
+
|
|
1153
|
+
# major
|
|
1154
|
+
git commit -m "MAIN-000/refactor: release from 1.1.1 to 2.0.0 - remove legacy API"
|
|
1155
|
+
```
|
|
1156
|
+
|
|
755
1157
|
## 📄 License
|
|
756
1158
|
|
|
757
1159
|
**Proprietary Commercial License**
|