PageSourceSearch

https://quantcalc.app/js/charts/tax-burden.js?v=a4a94e4a

js quantcalc.app collected 2026-10-02 01:15:07 UTC 4,153 bytes, 122 lines download raw bytes

1/**
2 * Tax Burden Chart - Bar chart comparing optimized vs naive tax per year
3 */
4
5let taxBurdenChart = null;
6
7// Two series, categorical slots 1-2 in sequence; solid fills, no strokes
8// (bars separate by the category gap, not by borders).
9function applyTaxBurdenTheme() {
10    if (!taxBurdenChart) return;
11    const P = QCChartTheme.palette();
12    taxBurdenChart.data.datasets.forEach((ds, i) => {
13        ds.backgroundColor = P.data[i];
14    });
15}
16
17function initTaxBurdenChart() {
18    const ctx = document.getElementById('taxBurdenChart');
19    if (!ctx) return;
20
21    taxBurdenChart = new Chart(ctx, {
22        type: 'bar',
23        data: {
24            labels: [],
25            datasets: [
26                {
27                    label: 'Recommended order',
28                    data: [],
29                    borderWidth: 0,
30                    borderRadius: 3,
31                    maxBarThickness: 24
32                },
33                {
34                    label: 'Tax + IRMAA',
35                    data: [],
36                    borderWidth: 0,
37                    borderRadius: 3,
38                    maxBarThickness: 24
39                }
40            ]
41        },
42        options: {
43            responsive: true,
44            maintainAspectRatio: false,
45            interaction: {
46                intersect: false,
47                mode: 'index'
48            },
49            plugins: {
50                legend: {
51                    position: 'top',
52                    align: 'start',
53                    labels: { boxWidth: 8, boxHeight: 8, padding: 8, font: { size: 10 } }
54                },
55                tooltip: {
56                    callbacks: {
57                        label: function(ctx) {
58                            return ctx.dataset.label + ': ' + formatCurrency(ctx.parsed.y);
59                        }
60                    }
61                }
62            },
63            scales: {
64                x: {
65                    ticks: {
66                        font: { size: 10 },
67                        maxRotation: 0,
68                        autoSkip: true,
69                        maxTicksLimit: 8
70                    }
71                },
72                y: {
73                    // Keeps the axis on clean $ steps when a scenario pays
74                    // no tax (empty series would otherwise tick $0..$1).
75                    suggestedMax: 1000,
76                    ticks: {
77                        font: { size: 10 },
78                        callback: function(val) { return formatCurrency(val); }
79                    }
80                }
81            }
82        }
83    });
84
85    applyTaxBurdenTheme();
86}
87
88QCChartTheme.register(() => {
89    if (!taxBurdenChart) return;
90    applyTaxBurdenTheme();
91    QCChartTheme.refresh(taxBurdenChart);
92});
93
94function updateTaxBurdenChart(yearlyTaxStats) {
95    if (!taxBurdenChart || !yearlyTaxStats || yearlyTaxStats.length === 0) return;
96
97    // Only show retirement years with tax data
98    const retireAge = AppState.inputs.retirementAge;
99    // Every retirement year, including the ones where the tax-aware order paid
100    // nothing: a chart that starts at the first taxed year (often 75, when RMDs
101    // begin) reads as missing data, when the flat zeros ARE the point.
102    const filtered = yearlyTaxStats.filter(yt => yt.age >= retireAge);
103
104    const labels = filtered.map(yt => 'Age ' + yt.age);
105    const taxOnly = filtered.map(yt => yt.taxPaid || 0);
106    const taxPlusIrmaa = filtered.map(yt => (yt.taxPaid || 0) + (yt.irmaaSurcharge || 0));
107
108    taxBurdenChart.data.labels = labels;
109    taxBurdenChart.data.datasets[0].data = taxOnly;
110    taxBurdenChart.data.datasets[1].data = taxPlusIrmaa;
111    taxBurdenChart.update();
112}
113
114// TEST SHIM (inert): classic scripts already expose these top-level
115// declarations as script globals in the browser, so these assignments
116// change nothing there. They exist so the vitest suite (which imports
117// this file as an ES module, where top-level declarations do NOT become
118// globals) can reach the functions via window.
119if (typeof window !== 'undefined') {
120    window.initTaxBurdenChart = initTaxBurdenChart;
121    window.updateTaxBurdenChart = updateTaxBurdenChart;
122}

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.