mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
fix(invest): mobile chart — fixed height, sane scales, horizontal x labels, compact legend/points, Chinese help
Root cause of the squashed chart: canvas had a fixed height=90 attr and no container, so on narrow viewports Chart.js compressed everything — legend overlapped the plot, x labels tilted 45deg, y ticks degenerated (-1000% scale). Fix: relative h-72/h-80 container + maintainAspectRatio:false, grace 5%, maxTicksLimit, maxRotation:0, smaller points (r2/1.5), compact legend/tooltip, Chinese help text.
This commit is contained in:
+2
-2
@@ -746,8 +746,8 @@ def _line_dataset(label: str, data: list, color: str, dashed: bool = False, widt
|
|||||||
'borderColor': color,
|
'borderColor': color,
|
||||||
'backgroundColor': color,
|
'backgroundColor': color,
|
||||||
'borderWidth': width,
|
'borderWidth': width,
|
||||||
'pointRadius': 4 if not dashed else 3,
|
'pointRadius': 2 if not dashed else 1.5,
|
||||||
'pointHoverRadius': 7 if not dashed else 5,
|
'pointHoverRadius': 4 if not dashed else 3,
|
||||||
'tension': 0.3,
|
'tension': 0.3,
|
||||||
'borderDash': [5, 5] if dashed else [],
|
'borderDash': [5, 5] if dashed else [],
|
||||||
'fill': False,
|
'fill': False,
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
<div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between mb-4">
|
<div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between mb-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-semibold tracking-widest text-stone-500 uppercase">{{ fy_label }} Performance vs Benchmarks</p>
|
<p class="text-xs font-semibold tracking-widest text-stone-500 uppercase">{{ fy_label }} Performance vs Benchmarks</p>
|
||||||
<p id="performanceChartHelp" class="text-xs text-stone-400 mt-1">Toggle between percentage return and dollar-value growth from the baseline date.</p>
|
<p id="performanceChartHelp" class="text-xs text-stone-400 mt-1">切换涨跌幅百分比与金额两种视图。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<div class="inline-flex rounded-lg border border-stone-200 bg-stone-50 p-1 text-xs font-semibold" role="group" aria-label="Chart period">
|
<div class="inline-flex rounded-lg border border-stone-200 bg-stone-50 p-1 text-xs font-semibold" role="group" aria-label="Chart period">
|
||||||
@@ -142,7 +142,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="performanceChart" height="90"></canvas>
|
<div class="relative h-72 md:h-80">
|
||||||
|
<canvas id="performanceChart"></canvas>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -363,14 +365,32 @@
|
|||||||
const unit = currentData().unit;
|
const unit = currentData().unit;
|
||||||
return {
|
return {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
interaction: { mode: 'index', intersect: false },
|
interaction: { mode: 'index', intersect: false },
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { position: 'top', align: 'start', labels: { usePointStyle: true, pointStyle: 'rect', pointStyleWidth: 14, padding: 20, font: { size: 12, weight: '600' } } },
|
legend: {
|
||||||
tooltip: { callbacks: { label: function (ctx) { const v = ctx.parsed.y; if (v === null || v === undefined) return ctx.dataset.label + ': —'; return ctx.dataset.label + ': ' + (unit === 'currency' ? formatCurrency(v) : formatPercent(v)); } } },
|
position: 'top',
|
||||||
|
align: 'start',
|
||||||
|
labels: { usePointStyle: true, pointStyle: 'rect', pointStyleWidth: 10, padding: 10, boxWidth: 10, font: { size: 11, weight: '600' } },
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
position: 'nearest',
|
||||||
|
padding: 8,
|
||||||
|
titleFont: { size: 11 },
|
||||||
|
bodyFont: { size: 10 },
|
||||||
|
callbacks: { label: function (ctx) { const v = ctx.parsed.y; if (v === null || v === undefined) return ctx.dataset.label + ': —'; return ctx.dataset.label + ': ' + (unit === 'currency' ? formatCurrency(v) : formatPercent(v)); } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
y: { ticks: { callback: function (v) { return unit === 'currency' ? formatCurrency(Number(v)) : formatPercent(Number(v)); }, font: { size: 11 } }, grid: { color: '#f5f5f4' } },
|
y: {
|
||||||
x: { grid: { display: false }, ticks: { font: { size: 11 } } },
|
grace: '5%',
|
||||||
|
ticks: { maxTicksLimit: 6, callback: function (v) { return unit === 'currency' ? formatCurrency(Number(v)) : formatPercent(Number(v)); }, font: { size: 10 } },
|
||||||
|
grid: { color: '#f5f5f4' },
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: { display: false },
|
||||||
|
ticks: { autoSkip: true, maxRotation: 0, maxTicksLimit: 8, font: { size: 10 } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -386,10 +406,10 @@
|
|||||||
chart.options = chartOptions();
|
chart.options = chartOptions();
|
||||||
chart.update();
|
chart.update();
|
||||||
if (help) {
|
if (help) {
|
||||||
const baseline = currentData().baseline || 'the baseline date';
|
const baseline = currentData().baseline || '基准日';
|
||||||
help.textContent = currentMode === 'value'
|
help.textContent = currentMode === 'value'
|
||||||
? `Dollar mode: actual total portfolio value vs SPY/QQQ benchmark value from ${baseline}.`
|
? `金额视图:组合总市值 vs SPY/QQQ 基准(自 ${baseline})`
|
||||||
: `Percentage mode: growth/decline since ${baseline}; cash-flow-adjusted metrics are shown in the cards above.`;
|
: `涨跌幅视图:自 ${baseline} 以来的涨跌幅;卡片上方为现金流调整指标`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user