Contract Clause Library

Contract Clause Library

Search, select, and build your custom contract document.

Available Clauses

Document Builder

No clauses added

Select clauses from the library to begin.

${clause.category}

${clause.text.substring(0, 150)}...

`; clauseList.appendChild(clauseEl); }); } }; // Renders the document builder with selected clauses const renderDocumentBuilder = () => { documentBuilder.innerHTML = ''; // Clear existing content if (documentClauses.length === 0) { documentBuilder.appendChild(emptyState); downloadPdfBtn.disabled = true; } else { documentClauses.forEach((clause, index) => { const clauseEl = document.createElement('div'); clauseEl.className = 'p-4 border border-slate-300 rounded-lg bg-white fade-in'; clauseEl.setAttribute('data-index', index); clauseEl.innerHTML = `

${clause.title}

${clause.text}

`; documentBuilder.appendChild(clauseEl); }); downloadPdfBtn.disabled = false; } }; // --- EVENT HANDLERS --- // Handles adding a clause to the builder const handleAddClause = (clauseId) => { if (documentClauses.some(c => c.id === clauseId)) return; // Already added const clauseToAdd = initialClauses.find(c => c.id === clauseId); if (clauseToAdd) { // Create a deep copy to allow for independent editing documentClauses.push(JSON.parse(JSON.stringify(clauseToAdd))); renderAll(); } }; // Handles removing a clause from the builder const handleRemoveClause = (index) => { documentClauses.splice(index, 1); renderAll(); }; // Handles moving a clause up or down const handleMoveClause = (index, direction) => { if (direction === 'up' && index > 0) { [documentClauses[index], documentClauses[index - 1]] = [documentClauses[index - 1], documentClauses[index]]; } else if (direction === 'down' && index < documentClauses.length - 1) { [documentClauses[index], documentClauses[index + 1]] = [documentClauses[index + 1], documentClauses[index]]; } renderAll(); }; // Handles editing a clause inline const handleEditClause = (index, wrapperEl) => { const currentText = documentClauses[index].text; // Adhering to specification: all data is customizable via manual input wrapperEl.innerHTML = `
`; // Focus on the textarea const textarea = wrapperEl.querySelector('.edit-textarea'); if(textarea) textarea.focus(); }; // Handles saving an edited clause const handleSaveClause = (index, newText) => { documentClauses[index].text = newText; renderDocumentBuilder(); // Just re-render the builder }; // Main event delegation for dynamic elements document.body.addEventListener('click', (e) => { const target = e.target; const parentClauseEl = target.closest('[data-index]'); const index = parentClauseEl ? parseInt(parentClauseEl.dataset.index, 10) : -1; if (target.closest('.add-clause-btn')) { const clauseId = parseInt(target.closest('.add-clause-btn').dataset.clauseId, 10); handleAddClause(clauseId); } if (target.closest('.remove-btn') && index !== -1) { handleRemoveClause(index); } if (target.closest('.move-up-btn') && index !== -1) { handleMoveClause(index, 'up'); } if (target.closest('.move-down-btn') && index !== -1) { handleMoveClause(index, 'down'); } if (target.closest('.edit-btn') && index !== -1) { const contentWrapper = parentClauseEl.querySelector('.clause-content-wrapper'); if (contentWrapper) handleEditClause(index, contentWrapper); } if (target.closest('.save-edit-btn') && index !== -1) { const textarea = parentClauseEl.querySelector('.edit-textarea'); if(textarea) handleSaveClause(index, textarea.value); } if (target.closest('.cancel-edit-btn')) { renderDocumentBuilder(); // Re-render to cancel edit } }); // PDF Download functionality const handleDownloadPdf = () => { // Check for jsPDF library if (typeof window.jspdf === 'undefined') { alert('PDF generation library is not available. Please check your internet connection.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); // Adhering to specification: professional formatting, consistent color scheme const margin = 40; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - (2 * margin); let y = margin; // Title doc.setFont('helvetica', 'bold'); doc.setFontSize(24); doc.setTextColor('#1e293b'); // slate-800 doc.text('Contract Document', pageWidth / 2, y, { align: 'center' }); y += 50; documentClauses.forEach((clause, index) => { if (y > doc.internal.pageSize.getHeight() - 80) { // Check for new page doc.addPage(); y = margin; } // Clause Title doc.setFont('helvetica', 'bold'); doc.setFontSize(14); doc.setTextColor('#334155'); // slate-700 const titleText = `${index + 1}. ${clause.title}`; doc.text(titleText, margin, y); y += 20; // Clause Text doc.setFont('helvetica', 'normal'); doc.setFontSize(11); doc.setTextColor('#475569'); // slate-600 // The splitTextToSize function handles text wrapping const clauseTextLines = doc.splitTextToSize(clause.text, usableWidth); doc.text(clauseTextLines, margin, y); y += (clauseTextLines.length * 12) + 25; // Line height * number of lines + spacing }); // Save the PDF doc.save('Contract-Document.pdf'); }; // --- INITIALIZATION --- // Populates the category filter dropdown from the initial data const initializeCategoryFilter = () => { const categories = [...new Set(initialClauses.map(c => c.category))]; categories.sort().forEach(category => { const option = document.createElement('option'); option.value = category; option.textContent = category; categoryFilter.appendChild(option); }); }; // Combined render function to update the whole UI const renderAll = () => { renderClauseLibrary(); renderDocumentBuilder(); }; // Attach event listeners for filters searchInput.addEventListener('input', renderClauseLibrary); categoryFilter.addEventListener('change', renderClauseLibrary); downloadPdfBtn.addEventListener('click', handleDownloadPdf); // Initial setup call initializeCategoryFilter(); renderAll(); });
Scroll to Top