@signosoft/signpad-js 0.6.0 → 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 +877 -656
- package/dist/index.d.ts +74 -505
- package/dist/signosoft-signpad.js +2038 -2038
- 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,101 +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/public/driver-leases/create",
|
|
63
|
-
{
|
|
64
|
-
method: "POST",
|
|
65
|
-
headers: { "Content-Type": "application/json" },
|
|
66
|
-
body: JSON.stringify({
|
|
67
|
-
token: "YOUR-LICENSE-TOKEN",
|
|
68
|
-
validityHours: 24,
|
|
69
|
-
}),
|
|
70
|
-
},
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
const errorText = await response.text();
|
|
75
|
-
throw new Error(
|
|
76
|
-
`License server error: ${response.statusText} - ${errorText}`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return await response.json();
|
|
81
|
-
} catch (error) {
|
|
82
|
-
console.error("Signosoft Signpad: License initialization failed", error);
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function applyLeaseToConfig(currentConfig) {
|
|
88
|
-
const lease = await fetchLease();
|
|
89
|
-
return {
|
|
90
|
-
...currentConfig,
|
|
91
|
-
lease,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
<br/>
|
|
97
|
-
|
|
98
|
-
## 🖊️ Device Compatibility
|
|
53
|
+
## 🔌 Device Compatibility
|
|
99
54
|
|
|
100
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.
|
|
101
56
|
|
|
@@ -108,6 +63,8 @@ The component communicates with signature tablets via the **WebHID API**. Device
|
|
|
108
63
|
|
|
109
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.
|
|
110
65
|
|
|
66
|
+
<a id="installation"></a>
|
|
67
|
+
|
|
111
68
|
## 📦 Installation
|
|
112
69
|
|
|
113
70
|
Install the library via npm:
|
|
@@ -126,131 +83,119 @@ yarn add @signosoft/signpad-js
|
|
|
126
83
|
pnpm add @signosoft/signpad-js
|
|
127
84
|
```
|
|
128
85
|
|
|
129
|
-
|
|
86
|
+
<a id="environment-requirements"></a>
|
|
87
|
+
|
|
88
|
+
## 🌍 Environment Requirements
|
|
130
89
|
|
|
131
90
|
To ensure proper communication via the **WebHID API**:
|
|
132
91
|
|
|
133
|
-
- **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.
|
|
134
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.
|
|
135
96
|
- **TypeScript:** Built-in support; no extra `@types` needed.
|
|
136
|
-
<br/>
|
|
137
97
|
|
|
138
|
-
|
|
98
|
+
<a id="lease-setup"></a>
|
|
139
99
|
|
|
140
|
-
|
|
100
|
+
## 🔐 Lease Setup
|
|
141
101
|
|
|
142
|
-
|
|
143
|
-
npm install @signosoft/signpad-js
|
|
144
|
-
```
|
|
102
|
+
To use the Signosoft Signpad component with physical signature hardware, a valid `lease` is **required**.
|
|
145
103
|
|
|
146
|
-
###
|
|
104
|
+
### CRITICAL: Mandatory Initialization
|
|
147
105
|
|
|
148
|
-
<
|
|
149
|
-
<summary>
|
|
150
|
-
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/angularjs/angularjs-original.svg" alt="Angular" width="15" height="15">
|
|
151
|
-
<b>Angular</b> <font color="green"><b>(Live)</b></font>
|
|
152
|
-
</summary>
|
|
153
|
-
<br>
|
|
106
|
+
Without a valid lease, the component can still render, but driver initialization and hardware communication features will be <b>disabled</b>.
|
|
154
107
|
|
|
155
|
-
|
|
108
|
+
### 1. Lease flow
|
|
156
109
|
|
|
157
|
-
|
|
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
|
|
158
115
|
|
|
159
|
-
|
|
116
|
+
> Security note: In production, call the lease API from your backend and return only the lease to frontend.
|
|
160
117
|
|
|
161
|
-
|
|
118
|
+
### 2. Pricing
|
|
162
119
|
|
|
163
|
-
|
|
164
|
-
npm install @signosoft/signpad-js
|
|
165
|
-
```
|
|
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)).
|
|
166
121
|
|
|
167
|
-
|
|
122
|
+
<a id="quick-start"></a>
|
|
168
123
|
|
|
169
|
-
|
|
124
|
+
## 🚀 Quick Start
|
|
170
125
|
|
|
171
|
-
|
|
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).
|
|
172
127
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
OnChanges,
|
|
179
|
-
SimpleChanges,
|
|
180
|
-
} from "@angular/core";
|
|
181
|
-
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
|
+
> ```
|
|
182
133
|
|
|
183
|
-
|
|
184
|
-
selector: "signosoft-signpad",
|
|
185
|
-
standalone: true,
|
|
186
|
-
})
|
|
187
|
-
export class SignosoftSignpadDirective implements OnChanges {
|
|
188
|
-
@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)).
|
|
189
135
|
|
|
190
|
-
|
|
136
|
+
### Framework Integration Guide
|
|
191
137
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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>
|
|
196
144
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
el[key] = (this as any)[key];
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
```
|
|
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`).
|
|
205
148
|
|
|
206
|
-
|
|
149
|
+
##### 1. Implementation in Component
|
|
207
150
|
|
|
208
|
-
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.
|
|
209
152
|
|
|
210
153
|
```typescript
|
|
211
154
|
import {
|
|
212
155
|
Component,
|
|
213
|
-
ViewChild,
|
|
214
156
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
215
|
-
|
|
157
|
+
ElementRef,
|
|
158
|
+
afterNextRender,
|
|
216
159
|
signal,
|
|
160
|
+
viewChild,
|
|
217
161
|
} from "@angular/core";
|
|
218
|
-
import "@signosoft/signpad-js"; //
|
|
219
|
-
import {
|
|
220
|
-
type SignosoftSignpad,
|
|
221
|
-
type SignpadConfig,
|
|
222
|
-
} from "@signosoft/signpad-js";
|
|
223
|
-
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";
|
|
224
164
|
|
|
225
165
|
@Component({
|
|
226
166
|
selector: "app-root",
|
|
227
|
-
|
|
228
|
-
imports: [SignosoftSignpadDirective],
|
|
229
|
-
templateUrl: "./app.component.html",
|
|
167
|
+
templateUrl: "./app.html",
|
|
230
168
|
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Mandatory for custom HTML tags
|
|
231
169
|
})
|
|
232
|
-
export class
|
|
233
|
-
|
|
234
|
-
|
|
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");
|
|
235
173
|
|
|
236
|
-
|
|
174
|
+
readonly config = signal<SignpadConfig>({
|
|
237
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
|
+
},
|
|
238
183
|
});
|
|
239
184
|
|
|
240
|
-
|
|
241
|
-
|
|
185
|
+
constructor() {
|
|
186
|
+
// Runs in the browser after the first render
|
|
187
|
+
afterNextRender(() => this.applyLeaseToConfig());
|
|
242
188
|
}
|
|
243
189
|
|
|
244
|
-
|
|
190
|
+
async fetchLease(): Promise<any> {
|
|
245
191
|
try {
|
|
246
192
|
const response = await fetch(
|
|
247
|
-
"https://test.signosoft.com/
|
|
193
|
+
"https://api-test.signosoft.com/v1/public/driver-leases",
|
|
248
194
|
{
|
|
249
195
|
method: "POST",
|
|
250
196
|
headers: { "Content-Type": "application/json" },
|
|
251
197
|
body: JSON.stringify({
|
|
252
|
-
|
|
253
|
-
validityHours: 24,
|
|
198
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
254
199
|
}),
|
|
255
200
|
},
|
|
256
201
|
);
|
|
@@ -269,7 +214,7 @@ export class AppComponent implements AfterViewInit {
|
|
|
269
214
|
}
|
|
270
215
|
}
|
|
271
216
|
|
|
272
|
-
|
|
217
|
+
async applyLeaseToConfig() {
|
|
273
218
|
const lease = await this.fetchLease();
|
|
274
219
|
this.config.update((currentConfig) => ({
|
|
275
220
|
...currentConfig,
|
|
@@ -277,23 +222,78 @@ export class AppComponent implements AfterViewInit {
|
|
|
277
222
|
}));
|
|
278
223
|
}
|
|
279
224
|
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
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" });
|
|
283
228
|
}
|
|
284
229
|
}
|
|
285
230
|
```
|
|
286
231
|
|
|
287
|
-
|
|
232
|
+
##### 2. Component Template
|
|
288
233
|
|
|
289
|
-
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.
|
|
290
235
|
|
|
291
236
|
```html
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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>
|
|
297
297
|
```
|
|
298
298
|
|
|
299
299
|
</details>
|
|
@@ -305,19 +305,23 @@ Use the custom tag in your HTML and bind the configuration object.
|
|
|
305
305
|
</summary>
|
|
306
306
|
<br>
|
|
307
307
|
|
|
308
|
-
|
|
308
|
+
#### <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg" alt="React" width="30" height="30"> React - Quick start
|
|
309
309
|
|
|
310
310
|
This guide describes how to integrate the `@signosoft/signpad-js` web component into a React application (Functional Components with Hooks).
|
|
311
311
|
|
|
312
|
-
|
|
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);
|
|
313
316
|
|
|
314
|
-
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
if (signpadRef.current) signpadRef.current.config = config;
|
|
319
|
+
}, [config]);
|
|
315
320
|
|
|
316
|
-
|
|
317
|
-
npm install @signosoft/signpad-js
|
|
321
|
+
return <signosoft-signpad ref={signpadRef} />;
|
|
318
322
|
```
|
|
319
323
|
|
|
320
|
-
|
|
324
|
+
##### 1. Implementation
|
|
321
325
|
|
|
322
326
|
In React, we use `useRef` for methods and `useEffect` to fetch a lease before updating config.
|
|
323
327
|
|
|
@@ -329,13 +333,12 @@ import type { SignpadConfig } from "@signosoft/signpad-js";
|
|
|
329
333
|
async function fetchLease() {
|
|
330
334
|
try {
|
|
331
335
|
const response = await fetch(
|
|
332
|
-
"https://test.signosoft.com/
|
|
336
|
+
"https://api-test.signosoft.com/v1/public/driver-leases",
|
|
333
337
|
{
|
|
334
338
|
method: "POST",
|
|
335
339
|
headers: { "Content-Type": "application/json" },
|
|
336
340
|
body: JSON.stringify({
|
|
337
|
-
|
|
338
|
-
validityHours: 24,
|
|
341
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
339
342
|
}),
|
|
340
343
|
},
|
|
341
344
|
);
|
|
@@ -357,7 +360,16 @@ async function fetchLease() {
|
|
|
357
360
|
function App() {
|
|
358
361
|
// Use ref to access component methods (ok, clear, connect, etc. by signpadRef.current)
|
|
359
362
|
const signpadRef = useRef<any>(null);
|
|
360
|
-
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
|
+
});
|
|
361
373
|
|
|
362
374
|
useEffect(() => {
|
|
363
375
|
fetchLease().then((lease) => setConfig((prev) => ({ ...prev, lease })));
|
|
@@ -383,19 +395,11 @@ export default App;
|
|
|
383
395
|
</summary>
|
|
384
396
|
<br>
|
|
385
397
|
|
|
386
|
-
|
|
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
|
|
387
399
|
|
|
388
400
|
This guide describes how to integrate the `@signosoft/signpad-js` web component into a Vue 3 application using the Composition API (`<script setup>`).
|
|
389
401
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
Install the core package using npm:
|
|
393
|
-
|
|
394
|
-
```bash
|
|
395
|
-
npm install @signosoft/signpad-js
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
## 2. Configure Vue to recognize Custom Elements
|
|
402
|
+
##### 1. Configure Vue to recognize Custom Elements
|
|
399
403
|
|
|
400
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.
|
|
401
405
|
|
|
@@ -419,7 +423,7 @@ export default defineConfig({
|
|
|
419
423
|
});
|
|
420
424
|
```
|
|
421
425
|
|
|
422
|
-
|
|
426
|
+
##### 2. Implementation
|
|
423
427
|
|
|
424
428
|
In Vue 3, we use `ref` for both the element and config, then fetch the lease and assign it to config.
|
|
425
429
|
|
|
@@ -432,16 +436,24 @@ import type { SignpadConfig, SignosoftSignpad, IPenData } from "@signosoft/signp
|
|
|
432
436
|
// Use ref to access component methods (ok, clear, connect, etc. by signpadRef.value)
|
|
433
437
|
const signpadRef = ref<SignosoftSignpad | null>(null);
|
|
434
438
|
|
|
435
|
-
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
|
+
});
|
|
436
449
|
|
|
437
450
|
const fetchLease = async () => {
|
|
438
451
|
try {
|
|
439
|
-
const response = await fetch("https://test.signosoft.com/
|
|
452
|
+
const response = await fetch("https://api-test.signosoft.com/v1/public/driver-leases", {
|
|
440
453
|
method: "POST",
|
|
441
454
|
headers: { "Content-Type": "application/json" },
|
|
442
455
|
body: JSON.stringify({
|
|
443
|
-
|
|
444
|
-
validityHours: 24,
|
|
456
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
445
457
|
}),
|
|
446
458
|
});
|
|
447
459
|
|
|
@@ -486,21 +498,15 @@ fetchLease()
|
|
|
486
498
|
</summary>
|
|
487
499
|
<br>
|
|
488
500
|
|
|
489
|
-
|
|
501
|
+
#### 🍦 Vanilla JS Integration Guide
|
|
490
502
|
|
|
491
503
|
This guide describes how to integrate the `@signosoft/signpad-js` web component into a plain JavaScript project without any frameworks.
|
|
492
504
|
|
|
493
|
-
|
|
505
|
+
##### 1. Load the component
|
|
494
506
|
|
|
495
|
-
Install the package
|
|
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).
|
|
496
508
|
|
|
497
|
-
|
|
498
|
-
npm install @signosoft/signpad-js
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
Or, 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).
|
|
502
|
-
|
|
503
|
-
## 2. JavaScript Implementation
|
|
509
|
+
##### 2. JavaScript Implementation
|
|
504
510
|
|
|
505
511
|
In Vanilla JS, you interact with the component by selecting it from the DOM and assigning the configuration directly to its `config` property.
|
|
506
512
|
|
|
@@ -512,23 +518,29 @@ import "@signosoft/signpad-js";
|
|
|
512
518
|
* Helps VS Code provide autocomplete for methods and properties.
|
|
513
519
|
* @type {import('@signosoft/signpad-js').SignosoftSignpad}
|
|
514
520
|
*/
|
|
515
|
-
const
|
|
521
|
+
const signosoftPad = document.querySelector("signosoft-signpad");
|
|
516
522
|
|
|
517
523
|
// 1. Initial Configuration
|
|
518
|
-
|
|
524
|
+
signosoftPad.config = {
|
|
519
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
|
+
},
|
|
520
533
|
};
|
|
521
534
|
|
|
522
535
|
const fetchLease = async () => {
|
|
523
536
|
try {
|
|
524
537
|
const response = await fetch(
|
|
525
|
-
"https://test.signosoft.com/
|
|
538
|
+
"https://api-test.signosoft.com/v1/public/driver-leases",
|
|
526
539
|
{
|
|
527
540
|
method: "POST",
|
|
528
541
|
headers: { "Content-Type": "application/json" },
|
|
529
542
|
body: JSON.stringify({
|
|
530
|
-
|
|
531
|
-
validityHours: 24,
|
|
543
|
+
key: "YOUR-LICENSE-TOKEN",
|
|
532
544
|
}),
|
|
533
545
|
},
|
|
534
546
|
);
|
|
@@ -548,13 +560,13 @@ const fetchLease = async () => {
|
|
|
548
560
|
};
|
|
549
561
|
|
|
550
562
|
fetchLease().then((lease) => {
|
|
551
|
-
|
|
563
|
+
signosoftPad.config = { ...signosoftPad.config, lease };
|
|
552
564
|
});
|
|
553
565
|
```
|
|
554
566
|
|
|
555
|
-
|
|
567
|
+
##### 3. HTML Structure
|
|
556
568
|
|
|
557
|
-
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.
|
|
558
570
|
|
|
559
571
|
```html
|
|
560
572
|
<!-- index.html -->
|
|
@@ -574,15 +586,17 @@ Add the custom element tag to your HTML and create the necessary control buttons
|
|
|
574
586
|
</html>
|
|
575
587
|
```
|
|
576
588
|
|
|
577
|
-
|
|
589
|
+
##### 4. Key Concepts
|
|
578
590
|
|
|
579
591
|
- **Direct Property Assignment:** Unlike some frameworks that use attributes, in Plain JS you should assign the configuration directly: `element.config = { ... }`.
|
|
580
592
|
- **Module System:** Ensure your `<script>` tag has `type="module"` to use the `import` statement.
|
|
581
593
|
- **Methods:** All methods like `.connect()`, `.ok()`, and `.clear()` are available directly on the DOM element object.
|
|
582
|
-
- **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.
|
|
583
595
|
|
|
584
596
|
</details>
|
|
585
597
|
|
|
598
|
+
<a id="properties"></a>
|
|
599
|
+
|
|
586
600
|
## 📋 Properties
|
|
587
601
|
|
|
588
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.
|
|
@@ -591,30 +605,36 @@ The component is primarily configured through a single `config` property. This o
|
|
|
591
605
|
| :------- | :-------------- | :------ | :----------------------------------------------- |
|
|
592
606
|
| `config` | `SignpadConfig` | `{}` | The main configuration object for the component. |
|
|
593
607
|
|
|
594
|
-
###
|
|
608
|
+
### What goes in `config`
|
|
595
609
|
|
|
596
|
-
|
|
597
|
-
| :---------- | :------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
598
|
-
| **`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.
|
|
599
611
|
|
|
600
|
-
|
|
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
|
+
```
|
|
601
623
|
|
|
602
|
-
|
|
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`
|
|
603
632
|
|
|
604
|
-
|
|
|
605
|
-
|
|
|
606
|
-
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
| `bottomBarVisible` | `boolean` | `true` | Shows/hides the entire bottom action bar. |
|
|
610
|
-
| `okButtonVisible` | `boolean` | `true` | Shows the **OK** button in the bottom bar. |
|
|
611
|
-
| `clearButtonVisible` | `boolean` | `true` | Shows the **Clear** button in the bottom bar. |
|
|
612
|
-
| `cancelButtonVisible` | `boolean` | `true` | Shows the **Cancel** button in the bottom bar. |
|
|
613
|
-
| `canvasLineVisible` | `boolean` | `true` | Shows the horizontal signature guide line on the canvas. |
|
|
614
|
-
| `deviceStatusTextVisible` | `boolean` | `true` | Shows the name of the connected device (e.g., "Wacom STU-540"). |
|
|
615
|
-
| `additionalTextVisible` | `boolean` | `true` | Shows helper status messages (e.g., "Ready to sign"). |
|
|
616
|
-
|
|
617
|
-
### 🖋️ canvasAndDrawingOptions
|
|
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)). |
|
|
636
|
+
|
|
637
|
+
### `canvasAndDrawingOptions`
|
|
618
638
|
|
|
619
639
|
Adjusts the visual appearance of the digital ink.
|
|
620
640
|
|
|
@@ -626,112 +646,132 @@ Adjusts the visual appearance of the digital ink.
|
|
|
626
646
|
|
|
627
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.
|
|
628
648
|
|
|
629
|
-
###
|
|
649
|
+
### `uiVisibilityOptions`
|
|
630
650
|
|
|
631
|
-
|
|
651
|
+
Toggles the visibility of specific User Interface elements.
|
|
632
652
|
|
|
633
|
-
| Field
|
|
634
|
-
|
|
|
635
|
-
| `
|
|
636
|
-
| `
|
|
637
|
-
| `
|
|
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`
|
|
638
668
|
|
|
639
|
-
|
|
669
|
+
Handles component localization. It can load external files or use inline definitions.
|
|
640
670
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
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.
|
|
647
|
-
|
|
648
|
-
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:
|
|
649
|
-
|
|
650
|
-
| Key | Default (English) |
|
|
651
|
-
| :--------------------------- | :-------------------------------------------------- |
|
|
652
|
-
| `OK` | OK |
|
|
653
|
-
| `CLEAR` | Clear |
|
|
654
|
-
| `CANCEL` | Cancel |
|
|
655
|
-
| `CLEAR_SIGNATURE` | Clear signature |
|
|
656
|
-
| `DISCONNECT` | Disconnect |
|
|
657
|
-
| `CONNECT_SIGNPAD` | Connect Signpad |
|
|
658
|
-
| `MOUSE` | Mouse |
|
|
659
|
-
| `CONNECTED_UNKNOWN_DEVICE` | Connected (Unknown Device) |
|
|
660
|
-
| `AUTO_CONNECTING` | Auto-connecting to device... |
|
|
661
|
-
| `CONNECTING_TO_DEVICE` | Connecting to your device... |
|
|
662
|
-
| `REVERTING_TO_DEFAULT` | Reverting to default input... |
|
|
663
|
-
| `DISCONNECTING_DEVICE` | Disconnecting device... |
|
|
664
|
-
| `COMPONENT_INITIALIZED` | Component initialized. |
|
|
665
|
-
| `TEXT_START_SIGNING` | Connect your device. |
|
|
666
|
-
| `TEXT_SIGN_WITH_PHYSICAL` | Please sign using the device pen. |
|
|
667
|
-
| `TEXT_SIGN_WITH_MOUSE` | Please sign using your mouse. |
|
|
668
|
-
| `TEXT_SIGN_GENERIC` | Sign in the area above using mouse or pen. |
|
|
669
|
-
| `TEXT_SIGNING_WITH_DEVICE` | Signing with {device} |
|
|
670
|
-
| `NO_DEVICE_FOUND` | No device found. |
|
|
671
|
-
| `CONNECTION_ERROR` | Connection error. |
|
|
672
|
-
| `ERROR_DISCONNECTION` | Error during disconnection. |
|
|
673
|
-
| `ERROR_INSTANTIATING_DRIVER` | Error instantiating driver. |
|
|
674
|
-
| `INSUFFICIENT_TOKENS` | Not enough tokens to sign. Reconnecting… |
|
|
675
|
-
| `INVALID_LICENSE_KEY` | Invalid license key. |
|
|
676
|
-
| `READY_TO_CONNECT` | Ready to connect. |
|
|
677
|
-
| `SIGNPAD_DETECTED` | Signpad detected. Click 'Connect Signpad' to begin. |
|
|
678
|
-
| `READY_TO_SIGN_MOUSE` | Ready to sign. Use your mouse. |
|
|
679
|
-
| `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. |
|
|
680
676
|
|
|
681
|
-
|
|
677
|
+
### `theme`
|
|
682
678
|
|
|
683
|
-
|
|
679
|
+
Color theme of the component.
|
|
684
680
|
|
|
685
|
-
|
|
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. |
|
|
686
684
|
|
|
687
|
-
|
|
685
|
+
### `actionHandlers`
|
|
688
686
|
|
|
689
687
|
These allow you to **inject custom logic** directly into the internal button flows. Your code runs alongside the component's internal logic.
|
|
690
688
|
|
|
691
|
-
| Handler | Arguments | Description
|
|
692
|
-
| :------------- | :---------------------------------- |
|
|
693
|
-
| `handleOk` | `data?: ISignatureConfirmationData` | Extends the internal OK button logic. Receives captured signature
|
|
694
|
-
| `handleClear` | — | Extends the internal Clear button logic.
|
|
695
|
-
| `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. |
|
|
694
|
+
|
|
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
|
+
```
|
|
696
716
|
|
|
697
|
-
|
|
717
|
+
### `eventCallbacks`
|
|
698
718
|
|
|
699
719
|
Standard event listeners triggered _after_ specific actions. Useful for observing the component from your application state.
|
|
700
720
|
|
|
701
|
-
| Callback | Payload | Description
|
|
702
|
-
| :------------- | :----------------------------------------------- |
|
|
703
|
-
| `onPen` | `event: CustomEvent<IPenData>` | Dispatched when a device or mouse fallback are in contact with component or signature pad display.
|
|
704
|
-
| `onConnecting` | `event: CustomEvent<void>` | Dispatched when a device is in loading state.
|
|
705
|
-
| `onConnect` | `event: CustomEvent<{ deviceInfo: any }>` | Dispatched when a device or mouse fallback connects.
|
|
706
|
-
| `onDisconnect` | `event: CustomEvent<void>` | Dispatched when the hardware connection is closed.
|
|
707
|
-
| `onOk` | `event: CustomEvent<ISignatureConfirmationData>` | Dispatched when signature is confirmed.
|
|
708
|
-
| `onClear` | `event: CustomEvent<void>` | Dispatched when the canvas has been cleared.
|
|
709
|
-
| `onCancel` | `event: CustomEvent<void>` | Dispatched when the user aborts the session.
|
|
710
|
-
| `onError` | `event: CustomEvent<ISignpadErrorDetail>` | Dispatched on critical failures (lease, driver, connection, billing, etc.).
|
|
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.). |
|
|
711
731
|
|
|
712
732
|
> `actionHandlers` and `eventCallbacks` are separate hooks and can both be configured.
|
|
713
733
|
|
|
714
|
-
|
|
734
|
+
**Listening with the config.** Pass the callbacks in `eventCallbacks`; each receives the `CustomEvent`:
|
|
715
735
|
|
|
716
736
|
```typescript
|
|
717
|
-
{
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
+
};
|
|
722
746
|
```
|
|
723
747
|
|
|
724
|
-
`
|
|
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:
|
|
725
749
|
|
|
726
|
-
|
|
727
|
-
-
|
|
728
|
-
|
|
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`.
|
|
729
760
|
|
|
730
|
-
|
|
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
|
+
```
|
|
731
771
|
|
|
732
772
|
---
|
|
733
773
|
|
|
734
|
-
###
|
|
774
|
+
### Full example
|
|
735
775
|
|
|
736
776
|
```typescript
|
|
737
777
|
this.config = {
|
|
@@ -752,6 +792,131 @@ this.config = {
|
|
|
752
792
|
};
|
|
753
793
|
```
|
|
754
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
|
+
|
|
755
920
|
## 🧩 Methods
|
|
756
921
|
|
|
757
922
|
The following methods are available on the component instance to control the signing process programmatically.
|
|
@@ -766,190 +931,322 @@ The following methods are available on the component instance to control the sig
|
|
|
766
931
|
| `clear()` | `Promise<void>` | Wipes the signature from the UI and hardware without ending the session. |
|
|
767
932
|
| `cancel()` | `Promise<void>` | Aborts the current session and resets the component state. |
|
|
768
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
|
+
|
|
769
951
|
---
|
|
770
952
|
|
|
771
|
-
|
|
953
|
+
### `connect()`
|
|
954
|
+
|
|
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.
|
|
772
956
|
|
|
773
|
-
|
|
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.
|
|
774
962
|
|
|
775
|
-
|
|
963
|
+
**Returns:** `Promise<void>`, resolved once the connection attempt (or fallback) is complete.
|
|
776
964
|
|
|
777
|
-
**
|
|
965
|
+
**Tip:** always call it from a click handler so the user can pick their device from the browser list.
|
|
778
966
|
|
|
779
|
-
|
|
967
|
+
---
|
|
780
968
|
|
|
781
|
-
|
|
969
|
+
### `disconnect()`
|
|
782
970
|
|
|
783
971
|
Safely terminates the communication channel with the signature tablet.
|
|
784
972
|
|
|
785
|
-
|
|
973
|
+
```js
|
|
974
|
+
await signosoftPad.disconnect();
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
What happens:
|
|
786
978
|
|
|
787
979
|
1. The hardware driver is stopped and released.
|
|
788
980
|
2. The internal cache of device information is cleared.
|
|
789
|
-
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.
|
|
790
982
|
4. Any active mouse listeners are removed.
|
|
791
983
|
5. A `SIGN_DISCONNECT` event is dispatched.
|
|
792
984
|
|
|
793
|
-
|
|
985
|
+
**Returns:** `Promise<void>`
|
|
794
986
|
|
|
795
|
-
|
|
796
|
-
- **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.
|
|
797
988
|
|
|
798
|
-
|
|
989
|
+
---
|
|
799
990
|
|
|
800
|
-
|
|
991
|
+
### `startSigning(options?: IDrawingOptions)`
|
|
801
992
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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({
|
|
805
998
|
color: "#0000FF",
|
|
806
999
|
maxWidth: 4,
|
|
807
1000
|
minWidth: 2,
|
|
808
1001
|
});
|
|
809
1002
|
```
|
|
810
1003
|
|
|
811
|
-
|
|
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.
|
|
812
1005
|
|
|
813
|
-
|
|
1006
|
+
**Returns:** `Promise<void>`
|
|
814
1007
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
4. Returns the final signature payload (coordinates, pressure, metadata).
|
|
1008
|
+
---
|
|
1009
|
+
|
|
1010
|
+
### `stopSigning()`
|
|
819
1011
|
|
|
820
|
-
|
|
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).
|
|
821
1013
|
|
|
822
1014
|
```js
|
|
823
|
-
[
|
|
824
|
-
points, // index 0: Data about each point
|
|
825
|
-
imageBase64, // index 1: Image in base64
|
|
826
|
-
metadata, // index 2: Information about tablet and canvas
|
|
827
|
-
];
|
|
1015
|
+
const [penDataBatch, imageBase64, metadata] = await signosoftPad.stopSigning();
|
|
828
1016
|
```
|
|
829
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
|
+
|
|
830
1030
|
```js
|
|
831
|
-
|
|
832
|
-
{
|
|
833
|
-
|
|
834
|
-
relativeY: number, // normalized 0..1
|
|
835
|
-
pressure: number,
|
|
836
|
-
inContact: boolean,
|
|
837
|
-
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;
|
|
838
1034
|
}
|
|
839
1035
|
```
|
|
840
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
|
+
|
|
841
1054
|
```js
|
|
842
|
-
|
|
843
|
-
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...";
|
|
1055
|
+
await signosoftPad.clear();
|
|
844
1056
|
```
|
|
845
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
|
+
|
|
846
1066
|
```js
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
// fallback produced the signature — the two paths report different fields.
|
|
850
|
-
|
|
851
|
-
// Mouse / touch fallback:
|
|
852
|
-
{
|
|
853
|
-
canvas: {
|
|
854
|
-
width: number,
|
|
855
|
-
height: number,
|
|
856
|
-
cssWidth: number,
|
|
857
|
-
cssHeight: number,
|
|
858
|
-
dpr: number
|
|
859
|
-
},
|
|
860
|
-
tablet: undefined,
|
|
861
|
-
duration: number // seconds elapsed between the first and last recorded pen sample
|
|
862
|
-
}
|
|
1067
|
+
await signosoftPad.cancel();
|
|
1068
|
+
```
|
|
863
1069
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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";
|
|
887
1121
|
```
|
|
888
1122
|
|
|
889
|
-
|
|
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>
|
|
890
1141
|
|
|
891
|
-
|
|
1142
|
+
## 🌐 Localization & Translation Keys
|
|
892
1143
|
|
|
893
|
-
`
|
|
1144
|
+
All texts come from `config.languageOptions` (`lang`, `langPath`, `translations`). There is no other way to set them.
|
|
894
1145
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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
|
|
903
1167
|
```
|
|
904
1168
|
|
|
905
|
-
|
|
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.
|
|
906
1170
|
|
|
907
|
-
|
|
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:
|
|
908
1172
|
|
|
909
|
-
|
|
1173
|
+
```ts
|
|
1174
|
+
languageOptions: { lang: "cs", translations: { START_NEW_SIGNATURE: "Podepsat znovu" } }
|
|
910
1175
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
- `metadata` — object with `canvas` (dimensions, DPR) and `tablet` (device info) fields
|
|
1176
|
+
languageOptions: { lang: "cs", translations: { cs: { OK: "Potvrdit" }, en: { OK: "Confirm" } } }
|
|
1177
|
+
```
|
|
914
1178
|
|
|
915
|
-
|
|
1179
|
+
A JSON file you already have works the same way, import it and pass it:
|
|
916
1180
|
|
|
917
|
-
|
|
1181
|
+
```ts
|
|
1182
|
+
import signpadTexts from "./i18n/signpad.json";
|
|
918
1183
|
|
|
919
|
-
|
|
1184
|
+
languageOptions: { lang: "cs", translations: signpadTexts }
|
|
1185
|
+
```
|
|
920
1186
|
|
|
921
|
-
|
|
1187
|
+
If the file is nested (it also holds your app's texts), pass only the part for the signpad: `translations: appTexts.signpad`.
|
|
922
1188
|
|
|
923
|
-
|
|
1189
|
+
**Changing the language at runtime.** Assign a new config with a different `lang`; the texts update without a reconnect:
|
|
924
1190
|
|
|
925
|
-
|
|
1191
|
+
```ts
|
|
1192
|
+
signosoftPad.config = {
|
|
1193
|
+
...signosoftPad.config,
|
|
1194
|
+
languageOptions: { ...signosoftPad.config.languageOptions, lang: "pt" },
|
|
1195
|
+
};
|
|
1196
|
+
```
|
|
926
1197
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
|
938
|
-
|
|
|
939
|
-
| `
|
|
940
|
-
| `
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
|
947
|
-
|
|
|
948
|
-
| `
|
|
949
|
-
| `
|
|
950
|
-
| `
|
|
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. |
|
|
951
1246
|
|
|
952
|
-
|
|
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>
|
|
953
1250
|
|
|
954
1251
|
## 🎨 Styling & Theming
|
|
955
1252
|
|
|
@@ -958,36 +1255,49 @@ The component uses a hybrid theming model:
|
|
|
958
1255
|
- It ships with complete default styles (works out of the box).
|
|
959
1256
|
- You can override the visual design through CSS variables.
|
|
960
1257
|
|
|
961
|
-
|
|
1258
|
+
### Design tokens
|
|
962
1259
|
|
|
963
|
-
|
|
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.
|
|
964
1261
|
|
|
965
|
-
|
|
1262
|
+
<a id="override-via-css"></a>
|
|
966
1263
|
|
|
967
|
-
|
|
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:
|
|
968
1267
|
|
|
969
1268
|
```css
|
|
970
1269
|
signosoft-signpad {
|
|
971
|
-
|
|
972
|
-
--
|
|
973
|
-
--
|
|
974
|
-
--
|
|
975
|
-
--
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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:
|
|
979
1283
|
|
|
1284
|
+
```css
|
|
1285
|
+
signosoft-signpad {
|
|
980
1286
|
/* Constraints */
|
|
981
|
-
--min-height:
|
|
982
|
-
--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;
|
|
983
1293
|
|
|
984
1294
|
/* CSS Variables */
|
|
985
|
-
--sign-font-family: Arial, Helvetica, sans-serif;
|
|
986
|
-
--sign-common-border-radius:
|
|
1295
|
+
--sign-font-family: var(--fontFamily-primary), Arial, Helvetica, sans-serif;
|
|
1296
|
+
--sign-common-border-radius: var(--borderRadius-s);
|
|
987
1297
|
|
|
988
1298
|
/* Top Bar */
|
|
989
|
-
--sign-top-bar-bg-base: var(--background-
|
|
990
|
-
--sign-top-bar-text-base: var(--
|
|
1299
|
+
--sign-top-bar-bg-base: var(--background-brandSubtle);
|
|
1300
|
+
--sign-top-bar-text-base: var(--brand-650);
|
|
991
1301
|
--sign-top-bar-padding: var(--spacing-constraints);
|
|
992
1302
|
--sign-top-bar-min-height: var(--min-height);
|
|
993
1303
|
--sign-top-bar-border-top-left-radius: var(--sign-common-border-radius);
|
|
@@ -996,26 +1306,27 @@ signosoft-signpad {
|
|
|
996
1306
|
--sign-top-bar-border-bottom-right-radius: 0;
|
|
997
1307
|
|
|
998
1308
|
/* Canvas Area */
|
|
999
|
-
--sign-canvas-bg-base: var(--
|
|
1309
|
+
--sign-canvas-bg-base: var(--brandBeige-100);
|
|
1000
1310
|
--sign-canvas-wrapper-border-top-left-radius: 0;
|
|
1001
1311
|
--sign-canvas-wrapper-border-top-right-radius: 0;
|
|
1002
1312
|
--sign-canvas-wrapper-border-bottom-left-radius: 0;
|
|
1003
1313
|
--sign-canvas-wrapper-border-bottom-right-radius: 0;
|
|
1004
1314
|
|
|
1005
1315
|
/* Line */
|
|
1006
|
-
--sign-line-height:
|
|
1007
|
-
--sign-line-margin-bottom:
|
|
1008
|
-
--sign-line-border-base: var(--
|
|
1009
|
-
--sign-line-additional-text-color: var(--
|
|
1010
|
-
--sign-line-margin:
|
|
1011
|
-
|
|
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);
|
|
1012
1323
|
--sign-canvas-height-offset: var(--sign-line-height);
|
|
1013
1324
|
|
|
1014
1325
|
/* Bottom Bar */
|
|
1015
|
-
--sign-bottom-bar-bg-base: var(--background-
|
|
1326
|
+
--sign-bottom-bar-bg-base: var(--background-brandSubtle);
|
|
1016
1327
|
--sign-bottom-bar-padding: var(--spacing-constraints);
|
|
1017
1328
|
--sign-bottom-bar-min-height: var(--min-height);
|
|
1018
|
-
--sign-bottom-bar-gap:
|
|
1329
|
+
--sign-bottom-bar-gap: var(--spacing-8);
|
|
1019
1330
|
--sign-bottom-bar-border-top-left-radius: 0;
|
|
1020
1331
|
--sign-bottom-bar-border-top-right-radius: 0;
|
|
1021
1332
|
--sign-bottom-bar-border-bottom-left-radius: var(--sign-common-border-radius);
|
|
@@ -1024,76 +1335,66 @@ signosoft-signpad {
|
|
|
1024
1335
|
);
|
|
1025
1336
|
|
|
1026
1337
|
/* Button general settings */
|
|
1027
|
-
--sign-button-font-size:
|
|
1028
|
-
--sign-button-padding:
|
|
1029
|
-
--sign-button-min-height: var(--
|
|
1030
|
-
--sign-button-border-radius:
|
|
1031
|
-
--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);
|
|
1032
1343
|
|
|
1033
1344
|
/* Primary Buttons (OK, Clear, Cancel) */
|
|
1034
|
-
--sign-button-primary-bg-base: var(--
|
|
1035
|
-
--sign-button-primary-bg-hover: var(--
|
|
1036
|
-
--sign-button-primary-bg-
|
|
1037
|
-
--sign-button-primary-
|
|
1038
|
-
--sign-button-primary-text-
|
|
1039
|
-
--sign-button-primary-text-
|
|
1040
|
-
--sign-button-primary-
|
|
1041
|
-
--sign-button-primary-border-
|
|
1042
|
-
--sign-button-primary-border-
|
|
1043
|
-
|
|
1044
|
-
/* Link Buttons (Connect signpad) */
|
|
1045
|
-
--sign-button-link-bg-base: transparent;
|
|
1046
|
-
--sign-button-link-bg-hover: var(--background-color-10);
|
|
1047
|
-
--sign-button-link-bg-disabled: transparent;
|
|
1048
|
-
--sign-button-link-text-base: var(--primary-color-0);
|
|
1049
|
-
--sign-button-link-text-hover: var(--primary-color-10);
|
|
1050
|
-
--sign-button-link-text-disabled: var(--grey-color);
|
|
1051
|
-
--sign-button-link-border-base: 1px solid var(--primary-color-0);
|
|
1052
|
-
--sign-button-link-border-hover: 1px solid var(--primary-color-10);
|
|
1053
|
-
--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;
|
|
1054
1355
|
|
|
1055
1356
|
/* Device State Text Colors */
|
|
1056
|
-
--sign-device-state-text-color-connected:
|
|
1057
|
-
--sign-device-state-text-color-disconnected: red;
|
|
1357
|
+
--sign-device-state-text-color-connected: var(--brand-650);
|
|
1058
1358
|
|
|
1059
1359
|
/* Loading Overlay */
|
|
1060
|
-
--sign-loading-overlay-bg-color:
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
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);
|
|
1065
1369
|
}
|
|
1066
1370
|
```
|
|
1067
1371
|
|
|
1068
|
-
###
|
|
1372
|
+
### Full CSS Variable Reference
|
|
1069
1373
|
|
|
1070
|
-
####
|
|
1374
|
+
#### General
|
|
1071
1375
|
|
|
1072
|
-
| Variable | Default
|
|
1073
|
-
| :---------------------------- |
|
|
1074
|
-
| `--
|
|
1075
|
-
| `--
|
|
1076
|
-
| `--background-color-0` | `#f1f2fd` | Secondary background color. |
|
|
1077
|
-
| `--background-color-10` | `#e3e4fc` | Main bar background color. |
|
|
1078
|
-
| `--text-color-0` | `#333e4a` | Main text color. |
|
|
1079
|
-
| `--white-color` | `#ffffff` | Pure white color. |
|
|
1080
|
-
| `--grey-color` | `#b5b9be` | Disabled state color. |
|
|
1081
|
-
| `--sign-font-family` | `Arial, sans-serif` | Global font family. |
|
|
1082
|
-
| `--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. |
|
|
1083
1380
|
|
|
1084
1381
|
#### Layout & Constraints
|
|
1085
1382
|
|
|
1086
|
-
| Variable | Default
|
|
1087
|
-
| :---------------------- |
|
|
1088
|
-
| `--
|
|
1089
|
-
| `--
|
|
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; }`).
|
|
1090
1391
|
|
|
1091
1392
|
#### Top Bar
|
|
1092
1393
|
|
|
1093
1394
|
| Variable | Default | Description |
|
|
1094
1395
|
| :------------------------------------------ | :--------------------------------- | :---------------------------------- |
|
|
1095
|
-
| `--sign-top-bar-bg-base` | `var(--background-
|
|
1096
|
-
| `--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. |
|
|
1097
1398
|
| `--sign-top-bar-padding` | `var(--spacing-constraints)` | Inner padding of the top bar. |
|
|
1098
1399
|
| `--sign-top-bar-min-height` | `var(--min-height)` | Minimum height of the top bar. |
|
|
1099
1400
|
| `--sign-top-bar-border-top-left-radius` | `var(--sign-common-border-radius)` | Top-left radius of the top bar. |
|
|
@@ -1103,29 +1404,29 @@ signosoft-signpad {
|
|
|
1103
1404
|
|
|
1104
1405
|
#### Canvas & Signature Line
|
|
1105
1406
|
|
|
1106
|
-
| Variable | Default
|
|
1107
|
-
| :------------------------------------------------- |
|
|
1108
|
-
| `--sign-canvas-bg-base` | `var(--
|
|
1109
|
-
| `--sign-canvas-wrapper-border-top-left-radius` | `0`
|
|
1110
|
-
| `--sign-canvas-wrapper-border-top-right-radius` | `0`
|
|
1111
|
-
| `--sign-canvas-wrapper-border-bottom-left-radius` | `0`
|
|
1112
|
-
| `--sign-canvas-wrapper-border-bottom-right-radius` | `0`
|
|
1113
|
-
| `--sign-line-height` | `
|
|
1114
|
-
| `--sign-line-margin-bottom` | `
|
|
1115
|
-
| `--sign-line-border-base` | `var(--
|
|
1116
|
-
| `--sign-line-additional-text-color` | `var(--
|
|
1117
|
-
| `--sign-line-margin` | `
|
|
1118
|
-
| `--sign-canvas-line-text-font-size` | `
|
|
1119
|
-
| `--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. |
|
|
1120
1421
|
|
|
1121
1422
|
#### Bottom Bar
|
|
1122
1423
|
|
|
1123
1424
|
| Variable | Default | Description |
|
|
1124
1425
|
| :--------------------------------------------- | :--------------------------------- | :--------------------------------------- |
|
|
1125
|
-
| `--sign-bottom-bar-bg-base` | `var(--background-
|
|
1426
|
+
| `--sign-bottom-bar-bg-base` | `var(--background-brandSubtle)` | Background color of the bottom bar. |
|
|
1126
1427
|
| `--sign-bottom-bar-padding` | `var(--spacing-constraints)` | Inner padding of the bottom bar. |
|
|
1127
1428
|
| `--sign-bottom-bar-min-height` | `var(--min-height)` | Minimum height of the bottom bar. |
|
|
1128
|
-
| `--sign-bottom-bar-gap` | `
|
|
1429
|
+
| `--sign-bottom-bar-gap` | `var(--spacing-8)` | Space between buttons in the bottom bar. |
|
|
1129
1430
|
| `--sign-bottom-bar-border-top-left-radius` | `0` | Top-left radius of the bottom bar. |
|
|
1130
1431
|
| `--sign-bottom-bar-border-top-right-radius` | `0` | Top-right radius of the bottom bar. |
|
|
1131
1432
|
| `--sign-bottom-bar-border-bottom-left-radius` | `var(--sign-common-border-radius)` | Bottom-left radius of the bottom bar. |
|
|
@@ -1133,167 +1434,87 @@ signosoft-signpad {
|
|
|
1133
1434
|
|
|
1134
1435
|
#### Buttons (General)
|
|
1135
1436
|
|
|
1136
|
-
| Variable | Default
|
|
1137
|
-
| :---------------------------- |
|
|
1138
|
-
| `--sign-button-font-size` | `
|
|
1139
|
-
| `--sign-button-padding` | `
|
|
1140
|
-
| `--sign-button-min-height` | `var(--
|
|
1141
|
-
| `--sign-button-border-radius` | `
|
|
1142
|
-
| `--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. |
|
|
1143
1444
|
|
|
1144
1445
|
#### Primary Buttons (OK, Clear, Cancel)
|
|
1145
1446
|
|
|
1146
|
-
| Variable | Description
|
|
1147
|
-
| :-------------------------------------- |
|
|
1148
|
-
| `--sign-button-primary-bg-base` | Background color of primary buttons.
|
|
1149
|
-
| `--sign-button-primary-bg-hover` | Hover background color.
|
|
1150
|
-
| `--sign-button-primary-bg-
|
|
1151
|
-
| `--sign-button-primary-
|
|
1152
|
-
| `--sign-button-primary-text-
|
|
1153
|
-
| `--sign-button-primary-text-
|
|
1154
|
-
| `--sign-button-primary-
|
|
1155
|
-
| `--sign-button-primary-border-
|
|
1156
|
-
| `--sign-button-primary-border-
|
|
1157
|
-
|
|
1158
|
-
#### Link/Connect Buttons
|
|
1159
|
-
|
|
1160
|
-
| Variable | Default | Description |
|
|
1161
|
-
| :----------------------------------- | :---------------------------------- | :------------------------------------------- |
|
|
1162
|
-
| `--sign-button-link-bg-base` | `transparent` | Background color for secondary/link buttons. |
|
|
1163
|
-
| `--sign-button-link-bg-hover` | `var(--background-color-10)` | Hover background color. |
|
|
1164
|
-
| `--sign-button-link-bg-disabled` | `transparent` | Disabled background color. |
|
|
1165
|
-
| `--sign-button-link-text-base` | `var(--primary-color-0)` | Text color for secondary/link buttons. |
|
|
1166
|
-
| `--sign-button-link-text-hover` | `var(--primary-color-10)` | Hover text color. |
|
|
1167
|
-
| `--sign-button-link-text-disabled` | `var(--grey-color)` | Disabled text color. |
|
|
1168
|
-
| `--sign-button-link-border-base` | `1px solid var(--primary-color-0)` | Border for secondary/link buttons. |
|
|
1169
|
-
| `--sign-button-link-border-hover` | `1px solid var(--primary-color-10)` | Hover border color. |
|
|
1170
|
-
| `--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. |
|
|
1171
1459
|
|
|
1172
1460
|
#### Status & Overlays
|
|
1173
1461
|
|
|
1174
|
-
| Variable | Default
|
|
1175
|
-
| :-------------------------------------------- |
|
|
1176
|
-
| `--sign-device-state-text-color-connected` | `
|
|
1177
|
-
| `--sign-
|
|
1178
|
-
| `--sign-loading-overlay-
|
|
1179
|
-
| `--sign-loading-overlay-
|
|
1180
|
-
| `--sign-loading-overlay-spinner-color`
|
|
1181
|
-
| `--sign-loading-overlay-spinner-
|
|
1182
|
-
| `--sign-loading-overlay-spinner-size` | `30px` | Size of the spinner icon. |
|
|
1183
|
-
|
|
1184
|
-
## 💬 Feedback & Support
|
|
1185
|
-
|
|
1186
|
-
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:
|
|
1187
|
-
|
|
1188
|
-
- 📧 **Support Email:** [esign@signosoft.com](mailto:esign@signosoft.com)
|
|
1189
|
-
- 🌐 **Official Website:** [www.signosoft.com](https://www.signosoft.com)
|
|
1190
|
-
|
|
1191
|
-
## 🛠️ Contributing
|
|
1192
|
-
|
|
1193
|
-
### Commit naming convention
|
|
1194
|
-
|
|
1195
|
-
- Every commit message must start with Jira task id and category:
|
|
1196
|
-
- `MAIN-000/<category>: <summary>`
|
|
1197
|
-
- For work in progress:
|
|
1198
|
-
- `MAIN-000/<category>: WIP - <summary>`
|
|
1199
|
-
|
|
1200
|
-
Allowed categories:
|
|
1201
|
-
|
|
1202
|
-
- `feat`: adding a new feature
|
|
1203
|
-
- `fix`: fixing a bug
|
|
1204
|
-
- `refactor`: performance/readability/maintainability change
|
|
1205
|
-
- `chore`: docs, formatting, tests, cleanup, tooling
|
|
1206
|
-
- `merge`: branch merge into `main`
|
|
1207
|
-
|
|
1208
|
-
Use infinitive verb form in commit summaries:
|
|
1209
|
-
|
|
1210
|
-
- <span style="color: #16a34a;"><strong>RIGHT:</strong></span> `add`, `fix`, `remove`, `update`, `refactor`
|
|
1211
|
-
- <span style="color: #dc2626;"><strong>WRONG:</strong></span> Continuous tense: `adding`, `removing` Past tense: `added`, `fixed`
|
|
1212
|
-
|
|
1213
|
-
Examples:
|
|
1214
|
-
|
|
1215
|
-
```bash
|
|
1216
|
-
git commit -m "MAIN-000/feat: add new button component; add button to templates"
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
```bash
|
|
1220
|
-
git commit -m "MAIN-000/fix: add stop directive to button component to prevent propagation"
|
|
1221
|
-
```
|
|
1222
|
-
|
|
1223
|
-
```bash
|
|
1224
|
-
git commit -m "MAIN-000/refactor: rewrite button component in TypeScript"
|
|
1225
|
-
```
|
|
1226
|
-
|
|
1227
|
-
```bash
|
|
1228
|
-
git commit -m "MAIN-000/chore: write button documentation"
|
|
1229
|
-
```
|
|
1230
|
-
|
|
1231
|
-
```bash
|
|
1232
|
-
git commit -m "MAIN-000/merge: new button"
|
|
1233
|
-
```
|
|
1234
|
-
|
|
1235
|
-
```bash
|
|
1236
|
-
git commit -m "MAIN-000/feat: WIP - basic signing functionality"
|
|
1237
|
-
```
|
|
1238
|
-
|
|
1239
|
-
### Versioning and release commits
|
|
1240
|
-
|
|
1241
|
-
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. |
|
|
1242
1470
|
|
|
1243
|
-
|
|
1244
|
-
- `MINOR` (`x.y.0`): backward-compatible feature set
|
|
1245
|
-
- `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.
|
|
1246
1472
|
|
|
1247
|
-
|
|
1473
|
+
<a id="accessibility"></a>
|
|
1248
1474
|
|
|
1249
|
-
|
|
1250
|
-
- Version bump with actual code/features in same commit: use `feat`/`fix`/`refactor` based on dominant change
|
|
1251
|
-
- For release commits, include both versions in message: `release from <old> to <new>`
|
|
1475
|
+
## ♿ Accessibility
|
|
1252
1476
|
|
|
1253
|
-
|
|
1477
|
+
The component targets WCAG 2.1 AA for everything it renders. What is in place:
|
|
1254
1478
|
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
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.
|
|
1258
1485
|
|
|
1259
|
-
|
|
1486
|
+
**Known limitations (plan an alternative in your app):**
|
|
1260
1487
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
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.
|
|
1264
1491
|
|
|
1265
|
-
|
|
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.
|
|
1266
1493
|
|
|
1267
|
-
|
|
1268
|
-
- `MINOR` (`x.Y.0`): increase minor, reset patch (`1.4.9` -> `1.5.0`)
|
|
1269
|
-
- `MAJOR` (`X.0.0`): increase major, reset minor and patch (`1.9.7` -> `2.0.0`)
|
|
1494
|
+
<a id="feedback--support"></a>
|
|
1270
1495
|
|
|
1271
|
-
|
|
1496
|
+
## 💬 Feedback & Support
|
|
1272
1497
|
|
|
1273
|
-
|
|
1274
|
-
# patch
|
|
1275
|
-
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:
|
|
1276
1499
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1500
|
+
- 📧 **Support Email:** [esign@signosoft.com](mailto:esign@signosoft.com)
|
|
1501
|
+
- 🌐 **Official Website:** [www.signosoft.com](https://www.signosoft.com)
|
|
1279
1502
|
|
|
1280
|
-
|
|
1281
|
-
git commit -m "MAIN-000/refactor: release from 1.1.1 to 2.0.0 - remove legacy API"
|
|
1282
|
-
```
|
|
1503
|
+
<a id="license"></a>
|
|
1283
1504
|
|
|
1284
1505
|
## 📄 License
|
|
1285
1506
|
|
|
1286
|
-
**
|
|
1507
|
+
**Source-available, proprietary commercial license**
|
|
1287
1508
|
|
|
1288
1509
|
Copyright © 2026, Signosoft. All rights reserved.
|
|
1289
1510
|
|
|
1290
|
-
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.
|
|
1291
1512
|
|
|
1292
|
-
###
|
|
1513
|
+
### Commercial Usage
|
|
1293
1514
|
|
|
1294
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).
|
|
1295
1516
|
|
|
1296
|
-
###
|
|
1517
|
+
### Key Terms
|
|
1297
1518
|
|
|
1298
1519
|
- **Unauthorized Use:** Any use without a current, fully paid license is expressly prohibited and constitutes a violation of our intellectual property rights.
|
|
1299
1520
|
- **Legal Action:** Signosoft reserves the right to pursue legal action to enforce its rights and seek damages for unauthorized use.
|