cleanup: stats activity chart — extract 30-day bar inline styles
Continuing the renderer-stats.ts inline-style extraction. The
"Aktivitaet (letzte 30 Tage)" bar chart built each day-column as
a 5-inline-style template:
<div style="flex: 1; display:flex; flex-direction:column;
align-items:center; gap:4px; min-width:0;">
<div style="width: 100%; height: 90px; display:flex;
align-items: flex-end;">
<div style="width:100%; height: 70%;
background: var(--accent, #9146ff);
border-radius: 2px 2px 0 0;" title="...">
<div style="font-size: 9px; color: var(--text-secondary);
white-space: nowrap;">
30 columns rendered per refresh meant ~7.5KB of duplicated inline
style attribute strings in the DOM after every refresh.
Extracted to .stats-day-col + .stats-day-bar-track + .stats-day-bar-
fill + .stats-day-label, plus .stats-activity-row + .stats-activity-
summary for the outer wrappers. Only the per-day height percent
stays inline (it's truly dynamic, per-day data).
Polish riders:
- Bar fill picks up height: 0.3s ease-out transition so the bars
animate up on data refresh instead of snapping
- Hover state shifts the bar from accent to accent-hover so the
hovered day reads as the focus
- Day-label spans get tabular-nums so the "05-12" type strings
align column-to-column
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
162b2845aa
commit
c9a5223eb6
@ -108,7 +108,7 @@ function renderStatsActivity(days: ArchiveStatsDay[]): void {
|
||||
|
||||
const maxCount = days.reduce((m, d) => Math.max(m, d.count), 0);
|
||||
if (maxCount === 0) {
|
||||
applyHtml(container, `<div style="color: var(--text-secondary);">${escapeStatsHtml(UI_TEXT.static.statsActivityEmpty)}</div>`);
|
||||
applyHtml(container, `<div class="form-note">${escapeStatsHtml(UI_TEXT.static.statsActivityEmpty)}</div>`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -118,11 +118,11 @@ function renderStatsActivity(days: ArchiveStatsDay[]): void {
|
||||
const showLabel = idx === 0 || idx === days.length - 1 || idx % 7 === 0;
|
||||
const dayLabel = showLabel ? d.date.slice(5) : '';
|
||||
return `
|
||||
<div style="flex: 1; display:flex; flex-direction:column; align-items:center; gap:4px; min-width:0;">
|
||||
<div style="width: 100%; height: 90px; display:flex; align-items: flex-end;">
|
||||
<div style="width:100%; height: ${heightPct}%; background: var(--accent, #9146ff); border-radius: 2px 2px 0 0;" title="${escapeStatsHtml(tooltip)}"></div>
|
||||
<div class="stats-day-col">
|
||||
<div class="stats-day-bar-track">
|
||||
<div class="stats-day-bar-fill" style="height: ${heightPct}%;" title="${escapeStatsHtml(tooltip)}"></div>
|
||||
</div>
|
||||
<div style="font-size: 9px; color: var(--text-secondary); white-space: nowrap;">${escapeStatsHtml(dayLabel)}</div>
|
||||
<div class="stats-day-label">${escapeStatsHtml(dayLabel)}</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
@ -130,8 +130,8 @@ function renderStatsActivity(days: ArchiveStatsDay[]): void {
|
||||
const totalCount = days.reduce((s, d) => s + d.count, 0);
|
||||
const totalBytes = days.reduce((s, d) => s + d.bytes, 0);
|
||||
applyHtml(container, `
|
||||
<div style="display:flex; gap:2px; align-items: flex-end; padding: 6px 0;">${bars}</div>
|
||||
<div style="font-size: 12px; color: var(--text-secondary); margin-top: 6px;">${escapeStatsHtml(UI_TEXT.static.statsActivitySummary
|
||||
<div class="stats-activity-row">${bars}</div>
|
||||
<div class="stats-activity-summary">${escapeStatsHtml(UI_TEXT.static.statsActivitySummary
|
||||
.replace('{count}', String(totalCount))
|
||||
.replace('{size}', formatBytesForStats(totalBytes)))}</div>
|
||||
`);
|
||||
|
||||
@ -2311,6 +2311,56 @@ select option {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 30-day activity chart — vertical bar per day with optional date
|
||||
label below every 7th column. */
|
||||
.stats-activity-row {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
align-items: flex-end;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.stats-day-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stats-day-bar-track {
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.stats-day-bar-fill {
|
||||
width: 100%;
|
||||
background: var(--accent, #9146ff);
|
||||
border-radius: 2px 2px 0 0;
|
||||
transition: height 0.3s ease-out, background 0.2s;
|
||||
}
|
||||
|
||||
.stats-day-bar-fill:hover {
|
||||
background: var(--accent-hover, #b97aff);
|
||||
}
|
||||
|
||||
.stats-day-label {
|
||||
font-size: 9px;
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.stats-activity-summary {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 6px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Old generic scrollbar rules were dead — superseded by the
|
||||
purple-themed *::-webkit-scrollbar block further down the file.
|
||||
Removed to avoid confusion when someone greps for scrollbar styles. */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user