Improve center label and details box styling

- Move center label from ECharts canvas to HTML overlay for better font rendering
- Style ₽ symbol separately with lighter color (#888) and slightly smaller size (20px)
- Use tighter line height (1.0) for center label
- Remove "Детали" heading from details box, use total as header
- Remove top border from details header
- Increase header font sizes to 20px
- Add more whitespace above "Top Items:"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Anton Volnuhin 2026-02-01 15:02:31 +03:00
parent fbbf94c3c4
commit c441d5979f
3 changed files with 77 additions and 42 deletions

55
app.js
View File

@ -1,10 +1,21 @@
// Format RUB amount: no decimals, thin space (U+2009) as thousands separator
// Format RUB amount: no decimals, narrow no-break space as thousands separator
function formatRUB(amount) {
return Math.round(amount)
.toLocaleString('ru-RU')
.replace(/\u00a0/g, '\u202F');
}
// Update HTML center label (month in gray, category optional, amount in black)
function updateCenterLabel(month, amount, category = null) {
const labelEl = document.getElementById('center-label');
if (!labelEl) return;
labelEl.querySelector('.center-month').textContent = month;
labelEl.querySelector('.center-category').textContent = category || '';
labelEl.querySelector('.center-amount-num').textContent = formatRUB(amount);
labelEl.querySelector('.center-rub').textContent = '\u202F₽';
}
// Initialize the chart
const chartDom = document.getElementById('chart-container');
const myChart = echarts.init(chartDom);
@ -50,13 +61,8 @@ function navigateToHistoryState(state) {
const russianMonth = getRussianMonthName(document.getElementById('month-select').value);
option.series.data = state.data;
if (state.contextName) {
option.graphic.elements[0].style.text = `${russianMonth}\n${state.contextName}\n${formatRUB(state.total)}\u202F₽`;
} else {
option.graphic.elements[0].style.text = russianMonth + '\n' + formatRUB(state.total) + '\u202F₽';
}
myChart.setOption(option, { replaceMerge: ['series'] });
updateCenterLabel(russianMonth, state.total, state.contextName);
setupHoverEvents({ total: state.total, data: state.data }, state.contextName);
// Restore drill path and update mini-charts
@ -705,22 +711,7 @@ function renderChart(data) {
}
},
graphic: {
elements: [
{
type: 'text',
left: 'center',
top: 'middle',
z: 100,
style: {
text: russianMonth + '\n' + formatRUB(sunburstData.total) + '\u202F₽',
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
fontWeight: 'bold',
fontSize: 18,
textAlign: 'left',
fill: '#000'
}
}
]
elements: [] // Center label is now HTML overlay
}
};
@ -914,9 +905,8 @@ function renderChart(data) {
// Update the center text to show the drilled-down category
const russianMonth = getRussianMonthName(document.getElementById('month-select').value);
option.graphic.elements[0].style.text = `${russianMonth}\n${params.name}\n${formatRUB(params.value)}\u202F₽`;
myChart.setOption(option, { replaceMerge: ['series'] });
updateCenterLabel(russianMonth, params.value, params.name);
// Update hover events with the new data structure, passing the drilled-down name
setupHoverEvents({ total: params.value, data: newData }, params.name);
@ -928,6 +918,9 @@ function renderChart(data) {
myChart.setOption(option);
// Update HTML center label
updateCenterLabel(russianMonth, sunburstData.total);
// Add click handler for the center to go back in history
const zr = myChart.getZr();
zr.on('click', function(params) {
@ -1685,11 +1678,6 @@ async function selectMonth(index) {
// Update the total amount in the center text
const russianMonth = getRussianMonthName(month);
if (targetName) {
option.graphic.elements[0].style.text = `${russianMonth}\n${targetName}\n${formatRUB(targetTotal)}\u202F₽`;
} else {
option.graphic.elements[0].style.text = russianMonth + '\n' + formatRUB(targetTotal) + '\u202F₽';
}
myChart.setOption({
series: [{
@ -1698,13 +1686,14 @@ async function selectMonth(index) {
layoutAnimation: true,
animationDuration: 500,
animationEasing: 'cubicInOut'
}],
graphic: option.graphic
}]
}, {
lazyUpdate: false,
silent: false
});
updateCenterLabel(russianMonth, targetTotal, targetName);
// Update hover events
setupHoverEvents({ total: targetTotal, data: targetData }, targetName);
@ -1780,8 +1769,6 @@ function adjustChartSize() {
// Update chart center position
option.series.center = [`${centerPosition}%`, '50%'];
option.graphic.elements[0].left = 'center';
option.graphic.elements[0].top = 'middle';
myChart.setOption(option);
}

View File

@ -21,6 +21,11 @@
</div>
<div class="content-wrapper">
<div id="chart-container"></div>
<div id="center-label" class="center-label">
<div class="center-month"></div>
<div class="center-category"></div>
<div class="center-amount"><span class="center-amount-num"></span><span class="center-rub"></span></div>
</div>
<button id="chart-eye-btn" class="chart-eye-btn" title="View transactions">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
@ -28,7 +33,6 @@
</svg>
</button>
<div id="details-box" class="details-box">
<h3>Детали</h3>
<div id="details-header" class="details-header">
<span class="hover-name">Hover over a segment to see details</span>
<span class="hover-amount"></span>

View File

@ -140,6 +140,51 @@ body {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
/* Center label overlay */
.center-label {
position: absolute;
left: 35%; /* Half of chart-container width (70% / 2) */
top: 50%;
transform: translate(-50%, -50%);
text-align: left;
pointer-events: none;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro", "Segoe UI", system-ui, sans-serif;
z-index: 10;
}
.center-month {
font-size: 18px;
font-weight: 400;
color: #666;
line-height: 1.0;
}
.center-category {
font-size: 18px;
font-weight: 600;
color: #333;
line-height: 1.0;
}
.center-category:empty {
display: none;
}
.center-amount {
font-size: 22px;
font-weight: 700;
line-height: 1.0;
}
.center-amount-num {
color: #000;
}
.center-rub {
color: #888;
font-size: 20px;
}
/* Override ECharts default pointer cursor - only eye button should have pointer */
#chart-container canvas {
cursor: default !important;
@ -174,7 +219,7 @@ body {
}
#details-box h4 {
margin: 10px 0 5px;
margin: 18px 0 5px;
color: #555;
font-size: 14px;
}
@ -186,7 +231,6 @@ body {
margin-bottom: 20px;
padding: 10px 0;
border-bottom: 1px solid #eee;
border-top: 1px solid #eee;
}
.hover-name {
@ -196,11 +240,11 @@ body {
flex: 1;
display: flex;
align-items: center;
font-size: 16px;
font-size: 20px;
}
.hover-amount {
font-size: 18px;
font-size: 20px;
color: #0066cc;
font-weight: bold;
margin-left: 10px;