@signosoft/signpad-js 0.5.3 → 0.7.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 +884 -639
- package/dist/index.d.ts +88 -471
- package/dist/signosoft-signpad.js +2221 -2110
- package/dist/signosoft-signpad.umd.cjs +73 -43
- package/package.json +15 -4
- package/presentation.gif +0 -0
- package/src/i18n/cs.json +38 -0
- package/src/i18n/en.json +38 -0
- package/src/i18n/pt.json +38 -0
- package/src/styles/primitive.css +292 -0
- package/src/styles/semantic-dark.css +82 -0
- package/src/styles/semantic.css +144 -0
- package/src/styles/signosoft-signpad.css +116 -36
- package/src/styles/styles-variables.css +61 -55
- package/docsVideo.gif +0 -0
package/README.md
CHANGED
|
@@ -1,102 +1,56 @@
|
|
|
1
1
|
# @signosoft/signpad-js
|
|
2
2
|
|
|
3
|
+
<a id="table-of-contents"></a>
|
|
4
|
+
|
|
3
5
|
## 📍 Table of Contents
|
|
4
6
|
|
|
5
|
-
|
|
|
6
|
-
|
|
|
7
|
-
| 📜 [Description](
|
|
8
|
-
| 🎬 [Demo](
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
7
|
+
| Overview | Getting Started & API | Customization & Support |
|
|
8
|
+
| :--------------------------------------- | :------------------------------------------- | :------------------------------------------------- |
|
|
9
|
+
| 📜 [Description](#description) | 📦 [Installation](#installation) | 🌐 [Localization](#localization--translation-keys) |
|
|
10
|
+
| 🎬 [Demo](#demo) | 🌍 [Requirements](#environment-requirements) | 🎨 [Styling](#styling--theming) |
|
|
11
|
+
| 🧰 [Tech stack](#tech-stack--built-with) | 🔐 [Licensing](#lease-setup) | ♿ [Accessibility](#accessibility) |
|
|
12
|
+
| 🔌 [Devices](#device-compatibility) | 🚀 [Quick Start](#quick-start) | |
|
|
13
|
+
| | 📋 [Properties](#properties) | 💬 [Feedback](#feedback--support) |
|
|
14
|
+
| | 📤 [Results & errors](#results--errors) | 📄 [License](#license) |
|
|
15
|
+
| | 🧩 [Methods](#methods) | |
|
|
16
|
+
| | 🔄 [States](#component-states) | |
|
|
17
|
+
| | ⚠️ [Error codes](#error-codes) | |
|
|
18
|
+
| | 🧱 [Types](#type-definitions) | |
|
|
19
|
+
|
|
20
|
+
<a id="description"></a>
|
|
16
21
|
|
|
17
22
|
## 📜 Description
|
|
18
23
|
|
|
19
|
-
`signosoft-signpad` is a
|
|
24
|
+
`signosoft-signpad` is a web component built with Lit that captures handwritten signatures in the browser. It reads signature tablets (Wacom STU, Ugee UG05, XP-Pen) over WebHID and uses mouse or touch input when no tablet is connected.
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
It works in plain JavaScript, React, Angular and Vue:
|
|
22
27
|
| **JavaScript** | **React** | **Angular** | **Vue** |
|
|
23
28
|
|:-:|:-:|:-:|:-:|
|
|
24
29
|
| <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="35" height="35"> | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg" alt="React" width="35" height="35"> | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/angularjs/angularjs-original.svg" alt="Angular" width="35" height="35"> | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/vuejs/vuejs-original.svg" alt="Vue" width="35" height="35"> ||
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
The component manages the WebHID connection, the signing session (connect, sign, OK / Clear / Cancel) and pressure-sensitive stroke rendering.
|
|
32
|
+
|
|
33
|
+
> **Before you start:** you need Chrome or Edge 89+, HTTPS (`localhost` is fine for development), a [lease](#lease-setup), and a click or tap before the browser can open the device picker. Details: [Environment Requirements](#environment-requirements).
|
|
34
|
+
> <br/>
|
|
35
|
+
|
|
36
|
+
<a id="demo"></a>
|
|
28
37
|
|
|
29
38
|
## 🎬 Demo
|
|
30
39
|
|
|
31
|
-
<img src="https://unpkg.com/@signosoft/signpad-js@latest/
|
|
40
|
+
<img src="https://unpkg.com/@signosoft/signpad-js@latest/presentation.gif" alt="Signosoft Signpad presentation" width="400" />
|
|
41
|
+
|
|
42
|
+
<a id="tech-stack--built-with"></a>
|
|
32
43
|
|
|
33
|
-
##
|
|
44
|
+
## 🧰 Tech Stack / Built With
|
|
34
45
|
|
|
35
46
|
- [](https://www.typescriptlang.org/)
|
|
36
47
|
- [](https://lit.dev/)
|
|
37
48
|
- [](https://vitejs.dev/)
|
|
38
49
|
<br/>
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
To use the Signosoft Signpad component with physical signature hardware, a valid `lease` is **required**.
|
|
51
|
+
<a id="device-compatibility"></a>
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Without a valid lease, the component can still render, but driver initialization and hardware communication features will be <b>disabled</b>.
|
|
47
|
-
|
|
48
|
-
### 1. Lease flow
|
|
49
|
-
|
|
50
|
-
1. Generate/get your license key in Signosoft portal.
|
|
51
|
-
2. Request a lease from the Signosoft lease API.
|
|
52
|
-
3. Put the returned lease into `config.lease`.
|
|
53
|
-
|
|
54
|
-
> Security note: In production, call the lease API from your backend and return only the lease to frontend.
|
|
55
|
-
|
|
56
|
-
### 2. Frontend implementation (plain JavaScript reference)
|
|
57
|
-
|
|
58
|
-
```JS
|
|
59
|
-
async function fetchLease() {
|
|
60
|
-
try {
|
|
61
|
-
const response = await fetch(
|
|
62
|
-
"https://test.signosoft.com/api/v1/driver/leases",
|
|
63
|
-
{
|
|
64
|
-
method: "POST",
|
|
65
|
-
headers: { "Content-Type": "application/json" },
|
|
66
|
-
body: JSON.stringify({
|
|
67
|
-
licenceKey: "YOUR-LICENSE-KEY",
|
|
68
|
-
validityHours: 24,
|
|
69
|
-
port: 4204,
|
|
70
|
-
}),
|
|
71
|
-
},
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
if (!response.ok) {
|
|
75
|
-
const errorText = await response.text();
|
|
76
|
-
throw new Error(
|
|
77
|
-
`License server error: ${response.statusText} - ${errorText}`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return await response.json();
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error("Signosoft Signpad: License initialization failed", error);
|
|
84
|
-
throw error;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function applyLeaseToConfig(currentConfig) {
|
|
89
|
-
const lease = await fetchLease();
|
|
90
|
-
return {
|
|
91
|
-
...currentConfig,
|
|
92
|
-
lease,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
<br/>
|
|
98
|
-
|
|
99
|
-
## 🖊️ Device Compatibility
|
|
53
|
+
## 🔌 Device Compatibility
|
|
100
54
|
|
|
101
55
|
The component communicates with signature tablets via the **WebHID API**. Devices are matched against a built-in configuration database; unrecognized devices fall back to a generic driver.
|
|
102
56
|
|
|
@@ -109,6 +63,8 @@ The component communicates with signature tablets via the **WebHID API**. Device
|
|
|
109
63
|
|
|
110
64
|
> **Mouse fallback:** If no supported device is connected (or the user dismisses the HID picker), the component automatically activates mouse/touch input so signing can still proceed in the browser.
|
|
111
65
|
|
|
66
|
+
<a id="installation"></a>
|
|
67
|
+
|
|
112
68
|
## 📦 Installation
|
|
113
69
|
|
|
114
70
|
Install the library via npm:
|
|
@@ -127,132 +83,119 @@ yarn add @signosoft/signpad-js
|
|
|
127
83
|
pnpm add @signosoft/signpad-js
|
|
128
84
|
```
|
|
129
85
|
|
|
130
|
-
|
|
86
|
+
<a id="environment-requirements"></a>
|
|
87
|
+
|
|
88
|
+
## 🌍 Environment Requirements
|
|
131
89
|
|
|
132
90
|
To ensure proper communication via the **WebHID API**:
|
|
133
91
|
|
|
134
|
-
- **HTTPS:** Required for all production environments (WebHID security policy).
|
|
92
|
+
- **HTTPS:** Required for all production environments (WebHID security policy). `http://localhost` is allowed for development.
|
|
93
|
+
- **User gesture:** The browser only opens the HID device picker in response to a user action (click/tap). The built-in Connect button already satisfies this; if you call `connect()` yourself, do it from an event handler (click/tap). The browser rejects the picker otherwise and the component falls back to mouse mode if enabled. Devices the user already authorized are reconnected without the picker.
|
|
135
94
|
- **Supported Browsers:** Chrome 89+, Edge 89+, Opera (Chromium-based).
|
|
95
|
+
- **Server-side rendering:** the package can be imported on the server, but the component only works in the browser (WebHID, canvas). Create it on the client, for example after the first render.
|
|
136
96
|
- **TypeScript:** Built-in support; no extra `@types` needed.
|
|
137
|
-
<br/>
|
|
138
97
|
|
|
139
|
-
|
|
98
|
+
<a id="lease-setup"></a>
|
|
140
99
|
|
|
141
|
-
|
|
100
|
+
## 🔐 Lease Setup
|
|
142
101
|
|
|
143
|
-
|
|
144
|
-
npm install @signosoft/signpad-js
|
|
145
|
-
```
|
|
102
|
+
To use the Signosoft Signpad component with physical signature hardware, a valid `lease` is **required**.
|
|
146
103
|
|
|
147
|
-
###
|
|
104
|
+
### CRITICAL: Mandatory Initialization
|
|
148
105
|
|
|
149
|
-
<
|
|
150
|
-
<summary>
|
|
151
|
-
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/angularjs/angularjs-original.svg" alt="Angular" width="15" height="15">
|
|
152
|
-
<b>Angular</b> <font color="green"><b>(Live)</b></font>
|
|
153
|
-
</summary>
|
|
154
|
-
<br>
|
|
106
|
+
Without a valid lease, the component can still render, but driver initialization and hardware communication features will be <b>disabled</b>.
|
|
155
107
|
|
|
156
|
-
|
|
108
|
+
### 1. Lease flow
|
|
157
109
|
|
|
158
|
-
|
|
110
|
+
1. Go to https://apps.signosoft.com/
|
|
111
|
+
2. Create account
|
|
112
|
+
3. In dashboard go to "manage account" -> "licence"
|
|
113
|
+
4. Create licence with your desired origins
|
|
114
|
+
5. Done
|
|
159
115
|
|
|
160
|
-
|
|
116
|
+
> Security note: In production, call the lease API from your backend and return only the lease to frontend.
|
|
161
117
|
|
|
162
|
-
|
|
118
|
+
### 2. Pricing
|
|
163
119
|
|
|
164
|
-
|
|
165
|
-
npm install @signosoft/signpad-js
|
|
166
|
-
```
|
|
120
|
+
Every signature completed on the signpad is billed against the credits of your lease: **0.0050 USD per signature** (JavaScript driver), excluding VAT. Current prices are on [signosoft.com/pricing](https://www.signosoft.com/pricing). When the credits run out, the component stops signing and reports `BIL-01` (`BILLING_DENIED`, see [error codes](#signpaderrorcode)).
|
|
167
121
|
|
|
168
|
-
|
|
122
|
+
<a id="quick-start"></a>
|
|
169
123
|
|
|
170
|
-
|
|
124
|
+
## 🚀 Quick Start
|
|
171
125
|
|
|
172
|
-
|
|
126
|
+
Every example below logs the finished signature in `onOk`; what that data contains is described in [Signature result](#signature-result-isignatureconfirmationdata). Before the framework guides, install the package ([Installation](#installation)), check the [requirements](#environment-requirements) and get a [lease](#lease-setup).
|
|
173
127
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
OnChanges,
|
|
180
|
-
SimpleChanges,
|
|
181
|
-
} from "@angular/core";
|
|
182
|
-
import type { SignosoftSignpad, SignpadConfig } from "@signosoft/signpad-js";
|
|
128
|
+
> **Plain JS snippets:** in this document `signosoftPad` is the element, obtained once like this. Framework guides (Angular, React, Vue) get it through their own refs.
|
|
129
|
+
>
|
|
130
|
+
> ```js
|
|
131
|
+
> const signosoftPad = document.querySelector("signosoft-signpad");
|
|
132
|
+
> ```
|
|
183
133
|
|
|
184
|
-
|
|
185
|
-
selector: "signosoft-signpad",
|
|
186
|
-
standalone: true,
|
|
187
|
-
})
|
|
188
|
-
export class SignosoftSignpadDirective implements OnChanges {
|
|
189
|
-
@Input() config?: SignpadConfig;
|
|
134
|
+
> **Size:** the component renders at a fixed `25rem × 31.25rem` (400×500 px) by itself; no wrapper element is needed. To resize or make it fluid, set `--signpad-width` / `--signpad-height` (see [Layout & Constraints](#layout--constraints)).
|
|
190
135
|
|
|
191
|
-
|
|
136
|
+
### Framework Integration Guide
|
|
192
137
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
138
|
+
<details>
|
|
139
|
+
<summary>
|
|
140
|
+
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/angularjs/angularjs-original.svg" alt="Angular" width="15" height="15">
|
|
141
|
+
<b>Angular</b> <font color="green"><b>(Live)</b></font>
|
|
142
|
+
</summary>
|
|
143
|
+
<br>
|
|
197
144
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
el[key] = (this as any)[key];
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
```
|
|
145
|
+
#### <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/angular/angular-original.svg" alt="Angular" width="30" height="30"> Angular Integration Guide
|
|
146
|
+
|
|
147
|
+
This guide describes how to integrate the `@signosoft/signpad-js` web component into an Angular application. It uses the current signal-based Angular APIs (`viewChild()`, `afterNextRender()`), so it needs **Angular 20 or newer** (standalone components are the default, so there is no `standalone: true`).
|
|
206
148
|
|
|
207
|
-
|
|
149
|
+
##### 1. Implementation in Component
|
|
208
150
|
|
|
209
|
-
Import the library
|
|
151
|
+
Import the library and add the **`CUSTOM_ELEMENTS_SCHEMA`**. Angular passes `[config]` to the element as a property, so no extra wrapper is needed.
|
|
210
152
|
|
|
211
153
|
```typescript
|
|
212
154
|
import {
|
|
213
155
|
Component,
|
|
214
|
-
ViewChild,
|
|
215
156
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
216
|
-
|
|
157
|
+
ElementRef,
|
|
158
|
+
afterNextRender,
|
|
217
159
|
signal,
|
|
160
|
+
viewChild,
|
|
218
161
|
} from "@angular/core";
|
|
219
|
-
import "@signosoft/signpad-js"; //
|
|
220
|
-
import {
|
|
221
|
-
type SignosoftSignpad,
|
|
222
|
-
type SignpadConfig,
|
|
223
|
-
} from "@signosoft/signpad-js";
|
|
224
|
-
import { SignosoftSignpadDirective } from "./directives/signosoft-signpad.directive";
|
|
162
|
+
import "@signosoft/signpad-js"; // Registers the Web Component
|
|
163
|
+
import type { SignosoftSignpad, SignpadConfig } from "@signosoft/signpad-js";
|
|
225
164
|
|
|
226
165
|
@Component({
|
|
227
166
|
selector: "app-root",
|
|
228
|
-
|
|
229
|
-
imports: [SignosoftSignpadDirective],
|
|
230
|
-
templateUrl: "./app.component.html",
|
|
167
|
+
templateUrl: "./app.html",
|
|
231
168
|
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Mandatory for custom HTML tags
|
|
232
169
|
})
|
|
233
|
-
export class
|
|
234
|
-
|
|
235
|
-
|
|
170
|
+
export class App {
|
|
171
|
+
// The element behind #pad in the template: call ok(), clear(), startSigning() on it
|
|
172
|
+
readonly pad = viewChild.required<ElementRef<SignosoftSignpad>>("pad");
|
|
236
173
|
|
|
237
|
-
|
|
174
|
+
readonly config = signal<SignpadConfig>({
|
|
238
175
|
lease: undefined,
|
|
176
|
+
eventCallbacks: {
|
|
177
|
+
onOk: (event) => {
|
|
178
|
+
// The signature: [points, imageBase64, metadata]
|
|
179
|
+
const [points, imageBase64, metadata] = event.detail;
|
|
180
|
+
console.log(points, imageBase64, metadata);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
239
183
|
});
|
|
240
184
|
|
|
241
|
-
|
|
242
|
-
|
|
185
|
+
constructor() {
|
|
186
|
+
// Runs in the browser after the first render
|
|
187
|
+
afterNextRender(() => this.applyLeaseToConfig());
|
|
243
188
|
}
|
|
244
189
|
|
|
245
|
-
|
|
190
|
+
async fetchLease(): Promise<any> {
|
|
246
191
|
try {
|
|
247
192
|
const response = await fetch(
|
|
248
|
-
"https://test.signosoft.com/
|
|
193
|
+
"https://api-test.signosoft.com/v1/public/driver-leases",
|
|
249
194
|
{
|
|
250
195
|
method: "POST",
|
|
251
196
|
headers: { "Content-Type": "application/json" },
|
|
252
197
|
body: JSON.stringify({
|
|
253
|
-
|
|
254
|
-
validityHours: 24,
|
|
255
|
-
port: 4204,
|
|
198
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
256
199
|
}),
|
|
257
200
|
},
|
|
258
201
|
);
|
|
@@ -271,7 +214,7 @@ export class AppComponent implements AfterViewInit {
|
|
|
271
214
|
}
|
|
272
215
|
}
|
|
273
216
|
|
|
274
|
-
|
|
217
|
+
async applyLeaseToConfig() {
|
|
275
218
|
const lease = await this.fetchLease();
|
|
276
219
|
this.config.update((currentConfig) => ({
|
|
277
220
|
...currentConfig,
|
|
@@ -279,23 +222,78 @@ export class AppComponent implements AfterViewInit {
|
|
|
279
222
|
}));
|
|
280
223
|
}
|
|
281
224
|
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
return this.
|
|
225
|
+
// Example: start a new session with blue ink from your own button
|
|
226
|
+
startBlue() {
|
|
227
|
+
return this.pad().nativeElement.startSigning({ color: "#0000ff" });
|
|
285
228
|
}
|
|
286
229
|
}
|
|
287
230
|
```
|
|
288
231
|
|
|
289
|
-
|
|
232
|
+
##### 2. Component Template
|
|
290
233
|
|
|
291
|
-
Use the custom tag in your HTML and bind the configuration object.
|
|
234
|
+
Use the custom tag in your HTML, name it with `#pad` and bind the configuration object.
|
|
292
235
|
|
|
293
236
|
```html
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
237
|
+
<signosoft-signpad #pad [config]="config()"></signosoft-signpad>
|
|
238
|
+
<button (click)="startBlue()">Sign with blue ink</button>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The `config` signal is the only link between your code and the component: assign a new object to it and the component updates. `this.pad().nativeElement` is the element itself, so every method from [Methods](#methods) is available on it.
|
|
242
|
+
|
|
243
|
+
##### 3. Optional: a typed directive
|
|
244
|
+
|
|
245
|
+
Not required. Add it if you want Angular to type-check `[config]` in templates and a typed handle to the element (`instance`) instead of `viewChild<ElementRef>`.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// signosoft.directive.ts
|
|
249
|
+
import { Directive, ElementRef, effect, inject, input } from "@angular/core";
|
|
250
|
+
import type { SignosoftSignpad, SignpadConfig } from "@signosoft/signpad-js";
|
|
251
|
+
|
|
252
|
+
@Directive({ selector: "signosoft-signpad" })
|
|
253
|
+
export class SignosoftSignpadDirective {
|
|
254
|
+
readonly config = input<SignpadConfig>();
|
|
255
|
+
private readonly host = inject<ElementRef<SignosoftSignpad>>(ElementRef);
|
|
256
|
+
|
|
257
|
+
get instance(): SignosoftSignpad {
|
|
258
|
+
return this.host.nativeElement;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
constructor() {
|
|
262
|
+
// Push every config change into the Web Component
|
|
263
|
+
effect(() => {
|
|
264
|
+
const config = this.config();
|
|
265
|
+
if (config) this.host.nativeElement.config = config;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
To use it, change three things in the component from steps 1 and 2:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { SignosoftSignpadDirective } from "./signosoft.directive";
|
|
275
|
+
|
|
276
|
+
@Component({
|
|
277
|
+
selector: "app-root",
|
|
278
|
+
templateUrl: "./app.html",
|
|
279
|
+
imports: [SignosoftSignpadDirective], // importing the file is not enough, list the directive here
|
|
280
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
281
|
+
})
|
|
282
|
+
export class App {
|
|
283
|
+
// Finds the directive by its class instead of the #pad template reference
|
|
284
|
+
readonly pad = viewChild.required(SignosoftSignpadDirective);
|
|
285
|
+
|
|
286
|
+
startBlue() {
|
|
287
|
+
// .instance is the SignosoftSignpad element
|
|
288
|
+
return this.pad().instance.startSigning({ color: "#0000ff" });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The template no longer needs `#pad`:
|
|
294
|
+
|
|
295
|
+
```html
|
|
296
|
+
<signosoft-signpad [config]="config()"></signosoft-signpad>
|
|
299
297
|
```
|
|
300
298
|
|
|
301
299
|
</details>
|
|
@@ -307,19 +305,23 @@ Use the custom tag in your HTML and bind the configuration object.
|
|
|
307
305
|
</summary>
|
|
308
306
|
<br>
|
|
309
307
|
|
|
310
|
-
|
|
308
|
+
#### <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg" alt="React" width="30" height="30"> React - Quick start
|
|
311
309
|
|
|
312
310
|
This guide describes how to integrate the `@signosoft/signpad-js` web component into a React application (Functional Components with Hooks).
|
|
313
311
|
|
|
314
|
-
|
|
312
|
+
It needs **React 19 or newer**: React 19 passes the `config` object to the custom element as a property. React 18 turns unknown JSX props into string attributes, so `config` would arrive as `"[object Object]"`. On React 18 assign it yourself in an effect:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
const signpadRef = useRef<any>(null);
|
|
315
316
|
|
|
316
|
-
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
if (signpadRef.current) signpadRef.current.config = config;
|
|
319
|
+
}, [config]);
|
|
317
320
|
|
|
318
|
-
|
|
319
|
-
npm install @signosoft/signpad-js
|
|
321
|
+
return <signosoft-signpad ref={signpadRef} />;
|
|
320
322
|
```
|
|
321
323
|
|
|
322
|
-
|
|
324
|
+
##### 1. Implementation
|
|
323
325
|
|
|
324
326
|
In React, we use `useRef` for methods and `useEffect` to fetch a lease before updating config.
|
|
325
327
|
|
|
@@ -331,14 +333,12 @@ import type { SignpadConfig } from "@signosoft/signpad-js";
|
|
|
331
333
|
async function fetchLease() {
|
|
332
334
|
try {
|
|
333
335
|
const response = await fetch(
|
|
334
|
-
"https://test.signosoft.com/
|
|
336
|
+
"https://api-test.signosoft.com/v1/public/driver-leases",
|
|
335
337
|
{
|
|
336
338
|
method: "POST",
|
|
337
339
|
headers: { "Content-Type": "application/json" },
|
|
338
340
|
body: JSON.stringify({
|
|
339
|
-
|
|
340
|
-
validityHours: 24,
|
|
341
|
-
port: 4204,
|
|
341
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
342
342
|
}),
|
|
343
343
|
},
|
|
344
344
|
);
|
|
@@ -360,7 +360,16 @@ async function fetchLease() {
|
|
|
360
360
|
function App() {
|
|
361
361
|
// Use ref to access component methods (ok, clear, connect, etc. by signpadRef.current)
|
|
362
362
|
const signpadRef = useRef<any>(null);
|
|
363
|
-
const [config, setConfig] = useState<SignpadConfig>({
|
|
363
|
+
const [config, setConfig] = useState<SignpadConfig>({
|
|
364
|
+
lease: undefined,
|
|
365
|
+
eventCallbacks: {
|
|
366
|
+
onOk: (event) => {
|
|
367
|
+
// The signature: [points, imageBase64, metadata]
|
|
368
|
+
const [points, imageBase64, metadata] = event.detail;
|
|
369
|
+
console.log(points, imageBase64, metadata);
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
});
|
|
364
373
|
|
|
365
374
|
useEffect(() => {
|
|
366
375
|
fetchLease().then((lease) => setConfig((prev) => ({ ...prev, lease })));
|
|
@@ -386,19 +395,11 @@ export default App;
|
|
|
386
395
|
</summary>
|
|
387
396
|
<br>
|
|
388
397
|
|
|
389
|
-
|
|
398
|
+
#### <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/vuejs/vuejs-original.svg" alt="Vue" width="30" height="30"> Vue.js Integration Guide
|
|
390
399
|
|
|
391
400
|
This guide describes how to integrate the `@signosoft/signpad-js` web component into a Vue 3 application using the Composition API (`<script setup>`).
|
|
392
401
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
Install the core package using npm:
|
|
396
|
-
|
|
397
|
-
```bash
|
|
398
|
-
npm install @signosoft/signpad-js
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
## 2. Configure Vue to recognize Custom Elements
|
|
402
|
+
##### 1. Configure Vue to recognize Custom Elements
|
|
402
403
|
|
|
403
404
|
By default, Vue will try to resolve `signosoft-signpad` as a Vue component and will throw a warning. You need to tell Vue to ignore this tag.
|
|
404
405
|
|
|
@@ -422,7 +423,7 @@ export default defineConfig({
|
|
|
422
423
|
});
|
|
423
424
|
```
|
|
424
425
|
|
|
425
|
-
|
|
426
|
+
##### 2. Implementation
|
|
426
427
|
|
|
427
428
|
In Vue 3, we use `ref` for both the element and config, then fetch the lease and assign it to config.
|
|
428
429
|
|
|
@@ -435,17 +436,24 @@ import type { SignpadConfig, SignosoftSignpad, IPenData } from "@signosoft/signp
|
|
|
435
436
|
// Use ref to access component methods (ok, clear, connect, etc. by signpadRef.value)
|
|
436
437
|
const signpadRef = ref<SignosoftSignpad | null>(null);
|
|
437
438
|
|
|
438
|
-
const signpadConfig = ref<SignpadConfig>({
|
|
439
|
+
const signpadConfig = ref<SignpadConfig>({
|
|
440
|
+
lease: undefined,
|
|
441
|
+
eventCallbacks: {
|
|
442
|
+
onOk: (event) => {
|
|
443
|
+
// The signature: [points, imageBase64, metadata]
|
|
444
|
+
const [points, imageBase64, metadata] = event.detail;
|
|
445
|
+
console.log(points, imageBase64, metadata);
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
});
|
|
439
449
|
|
|
440
450
|
const fetchLease = async () => {
|
|
441
451
|
try {
|
|
442
|
-
const response = await fetch("https://test.signosoft.com/
|
|
452
|
+
const response = await fetch("https://api-test.signosoft.com/v1/public/driver-leases", {
|
|
443
453
|
method: "POST",
|
|
444
454
|
headers: { "Content-Type": "application/json" },
|
|
445
455
|
body: JSON.stringify({
|
|
446
|
-
|
|
447
|
-
validityHours: 24,
|
|
448
|
-
port: 4204,
|
|
456
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
449
457
|
}),
|
|
450
458
|
});
|
|
451
459
|
|
|
@@ -490,21 +498,15 @@ fetchLease()
|
|
|
490
498
|
</summary>
|
|
491
499
|
<br>
|
|
492
500
|
|
|
493
|
-
|
|
501
|
+
#### 🍦 Vanilla JS Integration Guide
|
|
494
502
|
|
|
495
503
|
This guide describes how to integrate the `@signosoft/signpad-js` web component into a plain JavaScript project without any frameworks.
|
|
496
504
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
Install the package via npm:
|
|
500
|
-
|
|
501
|
-
```bash
|
|
502
|
-
npm install @signosoft/signpad-js
|
|
503
|
-
```
|
|
505
|
+
##### 1. Load the component
|
|
504
506
|
|
|
505
|
-
|
|
507
|
+
Install the package first (see [Installation](#installation)). If you are not using a bundler, you can include the script directly in your HTML (ensure the path points to the compiled bundle in `node_modules` or a CDN).
|
|
506
508
|
|
|
507
|
-
|
|
509
|
+
##### 2. JavaScript Implementation
|
|
508
510
|
|
|
509
511
|
In Vanilla JS, you interact with the component by selecting it from the DOM and assigning the configuration directly to its `config` property.
|
|
510
512
|
|
|
@@ -516,24 +518,29 @@ import "@signosoft/signpad-js";
|
|
|
516
518
|
* Helps VS Code provide autocomplete for methods and properties.
|
|
517
519
|
* @type {import('@signosoft/signpad-js').SignosoftSignpad}
|
|
518
520
|
*/
|
|
519
|
-
const
|
|
521
|
+
const signosoftPad = document.querySelector("signosoft-signpad");
|
|
520
522
|
|
|
521
523
|
// 1. Initial Configuration
|
|
522
|
-
|
|
524
|
+
signosoftPad.config = {
|
|
523
525
|
lease: undefined,
|
|
526
|
+
eventCallbacks: {
|
|
527
|
+
onOk: (event) => {
|
|
528
|
+
// The signature: [points, imageBase64, metadata]
|
|
529
|
+
const [points, imageBase64, metadata] = event.detail;
|
|
530
|
+
console.log(points, imageBase64, metadata);
|
|
531
|
+
},
|
|
532
|
+
},
|
|
524
533
|
};
|
|
525
534
|
|
|
526
535
|
const fetchLease = async () => {
|
|
527
536
|
try {
|
|
528
537
|
const response = await fetch(
|
|
529
|
-
"https://test.signosoft.com/
|
|
538
|
+
"https://api-test.signosoft.com/v1/public/driver-leases",
|
|
530
539
|
{
|
|
531
540
|
method: "POST",
|
|
532
541
|
headers: { "Content-Type": "application/json" },
|
|
533
542
|
body: JSON.stringify({
|
|
534
|
-
|
|
535
|
-
validityHours: 24,
|
|
536
|
-
port: 4204,
|
|
543
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
537
544
|
}),
|
|
538
545
|
},
|
|
539
546
|
);
|
|
@@ -553,13 +560,13 @@ const fetchLease = async () => {
|
|
|
553
560
|
};
|
|
554
561
|
|
|
555
562
|
fetchLease().then((lease) => {
|
|
556
|
-
|
|
563
|
+
signosoftPad.config = { ...signosoftPad.config, lease };
|
|
557
564
|
});
|
|
558
565
|
```
|
|
559
566
|
|
|
560
|
-
|
|
567
|
+
##### 3. HTML Structure
|
|
561
568
|
|
|
562
|
-
Add the custom element tag to your HTML and
|
|
569
|
+
Add the custom element tag to your HTML. The component renders its own Connect, OK, Clear and Cancel buttons, so no extra controls are required.
|
|
563
570
|
|
|
564
571
|
```html
|
|
565
572
|
<!-- index.html -->
|
|
@@ -579,15 +586,17 @@ Add the custom element tag to your HTML and create the necessary control buttons
|
|
|
579
586
|
</html>
|
|
580
587
|
```
|
|
581
588
|
|
|
582
|
-
|
|
589
|
+
##### 4. Key Concepts
|
|
583
590
|
|
|
584
591
|
- **Direct Property Assignment:** Unlike some frameworks that use attributes, in Plain JS you should assign the configuration directly: `element.config = { ... }`.
|
|
585
592
|
- **Module System:** Ensure your `<script>` tag has `type="module"` to use the `import` statement.
|
|
586
593
|
- **Methods:** All methods like `.connect()`, `.ok()`, and `.clear()` are available directly on the DOM element object.
|
|
587
|
-
- **DOM Events:**
|
|
594
|
+
- **DOM Events:** every callback is also dispatched as a DOM `CustomEvent` (name in the "DOM event" column of [Event Callbacks](#eventcallbacks)) that bubbles and crosses shadow boundaries, so you can use `addEventListener` on the element or a parent.
|
|
588
595
|
|
|
589
596
|
</details>
|
|
590
597
|
|
|
598
|
+
<a id="properties"></a>
|
|
599
|
+
|
|
591
600
|
## 📋 Properties
|
|
592
601
|
|
|
593
602
|
The component is primarily configured through a single `config` property. This object controls everything from licensing and hardware behavior to UI visibility and localization.
|
|
@@ -596,30 +605,36 @@ The component is primarily configured through a single `config` property. This o
|
|
|
596
605
|
| :------- | :-------------- | :------ | :----------------------------------------------- |
|
|
597
606
|
| `config` | `SignpadConfig` | `{}` | The main configuration object for the component. |
|
|
598
607
|
|
|
599
|
-
###
|
|
608
|
+
### What goes in `config`
|
|
600
609
|
|
|
601
|
-
|
|
602
|
-
| :---------- | :------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
603
|
-
| **`lease`** | `object` | Required for hardware mode | Lease payload returned from the lease API call. The component still renders without it, but driver initialization and hardware communication are disabled (see [Lease Setup](#-lease-setup)). |
|
|
610
|
+
Everything below is a key of the one `config` object. Every key is optional.
|
|
604
611
|
|
|
605
|
-
|
|
612
|
+
```typescript
|
|
613
|
+
const config: SignpadConfig = {
|
|
614
|
+
lease: {}, // licence, required for hardware mode
|
|
615
|
+
theme: "light", // look: "light" | "dark" | "auto"
|
|
616
|
+
languageOptions: {}, // texts
|
|
617
|
+
uiVisibilityOptions: {}, // show/hide UI parts
|
|
618
|
+
canvasAndDrawingOptions: {}, // pen color and width
|
|
619
|
+
actionHandlers: {}, // your code in the OK/Clear/Cancel flow
|
|
620
|
+
eventCallbacks: {}, // observe events
|
|
621
|
+
};
|
|
622
|
+
```
|
|
606
623
|
|
|
607
|
-
|
|
624
|
+
**Not part of `config`:**
|
|
625
|
+
|
|
626
|
+
- **Methods** such as `connect()`, `startSigning()`, `ok()` are called on the element, see [Methods](#methods).
|
|
627
|
+
- **DOM events** can be listened to with `addEventListener`, see [`eventCallbacks`](#eventcallbacks).
|
|
628
|
+
- **Size and colors of the component** are CSS variables, see [Styling & Theming](#styling--theming).
|
|
629
|
+
- **What you get back** (signature, errors) is described in [Results & Errors](#results--errors).
|
|
630
|
+
|
|
631
|
+
### `lease`
|
|
632
|
+
|
|
633
|
+
| Option | Type | Required | Description |
|
|
634
|
+
| :---------- | :------- | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
635
|
+
| **`lease`** | `object` | Required for hardware mode | Lease payload returned from the lease API call. The component still renders without it, but driver initialization and hardware communication are disabled (see [Lease Setup](#lease-setup)). |
|
|
608
636
|
|
|
609
|
-
|
|
610
|
-
| :--------------------------- | :-------- | :------ | :---------------------------------------------------------------- |
|
|
611
|
-
| `topBarVisible` | `boolean` | `true` | Shows/hides the entire top navigation bar. |
|
|
612
|
-
| `topBarClearButtonVisible` | `boolean` | `false` | Shows a "Clear" button in the top-left corner. |
|
|
613
|
-
| `topBarConnectButtonVisible` | `boolean` | `true` | Shows the connect/disconnect button area in the top-right corner. |
|
|
614
|
-
| `bottomBarVisible` | `boolean` | `true` | Shows/hides the entire bottom action bar. |
|
|
615
|
-
| `okButtonVisible` | `boolean` | `true` | Shows the **OK** button in the bottom bar. |
|
|
616
|
-
| `clearButtonVisible` | `boolean` | `true` | Shows the **Clear** button in the bottom bar. |
|
|
617
|
-
| `cancelButtonVisible` | `boolean` | `true` | Shows the **Cancel** button in the bottom bar. |
|
|
618
|
-
| `canvasLineVisible` | `boolean` | `true` | Shows the horizontal signature guide line on the canvas. |
|
|
619
|
-
| `deviceStatusTextVisible` | `boolean` | `true` | Shows the name of the connected device (e.g., "Wacom STU-540"). |
|
|
620
|
-
| `additionalTextVisible` | `boolean` | `true` | Shows helper status messages (e.g., "Ready to sign"). |
|
|
621
|
-
|
|
622
|
-
### 🖋️ canvasAndDrawingOptions
|
|
637
|
+
### `canvasAndDrawingOptions`
|
|
623
638
|
|
|
624
639
|
Adjusts the visual appearance of the digital ink.
|
|
625
640
|
|
|
@@ -631,93 +646,132 @@ Adjusts the visual appearance of the digital ink.
|
|
|
631
646
|
|
|
632
647
|
> Note: Since standard mice do not support physical pressure, the system emulates a normalized pressure of 0.5, ensuring consistent line rendering during fallback sessions.
|
|
633
648
|
|
|
634
|
-
###
|
|
649
|
+
### `uiVisibilityOptions`
|
|
635
650
|
|
|
636
|
-
|
|
651
|
+
Toggles the visibility of specific User Interface elements.
|
|
637
652
|
|
|
638
|
-
| Field
|
|
639
|
-
|
|
|
640
|
-
| `
|
|
641
|
-
| `
|
|
642
|
-
| `
|
|
653
|
+
| Field | Type | Default | Description |
|
|
654
|
+
| :------------------------------- | :-------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
655
|
+
| `topBarVisible` | `boolean` | `true` | Shows/hides the entire top navigation bar. |
|
|
656
|
+
| `topBarClearButtonVisible` | `boolean` | `false` | Shows a "Clear" button in the top-left corner. |
|
|
657
|
+
| `topBarConnectButtonVisible` | `boolean` | `true` | Shows the connect/disconnect button area in the top-right corner. The button is hidden while an OK, Clear or Cancel action is processing. |
|
|
658
|
+
| `bottomBarVisible` | `boolean` | `true` | Shows/hides the entire bottom action bar. |
|
|
659
|
+
| `okButtonVisible` | `boolean` | `true` | Shows the **OK** button in the bottom bar. |
|
|
660
|
+
| `clearButtonVisible` | `boolean` | `true` | Shows the **Clear** button in the bottom bar. |
|
|
661
|
+
| `cancelButtonVisible` | `boolean` | `true` | Shows the **Cancel** button in the bottom bar. |
|
|
662
|
+
| `canvasLineVisible` | `boolean` | `true` | Shows the horizontal signature guide line on the canvas. |
|
|
663
|
+
| `deviceStatusTextVisible` | `boolean` | `true` | Shows the name of the connected device (e.g., "Wacom STU-540"). |
|
|
664
|
+
| `additionalTextVisible` | `boolean` | `true` | Shows helper status messages (e.g., "Ready to sign"). |
|
|
665
|
+
| `startNewSignatureButtonVisible` | `boolean` | `true` | After OK or Cancel the signing session is over and nothing is captured until `startSigning()` is called. While in that state the bottom bar shows a **Start new signature** button instead of OK/Clear/Cancel. Set `false` to hide it and call `startSigning()` yourself (it is also hidden with `bottomBarVisible: false`). |
|
|
666
|
+
|
|
667
|
+
### `languageOptions`
|
|
643
668
|
|
|
644
|
-
|
|
669
|
+
Handles component localization. It can load external files or use inline definitions.
|
|
645
670
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
4. **Dynamic placeholders** (for example, `{device}` in `TEXT_SIGNING_WITH_DEVICE`) are replaced using the `LocalizationManager.t` helper. Keep the braces intact if you expect runtime interpolation.
|
|
652
|
-
|
|
653
|
-
The complete list of keys that can be translated is maintained in `src/i18n/en.json`. You can copy the structure when creating new language files or inline overrides. The defaults are:
|
|
654
|
-
|
|
655
|
-
| Key | Default (English) |
|
|
656
|
-
| :--------------------------- | :-------------------------------------------------- |
|
|
657
|
-
| `OK` | OK |
|
|
658
|
-
| `CLEAR` | Clear |
|
|
659
|
-
| `CANCEL` | Cancel |
|
|
660
|
-
| `CLEAR_SIGNATURE` | Clear signature |
|
|
661
|
-
| `DISCONNECT` | Disconnect |
|
|
662
|
-
| `CONNECT_SIGNPAD` | Connect Signpad |
|
|
663
|
-
| `MOUSE` | Mouse |
|
|
664
|
-
| `CONNECTED_UNKNOWN_DEVICE` | Connected (Unknown Device) |
|
|
665
|
-
| `AUTO_CONNECTING` | Auto-connecting to device... |
|
|
666
|
-
| `CONNECTING_TO_DEVICE` | Connecting to your device... |
|
|
667
|
-
| `REVERTING_TO_DEFAULT` | Reverting to default input... |
|
|
668
|
-
| `DISCONNECTING_DEVICE` | Disconnecting device... |
|
|
669
|
-
| `COMPONENT_INITIALIZED` | Component initialized. |
|
|
670
|
-
| `TEXT_START_SIGNING` | Connect your device. |
|
|
671
|
-
| `TEXT_SIGN_WITH_PHYSICAL` | Please sign using the device pen. |
|
|
672
|
-
| `TEXT_SIGN_WITH_MOUSE` | Please sign using your mouse. |
|
|
673
|
-
| `TEXT_SIGN_GENERIC` | Sign in the area above using mouse or pen. |
|
|
674
|
-
| `TEXT_SIGNING_WITH_DEVICE` | Signing with {device} |
|
|
675
|
-
| `NO_DEVICE_FOUND` | No device found. |
|
|
676
|
-
| `CONNECTION_ERROR` | Connection error. |
|
|
677
|
-
| `ERROR_DISCONNECTION` | Error during disconnection. |
|
|
678
|
-
| `ERROR_INSTANTIATING_DRIVER` | Error instantiating driver. |
|
|
679
|
-
| `INVALID_LICENSE_KEY` | Invalid license key. |
|
|
680
|
-
| `READY_TO_CONNECT` | Ready to connect. |
|
|
681
|
-
| `SIGNPAD_DETECTED` | Signpad detected. Click 'Connect Signpad' to begin. |
|
|
682
|
-
| `READY_TO_SIGN_MOUSE` | Ready to sign. Use your mouse. |
|
|
683
|
-
| `READY_TO_SIGN_PHYSICAL` | Ready to sign. Use the device pen. |
|
|
671
|
+
| Field | Type | Description |
|
|
672
|
+
| :------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
673
|
+
| `lang` | `string` | The active language code. Built-in translations are bundled for `'en'`, `'cs'` and `'pt'`; other languages require `langPath` or `translations` (see [Applying a language](#applying-a-language)). |
|
|
674
|
+
| `langPath` | `string` | URL path to fetch translation files. Expects files in `[path]/[lang].json` format. |
|
|
675
|
+
| `translations` | `object` | An inline object containing key-value pairs (e.g., `{ "OK": "Confirm signature" }`). Overrides files. |
|
|
684
676
|
|
|
685
|
-
|
|
677
|
+
### `theme`
|
|
686
678
|
|
|
687
|
-
|
|
679
|
+
Color theme of the component.
|
|
688
680
|
|
|
689
|
-
|
|
681
|
+
| Option | Type | Required | Description |
|
|
682
|
+
| :---------- | :---------------------------- | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
683
|
+
| **`theme`** | `'light' \| 'dark' \| 'auto'` | Optional, default `'light'` | Color theme. `'dark'` switches the bars, buttons and overlay to the dark design tokens; `'auto'` follows the OS `prefers-color-scheme` and updates live. The signature canvas stays light in every theme, because the default ink is black. Exported as type `SignpadTheme`. The component sets `data-theme` on its host element, so you can style around it. |
|
|
690
684
|
|
|
691
|
-
|
|
685
|
+
### `actionHandlers`
|
|
692
686
|
|
|
693
687
|
These allow you to **inject custom logic** directly into the internal button flows. Your code runs alongside the component's internal logic.
|
|
694
688
|
|
|
695
|
-
| Handler | Arguments | Description
|
|
696
|
-
| :------------- | :---------------------------------- |
|
|
697
|
-
| `handleOk` | `data?: ISignatureConfirmationData` | Extends the internal OK button logic. Receives captured signature
|
|
698
|
-
| `handleClear` | — | Extends the internal Clear button logic.
|
|
699
|
-
| `handleCancel` | — | Extends the internal Cancel button logic.
|
|
689
|
+
| Handler | Arguments | Description |
|
|
690
|
+
| :------------- | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
691
|
+
| `handleOk` | `data?: ISignatureConfirmationData` | Extends the internal OK button logic. Receives the captured signature, see [Signature result](#signature-result-isignatureconfirmationdata). |
|
|
692
|
+
| `handleClear` | — | Extends the internal Clear button logic. |
|
|
693
|
+
| `handleCancel` | — | Extends the internal Cancel button logic. |
|
|
700
694
|
|
|
701
|
-
|
|
695
|
+
Handlers may be `async`. The component awaits them, and only then dispatches the matching `sign-ok` / `sign-clear` / `sign-cancel` event.
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
const config: SignpadConfig = {
|
|
699
|
+
lease,
|
|
700
|
+
actionHandlers: {
|
|
701
|
+
// Runs after the signature is captured, before `sign-ok` is dispatched
|
|
702
|
+
handleOk: async (data) => {
|
|
703
|
+
if (!data) return;
|
|
704
|
+
const [, imageBase64, metadata] = data;
|
|
705
|
+
await fetch("/api/signatures", {
|
|
706
|
+
method: "POST",
|
|
707
|
+
headers: { "Content-Type": "application/json" },
|
|
708
|
+
body: JSON.stringify({ imageBase64, metadata }),
|
|
709
|
+
});
|
|
710
|
+
},
|
|
711
|
+
handleClear: () => console.log("Signature cleared"),
|
|
712
|
+
handleCancel: () => console.log("Signing cancelled"),
|
|
713
|
+
},
|
|
714
|
+
};
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### `eventCallbacks`
|
|
702
718
|
|
|
703
719
|
Standard event listeners triggered _after_ specific actions. Useful for observing the component from your application state.
|
|
704
720
|
|
|
705
|
-
| Callback | Payload | Description
|
|
706
|
-
| :------------- | :----------------------------------------------- |
|
|
707
|
-
| `onPen` | `event: CustomEvent<IPenData>` | Dispatched when a device or mouse fallback are in contact with component or signature pad display.
|
|
708
|
-
| `onConnecting` | `event: CustomEvent<void>` | Dispatched when a device is in loading state.
|
|
709
|
-
| `onConnect` | `event: CustomEvent<{ deviceInfo: any }>` | Dispatched when a device or mouse fallback connects.
|
|
710
|
-
| `onDisconnect` | `event: CustomEvent<void>` | Dispatched when the hardware connection is closed.
|
|
711
|
-
| `onOk` | `event: CustomEvent<ISignatureConfirmationData>` | Dispatched when signature is confirmed.
|
|
712
|
-
| `onClear` | `event: CustomEvent<void>` | Dispatched when the canvas has been cleared.
|
|
713
|
-
| `onCancel` | `event: CustomEvent<void>` | Dispatched when the user aborts the session.
|
|
714
|
-
| `onError` | `event: CustomEvent<
|
|
721
|
+
| Callback | DOM event | Payload | Description |
|
|
722
|
+
| :------------- | :---------------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
723
|
+
| `onPen` | `sign-pen` | `event: CustomEvent<IPenData>` | Dispatched when a device or mouse fallback are in contact with component or signature pad display. |
|
|
724
|
+
| `onConnecting` | `sign-connecting` | `event: CustomEvent<void>` | Dispatched when a device is in loading state. |
|
|
725
|
+
| `onConnect` | `sign-connect` | `event: CustomEvent<{ deviceInfo: any }>` | Dispatched when a device or mouse fallback connects. |
|
|
726
|
+
| `onDisconnect` | `sign-disconnect` | `event: CustomEvent<void>` | Dispatched when the hardware connection is closed. |
|
|
727
|
+
| `onOk` | `sign-ok` | `event: CustomEvent<ISignatureConfirmationData>` | Dispatched when signature is confirmed. The signature is in `event.detail`, see [Signature result](#signature-result-isignatureconfirmationdata). |
|
|
728
|
+
| `onClear` | `sign-clear` | `event: CustomEvent<void>` | Dispatched when the canvas has been cleared. |
|
|
729
|
+
| `onCancel` | `sign-cancel` | `event: CustomEvent<void>` | Dispatched when the user aborts the session. |
|
|
730
|
+
| `onError` | `sign-error` | `event: CustomEvent<ISignpadErrorDetail>` | Dispatched on critical failures (lease, driver, connection, billing, etc.). |
|
|
715
731
|
|
|
716
732
|
> `actionHandlers` and `eventCallbacks` are separate hooks and can both be configured.
|
|
717
733
|
|
|
734
|
+
**Listening with the config.** Pass the callbacks in `eventCallbacks`; each receives the `CustomEvent`:
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
const config: SignpadConfig = {
|
|
738
|
+
lease,
|
|
739
|
+
eventCallbacks: {
|
|
740
|
+
onOk: (event) => {
|
|
741
|
+
const [points, imageBase64] = event.detail; // see Signature result
|
|
742
|
+
console.log(`${points.length} points`, imageBase64);
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
};
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
**Listening without the config.** Every callback is also a DOM event (column "DOM event"), so `addEventListener` works on the element like on any other:
|
|
749
|
+
|
|
750
|
+
```js
|
|
751
|
+
signosoftPad.addEventListener("sign-ok", (event) => {
|
|
752
|
+
const [points, imageBase64] = event.detail; // see Signature result
|
|
753
|
+
console.log(`${points.length} points`, imageBase64);
|
|
754
|
+
});
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
#### `onPen` payload
|
|
758
|
+
|
|
759
|
+
`onPen` is emitted continuously while writing and provides a single `IPenData` object in `event.detail`.
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
interface IPenData {
|
|
763
|
+
relativeX: number; // normalized 0..1
|
|
764
|
+
relativeY: number; // normalized 0..1
|
|
765
|
+
pressure?: number;
|
|
766
|
+
timestamp?: number; // Unix ms (Date.now()) at the time of capture
|
|
767
|
+
inContact: boolean;
|
|
768
|
+
[key: string]: any; // the driver may add more fields; the component does not populate absoluteX, absoluteY, ready or sequence
|
|
769
|
+
}
|
|
770
|
+
```
|
|
771
|
+
|
|
718
772
|
---
|
|
719
773
|
|
|
720
|
-
###
|
|
774
|
+
### Full example
|
|
721
775
|
|
|
722
776
|
```typescript
|
|
723
777
|
this.config = {
|
|
@@ -738,6 +792,131 @@ this.config = {
|
|
|
738
792
|
};
|
|
739
793
|
```
|
|
740
794
|
|
|
795
|
+
<a id="results--errors"></a>
|
|
796
|
+
|
|
797
|
+
## 📤 Results & Errors
|
|
798
|
+
|
|
799
|
+
What the component hands back to your code: the captured signature and the errors it reports.
|
|
800
|
+
|
|
801
|
+
### Signature result (`ISignatureConfirmationData`)
|
|
802
|
+
|
|
803
|
+
The captured signature arrives in three places, always in the same shape: `ok()` returns it, `actionHandlers.handleOk(data)` receives it, and `eventCallbacks.onOk` (DOM event `sign-ok`) carries it in `event.detail`.
|
|
804
|
+
|
|
805
|
+
It is an **array of three items**, not an object, so read it by position or destructure it:
|
|
806
|
+
|
|
807
|
+
```ts
|
|
808
|
+
eventCallbacks: {
|
|
809
|
+
onOk: (event) => {
|
|
810
|
+
const [points, imageBase64, metadata] = event.detail;
|
|
811
|
+
},
|
|
812
|
+
},
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
| Index | Name | Type | Content |
|
|
816
|
+
| :---- | :------------ | :--------------- | :------------------------------------- |
|
|
817
|
+
| `0` | `points` | `IPenData[]` | Every captured pen sample. |
|
|
818
|
+
| `1` | `imageBase64` | `string \| null` | PNG data URL of the signature. |
|
|
819
|
+
| `2` | `metadata` | object | Canvas size, tablet info and duration. |
|
|
820
|
+
|
|
821
|
+
#### `[0]` points
|
|
822
|
+
|
|
823
|
+
Every captured pen sample, in order. `pressure` is `0.5` on the mouse/touch fallback.
|
|
824
|
+
|
|
825
|
+
```typescript
|
|
826
|
+
interface IPenDataPoint {
|
|
827
|
+
relativeX: number; // normalized 0..1
|
|
828
|
+
relativeY: number; // normalized 0..1
|
|
829
|
+
absoluteX?: number; // not populated by the component
|
|
830
|
+
absoluteY?: number; // not populated by the component
|
|
831
|
+
pressure: number;
|
|
832
|
+
timestamp: number; // Unix ms (Date.now()) at the time of capture
|
|
833
|
+
inContact: boolean;
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
#### `[1]` imageBase64
|
|
838
|
+
|
|
839
|
+
Base64 PNG data URL of the signature, e.g. `"data:image/png;base64,iVBORw0KGgo..."`, or `null` if no image could be captured.
|
|
840
|
+
|
|
841
|
+
#### `[2]` metadata
|
|
842
|
+
|
|
843
|
+
The fields depend on whether a physical tablet or the mouse/touch fallback produced the signature.
|
|
844
|
+
|
|
845
|
+
```typescript
|
|
846
|
+
interface ISignatureResultMeta {
|
|
847
|
+
canvas?: ISignatureCanvasMeta;
|
|
848
|
+
// Physical tablet only. `undefined` on the mouse/touch fallback.
|
|
849
|
+
tablet?: ISignatureTabletMeta;
|
|
850
|
+
// Mouse/touch fallback only: seconds between the first and last pen sample.
|
|
851
|
+
// Not reported for a physical tablet.
|
|
852
|
+
duration?: number;
|
|
853
|
+
[key: string]: any;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
interface ISignatureCanvasMeta {
|
|
857
|
+
width?: number;
|
|
858
|
+
height?: number;
|
|
859
|
+
// Mouse/touch fallback only:
|
|
860
|
+
cssWidth?: number;
|
|
861
|
+
cssHeight?: number;
|
|
862
|
+
dpr?: number;
|
|
863
|
+
// Physical tablet only:
|
|
864
|
+
top?: number;
|
|
865
|
+
left?: number;
|
|
866
|
+
right?: number;
|
|
867
|
+
bottom?: number;
|
|
868
|
+
[key: string]: any;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
interface ISignatureTabletMeta {
|
|
872
|
+
name?: string;
|
|
873
|
+
aspectRatio?: number;
|
|
874
|
+
isCanvasStyleTablet?: boolean;
|
|
875
|
+
screenWidth?: number;
|
|
876
|
+
screenHeight?: number;
|
|
877
|
+
drawingArea?: { x: number; y: number; width: number; height: number };
|
|
878
|
+
serialNumber?: string | Promise<string>;
|
|
879
|
+
vendorId?: number | null;
|
|
880
|
+
productId?: number | null;
|
|
881
|
+
[key: string]: any;
|
|
882
|
+
}
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
---
|
|
886
|
+
|
|
887
|
+
### Error handling (`onError`)
|
|
888
|
+
|
|
889
|
+
Critical failures (lease, driver, connection, billing, ...) do not throw. The component switches to the `ERROR` state, shows an overlay and reports the failure through `eventCallbacks.onError` (DOM event `sign-error`). Branch on `event.detail.code`:
|
|
890
|
+
|
|
891
|
+
```typescript
|
|
892
|
+
const config: SignpadConfig = {
|
|
893
|
+
lease,
|
|
894
|
+
eventCallbacks: {
|
|
895
|
+
onError: (event) => {
|
|
896
|
+
const { code, message, error } = event.detail;
|
|
897
|
+
if (code === SignpadErrorCode.BILLING_DENIED) {
|
|
898
|
+
notifyAdmin("Out of signing credits");
|
|
899
|
+
}
|
|
900
|
+
console.error(code, message, error);
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
`event.detail` is `ISignpadErrorDetail`:
|
|
907
|
+
|
|
908
|
+
```typescript
|
|
909
|
+
interface ISignpadErrorDetail {
|
|
910
|
+
code: SignpadErrorCode; // "AREA-NN", e.g. "CON-01", see Error codes
|
|
911
|
+
message: string; // error.message, for convenience
|
|
912
|
+
error: Error; // the original error
|
|
913
|
+
}
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
All values of `code`, what they mean and how the user recovers are in [Error codes](#error-codes).
|
|
917
|
+
|
|
918
|
+
<a id="methods"></a>
|
|
919
|
+
|
|
741
920
|
## 🧩 Methods
|
|
742
921
|
|
|
743
922
|
The following methods are available on the component instance to control the signing process programmatically.
|
|
@@ -752,180 +931,322 @@ The following methods are available on the component instance to control the sig
|
|
|
752
931
|
| `clear()` | `Promise<void>` | Wipes the signature from the UI and hardware without ending the session. |
|
|
753
932
|
| `cancel()` | `Promise<void>` | Aborts the current session and resets the component state. |
|
|
754
933
|
|
|
934
|
+
**Example:** a full signing round. The snippets for each method below call the method on the same `signosoftPad` element.
|
|
935
|
+
|
|
936
|
+
```js
|
|
937
|
+
await signosoftPad.connect(); // opens the WebHID picker, so call it from a click handler
|
|
938
|
+
|
|
939
|
+
// ... the user signs ...
|
|
940
|
+
|
|
941
|
+
const data = await signosoftPad.ok(); // ends the session and returns the signature
|
|
942
|
+
if (data) {
|
|
943
|
+
const [points, imageBase64, metadata] = data;
|
|
944
|
+
console.log(`${points.length} points`, imageBase64, metadata);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// OK and Cancel end the session, so start a new one before the next signature
|
|
948
|
+
await signosoftPad.startSigning();
|
|
949
|
+
```
|
|
950
|
+
|
|
755
951
|
---
|
|
756
952
|
|
|
757
|
-
|
|
953
|
+
### `connect()`
|
|
758
954
|
|
|
759
|
-
|
|
955
|
+
Connects a device to the component. Because it uses the **WebHID API**, it must be triggered by a **user gesture** (like a button click) to satisfy browser security requirements.
|
|
760
956
|
|
|
761
|
-
|
|
957
|
+
```js
|
|
958
|
+
await signosoftPad.connect(); // call it from a click handler
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
It first tries to silently reconnect to a previously authorized device. If that fails, it opens the browser's device selection dialog and, if no physical device is selected, falls back to mouse/touch mode automatically.
|
|
962
|
+
|
|
963
|
+
**Returns:** `Promise<void>`, resolved once the connection attempt (or fallback) is complete.
|
|
762
964
|
|
|
763
|
-
**
|
|
965
|
+
**Tip:** always call it from a click handler so the user can pick their device from the browser list.
|
|
764
966
|
|
|
765
|
-
|
|
967
|
+
---
|
|
766
968
|
|
|
767
|
-
|
|
969
|
+
### `disconnect()`
|
|
768
970
|
|
|
769
971
|
Safely terminates the communication channel with the signature tablet.
|
|
770
972
|
|
|
771
|
-
|
|
973
|
+
```js
|
|
974
|
+
await signosoftPad.disconnect();
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
What happens:
|
|
772
978
|
|
|
773
979
|
1. The hardware driver is stopped and released.
|
|
774
980
|
2. The internal cache of device information is cleared.
|
|
775
|
-
3. The component
|
|
981
|
+
3. The component switches to mouse input (`CONNECTED_MOUSE_FALLBACK`). Failures of the driver's own disconnect are logged, not surfaced.
|
|
776
982
|
4. Any active mouse listeners are removed.
|
|
777
983
|
5. A `SIGN_DISCONNECT` event is dispatched.
|
|
778
984
|
|
|
779
|
-
|
|
985
|
+
**Returns:** `Promise<void>`
|
|
780
986
|
|
|
781
|
-
|
|
782
|
-
- **Cleanup:** It is good practice to call `disconnect()` when the component is being destroyed or the user navigates away from the signing page to free up the USB port.
|
|
987
|
+
**Tip:** call it when the component is destroyed or the user leaves the signing page, to free the USB port.
|
|
783
988
|
|
|
784
|
-
|
|
989
|
+
---
|
|
785
990
|
|
|
786
|
-
|
|
991
|
+
### `startSigning(options?: IDrawingOptions)`
|
|
787
992
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
993
|
+
Prepares the component for a new signature. OK and Cancel end the signing session, so call this (or let the user press the built-in **Start new signature** button, see `startNewSignatureButtonVisible`) before the next signature.
|
|
994
|
+
|
|
995
|
+
```js
|
|
996
|
+
// Start a session with custom ink
|
|
997
|
+
await signosoftPad.startSigning({
|
|
791
998
|
color: "#0000FF",
|
|
792
999
|
maxWidth: 4,
|
|
793
1000
|
minWidth: 2,
|
|
794
1001
|
});
|
|
795
1002
|
```
|
|
796
1003
|
|
|
797
|
-
|
|
1004
|
+
It transitions the state to `CONNECTING`, resets any previous drawing data, and activates the pen input for either a physical device or mouse fallback.
|
|
798
1005
|
|
|
799
|
-
|
|
1006
|
+
**Returns:** `Promise<void>`
|
|
800
1007
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
4. Returns the final signature payload (coordinates, pressure, metadata).
|
|
1008
|
+
---
|
|
1009
|
+
|
|
1010
|
+
### `stopSigning()`
|
|
805
1011
|
|
|
806
|
-
|
|
1012
|
+
Low-level method that forces the driver to stop capturing data and returns the signature payload. Unlike `ok()`, it does not trigger the full finalization flow (UI transitions or device screen clearing).
|
|
807
1013
|
|
|
808
1014
|
```js
|
|
809
|
-
[
|
|
810
|
-
points, // index 0: Data about each point
|
|
811
|
-
imageBase64, // index 1: Image in base64
|
|
812
|
-
metadata, // index 2: Information about tablet and canvas
|
|
813
|
-
];
|
|
1015
|
+
const [penDataBatch, imageBase64, metadata] = await signosoftPad.stopSigning();
|
|
814
1016
|
```
|
|
815
1017
|
|
|
1018
|
+
**Returns:** `Promise<any>`, a 3-element tuple:
|
|
1019
|
+
|
|
1020
|
+
- `penDataBatch`: array of `IPenData` points collected during the session
|
|
1021
|
+
- `imageBase64`: PNG data URL of the signature drawn on canvas, or `null` if unavailable
|
|
1022
|
+
- `metadata`: object with `canvas` (dimensions, DPR) and `tablet` (device info) fields
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
### `ok()`
|
|
1027
|
+
|
|
1028
|
+
The standard way to confirm a signature.
|
|
1029
|
+
|
|
816
1030
|
```js
|
|
817
|
-
|
|
818
|
-
{
|
|
819
|
-
|
|
820
|
-
relativeY: number, // normalized 0..1
|
|
821
|
-
pressure: number,
|
|
822
|
-
inContact: boolean,
|
|
823
|
-
timestamp: number, // Unix ms (Date.now()) at the time of capture
|
|
1031
|
+
const data = await signosoftPad.ok();
|
|
1032
|
+
if (data) {
|
|
1033
|
+
const [points, imageBase64, metadata] = data;
|
|
824
1034
|
}
|
|
825
1035
|
```
|
|
826
1036
|
|
|
1037
|
+
It:
|
|
1038
|
+
|
|
1039
|
+
1. Stops the signing process.
|
|
1040
|
+
2. Clears the physical device's screen (if connected).
|
|
1041
|
+
3. Dispatches the `SIGN_OK` event.
|
|
1042
|
+
4. Returns the final signature payload (coordinates, pressure, metadata).
|
|
1043
|
+
|
|
1044
|
+
After `ok()` the signing session is over: nothing is captured until a new one starts. The bottom bar then shows **Start new signature** (see `startNewSignatureButtonVisible`), or you call `startSigning()` yourself.
|
|
1045
|
+
|
|
1046
|
+
**Returns:** `Promise<ISignatureConfirmationData | undefined>`, the same payload `handleOk` and `onOk` receive; see [Signature result](#signature-result-isignatureconfirmationdata).
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
### `clear()`
|
|
1051
|
+
|
|
1052
|
+
Resets the drawing state on both the web canvas and the physical tablet's display, so the user can start over without leaving the current session.
|
|
1053
|
+
|
|
827
1054
|
```js
|
|
828
|
-
|
|
829
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...";
|
|
1055
|
+
await signosoftPad.clear();
|
|
830
1056
|
```
|
|
831
1057
|
|
|
1058
|
+
**Returns:** `Promise<void>`. Dispatches `SIGN_CLEAR`.
|
|
1059
|
+
|
|
1060
|
+
---
|
|
1061
|
+
|
|
1062
|
+
### `cancel()`
|
|
1063
|
+
|
|
1064
|
+
Stops the session immediately, without collecting any signature data.
|
|
1065
|
+
|
|
832
1066
|
```js
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
// fallback produced the signature — the two paths report different fields.
|
|
836
|
-
|
|
837
|
-
// Mouse / touch fallback:
|
|
838
|
-
{
|
|
839
|
-
canvas: {
|
|
840
|
-
width: number,
|
|
841
|
-
height: number,
|
|
842
|
-
cssWidth: number,
|
|
843
|
-
cssHeight: number,
|
|
844
|
-
dpr: number
|
|
845
|
-
},
|
|
846
|
-
tablet: undefined,
|
|
847
|
-
duration: number // seconds elapsed between the first and last recorded pen sample
|
|
848
|
-
}
|
|
1067
|
+
await signosoftPad.cancel();
|
|
1068
|
+
```
|
|
849
1069
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1070
|
+
Like `ok()`, it ends the signing session: the bottom bar then shows **Start new signature**, or call `startSigning()` to begin again.
|
|
1071
|
+
|
|
1072
|
+
**Returns:** `Promise<void>`. Dispatches `SIGN_CANCEL`.
|
|
1073
|
+
|
|
1074
|
+
<a id="component-states"></a>
|
|
1075
|
+
|
|
1076
|
+
## 🔄 Component States
|
|
1077
|
+
|
|
1078
|
+
The component progresses through a finite set of states, exposed via the `SignpadState` enum (importable from `@signosoft/signpad-js`). Useful for observing the component lifecycle in your application.
|
|
1079
|
+
|
|
1080
|
+
| State | Value | Description |
|
|
1081
|
+
| :------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------------------- |
|
|
1082
|
+
| `CONNECTING` | `"connecting"` | Preparing or connecting. Also the initial state while waiting for the lease. |
|
|
1083
|
+
| `CONNECTED_PHYSICAL` | `"connected_physical"` | Physical tablet connected and ready. |
|
|
1084
|
+
| `CONNECTED_MOUSE_FALLBACK` | `"connected_mouse_fallback"` | No tablet found; mouse/touch fallback is active. |
|
|
1085
|
+
| `PROCESSING_OK` | `"processing_ok"` | OK action is being processed (finalizing signature). |
|
|
1086
|
+
| `PROCESSING_CLEAR` | `"processing_clear"` | Clear action is being processed. |
|
|
1087
|
+
| `PROCESSING_CANCEL` | `"processing_cancel"` | Cancel action is being processed. |
|
|
1088
|
+
| `DISCONNECTING` | `"disconnecting"` | Disconnect in progress. |
|
|
1089
|
+
| `ERROR` | `"error"` | An error occurred (see `onError` callback and [Error codes](#error-codes)). Recovery depends on the code. |
|
|
1090
|
+
|
|
1091
|
+
<a id="error-codes"></a>
|
|
1092
|
+
|
|
1093
|
+
## ⚠️ Error Codes
|
|
1094
|
+
|
|
1095
|
+
Reported through the `onError` callback (`sign-error` event), see [Error handling](#error-handling-onerror). The `code` field uses the `SignpadErrorCode` enum, importable from `@signosoft/signpad-js`. Format `AREA-NN`; it decides how the user gets out of the error:
|
|
1096
|
+
|
|
1097
|
+
| Code | `SignpadErrorCode` | Meaning | Recovery (component) | What your app should do |
|
|
1098
|
+
| :----- | :--------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------- |
|
|
1099
|
+
| LIC-01 | `LICENSE_MISSING` | No `lease` in config after ~6 s. Integration error. | None. Passing a valid `lease` recovers automatically. | Make sure `config.lease` is set; fix the lease fetch (check `onError` / network tab). |
|
|
1100
|
+
| LIC-02 | `LICENSE_INVALID` | The driver rejected the lease (malformed, or its signature/validity/origin check failed). An **expired** lease is reported the same way. | None. Pass a new `lease`. | Fetch a **fresh lease** on every mount and do not cache it across days; verify the licence allows the page's origin. |
|
|
1101
|
+
| DRV-01 | `DRIVER_INIT_FAILED` | Driver / WASM could not be started. | "Restart session" button | Log it; if it persists, check that WASM/`locateFile` assets load and the browser is supported. |
|
|
1102
|
+
| CON-01 | `CONNECT_FAILED` | Unexpected failure while connecting. | "Restart session" button | Nothing required; ask the user to retry or reconnect the device. |
|
|
1103
|
+
| NET-01 | `NETWORK_ERROR` | Network / billing check unreachable. | "Restart session" button | Check connectivity and any proxy/CSP blocking the licence server. |
|
|
1104
|
+
| BIL-01 | `BILLING_DENIED` | The lease ran out of signing credits. | "Restart session" button | Top up credits on the licence; a restart will not help until then. |
|
|
1105
|
+
| SES-01 | `START_SIGNING_FAILED` | The signing session could not be started. | "Restart session" button | Nothing required; ask the user to restart the session. |
|
|
1106
|
+
| ACT-01 | `ACTION_FAILED` | OK / Clear / Cancel failed. | "Restart session" button | Nothing required; the user can retry the action. |
|
|
1107
|
+
|
|
1108
|
+
The error overlay shows the message followed by the code, e.g. `... (Error code BIL-01)` (the suffix is not translated), and a "Restart session" button (for every code except `LIC-01`/`LIC-02`) is shown in the top bar. Nothing restarts automatically, so the user always sees the error first. The button works regardless of `topBarConnectButtonVisible`; if the top bar is hidden (`topBarVisible: false`) it is shown in the overlay instead.
|
|
1109
|
+
|
|
1110
|
+
<a id="type-definitions"></a>
|
|
1111
|
+
|
|
1112
|
+
## 🧱 Type Definitions
|
|
1113
|
+
|
|
1114
|
+
Every type is exported from `@signosoft/signpad-js` and ships with the package, so you get autocompletion and type checking without extra setup. Each one is defined once, where it is used:
|
|
1115
|
+
|
|
1116
|
+
```typescript
|
|
1117
|
+
import type {
|
|
1118
|
+
SignpadConfig,
|
|
1119
|
+
ISignatureConfirmationData,
|
|
1120
|
+
} from "@signosoft/signpad-js";
|
|
873
1121
|
```
|
|
874
1122
|
|
|
875
|
-
|
|
1123
|
+
| Type | Used for | Defined in |
|
|
1124
|
+
| :-------------------------------------------------------------------------------------------------------------------- | :------------------------------------------ | :----------------------------------------------------------------------------------- |
|
|
1125
|
+
| `SignpadConfig`, `SignpadTheme` | the `config` object | [Properties](#properties) |
|
|
1126
|
+
| `IDrawingOptions` | pen color and width | [`startSigning()`](#startsigning-options-idrawingoptions), [Properties](#properties) |
|
|
1127
|
+
| `IUIVisibilityOptions` | show/hide UI parts | [uiVisibilityOptions](#uivisibilityoptions) |
|
|
1128
|
+
| `ILanguageOptions`, `LanguageCode`, `ITranslationSet` | language and translations | [languageOptions](#languageoptions) |
|
|
1129
|
+
| `IActionHandlers` | `handleOk` / `handleClear` / `handleCancel` | [actionHandlers](#actionhandlers) |
|
|
1130
|
+
| `IEventCallbacks` | `onOk`, `onPen`, `onError`, ... | [eventCallbacks](#eventcallbacks) |
|
|
1131
|
+
| `IPenData` | `onPen` payload | [`onPen` payload](#onpen-payload) |
|
|
1132
|
+
| `ISignatureConfirmationData`, `IPenDataPoint`, `ISignatureResultMeta`, `ISignatureCanvasMeta`, `ISignatureTabletMeta` | the signature | [Signature result](#signature-result-isignatureconfirmationdata) |
|
|
1133
|
+
| `ISignpadErrorDetail` | `onError` payload | [Error handling](#error-handling-onerror) |
|
|
1134
|
+
| `SignpadState` | component state (enum) | [Component States](#component-states) |
|
|
1135
|
+
| `SignpadErrorCode` | error codes (enum) | [Error codes](#error-codes) |
|
|
1136
|
+
| `SignpadEventType` | `sign-*` DOM event names (enum) | [Event Callbacks](#eventcallbacks) |
|
|
1137
|
+
|
|
1138
|
+
The enums exist at runtime, so import them without `type`.
|
|
1139
|
+
|
|
1140
|
+
<a id="localization--translation-keys"></a>
|
|
876
1141
|
|
|
877
|
-
|
|
1142
|
+
## 🌐 Localization & Translation Keys
|
|
878
1143
|
|
|
879
|
-
`
|
|
1144
|
+
All texts come from `config.languageOptions` (`lang`, `langPath`, `translations`). There is no other way to set them.
|
|
880
1145
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1146
|
+
| You want | Set |
|
|
1147
|
+
| :---------------------------- | :------------------------------------------------------------------------------------------------------ |
|
|
1148
|
+
| English, Czech or Portuguese | `lang: "cs"` (built in) |
|
|
1149
|
+
| Another language, e.g. German | `lang: "de"` and `langPath: "/i18n"`, with a `de.json` you host |
|
|
1150
|
+
| Your own JSON file | `import yourJson from "./de.json"`, then `translations: yourJson` (works from any file in your project) |
|
|
1151
|
+
| Change a few texts only | `translations: { OK: "Confirm" }` |
|
|
1152
|
+
|
|
1153
|
+
### Applying a language
|
|
1154
|
+
|
|
1155
|
+
**Why there are two ways.** The component runs in the browser, and a browser can only load what a server hands out. So a translation file reaches the component in one of two ways:
|
|
1156
|
+
|
|
1157
|
+
- **Served as a static file (`langPath`).** Put the file in the folder your web server serves as static files and point `langPath` at its URL. The component downloads it with `fetch` when the language is needed. A file anywhere else in your project, such as `src/de.json` or `./../de.json`, is not served, so it cannot be loaded and the texts silently stay English.
|
|
1158
|
+
- **Bundled into your app (`translations`).** If the file lives anywhere else in your project, `import` it and pass it as `translations`. The bundler includes it in your build, so no served folder and no request are needed.
|
|
1159
|
+
|
|
1160
|
+
**Hosted file (`langPath`).** For any language that is not built in (anything except `en`, `cs`, `pt`):
|
|
1161
|
+
|
|
1162
|
+
1. Copy `node_modules/@signosoft/signpad-js/src/i18n/en.json`, translate the values and save it as `de.json` in a folder your app serves as static files.
|
|
1163
|
+
2. Set `langPath` to that folder. The component loads `${langPath}/${lang}.json`.
|
|
1164
|
+
|
|
1165
|
+
```ts
|
|
1166
|
+
languageOptions: { lang: "de", langPath: "/i18n" } // loads /i18n/de.json
|
|
889
1167
|
```
|
|
890
1168
|
|
|
891
|
-
|
|
1169
|
+
`langPath` is the URL of that folder, not a path on disk. If the server serves the folder at the site root, a file saved as `<static folder>/i18n/de.json` is reachable at `/i18n/de.json`, so `langPath` is `"/i18n"`. Which folder is served as static files depends on your project setup; check where your tooling copies files that are not bundled.
|
|
892
1170
|
|
|
893
|
-
|
|
1171
|
+
**Your own texts (`translations`).** Pass an object of key-value strings, flat or one object per language. Keys you leave out come from the selected language:
|
|
894
1172
|
|
|
895
|
-
|
|
1173
|
+
```ts
|
|
1174
|
+
languageOptions: { lang: "cs", translations: { START_NEW_SIGNATURE: "Podepsat znovu" } }
|
|
896
1175
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
- `metadata` — object with `canvas` (dimensions, DPR) and `tablet` (device info) fields
|
|
1176
|
+
languageOptions: { lang: "cs", translations: { cs: { OK: "Potvrdit" }, en: { OK: "Confirm" } } }
|
|
1177
|
+
```
|
|
900
1178
|
|
|
901
|
-
|
|
1179
|
+
A JSON file you already have works the same way, import it and pass it:
|
|
902
1180
|
|
|
903
|
-
|
|
1181
|
+
```ts
|
|
1182
|
+
import signpadTexts from "./i18n/signpad.json";
|
|
904
1183
|
|
|
905
|
-
|
|
1184
|
+
languageOptions: { lang: "cs", translations: signpadTexts }
|
|
1185
|
+
```
|
|
906
1186
|
|
|
907
|
-
|
|
1187
|
+
If the file is nested (it also holds your app's texts), pass only the part for the signpad: `translations: appTexts.signpad`.
|
|
908
1188
|
|
|
909
|
-
|
|
1189
|
+
**Changing the language at runtime.** Assign a new config with a different `lang`; the texts update without a reconnect:
|
|
910
1190
|
|
|
911
|
-
|
|
1191
|
+
```ts
|
|
1192
|
+
signosoftPad.config = {
|
|
1193
|
+
...signosoftPad.config,
|
|
1194
|
+
languageOptions: { ...signosoftPad.config.languageOptions, lang: "pt" },
|
|
1195
|
+
};
|
|
1196
|
+
```
|
|
912
1197
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
|
924
|
-
|
|
|
925
|
-
| `
|
|
926
|
-
| `
|
|
1198
|
+
**Good to know**
|
|
1199
|
+
|
|
1200
|
+
- Missing keys fall back to English. If the file cannot be loaded, the component logs a message and shows English.
|
|
1201
|
+
- If `translations` has an entry for the active language, `langPath` is not fetched at all. Put the complete set into one of the two.
|
|
1202
|
+
- `langPath` can also be one exact file (`"/i18n/signpad.json"`). The same file is requested again when `lang` changes, so use a folder if you need a file per language.
|
|
1203
|
+
- A relative `langPath` (`"i18n"`) is resolved against the page URL. Start with `/` if your app is served from a sub-route.
|
|
1204
|
+
- Keep placeholders such as `{device}` in your texts; they are replaced at runtime.
|
|
1205
|
+
|
|
1206
|
+
The complete list of keys that can be translated ships with the package as `node_modules/@signosoft/signpad-js/src/i18n/en.json`. Copy its structure when creating new language files or inline overrides. The defaults are:
|
|
1207
|
+
|
|
1208
|
+
| Key | Default (English) |
|
|
1209
|
+
| :------------------------- | :--------------------------------------------------------------------------------------- |
|
|
1210
|
+
| `OK` | OK |
|
|
1211
|
+
| `CLEAR` | Clear |
|
|
1212
|
+
| `CANCEL` | Cancel |
|
|
1213
|
+
| `CLEAR_SIGNATURE` | Clear signature |
|
|
1214
|
+
| `DISCONNECT` | Disconnect |
|
|
1215
|
+
| `CONNECT_SIGNPAD` | Connect Signpad |
|
|
1216
|
+
| `MOUSE` | Mouse |
|
|
1217
|
+
| `CONNECTED_UNKNOWN_DEVICE` | Connected (Unknown Device) |
|
|
1218
|
+
| `AUTO_CONNECTING` | Auto-connecting to device... |
|
|
1219
|
+
| `CONNECTING_TO_DEVICE` | Connecting to your device... |
|
|
1220
|
+
| `REVERTING_TO_DEFAULT` | Reverting to default input... |
|
|
1221
|
+
| `DISCONNECTING_DEVICE` | Disconnecting device... |
|
|
1222
|
+
| `INITIALIZING` | Preparing signature pad... |
|
|
1223
|
+
| `RESTART_SESSION` | Restart session |
|
|
1224
|
+
| `START_NEW_SIGNATURE` | Start new signature |
|
|
1225
|
+
| `PREPARING_SESSION` | Preparing session... |
|
|
1226
|
+
| `TEXT_PRESS_START_SIGNING` | Start a new signing session to continue. |
|
|
1227
|
+
| `CANVAS_ARIA_LABEL` | Signature drawing area |
|
|
1228
|
+
| `PROCESSING_OK` | Processing OK... |
|
|
1229
|
+
| `PROCESSING_CLEAR` | Clearing... |
|
|
1230
|
+
| `PROCESSING_CANCEL` | Canceling... |
|
|
1231
|
+
| `TEXT_START_SIGNING` | Connect your device. |
|
|
1232
|
+
| `TEXT_SIGN_WITH_PHYSICAL` | Please sign using the device pen. |
|
|
1233
|
+
| `TEXT_SIGN_WITH_MOUSE` | Please sign using your mouse. |
|
|
1234
|
+
| `TEXT_SIGN_GENERIC` | Sign in the area above using mouse or pen. |
|
|
1235
|
+
| `TEXT_SIGNING_WITH_DEVICE` | Signing with {device} |
|
|
1236
|
+
| `LICENSE_MISSING` | License key is missing. Provide a valid lease in the component config. |
|
|
1237
|
+
| `CONNECTION_ERROR` | Could not connect to the signature pad. Try again. |
|
|
1238
|
+
| `ERROR_DRIVER_INIT` | The signature driver could not be started. Try again or reload the page. |
|
|
1239
|
+
| `NETWORK_ERROR` | Network error. Check your internet connection and try again. |
|
|
1240
|
+
| `INSUFFICIENT_TOKENS` | Not enough signing credits. |
|
|
1241
|
+
| `ERROR_STARTING_SIGNING` | Could not start the signing session. |
|
|
1242
|
+
| `ERROR_PROCESSING_ACTION` | The action could not be completed. |
|
|
1243
|
+
| `INVALID_LICENSE_KEY` | The license key was rejected. Check that the lease is valid and issued for this website. |
|
|
1244
|
+
| `READY_TO_SIGN_MOUSE` | Ready to sign. Use your mouse. |
|
|
1245
|
+
| `READY_TO_SIGN_PHYSICAL` | Ready to sign. Use the device pen. |
|
|
927
1246
|
|
|
928
|
-
|
|
1247
|
+
If you add any new keys, mirror them in every language file you provide to guarantee they fall back gracefully to English.
|
|
1248
|
+
|
|
1249
|
+
<a id="styling--theming"></a>
|
|
929
1250
|
|
|
930
1251
|
## 🎨 Styling & Theming
|
|
931
1252
|
|
|
@@ -934,36 +1255,49 @@ The component uses a hybrid theming model:
|
|
|
934
1255
|
- It ships with complete default styles (works out of the box).
|
|
935
1256
|
- You can override the visual design through CSS variables.
|
|
936
1257
|
|
|
937
|
-
|
|
1258
|
+
### Design tokens
|
|
938
1259
|
|
|
939
|
-
|
|
1260
|
+
Defaults come from the Signosoft design system tokens, shipped inside the component (`src/styles/primitive.css` for raw scales, `src/styles/semantic.css` for meaning, e.g. `--background-brand`, `--content-primary`, `--border-focus`). They are declared on the component's `:host`, so they do not depend on the host page and do not leak into it. `--sign-*` variables below are the public API; their defaults point at semantic tokens. Override `--sign-*` on the `signosoft-signpad` element.
|
|
940
1261
|
|
|
941
|
-
|
|
1262
|
+
<a id="override-via-css"></a>
|
|
942
1263
|
|
|
943
|
-
|
|
1264
|
+
## 🪄 Override via CSS
|
|
1265
|
+
|
|
1266
|
+
Set the `--sign-*` variables on the element in your own stylesheet. Variables set there beat the component's defaults, in light and dark theme alike:
|
|
944
1267
|
|
|
945
1268
|
```css
|
|
946
1269
|
signosoft-signpad {
|
|
947
|
-
|
|
948
|
-
--
|
|
949
|
-
--
|
|
950
|
-
--
|
|
951
|
-
--
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1270
|
+
--sign-top-bar-bg-base: #f3e8f8;
|
|
1271
|
+
--sign-top-bar-text-base: #5d1776;
|
|
1272
|
+
--sign-bottom-bar-bg-base: #f3e8f8;
|
|
1273
|
+
--sign-button-primary-bg-base: #7a1f9a;
|
|
1274
|
+
--sign-button-primary-bg-hover: #5d1776;
|
|
1275
|
+
}
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
The full list of variables follows below.
|
|
1279
|
+
|
|
1280
|
+
### Copy-Paste Starter
|
|
1281
|
+
|
|
1282
|
+
Paste this directly into your app stylesheet and adjust values:
|
|
955
1283
|
|
|
1284
|
+
```css
|
|
1285
|
+
signosoft-signpad {
|
|
956
1286
|
/* Constraints */
|
|
957
|
-
--min-height:
|
|
958
|
-
--spacing-constraints:
|
|
1287
|
+
--min-height: var(--unit-48);
|
|
1288
|
+
--spacing-constraints: var(--spacing-12) var(--spacing-16);
|
|
1289
|
+
|
|
1290
|
+
/* Host Sizing (override to make the component fluid, e.g. width: 100%; height: 100vh;) */
|
|
1291
|
+
--signpad-width: 25rem;
|
|
1292
|
+
--signpad-height: 31.25rem;
|
|
959
1293
|
|
|
960
1294
|
/* CSS Variables */
|
|
961
|
-
--sign-font-family: Arial, Helvetica, sans-serif;
|
|
962
|
-
--sign-common-border-radius:
|
|
1295
|
+
--sign-font-family: var(--fontFamily-primary), Arial, Helvetica, sans-serif;
|
|
1296
|
+
--sign-common-border-radius: var(--borderRadius-s);
|
|
963
1297
|
|
|
964
1298
|
/* Top Bar */
|
|
965
|
-
--sign-top-bar-bg-base: var(--background-
|
|
966
|
-
--sign-top-bar-text-base: var(--
|
|
1299
|
+
--sign-top-bar-bg-base: var(--background-brandSubtle);
|
|
1300
|
+
--sign-top-bar-text-base: var(--brand-650);
|
|
967
1301
|
--sign-top-bar-padding: var(--spacing-constraints);
|
|
968
1302
|
--sign-top-bar-min-height: var(--min-height);
|
|
969
1303
|
--sign-top-bar-border-top-left-radius: var(--sign-common-border-radius);
|
|
@@ -972,26 +1306,27 @@ signosoft-signpad {
|
|
|
972
1306
|
--sign-top-bar-border-bottom-right-radius: 0;
|
|
973
1307
|
|
|
974
1308
|
/* Canvas Area */
|
|
975
|
-
--sign-canvas-bg-base: var(--
|
|
1309
|
+
--sign-canvas-bg-base: var(--brandBeige-100);
|
|
976
1310
|
--sign-canvas-wrapper-border-top-left-radius: 0;
|
|
977
1311
|
--sign-canvas-wrapper-border-top-right-radius: 0;
|
|
978
1312
|
--sign-canvas-wrapper-border-bottom-left-radius: 0;
|
|
979
1313
|
--sign-canvas-wrapper-border-bottom-right-radius: 0;
|
|
980
1314
|
|
|
981
1315
|
/* Line */
|
|
982
|
-
--sign-line-height:
|
|
983
|
-
--sign-line-margin-bottom:
|
|
984
|
-
--sign-line-border-base: var(--
|
|
985
|
-
--sign-line-additional-text-color: var(--
|
|
986
|
-
--sign-line-margin:
|
|
987
|
-
|
|
1316
|
+
--sign-line-height: var(--unit-24);
|
|
1317
|
+
--sign-line-margin-bottom: var(--spacing-16);
|
|
1318
|
+
--sign-line-border-base: var(--border-secondary);
|
|
1319
|
+
--sign-line-additional-text-color: var(--content-tertiary);
|
|
1320
|
+
--sign-line-margin: 0 var(--spacing-16) var(--sign-line-margin-bottom)
|
|
1321
|
+
var(--spacing-16);
|
|
1322
|
+
--sign-canvas-line-text-font-size: var(--text-s-regular-fontSize);
|
|
988
1323
|
--sign-canvas-height-offset: var(--sign-line-height);
|
|
989
1324
|
|
|
990
1325
|
/* Bottom Bar */
|
|
991
|
-
--sign-bottom-bar-bg-base: var(--background-
|
|
1326
|
+
--sign-bottom-bar-bg-base: var(--background-brandSubtle);
|
|
992
1327
|
--sign-bottom-bar-padding: var(--spacing-constraints);
|
|
993
1328
|
--sign-bottom-bar-min-height: var(--min-height);
|
|
994
|
-
--sign-bottom-bar-gap:
|
|
1329
|
+
--sign-bottom-bar-gap: var(--spacing-8);
|
|
995
1330
|
--sign-bottom-bar-border-top-left-radius: 0;
|
|
996
1331
|
--sign-bottom-bar-border-top-right-radius: 0;
|
|
997
1332
|
--sign-bottom-bar-border-bottom-left-radius: var(--sign-common-border-radius);
|
|
@@ -1000,76 +1335,66 @@ signosoft-signpad {
|
|
|
1000
1335
|
);
|
|
1001
1336
|
|
|
1002
1337
|
/* Button general settings */
|
|
1003
|
-
--sign-button-font-size:
|
|
1004
|
-
--sign-button-padding:
|
|
1005
|
-
--sign-button-min-height: var(--
|
|
1006
|
-
--sign-button-border-radius:
|
|
1007
|
-
--sign-button-font-weight:
|
|
1338
|
+
--sign-button-font-size: var(--text-m-semibold-fontSize);
|
|
1339
|
+
--sign-button-padding: var(--spacing-10);
|
|
1340
|
+
--sign-button-min-height: var(--unit-48);
|
|
1341
|
+
--sign-button-border-radius: var(--borderRadius-pill);
|
|
1342
|
+
--sign-button-font-weight: var(--text-m-semibold-fontWeight);
|
|
1008
1343
|
|
|
1009
1344
|
/* Primary Buttons (OK, Clear, Cancel) */
|
|
1010
|
-
--sign-button-primary-bg-base: var(--
|
|
1011
|
-
--sign-button-primary-bg-hover: var(--
|
|
1012
|
-
--sign-button-primary-bg-
|
|
1013
|
-
--sign-button-primary-
|
|
1014
|
-
--sign-button-primary-text-
|
|
1015
|
-
--sign-button-primary-text-
|
|
1016
|
-
--sign-button-primary-
|
|
1017
|
-
--sign-button-primary-border-
|
|
1018
|
-
--sign-button-primary-border-
|
|
1019
|
-
|
|
1020
|
-
/* Link Buttons (Connect signpad) */
|
|
1021
|
-
--sign-button-link-bg-base: transparent;
|
|
1022
|
-
--sign-button-link-bg-hover: var(--background-color-10);
|
|
1023
|
-
--sign-button-link-bg-disabled: transparent;
|
|
1024
|
-
--sign-button-link-text-base: var(--primary-color-0);
|
|
1025
|
-
--sign-button-link-text-hover: var(--primary-color-10);
|
|
1026
|
-
--sign-button-link-text-disabled: var(--grey-color);
|
|
1027
|
-
--sign-button-link-border-base: 1px solid var(--primary-color-0);
|
|
1028
|
-
--sign-button-link-border-hover: 1px solid var(--primary-color-10);
|
|
1029
|
-
--sign-button-link-border-disabled: 1px solid var(--grey-color);
|
|
1345
|
+
--sign-button-primary-bg-base: var(--background-brand);
|
|
1346
|
+
--sign-button-primary-bg-hover: var(--background-brandHover);
|
|
1347
|
+
--sign-button-primary-bg-pressed: var(--background-brandPressed);
|
|
1348
|
+
--sign-button-primary-bg-disabled: var(--background-disabled);
|
|
1349
|
+
--sign-button-primary-text-base: var(--content-primaryInverse);
|
|
1350
|
+
--sign-button-primary-text-hover: var(--content-primaryInverse);
|
|
1351
|
+
--sign-button-primary-text-disabled: var(--content-disabled);
|
|
1352
|
+
--sign-button-primary-border-base: var(--borderWidth-xs) solid transparent;
|
|
1353
|
+
--sign-button-primary-border-hover: var(--borderWidth-xs) solid transparent;
|
|
1354
|
+
--sign-button-primary-border-disabled: var(--borderWidth-xs) solid transparent;
|
|
1030
1355
|
|
|
1031
1356
|
/* Device State Text Colors */
|
|
1032
|
-
--sign-device-state-text-color-connected:
|
|
1033
|
-
--sign-device-state-text-color-disconnected: red;
|
|
1357
|
+
--sign-device-state-text-color-connected: var(--brand-650);
|
|
1034
1358
|
|
|
1035
1359
|
/* Loading Overlay */
|
|
1036
|
-
--sign-loading-overlay-bg-color:
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1360
|
+
--sign-loading-overlay-bg-color: color-mix(
|
|
1361
|
+
in srgb,
|
|
1362
|
+
var(--background-primary) 80%,
|
|
1363
|
+
transparent
|
|
1364
|
+
);
|
|
1365
|
+
--sign-loading-overlay-text-color: var(--content-primary);
|
|
1366
|
+
--sign-loading-overlay-spinner-color: var(--content-brandHover);
|
|
1367
|
+
--sign-loading-overlay-spinner-border-color: var(--background-brandSubtle);
|
|
1368
|
+
--sign-loading-overlay-spinner-size: var(--unit-32);
|
|
1041
1369
|
}
|
|
1042
1370
|
```
|
|
1043
1371
|
|
|
1044
|
-
###
|
|
1372
|
+
### Full CSS Variable Reference
|
|
1045
1373
|
|
|
1046
|
-
####
|
|
1374
|
+
#### General
|
|
1047
1375
|
|
|
1048
|
-
| Variable | Default
|
|
1049
|
-
| :---------------------------- |
|
|
1050
|
-
| `--
|
|
1051
|
-
| `--
|
|
1052
|
-
| `--background-color-0` | `#f1f2fd` | Secondary background color. |
|
|
1053
|
-
| `--background-color-10` | `#e3e4fc` | Main bar background color. |
|
|
1054
|
-
| `--text-color-0` | `#333e4a` | Main text color. |
|
|
1055
|
-
| `--white-color` | `#ffffff` | Pure white color. |
|
|
1056
|
-
| `--grey-color` | `#b5b9be` | Disabled state color. |
|
|
1057
|
-
| `--sign-font-family` | `Arial, sans-serif` | Global font family. |
|
|
1058
|
-
| `--sign-common-border-radius` | `8px` | Shared radius token reused by bars and buttons. |
|
|
1376
|
+
| Variable | Default | Description |
|
|
1377
|
+
| :---------------------------- | :-------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1378
|
+
| `--sign-font-family` | `var(--fontFamily-primary), Arial, Helvetica, sans-serif` | Global font family. The default starts with the design system font `Inter`, which is **not bundled** — load it on the host page, otherwise the `Arial` fallback is used. |
|
|
1379
|
+
| `--sign-common-border-radius` | `var(--borderRadius-s)` | Shared radius token reused by bars and buttons. |
|
|
1059
1380
|
|
|
1060
1381
|
#### Layout & Constraints
|
|
1061
1382
|
|
|
1062
|
-
| Variable | Default
|
|
1063
|
-
| :---------------------- |
|
|
1064
|
-
| `--
|
|
1065
|
-
| `--
|
|
1383
|
+
| Variable | Default | Description |
|
|
1384
|
+
| :---------------------- | :------------------------------------ | :--------------------------------------------------------------------------- |
|
|
1385
|
+
| `--signpad-width` | `25rem` | Host element width. Override to resize, e.g. `100%` for a fluid container. |
|
|
1386
|
+
| `--signpad-height` | `31.25rem` | Host element height. Override to resize, e.g. `100vh` for a fluid container. |
|
|
1387
|
+
| `--min-height` | `var(--unit-48)` | Minimum height for bars and buttons. |
|
|
1388
|
+
| `--spacing-constraints` | `var(--spacing-12) var(--spacing-16)` | Default padding/margin for containers. |
|
|
1389
|
+
|
|
1390
|
+
> **Sizing:** By default the component renders at a fixed `400x500` size so it doesn't stretch to fill its parent on integration. To make it fluid, override `--signpad-width` / `--signpad-height` on the host element (e.g. `signosoft-signpad { --signpad-width: 100%; --signpad-height: 100vh; }`).
|
|
1066
1391
|
|
|
1067
1392
|
#### Top Bar
|
|
1068
1393
|
|
|
1069
1394
|
| Variable | Default | Description |
|
|
1070
1395
|
| :------------------------------------------ | :--------------------------------- | :---------------------------------- |
|
|
1071
|
-
| `--sign-top-bar-bg-base` | `var(--background-
|
|
1072
|
-
| `--sign-top-bar-text-base` | `var(--
|
|
1396
|
+
| `--sign-top-bar-bg-base` | `var(--background-brandSubtle)` | Background color of the top bar. |
|
|
1397
|
+
| `--sign-top-bar-text-base` | `var(--brand-650)` | Text color in the top bar. |
|
|
1073
1398
|
| `--sign-top-bar-padding` | `var(--spacing-constraints)` | Inner padding of the top bar. |
|
|
1074
1399
|
| `--sign-top-bar-min-height` | `var(--min-height)` | Minimum height of the top bar. |
|
|
1075
1400
|
| `--sign-top-bar-border-top-left-radius` | `var(--sign-common-border-radius)` | Top-left radius of the top bar. |
|
|
@@ -1079,29 +1404,29 @@ signosoft-signpad {
|
|
|
1079
1404
|
|
|
1080
1405
|
#### Canvas & Signature Line
|
|
1081
1406
|
|
|
1082
|
-
| Variable | Default
|
|
1083
|
-
| :------------------------------------------------- |
|
|
1084
|
-
| `--sign-canvas-bg-base` | `var(--
|
|
1085
|
-
| `--sign-canvas-wrapper-border-top-left-radius` | `0`
|
|
1086
|
-
| `--sign-canvas-wrapper-border-top-right-radius` | `0`
|
|
1087
|
-
| `--sign-canvas-wrapper-border-bottom-left-radius` | `0`
|
|
1088
|
-
| `--sign-canvas-wrapper-border-bottom-right-radius` | `0`
|
|
1089
|
-
| `--sign-line-height` | `
|
|
1090
|
-
| `--sign-line-margin-bottom` | `
|
|
1091
|
-
| `--sign-line-border-base` | `var(--
|
|
1092
|
-
| `--sign-line-additional-text-color` | `var(--
|
|
1093
|
-
| `--sign-line-margin` | `
|
|
1094
|
-
| `--sign-canvas-line-text-font-size` | `
|
|
1095
|
-
| `--sign-canvas-height-offset` | `var(--sign-line-height)`
|
|
1407
|
+
| Variable | Default | Description |
|
|
1408
|
+
| :------------------------------------------------- | :--------------------------------------------------------------------- | :--------------------------------------------------------------------------- |
|
|
1409
|
+
| `--sign-canvas-bg-base` | `var(--brandBeige-100)` | Background color of the drawing area. |
|
|
1410
|
+
| `--sign-canvas-wrapper-border-top-left-radius` | `0` | Top-left canvas radius, typically rounded when the top bar is hidden. |
|
|
1411
|
+
| `--sign-canvas-wrapper-border-top-right-radius` | `0` | Top-right canvas radius, typically rounded when the top bar is hidden. |
|
|
1412
|
+
| `--sign-canvas-wrapper-border-bottom-left-radius` | `0` | Bottom-left canvas radius, typically rounded when the bottom bar is hidden. |
|
|
1413
|
+
| `--sign-canvas-wrapper-border-bottom-right-radius` | `0` | Bottom-right canvas radius, typically rounded when the bottom bar is hidden. |
|
|
1414
|
+
| `--sign-line-height` | `var(--unit-24)` | Vertical offset/height for the guide line. |
|
|
1415
|
+
| `--sign-line-margin-bottom` | `var(--spacing-16)` | Bottom spacing used in canvas line layout. |
|
|
1416
|
+
| `--sign-line-border-base` | `var(--border-secondary)` | Color of the signature guide line. |
|
|
1417
|
+
| `--sign-line-additional-text-color` | `var(--content-tertiary)` | Color of the helper text below the line. |
|
|
1418
|
+
| `--sign-line-margin` | `0 var(--spacing-16) var(--sign-line-margin-bottom) var(--spacing-16)` | Spacing around the guide line. |
|
|
1419
|
+
| `--sign-canvas-line-text-font-size` | `var(--text-s-regular-fontSize)` | Font size for guide line labels. |
|
|
1420
|
+
| `--sign-canvas-height-offset` | `var(--sign-line-height)` | Canvas offset calculation for the guide line. |
|
|
1096
1421
|
|
|
1097
1422
|
#### Bottom Bar
|
|
1098
1423
|
|
|
1099
1424
|
| Variable | Default | Description |
|
|
1100
1425
|
| :--------------------------------------------- | :--------------------------------- | :--------------------------------------- |
|
|
1101
|
-
| `--sign-bottom-bar-bg-base` | `var(--background-
|
|
1426
|
+
| `--sign-bottom-bar-bg-base` | `var(--background-brandSubtle)` | Background color of the bottom bar. |
|
|
1102
1427
|
| `--sign-bottom-bar-padding` | `var(--spacing-constraints)` | Inner padding of the bottom bar. |
|
|
1103
1428
|
| `--sign-bottom-bar-min-height` | `var(--min-height)` | Minimum height of the bottom bar. |
|
|
1104
|
-
| `--sign-bottom-bar-gap` | `
|
|
1429
|
+
| `--sign-bottom-bar-gap` | `var(--spacing-8)` | Space between buttons in the bottom bar. |
|
|
1105
1430
|
| `--sign-bottom-bar-border-top-left-radius` | `0` | Top-left radius of the bottom bar. |
|
|
1106
1431
|
| `--sign-bottom-bar-border-top-right-radius` | `0` | Top-right radius of the bottom bar. |
|
|
1107
1432
|
| `--sign-bottom-bar-border-bottom-left-radius` | `var(--sign-common-border-radius)` | Bottom-left radius of the bottom bar. |
|
|
@@ -1109,167 +1434,87 @@ signosoft-signpad {
|
|
|
1109
1434
|
|
|
1110
1435
|
#### Buttons (General)
|
|
1111
1436
|
|
|
1112
|
-
| Variable | Default
|
|
1113
|
-
| :---------------------------- |
|
|
1114
|
-
| `--sign-button-font-size` | `
|
|
1115
|
-
| `--sign-button-padding` | `
|
|
1116
|
-
| `--sign-button-min-height` | `var(--
|
|
1117
|
-
| `--sign-button-border-radius` | `
|
|
1118
|
-
| `--sign-button-font-weight` | `
|
|
1437
|
+
| Variable | Default | Description |
|
|
1438
|
+
| :---------------------------- | :---------------------------------- | :----------------------------- |
|
|
1439
|
+
| `--sign-button-font-size` | `var(--text-m-semibold-fontSize)` | Font size for all buttons. |
|
|
1440
|
+
| `--sign-button-padding` | `var(--spacing-10)` | Inner padding for all buttons. |
|
|
1441
|
+
| `--sign-button-min-height` | `var(--unit-48)` | Minimum height for buttons. |
|
|
1442
|
+
| `--sign-button-border-radius` | `var(--borderRadius-pill)` | Border radius for all buttons. |
|
|
1443
|
+
| `--sign-button-font-weight` | `var(--text-m-semibold-fontWeight)` | Font weight for all buttons. |
|
|
1119
1444
|
|
|
1120
1445
|
#### Primary Buttons (OK, Clear, Cancel)
|
|
1121
1446
|
|
|
1122
|
-
| Variable | Description
|
|
1123
|
-
| :-------------------------------------- |
|
|
1124
|
-
| `--sign-button-primary-bg-base` | Background color of primary buttons.
|
|
1125
|
-
| `--sign-button-primary-bg-hover` | Hover background color.
|
|
1126
|
-
| `--sign-button-primary-bg-
|
|
1127
|
-
| `--sign-button-primary-
|
|
1128
|
-
| `--sign-button-primary-text-
|
|
1129
|
-
| `--sign-button-primary-text-
|
|
1130
|
-
| `--sign-button-primary-
|
|
1131
|
-
| `--sign-button-primary-border-
|
|
1132
|
-
| `--sign-button-primary-border-
|
|
1133
|
-
|
|
1134
|
-
#### Link/Connect Buttons
|
|
1135
|
-
|
|
1136
|
-
| Variable | Default | Description |
|
|
1137
|
-
| :----------------------------------- | :---------------------------------- | :------------------------------------------- |
|
|
1138
|
-
| `--sign-button-link-bg-base` | `transparent` | Background color for secondary/link buttons. |
|
|
1139
|
-
| `--sign-button-link-bg-hover` | `var(--background-color-10)` | Hover background color. |
|
|
1140
|
-
| `--sign-button-link-bg-disabled` | `transparent` | Disabled background color. |
|
|
1141
|
-
| `--sign-button-link-text-base` | `var(--primary-color-0)` | Text color for secondary/link buttons. |
|
|
1142
|
-
| `--sign-button-link-text-hover` | `var(--primary-color-10)` | Hover text color. |
|
|
1143
|
-
| `--sign-button-link-text-disabled` | `var(--grey-color)` | Disabled text color. |
|
|
1144
|
-
| `--sign-button-link-border-base` | `1px solid var(--primary-color-0)` | Border for secondary/link buttons. |
|
|
1145
|
-
| `--sign-button-link-border-hover` | `1px solid var(--primary-color-10)` | Hover border color. |
|
|
1146
|
-
| `--sign-button-link-border-disabled` | `1px solid var(--grey-color)` | Disabled border color. |
|
|
1447
|
+
| Variable | Default | Description |
|
|
1448
|
+
| :-------------------------------------- | :---------------------------------------- | :-------------------------------------------------- |
|
|
1449
|
+
| `--sign-button-primary-bg-base` | `var(--background-brand)` | Background color of primary buttons. |
|
|
1450
|
+
| `--sign-button-primary-bg-hover` | `var(--background-brandHover)` | Hover background color. |
|
|
1451
|
+
| `--sign-button-primary-bg-pressed` | `var(--background-brandPressed)` | Background while the button is pressed (`:active`). |
|
|
1452
|
+
| `--sign-button-primary-bg-disabled` | `var(--background-disabled)` | Disabled background color. |
|
|
1453
|
+
| `--sign-button-primary-text-base` | `var(--content-primaryInverse)` | Text color for primary buttons. |
|
|
1454
|
+
| `--sign-button-primary-text-hover` | `var(--content-primaryInverse)` | Hover text color. |
|
|
1455
|
+
| `--sign-button-primary-text-disabled` | `var(--content-disabled)` | Disabled text color. |
|
|
1456
|
+
| `--sign-button-primary-border-base` | `var(--borderWidth-xs) solid transparent` | Border of primary buttons. |
|
|
1457
|
+
| `--sign-button-primary-border-hover` | `var(--borderWidth-xs) solid transparent` | Hover border color. |
|
|
1458
|
+
| `--sign-button-primary-border-disabled` | `var(--borderWidth-xs) solid transparent` | Disabled border color. |
|
|
1147
1459
|
|
|
1148
1460
|
#### Status & Overlays
|
|
1149
1461
|
|
|
1150
|
-
| Variable | Default
|
|
1151
|
-
| :-------------------------------------------- |
|
|
1152
|
-
| `--sign-device-state-text-color-connected` | `
|
|
1153
|
-
| `--sign-
|
|
1154
|
-
| `--sign-loading-overlay-
|
|
1155
|
-
| `--sign-loading-overlay-
|
|
1156
|
-
| `--sign-loading-overlay-spinner-color`
|
|
1157
|
-
| `--sign-loading-overlay-spinner-
|
|
1158
|
-
| `--sign-loading-overlay-spinner-size` | `30px` | Size of the spinner icon. |
|
|
1159
|
-
|
|
1160
|
-
## 💬 Feedback & Support
|
|
1161
|
-
|
|
1162
|
-
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:
|
|
1163
|
-
|
|
1164
|
-
- 📧 **Support Email:** [esign@signosoft.com](mailto:esign@signosoft.com)
|
|
1165
|
-
- 🌐 **Official Website:** [www.signosoft.com](https://www.signosoft.com)
|
|
1166
|
-
|
|
1167
|
-
## 🛠️ Contributing
|
|
1168
|
-
|
|
1169
|
-
### Commit naming convention
|
|
1170
|
-
|
|
1171
|
-
- Every commit message must start with Jira task id and category:
|
|
1172
|
-
- `MAIN-000/<category>: <summary>`
|
|
1173
|
-
- For work in progress:
|
|
1174
|
-
- `MAIN-000/<category>: WIP - <summary>`
|
|
1175
|
-
|
|
1176
|
-
Allowed categories:
|
|
1177
|
-
|
|
1178
|
-
- `feat`: adding a new feature
|
|
1179
|
-
- `fix`: fixing a bug
|
|
1180
|
-
- `refactor`: performance/readability/maintainability change
|
|
1181
|
-
- `chore`: docs, formatting, tests, cleanup, tooling
|
|
1182
|
-
- `merge`: branch merge into `main`
|
|
1183
|
-
|
|
1184
|
-
Use infinitive verb form in commit summaries:
|
|
1185
|
-
|
|
1186
|
-
- <span style="color: #16a34a;"><strong>RIGHT:</strong></span> `add`, `fix`, `remove`, `update`, `refactor`
|
|
1187
|
-
- <span style="color: #dc2626;"><strong>WRONG:</strong></span> Continuous tense: `adding`, `removing` Past tense: `added`, `fixed`
|
|
1188
|
-
|
|
1189
|
-
Examples:
|
|
1190
|
-
|
|
1191
|
-
```bash
|
|
1192
|
-
git commit -m "MAIN-000/feat: add new button component; add button to templates"
|
|
1193
|
-
```
|
|
1194
|
-
|
|
1195
|
-
```bash
|
|
1196
|
-
git commit -m "MAIN-000/fix: add stop directive to button component to prevent propagation"
|
|
1197
|
-
```
|
|
1198
|
-
|
|
1199
|
-
```bash
|
|
1200
|
-
git commit -m "MAIN-000/refactor: rewrite button component in TypeScript"
|
|
1201
|
-
```
|
|
1202
|
-
|
|
1203
|
-
```bash
|
|
1204
|
-
git commit -m "MAIN-000/chore: write button documentation"
|
|
1205
|
-
```
|
|
1206
|
-
|
|
1207
|
-
```bash
|
|
1208
|
-
git commit -m "MAIN-000/merge: new button"
|
|
1209
|
-
```
|
|
1210
|
-
|
|
1211
|
-
```bash
|
|
1212
|
-
git commit -m "MAIN-000/feat: WIP - basic signing functionality"
|
|
1213
|
-
```
|
|
1214
|
-
|
|
1215
|
-
### Versioning and release commits
|
|
1216
|
-
|
|
1217
|
-
Follow Semantic Versioning:
|
|
1462
|
+
| Variable | Default | Description |
|
|
1463
|
+
| :-------------------------------------------- | :--------------------------------------------------------------- | :--------------------------------- |
|
|
1464
|
+
| `--sign-device-state-text-color-connected` | `var(--brand-650)` | Text color when device is ready. |
|
|
1465
|
+
| `--sign-loading-overlay-bg-color` | `color-mix(in srgb, var(--background-primary) 80%, transparent)` | Background of the loading spinner. |
|
|
1466
|
+
| `--sign-loading-overlay-text-color` | `var(--content-primary)` | Loading overlay text color. |
|
|
1467
|
+
| `--sign-loading-overlay-spinner-color` | `var(--content-brandHover)` | Color of the animated spinner. |
|
|
1468
|
+
| `--sign-loading-overlay-spinner-border-color` | `var(--background-brandSubtle)` | Base border color of spinner ring. |
|
|
1469
|
+
| `--sign-loading-overlay-spinner-size` | `var(--unit-32)` | Size of the spinner icon. |
|
|
1218
1470
|
|
|
1219
|
-
|
|
1220
|
-
- `MINOR` (`x.y.0`): backward-compatible feature set
|
|
1221
|
-
- `PATCH` (`x.y.z`): backward-compatible bug fixes
|
|
1471
|
+
> **Accessibility (WCAG 2.1 AA):** if you override `--sign-loading-overlay-bg-color` or `--sign-loading-overlay-text-color`, keep the text/background contrast ratio at **4.5:1 or higher**. The overlay background is semi-transparent, so measure against the worst case: the overlay color blended over the canvas, including dark pen strokes. Raise the background alpha if the text is hard to read over strokes.
|
|
1222
1472
|
|
|
1223
|
-
|
|
1473
|
+
<a id="accessibility"></a>
|
|
1224
1474
|
|
|
1225
|
-
|
|
1226
|
-
- Version bump with actual code/features in same commit: use `feat`/`fix`/`refactor` based on dominant change
|
|
1227
|
-
- For release commits, include both versions in message: `release from <old> to <new>`
|
|
1475
|
+
## ♿ Accessibility
|
|
1228
1476
|
|
|
1229
|
-
|
|
1477
|
+
The component targets WCAG 2.1 AA for everything it renders. What is in place:
|
|
1230
1478
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1479
|
+
- **Names and state:** all controls are native `<button>`s with visible, localized text; disabled buttons use the native `disabled` state. The drawing canvas has `role="img"` and a localized name (`CANVAS_ARIA_LABEL`).
|
|
1480
|
+
- **Announcements:** the component container sets `aria-busy` while connecting, disconnecting or processing OK/Clear/Cancel. The error/busy overlay message is announced (`role="alert"` for errors, `role="status"` while busy). Device status changes (e.g. connected / mouse) are announced through a polite live region.
|
|
1481
|
+
- **Keyboard and focus:** all buttons are reachable with Tab and show a `:focus-visible` outline (`--border-focus`). When an error appears and focus is already inside the component, focus moves to the "Restart session" button.
|
|
1482
|
+
- **Language:** the active language is set as `lang` on the component content, so screen readers pronounce `cs`/`pt` text correctly.
|
|
1483
|
+
- **Motion:** the spinner slows down and button transitions are disabled under `prefers-reduced-motion: reduce`.
|
|
1484
|
+
- **High contrast:** under `forced-colors: active` buttons, focus ring and overlay use system colors. The signing surface keeps its own light background (`forced-color-adjust: none`), because the ink is drawn on a canvas in `canvasAndDrawingOptions.color` (black by default) and would be invisible on a forced dark background.
|
|
1234
1485
|
|
|
1235
|
-
|
|
1486
|
+
**Known limitations (plan an alternative in your app):**
|
|
1236
1487
|
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1488
|
+
- **Signing itself is pointer-only** (pen, mouse or touch). The canvas cannot be operated with a keyboard or screen reader (WCAG 2.1.1). If you must support people who cannot draw, offer another way to sign or confirm in your app (e.g. a typed-name signature or an assisted flow) and treat the signpad as one option.
|
|
1489
|
+
- **Default color contrast:** the defaults are chosen for WCAG 2.1 AA: white on the primary button is 5.1:1 (`--background-brand` = `brand-650`; hover 6.5:1, pressed 8.3:1), top bar and device status text are at least 4.5:1, the spinner and focus ring are above 3:1. `--background-brand` was changed from `brand-500` (white on it is 2.7:1) to `brand-650` for this reason; `--background-brandHover` and `--background-brandPressed` are `brand-700` and `brand-750`. Disabled controls are exempt. If you override colors, re-check the ratios.
|
|
1490
|
+
- **Zoom and text size:** spacing, sizes and radii come from design tokens converted to `rem`, so they scale with the browser font size and page zoom. Border widths stay in `px`. `--signpad-width` / `--signpad-height` are also `rem`; reflow at 400% zoom depends on your page layout around the component.
|
|
1240
1491
|
|
|
1241
|
-
|
|
1492
|
+
**If you override styles:** keep text/background contrast at 4.5:1 (3:1 for borders and UI components), do not remove the `:focus-visible` outline, and keep controls at least 24×24 px.
|
|
1242
1493
|
|
|
1243
|
-
|
|
1244
|
-
- `MINOR` (`x.Y.0`): increase minor, reset patch (`1.4.9` -> `1.5.0`)
|
|
1245
|
-
- `MAJOR` (`X.0.0`): increase major, reset minor and patch (`1.9.7` -> `2.0.0`)
|
|
1494
|
+
<a id="feedback--support"></a>
|
|
1246
1495
|
|
|
1247
|
-
|
|
1496
|
+
## 💬 Feedback & Support
|
|
1248
1497
|
|
|
1249
|
-
|
|
1250
|
-
# patch
|
|
1251
|
-
git commit -m "MAIN-000/fix: release from 1.0.0 to 1.0.1 - fix onPen throttle edge case"
|
|
1498
|
+
Found a bug, missing a feature, or need help with an integration? Write to us:
|
|
1252
1499
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1500
|
+
- 📧 **Support Email:** [esign@signosoft.com](mailto:esign@signosoft.com)
|
|
1501
|
+
- 🌐 **Official Website:** [www.signosoft.com](https://www.signosoft.com)
|
|
1255
1502
|
|
|
1256
|
-
|
|
1257
|
-
git commit -m "MAIN-000/refactor: release from 1.1.1 to 2.0.0 - remove legacy API"
|
|
1258
|
-
```
|
|
1503
|
+
<a id="license"></a>
|
|
1259
1504
|
|
|
1260
1505
|
## 📄 License
|
|
1261
1506
|
|
|
1262
|
-
**
|
|
1507
|
+
**Source-available, proprietary commercial license**
|
|
1263
1508
|
|
|
1264
1509
|
Copyright © 2026, Signosoft. All rights reserved.
|
|
1265
1510
|
|
|
1266
|
-
This software is proprietary to Signosoft and is protected by copyright laws.
|
|
1511
|
+
This software is proprietary to Signosoft and is protected by copyright laws. The source is available to inspect, but it is **not open source** (not an OSI-approved license): use requires a Signosoft account and paid tokens.
|
|
1267
1512
|
|
|
1268
|
-
###
|
|
1513
|
+
### Commercial Usage
|
|
1269
1514
|
|
|
1270
1515
|
A valid, paid, and active commercial license agreement with Signosoft is **strictly required** for the use, operation, reproduction, distribution, or deployment of this software in any environment (including development, testing, staging, and production).
|
|
1271
1516
|
|
|
1272
|
-
###
|
|
1517
|
+
### Key Terms
|
|
1273
1518
|
|
|
1274
1519
|
- **Unauthorized Use:** Any use without a current, fully paid license is expressly prohibited and constitutes a violation of our intellectual property rights.
|
|
1275
1520
|
- **Legal Action:** Signosoft reserves the right to pursue legal action to enforce its rights and seek damages for unauthorized use.
|