1import { Controller } from "@hotwired/stimulus" 2 3export default class extends Controller { 4 static targets = [ 5 "actionSelect", 6 "checkbox", 7 "submitButton", 8 "status", 9 "dialog", 10 "title", 11 "description", 12 "detailsPrimary", 13 "detailsSecondary", 14 "confirmButton", 15 ] 16 17 connect() { 18 this.confirmed = false 19 this.update() 20 } 21 22 update() { 23 const summary = this.summary 24 this.submitButtonTarget.disabled = !summary.canSubmit 25 26 if (!summary.actionLabel) { 27 this.statusTarget.textContent = "Choose an update and compatible records." 28 } else if (summary.selectedCount === 0) { 29 this.statusTarget.textContent = "Select records to update." 30 } else if (summary.compatibleCount === 0) { 31 this.statusTarget.textContent = `No selected ${summary.targetType.toLowerCase()} records match this update.` 32 } else { 33 this.statusTarget.textContent = `${this.pluralize(summary.compatibleCount, "record")} ready. ${this.pluralize(summary.incompatibleCount, "record")} won't match this update.` 34 } 35 } 36 37 submit(event) { 38 if (this.confirmed) { 39 this.confirmed = false 40 return 41 } 42 43 event.preventDefault() 44 const summary = this.summary 45 this.update() 46 if (!summary.canSubmit) return 47 48 this.titleTarget.textContent = summary.destructive ? "Apply destructive bulk update?" : "Apply bulk update?" 49 this.descriptionTarget.textContent = summary.destructive 50 ? "This changes imported records to a lapsed, canceled, archived, or draft state." 51 : "This changes the selected imported records." 52 this.detailsPrimaryTarget.textContent = `${summary.actionLabel}: ${this.pluralize(summary.compatibleCount, "record")} will be updated` 53 this.detailsSecondaryTarget.textContent = [ 54 `${this.pluralize(summary.selectedCount, "record")} selected`, 55 `${this.pluralize(summary.incompatibleCount, "record")} won't match this update`, 56 summary.selectedTypes.length > 0 ? `Selected types: ${summary.selectedTypes.join(", ")}` : null, 57 ].filter(Boolean).join(" ⢠") 58 this.confirmButtonTarget.textContent = summary.destructive ? "Apply update" : "Apply bulk update" 59 this.dialogTarget.showModal() 60 } 61 62 confirm() { 63 this.confirmed = true 64 this.dialogTarget.close() 65 this.element.requestSubmit(this.submitButtonTarget) 66 } 67 68 close() { 69 this.dialogTarget.close() 70 } 71 72 backdropClose(event) { 73 if (event.target === this.dialogTarget) this.close() 74 } 75 76 get summary() { 77 const option = this.actionSelectTarget.selectedOptions[0] 78 const actionLabel = option?.value ? option.textContent.trim() : "" 79 const targetType = option?.dataset.targetType || "" 80 const destructive = option?.dataset.destructive === "true" 81 const selected = this.checkboxTargets.filter((checkbox) => checkbox.checked) 82 const selectedTypes = [...new Set(selected.map((checkbox) => checkbox.dataset.recordType).filter(Boolean))].sort() 83 const compatibleCount = selected.filter((checkbox) => checkbox.dataset.recordType === targetType).length 84 const selectedCount = selected.length 85 86 return { 87 actionLabel, 88 targetType, 89 destructive, 90 selectedCount, 91 selectedTypes, 92 compatibleCount, 93 incompatibleCount: Math.max(selectedCount - compatibleCount, 0), 94 canSubmit: actionLabel.length > 0 && selectedCount > 0 && compatibleCount > 0, 95 } 96 } 97 98 pluralize(count, singular, plural = `${singular}s`) { 99 return `${count} ${count === 1 ? singular : plural}` 100 } 101}
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.