1import { Controller } from "@hotwired/stimulus" 2import currency from 'currency.js' 3 4// Connects to data-controller="numiko--tax-calculator" 5export default class extends Controller { 6 static targets = [ 7 'amountInput', 'frequencies', 'deductionText', 8 'savingsText', 'differenceText', 'ineligibleText', 9 'frequencyPopupTrigger', 'regularInput', 'regularLabel' 10 ] 11 12 static values = { 13 defaultCurrency: String, 14 baseRate: Number, 15 higherRate: Number, 16 baseLimit: Number, 17 higherLimit: Number, 18 eurConfig: String, 19 frequencies: Object, 20 currentFrequency: { type: String, default: 'monthly' } 21 } 22 23 connect () { 24 const content = document.getElementById(this.frequencyPopupTriggerTarget.getAttribute('data-bs-content-id')).innerHTML 25 new bootstrap.Popover( 26 this.frequencyPopupTriggerTarget, 27 { content: content, html: true, sanitize: false, trigger: 'click' } 28 ) 29 this.calculateTaxRelief() 30 document.addEventListener('click', this.clickedOutsidePopover.bind(this)) 31 } 32 33 disconnnect () { 34 document.removeEventListener('click', this.clickedOutsidePopover) 35 } 36 37 calculateTaxRelief (event) { 38 this.#hidePopover() 39 40 let frequency = this.currentFrequencyValue 41 if (event && event.params.frequency) { 42 frequency = event.params.frequency 43 this.#setRegularOption(frequency) 44 this.currentFrequencyValue = event.params.frequency 45 } else { 46 this.#setRegularOption(frequency) 47 } 48 49 const amountObj = currency(this.amountInputTarget.value) 50 51 /* Currently NOT taking into account contributions over one (tax) year if regular donation */ 52 if (this.higherLimitValue && (amountObj.value > this.higherLimitValue)) { 53 this.ineligibleTextTarget.style.display = 'block' 54 this.deductionTextTarget.style.display = 'none' 55 } else { 56 this.ineligibleTextTarget.style.display = 'none' 57 this.deductionTextTarget.style.display = 'block' 58 } 59 60 this.#calculateEffectiveRelief(amountObj, frequency) 61 return 62 } 63 64 #setRegularOption (frequency) { 65 const title = this.frequenciesValue[frequency] 66 if (frequency != 'single') { 67 this.regularInputTarget.checked = true 68 this.regularInputTarget.id = `tax-calc-frequency-${frequency}` 69 this.regularInputTarget.value = frequency 70 this.regularInputTarget.dataset["numiko-TaxCalculatorFrequencyParam"] = frequency 71 72 this.regularLabelTarget.parentElement.htmlFor = `tax-calc-frequency-${frequency}` 73 this.regularLabelTarget.innerHTML = title 74 } 75 } 76 77 #hidePopover () { 78 bootstrap.Popover.getOrCreateInstance(this.frequencyPopupTriggerTarget).hide() 79 } 80 81 clickedOutsidePopover (event) { 82 if (!event.target.closest('.option')) { this.#hidePopover() } 83 } 84 85 #formatAmount (amount) { 86 const currencyConfig = JSON.parse(this.eurConfigValue) 87 const amountStr = currency(amount, Object.assign( 88 { formatWithSymbol: true }, 89 currencyConfig, 90 )).format() 91 return `<em>${amountStr}</em>` 92 } 93 94 #annualizedAmount (amountObj, frequency) { 95 switch (frequency) { 96 case 'monthly': 97 return amountObj.multiply(12) 98 case 'quarterly': 99 return amountObj.multiply(4) 100 case 'biannually': 101 return amountObj.multiply(2) 102 default: 103 return amountObj 104 } 105 } 106 107 #calculateEffectiveRelief (amountObj, frequency) { 108 const annualAmount = this.#annualizedAmount(amountObj, frequency) 109 let deduction = null 110 111 if (this.baseLimitValue && (annualAmount.value > this.baseLimitValue)) { 112 let baseAmount = currency(this.baseLimitValue) 113 let baseReduction = baseAmount.multiply(this.baseRateValue) 114 let higherAmount = annualAmount.subtract(baseAmount) 115 let higherReduction = higherAmount.multiply(this.higherRateValue) 116 117 deduction = baseReduction.add(higherReduction) 118 } else { 119 deduction = annualAmount.multiply(this.baseRateValue).value 120 } 121 this.savingsTextTarget.innerHTML = this.#formatAmount(deduction) 122 let difference = annualAmount.subtract(deduction)
123 this.differenceTextTarget.innerHTML = this.#formatAmount(difference) 124 } 125}
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.