UNPKG

clickwrap

Version:

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

10 lines (9 loc) 6.3 kB
(function(i,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("@harbour-enterprises/superdoc")):typeof define=="function"&&define.amd?define(["exports","@harbour-enterprises/superdoc"],c):(i=typeof globalThis<"u"?globalThis:i||self,c(i.Clickwrap={},i.SuperDoc))})(this,function(i,c){"use strict";class l{constructor(e={}){this.config={documentId:null,document:null,documentElement:null,superdocConfig:{},signaturePad:null,consentElements:[],acceptButton:null,declineButton:null,onReady:null,onProgress:null,onAccept:null,onDecline:null,...e,requirements:{scrollThreshold:.95,signature:!1,consents:[],...e.requirements||{}}},this.state={scrollProgress:0,hasValidSignature:!1,consentStates:{},startTime:new Date,isReady:!1},this.superdoc=null,this.init()}async init(){if(this.config.document&&this.config.documentElement)await this.initializeSuperdoc();else if(this.config.documentElement){const e=typeof this.config.documentElement=="string"?document.querySelector(this.config.documentElement):this.config.documentElement;e&&this.trackScroll(e)}this.setupUI()}async initializeSuperdoc(){try{this.superdoc=new c.SuperDoc({selector:this.config.documentElement,document:this.config.document,documentMode:"viewing",pagination:!0,toolbar:!1,rulers:!1,...this.config.superdocConfig,onReady:()=>{console.log("SuperDoc ready"),this.onSuperdocReady()},onException:e=>{console.error("SuperDoc error:",e),this.handleDocumentError(e)}})}catch(e){console.error("Failed to initialize SuperDoc:",e),this.handleDocumentError(e)}}onSuperdocReady(){var t,n;const e=this.getSuperdocScrollElement();e&&this.trackScroll(e),this.state.isReady=!0,(n=(t=this.config).onReady)==null||n.call(t)}getSuperdocScrollElement(){const e=typeof this.config.documentElement=="string"?document.querySelector(this.config.documentElement):this.config.documentElement;return e?e.querySelector(".super-editor-scroll-container")||e.querySelector("[data-scroll-container]")||e.querySelector(".superdoc-content")||e:null}setupUI(){var e,t,n;this.config.signaturePad&&this.trackSignature(this.config.signaturePad),((e=this.config.consentElements)==null?void 0:e.length)>0&&this.trackConsents(this.config.consentElements),(t=this.config.acceptButton)==null||t.addEventListener("click",()=>{this.validate()&&this.handleAccept()}),(n=this.config.declineButton)==null||n.addEventListener("click",()=>{this.handleDecline()}),this.validate()}handleDocumentError(e){const t=typeof this.config.documentElement=="string"?document.querySelector(this.config.documentElement):this.config.documentElement;t&&(t.innerHTML=` <div style="padding: 2rem; text-align: center; color: #ef4444;"> <h3>Document Load Error</h3> <p>Unable to load the agreement document.</p> <p style="font-size: 0.875rem; color: #6b7280; margin-top: 1rem;"> ${(e==null?void 0:e.message)||"Please try refreshing the page."} </p> </div> `)}trackScroll(e){let t;const n=()=>{const{scrollTop:s,scrollHeight:o,clientHeight:r}=e,a=Math.min((s+r)/o,1);a>this.state.scrollProgress&&(this.state.scrollProgress=a,this.validate())};e.addEventListener("scroll",()=>{clearTimeout(t),t=setTimeout(n,100)}),n()}trackSignature(e){const t=()=>{this.state.hasValidSignature=!e.isEmpty(),this.validate()};if(e.addEventListener("endStroke",t),e.clear){const n=e.clear.bind(e);e.clear=()=>{n(),this.state.hasValidSignature=!1,this.validate()}}}trackConsents(e){Array.from(e).forEach(t=>{const n=t.name||t.dataset.name;n&&(this.state.consentStates[n]=t.checked||!1,t.addEventListener("change",s=>{this.state.consentStates[n]=s.target.checked,this.validate()}))})}validate(){var s,o,r;const{requirements:e}=this.config,t={};e.scrollThreshold>0&&(this.superdoc||this.config.documentElement)&&(t.scroll=this.state.scrollProgress>=e.scrollThreshold),e.signature&&(t.signature=this.state.hasValidSignature),((s=e.consents)==null?void 0:s.length)>0&&(t.consents=e.consents.every(a=>this.state.consentStates[a]===!0));const n=Object.values(t).every(Boolean);return this.config.acceptButton&&(this.config.acceptButton.disabled=!n),(r=(o=this.config).onProgress)==null||r.call(o,{checks:t,isValid:n,progress:this.getProgress()}),n}getData(){const e={timestamp:new Date().toISOString(),documentId:this.config.documentId,documentUrl:typeof this.config.document=="string"?this.config.document:null,duration:Math.round((Date.now()-this.state.startTime)/1e3),scrollProgress:Math.round(this.state.scrollProgress*100)+"%",consents:{...this.state.consentStates}};return this.config.signaturePad&&!this.config.signaturePad.isEmpty()&&(this.config.signaturePad.toDataURL?e.signature=this.config.signaturePad.toDataURL():this.config.signaturePad.toSVG&&(e.signature=this.config.signaturePad.toSVG())),e}async handleAccept(){if(!this.validate())return!1;const e={...this.getData(),action:"accepted"};return this.config.onAccept&&await this.config.onAccept(e),e}async handleDecline(){const e={timestamp:new Date().toISOString(),documentId:this.config.documentId,action:"declined"};return this.config.onDecline&&await this.config.onDecline(e),e}reset(){var t;this.state={scrollProgress:0,hasValidSignature:!1,consentStates:{},startTime:new Date,isReady:this.state.isReady};let e=null;this.superdoc?e=this.getSuperdocScrollElement():e=typeof this.config.documentElement=="string"?document.querySelector(this.config.documentElement):this.config.documentElement,e&&(e.scrollTop=0),(t=this.config.signaturePad)!=null&&t.clear&&this.config.signaturePad.clear(),Array.from(this.config.consentElements||[]).forEach(n=>{n.checked=!1}),this.validate()}destroy(){var e,t;this.superdoc&&((t=(e=this.superdoc).destroy)==null||t.call(e),this.superdoc=null),this.state={scrollProgress:0,hasValidSignature:!1,consentStates:{},startTime:null,isReady:!1}}getProgress(){var s;const{requirements:e}=this.config;let t=0,n=0;return e.scrollThreshold>0&&(n++,this.state.scrollProgress>=e.scrollThreshold&&t++),e.signature&&(n++,this.state.hasValidSignature&&t++),((s=e.consents)==null?void 0:s.length)>0&&(n+=e.consents.length,t+=e.consents.filter(o=>this.state.consentStates[o]).length),{completed:t,total:n,percentage:n>0?Math.round(t/n*100):0}}}i.Clickwrap=l,i.default=l,Object.defineProperties(i,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});