Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,114 @@
|
||||
// Generated, do not edit. sha256:ae0f941ec7a830f9f94ae85e37de1157b6e15f16cc2e9dc3f0c5c7243d3eaafd
|
||||
import { listAssets } from '../data/assets.js';
|
||||
import { listAccounts } from '../data/accounts.js';
|
||||
import { getPref, setPref } from '../data/preferences.js';
|
||||
import { t } from '../i18n/index.js';
|
||||
import { esc } from '../ui/escape.js';
|
||||
import { fmtBaseSigned, fmtPct, pctTone, colouredSigns } from '../ui/format.js';
|
||||
import { assetLabel, assetLabelHtml, byKindAndLabel } from '../ui/asset-label.js';
|
||||
import { CHEVRON_RIGHT } from '../ui/icons.js';
|
||||
import { rangeLabel, rangeCycleButton, wireRangeCycle } from '../ui/range-cycle.js';
|
||||
import { isOwnWealth } from '../../shared/wealth-scope.js';
|
||||
import { ENUMS, TARGETS, targetHash } from '../../shared/config.js';
|
||||
import { dagreeksVanNu } from '../wealth/series.js';
|
||||
import { periodChoices } from '../wealth/return-view.js';
|
||||
import { lineForData, drawLine } from '../wealth/return-dialog.js';
|
||||
|
||||
const PREF_SCOPE = 'widget.return';
|
||||
const RANGES = Object.freeze({ day: 1, week: 7, month: 30, year: 365, all: 0 });
|
||||
const PERIOD_OF = Object.freeze({ day: 'd1', week: 'd7', month: 'd30', year: 'd365', all: 'start' });
|
||||
const DEFAULT_RANGE = 'month';
|
||||
|
||||
const dataOf = instance => (typeof instance?.params?.data === 'string' ? instance.params.data : 'total');
|
||||
|
||||
function titleHtml(data, assets) {
|
||||
const base = esc(t('dashboard.widget.return'));
|
||||
if (data.startsWith('asset:')) {
|
||||
const a = assets.find(x => x.id === Number(data.slice(6)));
|
||||
return a ? `${base}: ${assetLabelHtml(a)}` : base;
|
||||
}
|
||||
if (data.startsWith('kind:')) return `${base}: ${esc(t('enums.asset_kind.' + data.slice(5)))}`;
|
||||
return base;
|
||||
}
|
||||
|
||||
export async function renderReturnWidget(host, instance) {
|
||||
const data = dataOf(instance);
|
||||
const key = instance?.instId || 'return';
|
||||
const [accounts, assets, stored] = await Promise.all([
|
||||
listAccounts(), listAssets(), getPref(PREF_SCOPE, key, DEFAULT_RANGE).catch(() => DEFAULT_RANGE),
|
||||
]);
|
||||
let range = RANGES[stored] != null ? stored : DEFAULT_RANGE;
|
||||
host.innerHTML = `<div class="card return-card">
|
||||
<div class="flex-between">
|
||||
<div class="t-label">${titleHtml(data, assets)}</div>
|
||||
<a class="wealth-detail-btn card-go-btn" href="${targetHash(TARGETS.WEALTH)}" title="${esc(t('home.toWealth'))}" aria-label="${esc(t('home.toWealth'))}">${CHEVRON_RIGHT}</a>
|
||||
</div>
|
||||
<div class="return-w-amount sigw-price" data-return-amount>…</div>
|
||||
<div class="flex-between spot-sub">
|
||||
<div class="return-w-pct" data-return-pct></div>
|
||||
<div class="range-cycle" data-return-range>${rangeCycleButton(range)}</div>
|
||||
</div>
|
||||
<div class="return-view--widget"><div class="return-plot"><canvas data-return-chart></canvas></div></div>
|
||||
</div>`;
|
||||
const amountEl = host.querySelector('[data-return-amount]');
|
||||
const pctEl = host.querySelector('[data-return-pct]');
|
||||
let chart = null;
|
||||
let gen = 0;
|
||||
|
||||
try {
|
||||
const excludedAccounts = new Set(accounts.filter(ac => !isOwnWealth(ac)).map(ac => ac.id));
|
||||
const reeks = await dagreeksVanNu({ excludedAccounts });
|
||||
if (!host.isConnected) return;
|
||||
const held = assets.filter(a => (reeks.perAsset?.get(a.id) || []).some(v => v > 0));
|
||||
const periods = periodChoices(reeks.dagen?.length || 0, null);
|
||||
|
||||
const draw = async () => {
|
||||
const g = ++gen;
|
||||
const btn = host.querySelector('.range-cycle-btn');
|
||||
if (btn) btn.textContent = rangeLabel(range);
|
||||
const venster = periods.find(p => p.key === PERIOD_OF[range])?.venster || null;
|
||||
const line = lineForData({ reeks, assets: held, data, venster });
|
||||
amountEl.innerHTML = line.amount == null ? '-'
|
||||
: `<span class="val">${esc(fmtBaseSigned(line.amount, 'both'))}</span>`;
|
||||
pctEl.innerHTML = line.pct == null ? '' : `<span class="delta ${pctTone(line.pct, { fine: range === 'day' })}">${esc(fmtPct(line.pct, { fine: range === 'day', signs: colouredSigns() }))}</span>`;
|
||||
chart?.destroy();
|
||||
chart = null;
|
||||
let canvas = host.querySelector('[data-return-chart]');
|
||||
const plot = host.querySelector('.return-plot');
|
||||
if (line.points.length < 2) { plot.innerHTML = `<div class="t-muted-cell">${esc(t('wealth.noChart'))}</div>`; return; }
|
||||
if (!canvas) { plot.innerHTML = '<canvas data-return-chart></canvas>'; canvas = plot.firstElementChild; }
|
||||
const c = await drawLine(canvas, reeks, line.points).catch(() => null);
|
||||
if (g !== gen || !host.isConnected) { c?.destroy(); return; }
|
||||
chart = c;
|
||||
};
|
||||
await draw();
|
||||
wireRangeCycle(host.querySelector('[data-return-range]'), () => range, next => {
|
||||
range = next;
|
||||
setPref(PREF_SCOPE, key, range).catch(() => { });
|
||||
draw();
|
||||
}, RANGES);
|
||||
} catch (err) {
|
||||
console.error('[return-widget]', err);
|
||||
amountEl.textContent = '⚠ ' + err.message;
|
||||
}
|
||||
}
|
||||
|
||||
export async function renderReturnSettings(host, instance, commit) {
|
||||
const assets = (await listAssets()).sort(byKindAndLabel);
|
||||
const current = dataOf(instance);
|
||||
const opt = (value, label) => `<option value="${esc(value)}"${value === current ? ' selected' : ''}>${esc(label)}</option>`;
|
||||
const kinds = ENUMS.ASSET_KIND.filter(k => assets.some(a => a.kind === k));
|
||||
const row = document.createElement('div');
|
||||
row.className = 'dash-set-row';
|
||||
row.innerHTML = `<span class="t-label t-label--ter">${esc(t('wealth.returnData'))}</span>
|
||||
<select class="input dash-set-select" data-return-setting>
|
||||
${opt('total', t('wealth.returnTotal'))}
|
||||
<optgroup label="${esc(t('wealth.returnKinds'))}">${kinds.map(k => opt(`kind:${k}`, t('enums.asset_kind.' + k))).join('')}</optgroup>
|
||||
<optgroup label="${esc(t('wealth.returnAssets'))}">${assets.map(a => opt(`asset:${a.id}`, assetLabel(a))).join('')}</optgroup>
|
||||
</select>`;
|
||||
host.appendChild(row);
|
||||
row.querySelector('[data-return-setting]').addEventListener('change', e => {
|
||||
instance.params = { ...(instance.params || {}), data: e.target.value };
|
||||
commit();
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user