Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,157 @@
|
||||
// Generated, do not edit. sha256:148777e8a3d0dd6536d8b6d4b8cae6c0c4cbf2ed945fdf8bb8ed57c23e617bd8
|
||||
import { listBookableAssets } from '../data/assets.js';
|
||||
import { appSetting, setAppSetting } from '../app-settings.js';
|
||||
import { t } from '../i18n/index.js';
|
||||
import { renderNotifications } from '../actions/notifications-widget.js';
|
||||
import { currentStanding } from '../wealth/data.js';
|
||||
import { fmtBase, fmtBaseSigned, colouredSigns, fmtUnit, val } from '../ui/format.js';
|
||||
import { esc } from '../ui/escape.js';
|
||||
import { assetLabel, assetLabelHtml } from '../ui/asset-label.js';
|
||||
import { originsHtml } from '../ui/price-origin.js';
|
||||
import { setPageHint } from '../ui/page-hint.js';
|
||||
import { CHEVRON_RIGHT } from '../ui/icons.js';
|
||||
import { renderDashboard } from '../dashboard/dashboard.js';
|
||||
import { renderSpotWidget, renderSpotSettings } from '../widgets/spot.js';
|
||||
import { secondCurrencyOf, secondAmount, renderSecondCurrencySetting } from '../widgets/second-currency.js';
|
||||
import { STANDING_GROUPINGS, standingGroupingOf, groupStanding, standingToday, standingShowsToday } from '../widgets/standing-groups.js';
|
||||
import { renderMetricsWidget } from '../widgets/metrics.js';
|
||||
import { renderClocksWidget, renderClocksSettings } from '../widgets/clocks.js';
|
||||
import { renderReturnWidget, renderReturnSettings } from '../widgets/return.js';
|
||||
import { registerWidget, availableWidgets } from '../widgets/registry.js';
|
||||
import { DASHBOARD, TARGETS, targetHash } from '../../shared/config.js';
|
||||
|
||||
export const title = () => t('home.title');
|
||||
|
||||
registerWidget('notifications', { render: host => renderNotifications(host) });
|
||||
registerWidget('standing', { render: (host, inst) => renderStandingWidget(host, inst), settings: renderStandingSettings });
|
||||
registerWidget('spot', { render: (host, inst) => renderSpotWidget(host, inst), settings: renderSpotSettings });
|
||||
registerWidget('metrics', { render: host => renderMetricsWidget(host) });
|
||||
registerWidget('clocks', { render: (host, inst) => renderClocksWidget(host, inst), settings: renderClocksSettings });
|
||||
registerWidget('return', { render: (host, inst) => renderReturnWidget(host, inst), settings: renderReturnSettings });
|
||||
|
||||
async function buildCatalog(layout) {
|
||||
const present = new Set(layout.map(it => it.instId));
|
||||
const persona = appSetting('persona');
|
||||
const { renderers } = availableWidgets();
|
||||
const items = [];
|
||||
for (const [type, def] of Object.entries(DASHBOARD.WIDGETS)) {
|
||||
if (def.instanceable || present.has(type) || !renderers[type]) continue;
|
||||
if (def.persona && def.persona !== persona) continue;
|
||||
items.push({ instId: type, type, params: null, label: t(def.label_key) });
|
||||
}
|
||||
let n = 1;
|
||||
while (present.has(`return-${n}`)) n++;
|
||||
items.push({ instId: `return-${n}`, type: 'return', params: null, label: t('dashboard.widget.return') });
|
||||
for (const a of await listBookableAssets()) {
|
||||
const instId = `spot-${a.id}`;
|
||||
if (present.has(instId)) continue;
|
||||
items.push({
|
||||
instId, type: 'spot', params: { assetId: a.id },
|
||||
label: `${t('dashboard.widget.spot')}: ${assetLabel(a)}`,
|
||||
labelHtml: `${esc(t('dashboard.widget.spot'))}: ${assetLabelHtml(a)}`,
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
export async function render(el, opts = {}) {
|
||||
if (!el.querySelector('#dashboard-root')) el.innerHTML = '<div id="dashboard-root"></div>';
|
||||
|
||||
const { renderers, settings } = availableWidgets();
|
||||
await renderDashboard(el.querySelector('#dashboard-root'), {
|
||||
renderers,
|
||||
settings,
|
||||
edit: !!opts.edit,
|
||||
onEdit: opts.setEdit,
|
||||
catalog: buildCatalog,
|
||||
toolbarHost: document.getElementById('nav-tools') || document.getElementById('page-tools'),
|
||||
});
|
||||
|
||||
setPageHint(el, { title: t('home.hintTitle'), rows: [
|
||||
t('home.hintEdit'), t('home.hintDone'), t('home.hintAdd'), t('home.hintWidget'), t('home.hintProfiles'),
|
||||
] });
|
||||
}
|
||||
|
||||
function standingGroupLabel(g, by) {
|
||||
const r = g.sample;
|
||||
if (by === 'asset') return assetLabelHtml(r.asset);
|
||||
if (by === 'assetKind') return esc(t('enums.asset_kind.' + r.asset.kind));
|
||||
if (by === 'accountKind') return esc(g.key === 'none' ? t('wealth.noAccount') : t('enums.account_kind.' + g.key));
|
||||
return esc(r.account.name);
|
||||
}
|
||||
|
||||
function todayHtml(amount, cls, always = false) {
|
||||
if (amount == null || (!always && Math.abs(amount) < 0.005)) return '';
|
||||
const tone = amount > 0.005 ? 't-pos' : amount < -0.005 ? 't-neg' : '';
|
||||
return `<span class="${[cls, tone].filter(Boolean).join(' ')}">${val(fmtBaseSigned(amount, colouredSigns()))}</span>`;
|
||||
}
|
||||
|
||||
async function renderStandingSettings(host, instance, commit) {
|
||||
host.classList.add('dash-settings--wide');
|
||||
const huidig = () => standingGroupingOf(instance.params && instance.params.groupBy);
|
||||
const label = { account: 'home.groupAccount', accountKind: 'home.groupAccountKind', asset: 'home.groupAsset', assetKind: 'home.groupAssetKind' };
|
||||
const chips = () => STANDING_GROUPINGS.map(id => `<button type="button" class="fbar-item${huidig() === id ? ' is-on' : ''}"
|
||||
data-standing-group="${id}" aria-pressed="${huidig() === id}">${esc(t(label[id]))}</button>`).join('');
|
||||
const block = document.createElement('div');
|
||||
block.className = 'dash-set-row';
|
||||
block.innerHTML = `<span class="t-label t-label--ter">${esc(t('home.groupBy'))}</span><div class="fbar fbar--chip">${chips()}</div>`;
|
||||
host.appendChild(block);
|
||||
block.addEventListener('click', e => {
|
||||
const chip = e.target.closest('[data-standing-group]');
|
||||
if (!chip || chip.dataset.standingGroup === huidig()) return;
|
||||
instance.params = { ...(instance.params || {}), groupBy: standingGroupingOf(chip.dataset.standingGroup) };
|
||||
block.querySelector('.fbar').innerHTML = chips();
|
||||
commit({ resize: true });
|
||||
});
|
||||
const today = document.createElement('div');
|
||||
today.className = 'dash-set-row';
|
||||
today.innerHTML = `<label class="clockw-check"><span class="toggle-switch"><input type="checkbox" data-standing-today${standingShowsToday(instance.params) ? ' checked' : ''}/><span class="toggle-track"></span></span><span class="clockw-check-name">${esc(t('home.showToday'))}</span></label>`;
|
||||
host.appendChild(today);
|
||||
today.addEventListener('change', e => {
|
||||
const box = e.target.closest('[data-standing-today]');
|
||||
if (!box) return;
|
||||
instance.params = { ...(instance.params || {}), showToday: box.checked };
|
||||
commit({ resize: true });
|
||||
});
|
||||
await renderSecondCurrencySetting(host, instance, commit);
|
||||
}
|
||||
|
||||
async function renderStandingWidget(host, instance) {
|
||||
const by = standingGroupingOf(instance && instance.params && instance.params.groupBy);
|
||||
const second = secondCurrencyOf(instance);
|
||||
host.innerHTML = `<div class="card standing-card">
|
||||
<div class="flex-between">
|
||||
<div class="t-label">${t('home.standing')}</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 id="standing-body" class="t-body-sm t-muted">…</div>
|
||||
</div>`;
|
||||
try {
|
||||
const s = await currentStanding();
|
||||
const body = host.querySelector('#standing-body');
|
||||
if (s.rows.length === 0) { body.textContent = t('home.standingNone'); return; }
|
||||
|
||||
const toon = standingShowsToday(instance && instance.params);
|
||||
const perGroup = toon ? standingToday(s.rows, by, s.today) : new Map();
|
||||
const lines = groupStanding(s.rows, by).map(g => `<div class="dash-trow stw-acc">
|
||||
<span class="tname"><span class="stw-dot" aria-hidden="true"></span>${standingGroupLabel(g, by)}</span>
|
||||
<span class="stw-today-cell">${todayHtml(perGroup.get(g.key), 'stw-today-row')}</span>
|
||||
<span class="n po-host">${val(fmtBase(g.value))}${originsHtml(g.origins)}</span>
|
||||
</div>`).join('');
|
||||
const vandaag = toon && s.today
|
||||
? `<div class="stw-today">${todayHtml(s.today.total, '', true)}</div>` : '';
|
||||
const tweede = second ? await secondAmount(s.total, second, { decimals: 0 }) : '';
|
||||
|
||||
body.className = '';
|
||||
body.innerHTML = `
|
||||
<div class="sigw-price po-host" style="margin:0.4rem 0 0.1rem">${val(fmtBase(s.total))}${tweede ? `<span class="amount-second">${val(tweede)}</span>` : ''}${originsHtml(s.rows.map(r => ({ origin: r.origin, asset: r.asset })))}</div>
|
||||
${vandaag}
|
||||
<div class="t-muted-cell" style="margin-bottom:0.6rem">${s.rows.every(r => r.asset.id === s.rows[0].asset.id) ? val(fmtUnit(s.totalSats, s.rows[0].asset)) : ''}</div>
|
||||
<div class="dash-tables stw-grid">${lines}</div>`;
|
||||
|
||||
} catch (err) {
|
||||
console.error('[standing-widget]', err);
|
||||
const body = host.querySelector('#standing-body');
|
||||
if (body) body.textContent = '⚠ ' + err.message;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user