hover tooltips on Übersicht table with breakdown details
This commit is contained in:
18
calc.js
18
calc.js
@@ -44,8 +44,12 @@ function run_simulation(p) {
|
||||
const renter_nw_arr = [];
|
||||
const cum_buy_arr = [];
|
||||
const cum_rent_arr = [];
|
||||
const detail_arr = [];
|
||||
const labels = [];
|
||||
|
||||
// yearly accumulators (reset each year)
|
||||
let yr_interest = 0, yr_principal = 0, yr_upkeep = 0;
|
||||
|
||||
let breakeven_month = null;
|
||||
|
||||
const invest_r_m = p.invest_return / 100 / 12;
|
||||
@@ -65,7 +69,10 @@ function run_simulation(p) {
|
||||
// German context: no mortgage interest tax deduction for private buyers
|
||||
const total_buy_m = monthly_payment + prop_tax_m + maint_m + insurance_m;
|
||||
|
||||
cum_buy_cost += total_buy_m;
|
||||
yr_interest += interest_m;
|
||||
yr_principal += principal_m;
|
||||
yr_upkeep += prop_tax_m + maint_m + insurance_m;
|
||||
cum_buy_cost += total_buy_m;
|
||||
|
||||
buyer_portfolio *= (1 + invest_r_m);
|
||||
const selling_costs = home_value * (p.closing_sell_pct / 100);
|
||||
@@ -103,6 +110,14 @@ function run_simulation(p) {
|
||||
renter_nw_arr.push(Math.round(portfolio - tax_on_gains));
|
||||
cum_buy_arr.push(Math.round(cum_buy_cost));
|
||||
cum_rent_arr.push(Math.round(cum_rent_cost));
|
||||
detail_arr.push({
|
||||
house_value: Math.round(home_value),
|
||||
buyer_portfolio: Math.round(buyer_portfolio),
|
||||
yr_interest: Math.round(yr_interest),
|
||||
yr_principal: Math.round(yr_principal),
|
||||
yr_upkeep: Math.round(yr_upkeep),
|
||||
});
|
||||
yr_interest = 0; yr_principal = 0; yr_upkeep = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +131,7 @@ function run_simulation(p) {
|
||||
renter_nw_arr,
|
||||
cum_buy_arr,
|
||||
cum_rent_arr,
|
||||
detail_arr,
|
||||
monthly_payment: Math.round(monthly_payment),
|
||||
final_buyer_nw,
|
||||
final_renter_nw,
|
||||
|
||||
@@ -409,6 +409,8 @@ th {
|
||||
|
||||
tbody tr:hover { background: rgba(255,255,255,0.03); }
|
||||
|
||||
td.has-tip { cursor: default; border-bottom-style: dashed; }
|
||||
|
||||
tr.winner-row td { font-weight: 600; }
|
||||
|
||||
/* ===== Footer ===== */
|
||||
|
||||
83
ui.js
83
ui.js
@@ -140,22 +140,91 @@ const update_chart = (chart, labels, data_buy, data_rent, breakeven_month, years
|
||||
chart.update('none');
|
||||
};
|
||||
|
||||
// ===== Tooltip =====
|
||||
|
||||
const tooltip_el = (() => {
|
||||
const el = document.createElement('div');
|
||||
el.id = 'tbl_tooltip';
|
||||
el.style.cssText = `
|
||||
position:fixed; z-index:1000; pointer-events:none; display:none;
|
||||
background:rgba(13,17,23,0.96); border:1px solid rgba(255,255,255,0.12);
|
||||
border-radius:10px; padding:0.7rem 1rem; font-size:0.8rem; line-height:1.8;
|
||||
color:#e6edf3; white-space:nowrap; box-shadow:0 8px 32px rgba(0,0,0,0.5);
|
||||
`;
|
||||
document.body.appendChild(el);
|
||||
return el;
|
||||
})();
|
||||
|
||||
const show_tooltip = (e, html) => {
|
||||
tooltip_el.innerHTML = html;
|
||||
tooltip_el.style.display = 'block';
|
||||
move_tooltip(e);
|
||||
};
|
||||
|
||||
const move_tooltip = (e) => {
|
||||
const pad = 14;
|
||||
const tw = tooltip_el.offsetWidth;
|
||||
const th = tooltip_el.offsetHeight;
|
||||
let x = e.clientX + pad;
|
||||
let y = e.clientY + pad;
|
||||
if (x + tw > window.innerWidth - pad) x = e.clientX - tw - pad;
|
||||
if (y + th > window.innerHeight - pad) y = e.clientY - th - pad;
|
||||
tooltip_el.style.left = x + 'px';
|
||||
tooltip_el.style.top = y + 'px';
|
||||
};
|
||||
|
||||
const hide_tooltip = () => { tooltip_el.style.display = 'none'; };
|
||||
|
||||
const tip_row = (label, value, color) =>
|
||||
`<span style="color:${color || '#8b949e'}">${label}</span> <b>${fmt_eur(value)}</b>`;
|
||||
|
||||
// ===== Table =====
|
||||
|
||||
const build_table = (result) => {
|
||||
const tbody = document.getElementById('table_body');
|
||||
tbody.innerHTML = '';
|
||||
for (let i = 0; i < result.labels.length; i++) {
|
||||
const d = result.detail_arr[i];
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>${result.labels[i]}</td>
|
||||
<td>${fmt_eur(result.buyer_nw_arr[i])}</td>
|
||||
<td>${fmt_eur(result.renter_nw_arr[i])}</td>
|
||||
<td>${fmt_eur(result.cum_buy_arr[i])}</td>
|
||||
<td>${fmt_eur(result.cum_rent_arr[i])}</td>
|
||||
`;
|
||||
|
||||
const td_buy_nw = document.createElement('td');
|
||||
td_buy_nw.textContent = fmt_eur(result.buyer_nw_arr[i]);
|
||||
td_buy_nw.classList.add('has-tip');
|
||||
td_buy_nw.addEventListener('mouseenter', (e) => show_tooltip(e, [
|
||||
tip_row('Immobilienwert', d.house_value, '#f59e0b'),
|
||||
tip_row('ETF-Portfolio', d.buyer_portfolio, '#f59e0b'),
|
||||
].join('<br>')));
|
||||
|
||||
const td_rent_nw = document.createElement('td');
|
||||
td_rent_nw.textContent = fmt_eur(result.renter_nw_arr[i]);
|
||||
td_rent_nw.classList.add('has-tip');
|
||||
td_rent_nw.addEventListener('mouseenter', (e) => show_tooltip(e, [
|
||||
tip_row('ETF-Portfolio (nach Steuer)', result.renter_nw_arr[i], '#2dd4bf'),
|
||||
].join('<br>')));
|
||||
|
||||
const td_buy_cost = document.createElement('td');
|
||||
td_buy_cost.textContent = fmt_eur(result.cum_buy_arr[i]);
|
||||
td_buy_cost.classList.add('has-tip');
|
||||
td_buy_cost.addEventListener('mouseenter', (e) => show_tooltip(e, [
|
||||
`<span style="color:#8b949e;font-size:0.72rem;text-transform:uppercase;letter-spacing:.05em">dieses Jahr</span>`,
|
||||
tip_row('Zinsen', d.yr_interest, '#f59e0b'),
|
||||
tip_row('Tilgung', d.yr_principal, '#f59e0b'),
|
||||
tip_row('Nebenkosten', d.yr_upkeep, '#f59e0b'),
|
||||
].join('<br>')));
|
||||
|
||||
const td_rent_cost = document.createElement('td');
|
||||
td_rent_cost.textContent = fmt_eur(result.cum_rent_arr[i]);
|
||||
|
||||
tr.appendChild(document.createElement('td')).textContent = result.labels[i];
|
||||
tr.appendChild(td_buy_nw);
|
||||
tr.appendChild(td_rent_nw);
|
||||
tr.appendChild(td_buy_cost);
|
||||
tr.appendChild(td_rent_cost);
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
|
||||
tbody.addEventListener('mousemove', move_tooltip);
|
||||
tbody.addEventListener('mouseleave', hide_tooltip);
|
||||
};
|
||||
|
||||
// ===== Result cards =====
|
||||
|
||||
Reference in New Issue
Block a user