1import { Controller } from "@hotwired/stimulus" 2 3export default class extends Controller { 4 static targets = ["checkbox", "notes", "timeline", "item"] 5 static values = { eventType: String, eventTitle: String } 6 7 toggle(event) { 8 const checkbox = event.currentTarget 9 const item = checkbox.closest("[data-event-planner-target='item']") 10 const notesWrapper = item.querySelector("[data-notes-wrapper]") 11 12 if (checkbox.checked) { 13 item.classList.add("bg-primary-50", "border-primary-200") 14 item.classList.remove("border-stone-200") 15 if (notesWrapper) notesWrapper.classList.remove("hidden") 16 } else { 17 item.classList.remove("bg-primary-50", "border-primary-200") 18 item.classList.add("border-stone-200") 19 if (notesWrapper) notesWrapper.classList.add("hidden") 20 } 21 } 22 23 async downloadPdf() { 24 const { jsPDF } = await import("jspdf") 25 const doc = new jsPDF() 26 const pageWidth = doc.internal.pageSize.getWidth() 27 const margin = 20 28 const maxWidth = pageWidth - margin * 2 29 let y = 20 30 31 // Header 32 doc.setFontSize(20) 33 doc.setFont("helvetica", "bold") 34 doc.text(this.eventTitleValue + " Planning Guide", margin, y) 35 y += 8 36 37 doc.setFontSize(10) 38 doc.setFont("helvetica", "normal") 39 doc.setTextColor(100) 40 doc.text("Generated from ZeroWaste.Org", margin, y) 41 doc.setTextColor(0) 42 y += 12 43 44 // Divider 45 doc.setDrawColor(200) 46 doc.line(margin, y, pageWidth - margin, y) 47 y += 10 48 49 // Checklist 50 doc.setFontSize(14) 51 doc.setFont("helvetica", "bold") 52 doc.text("Planning Checklist", margin, y) 53 y += 8 54 55 this.checkboxTargets.forEach((checkbox, index) => { 56 if (y > 270) { 57 doc.addPage() 58 y = 20 59 } 60 61 const item = checkbox.closest("[data-event-planner-target='item']") 62 const label = item.querySelector("[data-label]").textContent.trim() 63 const checked = checkbox.checked 64 65 // Checkbox symbol 66 doc.setFontSize(11) 67 doc.setFont("helvetica", "normal") 68 const symbol = checked ? "[\u2713]" : "[ ]" 69 doc.text(symbol, margin, y) 70 71 // Label text (wrapped) 72 const lines = doc.splitTextToSize(label, maxWidth - 12) 73 doc.text(lines, margin + 12, y) 74 y += lines.length * 5 + 2 75 76 // Notes if present 77 if (this.notesTargets[index]) { 78 const notesText = this.notesTargets[index].value.trim() 79 if (notesText) { 80 doc.setFontSize(9) 81 doc.setTextColor(100) 82 const noteLines = doc.splitTextToSize("Notes: " + notesText, maxWidth - 16) 83 doc.text(noteLines, margin + 16, y) 84 y += noteLines.length * 4 + 2 85 doc.setTextColor(0) 86 } 87 } 88 89 y += 3 90 }) 91 92 // Timeline section 93 const timelineEntries = this.timelineTargets.filter(t => t.value) 94 if (timelineEntries.length > 0) { 95 y += 5 96 if (y > 250) { 97 doc.addPage() 98 y = 20 99 } 100 101 doc.setDrawColor(200) 102 doc.line(margin, y, pageWidth - margin, y) 103 y += 10 104 105 doc.setFontSize(14) 106 doc.setFont("helvetica", "bold") 107 doc.setTextColor(0) 108 doc.text("Your Timeline", margin, y) 109 y += 8
110 111 doc.setFontSize(11) 112 doc.setFont("helvetica", "normal") 113 timelineEntries.forEach(field => { 114 if (y > 270) { 115 doc.addPage() 116 y = 20 117 } 118 const label = field.closest("[data-timeline-item]").querySelector("label").textContent.trim() 119 doc.text(label + ": " + field.value, margin, y) 120 y += 7 121 }) 122 } 123 124 // Footer on last page 125 const pageHeight = doc.internal.pageSize.getHeight() 126 doc.setFontSize(8) 127 doc.setTextColor(150) 128 doc.text("ZeroWaste.Org \u2014 Plan your zero waste event", margin, pageHeight - 10) 129 130 const filename = this.eventTypeValue.replace(/_/g, "-") + "-planning-guide.pdf" 131 doc.save(filename) 132 } 133}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.