mirror of
https://github.com/tabler/tabler.git
synced 2026-08-05 19:03:18 +04:00
Add "Signature" component documentation to the UI components section in docs.json
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
---
|
||||
title: Signature
|
||||
summary: A signature pad lets users sign with a mouse, a pen, or a finger. Use it to confirm an order, accept a contract, or hand over a delivery.
|
||||
docs-libs: [signature_pad]
|
||||
description: Collect a drawn signature from users.
|
||||
layout: '@layouts/DocsMdxLayout.astro'
|
||||
---
|
||||
import Example from '@components/Example.astro';
|
||||
import Icon from '@ui/Icon.astro';
|
||||
import { Code } from 'astro:components';
|
||||
import { site } from '@shared/lib/site.ts';
|
||||
|
||||
## Overview
|
||||
|
||||
The signature pad is a `canvas` element with Tabler styles around it. Drawing is handled by the [Signature Pad](https://github.com/szimek/signature_pad) library.
|
||||
|
||||
Use the `signature` class on the wrapper and `signature-canvas` on the canvas. The wrapper draws a solid border, and the canvas adds a dashed one, so the signing area is easy to see. The cursor turns into a crosshair over the canvas.
|
||||
|
||||
Try it below. Draw with the mouse, or with a finger on a touch screen.
|
||||
|
||||
<Example>
|
||||
<div class="signature position-relative" style="width: 100%; max-width: 26rem;"> <canvas id="signature-overview" class="signature-canvas" width="400" height="200" style="height: 10rem"></canvas> </div>
|
||||
</Example>
|
||||
|
||||
## Installation
|
||||
|
||||
The library is not part of the Tabler bundle. Install it with npm:
|
||||
|
||||
```shell
|
||||
npm install signature_pad
|
||||
```
|
||||
|
||||
Or include it from a CDN:
|
||||
|
||||
<Code
|
||||
lang="html"
|
||||
code={`<script src="${site.cdnUrl}/dist/libs/signature_pad/dist/signature_pad.umd.min.js"></script>`}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
### Markup
|
||||
|
||||
Wrap the canvas in a `signature` element. Set `width` and `height` on the canvas, so it has a size before the script runs.
|
||||
|
||||
Also set the height in CSS. Tabler styles the canvas with `width: 100%`, so without a CSS height the canvas keeps the ratio of its attributes. The resize code below then reads a new height on every run, and the pad gets shorter each time.
|
||||
|
||||
<Example codeOnly>
|
||||
<div class="signature position-relative"> <canvas id="signature-default" class="signature-canvas" width="400" height="400" style="height: 16rem"></canvas> </div>
|
||||
</Example>
|
||||
|
||||
### Init the pad
|
||||
|
||||
Create the pad after the DOM is ready. Set a transparent background, so the canvas follows the card or the page. Take the pen color from the canvas text color, so the signature also works in dark mode.
|
||||
|
||||
```js
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const canvas = document.getElementById('signature-default')
|
||||
|
||||
const signaturePad = new SignaturePad(canvas, {
|
||||
backgroundColor: 'transparent',
|
||||
penColor: getComputedStyle(canvas).color,
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Keep the drawing sharp
|
||||
|
||||
A canvas has two sizes: the CSS size and the pixel size. On a retina screen they differ, and the line looks blurry. Scale the canvas to the device pixel ratio, and do it again on resize.
|
||||
|
||||
```js
|
||||
function resizeCanvas() {
|
||||
const ratio = Math.max(window.devicePixelRatio || 1, 1)
|
||||
|
||||
canvas.width = canvas.offsetWidth * ratio
|
||||
canvas.height = canvas.offsetHeight * ratio
|
||||
canvas.getContext('2d').scale(ratio, ratio)
|
||||
|
||||
// resizing clears the canvas, so put the drawing back
|
||||
signaturePad.fromData(signaturePad.toData())
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resizeCanvas)
|
||||
resizeCanvas()
|
||||
```
|
||||
|
||||
### Clear button
|
||||
|
||||
Place a button in the corner of the wrapper and call `clear()` on it. The wrapper needs `position-relative` for this.
|
||||
|
||||
<Example>
|
||||
<div class="signature position-relative" style="width: 100%; max-width: 26rem;"> <div class="position-absolute top-0 end-0 p-2"> <button type="button" class="btn btn-icon" id="signature-clear-button" title="Clear signature"> <Icon name="trash" /> </button> </div> <canvas id="signature-clear" class="signature-canvas" width="400" height="200" style="height: 10rem"></canvas> </div>
|
||||
</Example>
|
||||
|
||||
```js
|
||||
document.getElementById('signature-clear-button').addEventListener('click', function () {
|
||||
signaturePad.clear()
|
||||
})
|
||||
```
|
||||
|
||||
### Pen color
|
||||
|
||||
Change `penColor` at any time. New strokes use the new color, and the old ones keep theirs.
|
||||
|
||||
```js
|
||||
document.getElementById('pen-color').addEventListener('input', function (event) {
|
||||
signaturePad.penColor = event.target.value
|
||||
})
|
||||
```
|
||||
|
||||
### Save the signature
|
||||
|
||||
Use `toDataURL()` to read the drawing. Without arguments you get a PNG. Pass `image/svg+xml` for a vector file. Check `isEmpty()` first, so you do not save a blank pad.
|
||||
|
||||
```js
|
||||
if (signaturePad.isEmpty()) {
|
||||
// ask the user to sign first
|
||||
} else {
|
||||
const png = signaturePad.toDataURL()
|
||||
const svg = signaturePad.toDataURL('image/svg+xml')
|
||||
}
|
||||
```
|
||||
|
||||
Use `fromDataURL()` to show a signature you saved earlier.
|
||||
|
||||
### Pad inside a modal
|
||||
|
||||
A canvas has no size while the modal is hidden, so the pad must start after the modal opens. Listen for the Bootstrap `shown.bs.modal` event instead of `DOMContentLoaded`.
|
||||
|
||||
```js
|
||||
document.getElementById('modal-signature').addEventListener('shown.bs.modal', function () {
|
||||
// create the pad here
|
||||
})
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Signature in a form
|
||||
|
||||
Put the pad in a form group with a label, the same as any other field. Add the legal text under it when the signature is binding.
|
||||
|
||||
<Example bg="surface-secondary" column>
|
||||
<div class="card"> <div class="card-body"> <h3 class="card-title">Confirm transfer</h3> <div class="mb-3"> <label class="form-label required">Signature</label> <div class="signature position-relative"> <div class="position-absolute top-0 end-0 p-2"> <button type="button" class="btn btn-icon" id="signature-form-clear" title="Clear signature"> <Icon name="trash" /> </button> </div> <canvas id="signature-form" class="signature-canvas" width="400" height="180" style="height: 9rem"></canvas> </div> </div> <div class="text-secondary fs-5">I agree that this signature is the electronic representation of my signature for all purposes when I use it on documents.</div> <div class="mt-4 d-flex"> <button type="button" class="btn">Cancel</button> <button type="button" class="btn btn-primary ms-auto">Confirm transfer</button> </div> </div> </div>
|
||||
</Example>
|
||||
|
||||
## Accessibility
|
||||
|
||||
- A canvas cannot be used with a keyboard. Offer a second way to sign, for example a text field where the user types their full name.
|
||||
- Give the pad a real `<label>`, so users know what they sign. Link it to the field with `for` and `id`.
|
||||
- Use a `<button type="button">` for the clear action. A `div` with button classes is not reachable with the keyboard.
|
||||
- Tell users what happens with the drawing. A signature is personal data, so say where you store it and for how long.
|
||||
- Do not rely on the dashed border alone. Add a short hint, for example `Sign inside the box`.
|
||||
|
||||
<script>{`
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
if (typeof SignaturePad === 'undefined') return;
|
||||
|
||||
['signature-overview', 'signature-clear', 'signature-form'].forEach(function (id) {
|
||||
const canvas = document.getElementById(id);
|
||||
if (!canvas) return;
|
||||
|
||||
const signaturePad = new SignaturePad(canvas, {
|
||||
backgroundColor: 'transparent',
|
||||
penColor: getComputedStyle(canvas).color,
|
||||
});
|
||||
|
||||
function resizeCanvas() {
|
||||
const ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||
canvas.width = canvas.offsetWidth * ratio;
|
||||
canvas.height = canvas.offsetHeight * ratio;
|
||||
canvas.getContext('2d').scale(ratio, ratio);
|
||||
signaturePad.fromData(signaturePad.toData());
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
resizeCanvas();
|
||||
|
||||
const button = document.getElementById(id + '-button') || document.getElementById(id + '-clear');
|
||||
if (button) {
|
||||
button.addEventListener('click', function () {
|
||||
signaturePad.clear();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
`}</script>
|
||||
@@ -272,6 +272,10 @@
|
||||
"title": "Segmented Control",
|
||||
"url": "/ui/components/segmented-control/"
|
||||
},
|
||||
{
|
||||
"title": "Signature",
|
||||
"url": "/ui/components/signature/"
|
||||
},
|
||||
{
|
||||
"title": "Spinner",
|
||||
"url": "/ui/components/spinner/"
|
||||
|
||||
Reference in New Issue
Block a user