npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

clickwrap

v0.1.0

Published

Complete clickwrap solution with built-in document viewing. Track agreements, signatures, and consents.

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 clickwrap

Quick 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 met
  • getData() - Get current agreement data
  • reset() - Clear all inputs and start over
  • getProgress() - 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:

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.