1import BaseController from "controllers/base_controller" 2 3const MIN_GUESTS = 1 4const MAX_GUESTS = 100 5 6export default class extends BaseController { 7 static targets = ["cartForm", "guestForm", "guestInput", "subtotal", "perGuest", "submitButton", "successMessage", "pulse", "guestsDisplay"] 8 static outlets = ["mini-cart-item"] 9 static values = { 10 guests: Number 11 } 12 13 connect() { 14 if (this.hasGuestFormTarget) { 15 this.debouncedSubmitGuestForm = this.debounce(() => this.guestFormTarget.requestSubmit(), 500) 16 } 17 } 18 19 increment(event) { 20 event.preventDefault() 21 if (!this.hasGuestFormTarget) return 22 const value = this.currentGuestValue() 23 if (value >= MAX_GUESTS) return 24 this.setGuestValue(value + 1) 25 this.addPulse() 26 this.debouncedSubmitGuestForm() 27 } 28 29 decrement(event) { 30 event.preventDefault() 31 if (!this.hasGuestFormTarget) return 32 const value = this.currentGuestValue() 33 if (value <= MIN_GUESTS) return 34 this.setGuestValue(value - 1) 35 this.addPulse() 36 this.debouncedSubmitGuestForm(
36) 37 } 38 39 commitGuestCount(event) { 40 if (!this.hasGuestFormTarget) return 41 const raw = event?.target?.value 42 const n = parseInt(raw, 10) 43 this.setGuestValue(isNaN(n) ? MIN_GUESTS : n) 44 this.addPulse() 45 this.debouncedSubmitGuestForm() 46 } 47 48 handleGuestInputKeydown(event) { 49 if (event.key !== "Enter") return 50 event.preventDefault() 51 this.commitGuestCount(event) 52 } 53 54 currentGuestValue() { 55 if (this.hasGuestsDisplayTarget) { 56 const n = parseInt(this.guestsDisplayTarget.value, 10) 57 if (!isNaN(n)) return n 58 } 59 if (!this.hasGuestInputTarget) return MIN_GUESTS 60 const n = parseInt(this.guestInputTarget.value, 10) 61 return isNaN(n) ? MIN_GUESTS : n 62 } 63 64 setGuestValue(n) { 65 const clamped = Math.max(MIN_GUESTS, Math.min(MAX_GUESTS, n)) 66 if (this.hasGuestInputTarget) { 67 this.guestInputTarget.value = clamped 68 } 69 if (this.hasGuestsDisplayTarget) { 70 this.guestsDisplayTargets.forEach((el) => { el.value = clamped }) 71 } 72 } 73 74 addPulse() { 75 this.pulseTargets.forEach((el) => el.classList.add("animate-pulse")) 76 } 77 78 recalculate() { 79 let totalCents = 0 80 81 this.miniCartItemOutlets.forEach((itemController) => { 82 totalCents += itemController.priceValue * itemController.quantityValue 83 }) 84 85 this.subtotalTarget.textContent = this.formatPriceNoCents(totalCents) 86 87 if (this.guestsValue > 0) { 88 const perGuestCents = Math.round(totalCents / this.guestsValue) 89 this.perGuestTarget.textContent = this.formatPrice(perGuestCents) 90 } 91 } 92 93 async handleSubmit() { 94 this.showSuccessMessage() 95 this.dispatch("added") 96 } 97 98 async showSuccessMessage() { 99 this.submitButtonTarget.classList.add("hidden") 100 this.successMessageTarget.classList.remove("hidden") 101 await this.delay(5000) 102 this.successMessageTarget.classList.add("hidden") 103 this.submitButtonTarget.classList.remove("hidden") 104 } 105 106 formatPrice(cents) { 107 return new Intl.NumberFormat('en-GB', { 108 style: 'currency', 109 currency: 'GBP', 110 minimumFractionDigits: 2, 111 maximumFractionDigits: 2 112 }).format(cents / 100) 113 } 114 115 formatPriceNoCents(cents) { 116 return new Intl.NumberFormat('en-GB', { 117 style: 'currency', 118 currency: 'GBP', 119 minimumFractionDigits: 0, 120 maximumFractionDigits: 0 121 }).format(cents / 100) 122 } 123}
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.