clickwrap
v0.1.0
Published
Complete clickwrap solution with built-in document viewing. Track agreements, signatures, and consents.
Maintainers
Readme
SuperDoc Clickwrap
Orchestrate legal agreements with SuperDoc. Track document reading, signatures, and consent checkboxes without the UI overhead.
Why this exists
SuperDoc is great at rendering documents. But legal agreements need more - scroll tracking, signatures, consent checkboxes. This orchestrator handles that coordination while you keep full control of your UI.
Note: Clickwrap includes SuperDoc internally. You don't need to install or initialize SuperDoc separately.
Install
npm install clickwrapQuick Start
import Clickwrap from 'clickwrap'
import SignaturePad from 'signature_pad'
// Initialize Clickwrap with built-in SuperDoc
const clickwrap = new Clickwrap({
// Document configuration - Clickwrap handles SuperDoc internally!
document: '/terms.pdf', // PDF/DOCX file or URL
documentElement: '#document', // Where to render
documentId: 'terms-v2.1', // Optional document identifier
// UI Components
signaturePad: new SignaturePad(canvas),
consentElements: document.querySelectorAll('.consent'),
acceptButton: document.querySelector('#accept'),
// Requirements
requirements: {
scrollThreshold: 0.95,
signature: true,
consents: ['terms', 'privacy']
},
// Callbacks
onReady: () => console.log('Document loaded!'),
onAccept: async (data) => {
// Send to your backend
await api.saveAgreement(data)
}
})What it does
✅ Handles SuperDoc initialization internally
✅ Tracks document scroll progress
✅ Validates signatures (bring your own signature library)
✅ Manages consent checkboxes
✅ Enables accept button when requirements are met
✅ Collects timestamped agreement data
✅ Provides progress tracking and validation states
What it doesn't do
❌ No UI components - you build the interface
❌ No backend - you handle data storage
❌ No styling - you control the look
The Data You Get
When a user accepts, you receive everything needed for legal compliance:
{
timestamp: "2024-01-15T10:30:00Z",
documentId: "terms-v2.1",
documentUrl: "/terms.pdf", // if document was a URL
duration: 47, // seconds spent
scrollProgress: "100%", // how far they scrolled
signature: "data:image/png;...", // signature image (if required)
consents: {
terms: true,
privacy: true,
marketing: false
},
action: "accepted" // or "declined"
}API
Config Options
new Clickwrap({
// Document configuration
document: '/terms.pdf', // URL or File object (PDF/DOCX)
documentElement: '#document', // Selector or element where to render
documentId: 'terms-v2.1', // Optional document identifier
superdocConfig: {}, // Optional SuperDoc configuration
// Components (bring your own)
signaturePad: pad, // SignaturePad or compatible
consentElements: checkboxes, // Your checkbox elements
acceptButton: button, // Gets enabled when valid
declineButton: button, // Optional decline
// Requirements
requirements: {
scrollThreshold: 0.95, // 0-1 (95% = nearly complete)
signature: true, // Signature required?
consents: ['terms', 'privacy'] // Required checkbox names
},
// Callbacks
onReady: () => {}, // Document loaded and ready
onProgress: (state) => {}, // Track completion
onAccept: async (data) => {}, // Handle acceptance
onDecline: async (data) => {} // Handle rejection
})Methods
validate()- Check if all requirements are metgetData()- Get current agreement datareset()- Clear all inputs and start overgetProgress()- Get completion percentage (returns {completed, total, percentage})destroy()- Clean up instance and SuperDoc
React Example
function AgreementModal({ document }) {
const canvasRef = useRef()
const clickwrapRef = useRef()
useEffect(() => {
// Initialize Clickwrap with built-in SuperDoc
const signaturePad = new SignaturePad(canvasRef.current)
clickwrapRef.current = new Clickwrap({
// Document configuration
document: document.url, // PDF/DOCX URL
documentElement: '#agreement-doc', // Where to render
documentId: document.id,
// UI Components
signaturePad,
consentElements: document.querySelectorAll('.consent'),
acceptButton: document.querySelector('#accept'),
// Requirements
requirements: {
scrollThreshold: 0.95,
signature: true,
consents: ['terms']
},
// Callbacks
onReady: () => {
console.log('Document loaded!')
},
onAccept: async (data) => {
await api.recordAgreement(data)
closeModal()
}
})
return () => {
clickwrapRef.current?.destroy()
}
}, [document])
return (
<div className="agreement-modal">
<div id="agreement-doc" />
<canvas ref={canvasRef} />
<label>
<input type="checkbox" name="terms" className="consent" />
I accept the terms
</label>
<button id="accept">Accept</button>
</div>
)
}Vue Example
<template>
<div class="agreement">
<div id="documentViewer"></div>
<canvas ref="signatureCanvas"></canvas>
<label>
<input type="checkbox" name="terms" ref="termsCheck">
Accept terms
</label>
<button ref="acceptBtn">Accept</button>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import Clickwrap from 'clickwrap'
import SignaturePad from 'signature_pad'
const props = defineProps(['documentUrl'])
const emit = defineEmits(['accepted'])
const signatureCanvas = ref()
const termsCheck = ref()
const acceptBtn = ref()
let clickwrap = null
onMounted(() => {
clickwrap = new Clickwrap({
// Document configuration
document: props.documentUrl,
documentElement: '#documentViewer',
// UI Components
signaturePad: new SignaturePad(signatureCanvas.value),
consentElements: [termsCheck.value],
acceptButton: acceptBtn.value,
// Requirements
requirements: {
signature: true,
consents: ['terms']
},
// Callbacks
onReady: () => {
console.log('Document ready!')
},
onAccept: async (data) => {
await saveAgreement(data)
emit('accepted')
}
})
})
onUnmounted(() => {
clickwrap?.destroy()
})
</script>Without SuperDoc
Works with any scrollable element if you don't need document rendering:
const clickwrap = new Clickwrap({
// No 'document' parameter - just track existing element
documentElement: document.querySelector('.terms-text'),
documentId: 'terms-v2.1',
signaturePad: new SignaturePad(canvas),
consentElements: document.querySelectorAll('.consent'),
acceptButton: document.querySelector('#accept'),
requirements: {
scrollThreshold: 0.95,
signature: true,
consents: ['terms']
},
onAccept: async (data) => {
await api.saveAgreement(data)
}
})Signature Libraries
Works with any signature library that has isEmpty() and clear() methods:
- signature_pad ✅
- React Signature Canvas ✅
- Vue Signature Pad ✅
- Custom implementations ✅
Browser Support
Modern browsers only. No IE11.
License
MIT
About
Built by the SuperDoc team to make legal agreements easier. Part of the SuperDoc ecosystem.
