forked from blackjk3/react-signature-pad
-
-
Notifications
You must be signed in to change notification settings - Fork 124
Closed
Labels
scope: exampleAn example or examples could be improvedAn example or examples could be improved
Milestone
Description
I have worked through some of the deficiencies in the prior react hooks example posted, so please find an updated version with the following changes which I think is worthy of posting for some of the reasons below:
- It's now a "controlled" component, eg. rather than handling both the button, modal and preview, this component just handles the modal. This lets you customise the appearance easily, and I have provided an example of this at the bottom.
- I've added a "signee name" input, which makes sense for most use cases, but is toggle-able via the
displayNameInput
prop. - I've added validation ensuring you can't hit "save" without filling in the required fields. This is tricky, and thus my main reasoning for posting this example. It's tricky because using hooks you don't get access to an easy lifecycle event to know what the internal state of the signaturepad is. You could call isEmpty() inside a poll, but this example uses onDrawEnd which I think is elegant.
You'll note I've left bootstrap classes in. If you're on Bootstrap it'll look nice out of the box, otherwise you'll want to drop in your own utility classes.
import React, { useState, useRef, useCallback } from 'react'
import PropTypes from 'prop-types'
import SignatureCanvas from 'react-signature-canvas'
import Modal, { Footer } from 'react-modal'
const ModalSignatureCanvas = ({onSave, onHide, widthRatio, canvasProps, displayNameInput = false}) => {
const [signatureResult, setSignatureResult] = useState('')
const [name, setName] = useState('')
const sigCanvas = useRef({})
const sigPad = useRef({})
const setNameOnChange = (event) => {
setName(event.target.value)
}
const setSignatureOnChange = () => {
const dataURL = sigCanvas.current.toDataURL()
setSignatureResult(dataURL)
}
const saveInput = () => {
onSave({dataURL: signatureResult, name: name})
}
const clearInput = () => {
sigPad.current.clear()
setSignatureResult('')
}
const measuredRef = useCallback(node => {
const resizeCanvas = (signaturePad, canvas) => {
canvas.width = canvas.parentElement.clientWidth // width of the .canvasWrapper
canvas.height = canvas.parentElement.clientWidth / widthRatio
signaturePad.clear()
}
if (node !== null) {
sigCanvas.current = node.getCanvas()
sigPad.current = node.getSignaturePad()
resizeCanvas(node.getSignaturePad(), node.getCanvas())
}
}, [widthRatio])
const isNameValidIfRequired = (displayNameInput && !!name) || !displayNameInput
const isSignatureValid = !!signatureResult
const isFormValid = isNameValidIfRequired && isSignatureValid
return (
<Modal
title="Enter your signature"
onHide={onHide}
>
<div className="canvasWrapper">
<SignatureCanvas
canvasProps={canvasProps}
ref={measuredRef}
onEnd={setSignatureOnChange}
/>
</div>
{displayNameInput &&
<div className="nameInput">
<label>Name of person entering signature:</label>
<input type="text" className="form-control" onChange={setNameOnChange} value={name} />
</div>}
<Footer>
<div className="btn-group btn-block">
<button type="button" className="btn btn-secondary w-50" onClick={clearInput}>Clear</button>
<button type="button" className="btn btn-primary w-50" onClick={saveInput} disabled={!isFormValid}>Save</button>
</div>
</Footer>
</Modal>
)
}
ModalSignatureCanvas.propTypes = {
canvasProps: PropTypes.object,
widthRatio: PropTypes.number.isRequired,
onSave: PropTypes.func,
onHide: PropTypes.func,
displayNameInput: PropTypes.bool,
}
export default ModalSignatureCanvas
Example: Show signature and a change button
const SignatureCaptureInput = ({signature, signee, onClick, onHide, onSave, isModalOpen}) => {
const buttonText = signature ? 'Change signature' : 'Collect signature'
return (
<>
{signature &&
<img className="img-fluid border mb-2" src={signature} />}
{signee && <div className="blockquote-footer mb-2">{signee}</div>}
<button type="button" className={classNames('btn btn-block', {'btn-secondary': signature, 'btn-primary': !signature})} onClick={onClick}>{buttonText}</button>
{isModalOpen && <ModalSignatureCanvas widthRatio={3} onSave={onSave} onHide={onHide} displayNameInput />}
</>
)
}
SignatureCaptureInput.propTypes = {
signature: PropTypes.string,
signee: PropTypes.string,
onClick: PropTypes.func,
onHide: PropTypes.func,
onSave: PropTypes.func,
isModalOpen: PropTypes.bool,
}
// Usage
const [isModalOpen, setModalOpen] = useState(false)
const handleSignatureChange({dataURL, name}) => { ... }
<SignatureCaptureInput
onSave={handleSignatureChange}
signee={clientSignee}
signature={clientSignature}
onClick={() => setModalOpen(true})}
onHide={() => setModalOpen(false})}
isModalOpen={isModalOpen}
/>
Which looks like:
Prompt for collection:
Modal open:
Preview on completion:
Apologies if this isn't in the right place to post this, but the docs just don't cover the intricacies of implementing this component. Hope this helps someone!
hiimtmac, tsafs, nealeu and adamaslan
Metadata
Metadata
Assignees
Labels
scope: exampleAn example or examples could be improvedAn example or examples could be improved