// Generated, do not edit. sha256:c1446a22c58388fe401f93d2f54194b187120f3e6ec07b997b51d0acb9bb51b4 import * as dialog from '../ui/dialog.js'; import { esc } from '../ui/escape.js'; import { fmtBaseSigned, fmtPct, pctTone, colouredSigns, isBaseAsset } from '../ui/format.js'; import { assetLabel, byKindAndLabel } from '../ui/asset-label.js'; import { t } from '../i18n/index.js'; import { localDateNl } from '../../shared/time.js'; import { ENUMS } from '../../shared/config.js'; import { seriesFor } from './series.js'; import { periodChoices, startPeriod, returnLine, idsForSelection, contributorsPerDay } from './return-view.js'; import { getPref, setPref } from '../data/preferences.js'; import { drawReturnChart } from './return-chart.js'; import { carriedDays } from './carried.js'; import { selectLabel } from '../ui/select-label.js'; const DASH = '-'; const PREF_SCOPE = 'wealth.return'; const live = new Set(); export function openReturnDialog({ reeks, assets = [], page = null, selection = null } = {}) { const closed = dialog.detail({ title: t('wealth.returnTitle'), html: '
' }); const hosts = document.querySelectorAll('.dialog-overlay [data-return]'); const host = hosts[hosts.length - 1]; if (!host) return; const controls = host.closest('.dialog')?.querySelector('[data-dialog-aside]') || null; const ids = idsForSelection(selection, assets); const view = mount(host, { reeks, assets, page, data: dataKeyFor(selection, ids), selectionIds: ids, controls }); closed.then(() => view.destroy()); } export async function mountReturnPanel(host, { reeks, asset }) { const key = String(asset.id); const stored = await getPref(PREF_SCOPE, key, null).catch(() => null); if (!host.isConnected) return null; return mount(host, { reeks, assets: [asset], page: null, data: `asset:${asset.id}`, stored, onPeriod: period => setPref(PREF_SCOPE, key, period).catch(() => { }) }); } export function refreshReturn() { for (const v of [...live]) v.render(); } export function lineForData({ reeks, assets = [], data = 'total', venster = null, selectionIds = null }) { const ids = idsOf(data, assets, selectionIds); const line = returnLine(seriesFor(reeks, ids && ids.length === 1 ? ids[0] : ids), venster); const noPct = withoutPct(ids, assets); const top = contributorsOf(reeks, ids ?? assets.map(a => a.id), venster, assets); const carried = carriedDays(reeks, ids, assets); return { amount: line.amount, pct: noPct ? null : line.pct, points: line.points.map(p => ({ ...p, pct: noPct ? null : p.pct, top: top.get(p.i) || [], carried: carried.has(p.i) })), }; } export function drawLine(canvas, reeks, points) { return drawReturnChart(canvas, points, points.map(p => localDateNl(`${reeks.dagen[p.i]}T00:00:00Z`))); } function dataKeyFor(selection, ids) { if (!ids) return 'total'; if (selection?.kind) return `kind:${selection.kind}`; if (ids.length === 1) return `asset:${ids[0]}`; return 'selection'; } function idsOf(key, assets, selectionIds) { if (key === 'total') return null; if (key === 'selection') return selectionIds; if (key.startsWith('kind:')) return assets.filter(a => a.kind === key.slice(5)).map(a => a.id); return [Number(key.slice(6))]; } const withoutPct = (ids, assets) => ids?.length === 1 && isBaseAsset(assets.find(a => a.id === ids[0]) || {}); function contributorsOf(reeks, ids, venster, assets) { const byId = new Map(assets.map(a => [a.id, a])); const named = new Map(); for (const [i, list] of contributorsPerDay(reeks, ids, venster)) { named.set(i, list.filter(c => byId.has(c.id)).map(c => ({ label: assetLabel(byId.get(c.id)), day: c.day, pct: isBaseAsset(byId.get(c.id)) ? null : c.pct, }))); } return named; } function mount(host, { reeks, assets, page, data, selectionIds = null, controls = null, stored = null, onPeriod = null }) { const length = reeks?.dagen?.length || 0; const periods = periodChoices(length, page); const state = { data, period: startPeriod(periods, stored), chart: null, gen: 0, lines: null }; const view = { render() { if (!host.isConnected) { view.destroy(); return; } if (!length) { host.innerHTML = `
${t('wealth.noChart')}
`; return; } const lines = new Map(periods.map(p => [p.key, lineForData({ reeks, assets, data: state.data, venster: p.venster, selectionIds })])); state.lines = lines; if (controls) controls.innerHTML = dataSelect(assets, state.data, selectionIds); host.innerHTML = `
${periods.map(p => periodButton(p, lines.get(p.key), state.period)).join('')}
`; drawChart(lines.get(state.period)?.points || []); }, destroy() { live.delete(view); state.chart?.destroy(); state.chart = null; }, }; async function drawChart(points) { const gen = ++state.gen; state.chart?.destroy(); state.chart = null; const canvas = host.querySelector('[data-return-chart]'); if (!canvas) return; if (points.length < 2) { canvas.parentElement.innerHTML = `
${t('wealth.noChart')}
`; return; } const chart = await drawLine(canvas, reeks, points).catch(() => null); if (gen !== state.gen || !host.isConnected) { chart?.destroy(); return; } state.chart = chart; } host.addEventListener('click', e => { const btn = e.target.closest('[data-period]'); if (!btn || btn.disabled || btn.dataset.period === state.period) return; state.period = btn.dataset.period; onPeriod?.(state.period); for (const b of host.querySelectorAll('[data-period]')) b.classList.toggle('is-on', b === btn); if (!host.querySelector('[data-return-chart]')) { host.querySelector('.return-plot').innerHTML = ''; } drawChart(state.lines?.get(state.period)?.points || []); }); controls?.addEventListener('change', e => { const sel = e.target.closest('[data-return-data]'); if (!sel) return; state.data = sel.value; view.render(); }); live.add(view); view.render(); return view; } function periodLabel(p) { if (p.key === 'page') return p.label ?? DASH; if (p.days == null) return t('wealth.sinceStart'); if (p.days === 1) return t('wealth.periodDay'); if (p.days === 365) return t('wealth.periodYear'); return t('wealth.periodDays', { n: p.days }); } const tone = v => (v == null ? '' : v > 0.005 ? 't-pos' : v < -0.005 ? 't-neg' : ''); function periodButton(p, line, current) { const on = p.key === current ? ' is-on' : ''; const off = p.venster ? '' : ' disabled'; const amount = line?.amount == null ? DASH : `${esc(fmtBaseSigned(line.amount, colouredSigns()))}`; const pct = line?.pct == null ? '' : fmtPct(line.pct, { fine: p.days === 1, signs: colouredSigns() }); return ``; } function dataSelect(assets, current, selectionIds) { const opt = (value, label) => ``; const kinds = ENUMS.ASSET_KIND.filter(k => assets.some(a => a.kind === k)); const sorted = [...assets].sort(byKindAndLabel); return selectLabel(``); }