better colors
This commit is contained in:
parent
b7791e4612
commit
ab3db53817
291
app.js
291
app.js
@ -61,6 +61,9 @@ function transformToSunburst(data) {
|
|||||||
const categories = [];
|
const categories = [];
|
||||||
const categoryMap = {};
|
const categoryMap = {};
|
||||||
|
|
||||||
|
// Store raw transactions for each microcategory to display in details
|
||||||
|
const transactionMap = {};
|
||||||
|
|
||||||
// Predefined colors for categories
|
// Predefined colors for categories
|
||||||
const colors = [
|
const colors = [
|
||||||
'#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
|
'#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
|
||||||
@ -73,16 +76,28 @@ function transformToSunburst(data) {
|
|||||||
const subcategory = item.subcategory || '';
|
const subcategory = item.subcategory || '';
|
||||||
const microcategory = item.microcategory || '';
|
const microcategory = item.microcategory || '';
|
||||||
const amount = Math.abs(parseFloat(item.amount_rub));
|
const amount = Math.abs(parseFloat(item.amount_rub));
|
||||||
|
const transactionKey = `${category}|${subcategory}|${microcategory}`;
|
||||||
|
|
||||||
if (!isNaN(amount)) {
|
if (!isNaN(amount)) {
|
||||||
totalSpending += amount;
|
totalSpending += amount;
|
||||||
|
|
||||||
|
// Save transaction data for detail box
|
||||||
|
if (!transactionMap[transactionKey]) {
|
||||||
|
transactionMap[transactionKey] = [];
|
||||||
|
}
|
||||||
|
transactionMap[transactionKey].push({
|
||||||
|
name: item.simple_name || 'Transaction',
|
||||||
|
value: amount,
|
||||||
|
date: item.date
|
||||||
|
});
|
||||||
|
|
||||||
if (!categoryMap[category] && category !== '') {
|
if (!categoryMap[category] && category !== '') {
|
||||||
categoryMap[category] = {
|
categoryMap[category] = {
|
||||||
name: category,
|
name: category,
|
||||||
value: 0,
|
value: 0,
|
||||||
children: {},
|
children: {},
|
||||||
itemStyle: {}
|
itemStyle: {},
|
||||||
|
transactions: transactionMap[transactionKey] // Store transactions
|
||||||
};
|
};
|
||||||
categories.push(categoryMap[category]);
|
categories.push(categoryMap[category]);
|
||||||
}
|
}
|
||||||
@ -93,7 +108,8 @@ function transformToSunburst(data) {
|
|||||||
name: subcategory,
|
name: subcategory,
|
||||||
value: 0,
|
value: 0,
|
||||||
children: {},
|
children: {},
|
||||||
itemStyle: {}
|
itemStyle: {},
|
||||||
|
transactions: transactionMap[transactionKey] // Store transactions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +118,8 @@ function transformToSunburst(data) {
|
|||||||
categoryMap[category].children[subcategory].children[microcategory] = {
|
categoryMap[category].children[subcategory].children[microcategory] = {
|
||||||
name: microcategory,
|
name: microcategory,
|
||||||
value: 0,
|
value: 0,
|
||||||
itemStyle: {}
|
itemStyle: {},
|
||||||
|
transactions: transactionMap[transactionKey] // Store transactions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,13 +145,15 @@ function transformToSunburst(data) {
|
|||||||
// Assign colors to categories
|
// Assign colors to categories
|
||||||
categories.forEach((category, index) => {
|
categories.forEach((category, index) => {
|
||||||
const colorIndex = index % colors.length;
|
const colorIndex = index % colors.length;
|
||||||
|
const baseColor = colors[colorIndex];
|
||||||
const categoryNode = {
|
const categoryNode = {
|
||||||
name: category.name,
|
name: category.name,
|
||||||
value: category.value,
|
value: category.value,
|
||||||
children: [],
|
children: [],
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: colors[colorIndex]
|
color: baseColor
|
||||||
}
|
},
|
||||||
|
transactions: category.transactions
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get subcategories and sort by value
|
// Get subcategories and sort by value
|
||||||
@ -144,15 +163,22 @@ function transformToSunburst(data) {
|
|||||||
}
|
}
|
||||||
subcategories.sort((a, b) => b.value - a.value);
|
subcategories.sort((a, b) => b.value - a.value);
|
||||||
|
|
||||||
|
// Generate color variations for subcategories based on their size
|
||||||
|
const subcatColors = generateColorGradient(baseColor, subcategories.length || 1);
|
||||||
|
|
||||||
// Process each subcategory
|
// Process each subcategory
|
||||||
subcategories.forEach(subcategory => {
|
subcategories.forEach((subcategory, subIndex) => {
|
||||||
|
// Adjust subcategory color based on its relative size within category
|
||||||
|
const subcatColor = subcatColors[subIndex];
|
||||||
|
|
||||||
const subcategoryNode = {
|
const subcategoryNode = {
|
||||||
name: subcategory.name,
|
name: subcategory.name,
|
||||||
value: subcategory.value,
|
value: subcategory.value,
|
||||||
children: [],
|
children: [],
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: colors[colorIndex]
|
color: subcatColor
|
||||||
}
|
},
|
||||||
|
transactions: subcategory.transactions
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get microcategories and sort by value
|
// Get microcategories and sort by value
|
||||||
@ -162,14 +188,18 @@ function transformToSunburst(data) {
|
|||||||
}
|
}
|
||||||
microcategories.sort((a, b) => b.value - a.value);
|
microcategories.sort((a, b) => b.value - a.value);
|
||||||
|
|
||||||
|
// Generate color variations for microcategories based on their size
|
||||||
|
const microColors = generateColorGradient(subcatColor, microcategories.length || 1);
|
||||||
|
|
||||||
// Add microcategories to subcategory
|
// Add microcategories to subcategory
|
||||||
microcategories.forEach(micro => {
|
microcategories.forEach((micro, microIndex) => {
|
||||||
subcategoryNode.children.push({
|
subcategoryNode.children.push({
|
||||||
name: micro.name,
|
name: micro.name,
|
||||||
value: micro.value,
|
value: micro.value,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: colors[colorIndex]
|
color: microColors[microIndex]
|
||||||
}
|
},
|
||||||
|
transactions: micro.transactions
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -180,8 +210,9 @@ function transformToSunburst(data) {
|
|||||||
name: subcategory.name,
|
name: subcategory.name,
|
||||||
value: subcategory.value,
|
value: subcategory.value,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: colors[colorIndex]
|
color: subcatColor
|
||||||
}
|
},
|
||||||
|
transactions: subcategory.transactions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -240,7 +271,7 @@ function renderChart(data) {
|
|||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
rotate: 'radial',
|
rotate: 'radial',
|
||||||
fontSize: 12,
|
fontSize: 13,
|
||||||
lineHeight: 15,
|
lineHeight: 15,
|
||||||
verticalAlign: 'center',
|
verticalAlign: 'center',
|
||||||
position: 'inside',
|
position: 'inside',
|
||||||
@ -258,9 +289,12 @@ function renderChart(data) {
|
|||||||
r0: '45%',
|
r0: '45%',
|
||||||
r: '70%',
|
r: '70%',
|
||||||
label: {
|
label: {
|
||||||
show: false,
|
show: function(param) {
|
||||||
|
// Show labels for sectors that are at least 5% of the total
|
||||||
|
return param.percent >= 0.05;
|
||||||
|
},
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
align: 'left',
|
align: 'center',
|
||||||
position: 'inside',
|
position: 'inside',
|
||||||
distance: 5,
|
distance: 5,
|
||||||
formatter: function(param) {
|
formatter: function(param) {
|
||||||
@ -522,7 +556,7 @@ function renderChart(data) {
|
|||||||
myChart.setOption(option);
|
myChart.setOption(option);
|
||||||
|
|
||||||
// Set up hover events for the details box
|
// Set up hover events for the details box
|
||||||
setupHoverEvents();
|
setupHoverEvents(sunburstData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a color gradient
|
// Function to generate a color gradient
|
||||||
@ -530,18 +564,37 @@ function generateColorGradient(baseColor, steps) {
|
|||||||
const result = [];
|
const result = [];
|
||||||
const base = tinycolor(baseColor);
|
const base = tinycolor(baseColor);
|
||||||
|
|
||||||
// Generate lighter shades for better contrast
|
// Get the base hue value (0-360)
|
||||||
|
const baseHue = base.toHsl().h;
|
||||||
|
|
||||||
|
// Create a more dramatic gradient based on size
|
||||||
for (let i = 0; i < steps; i++) {
|
for (let i = 0; i < steps; i++) {
|
||||||
// Create various tints and shades based on the position
|
// Calculate percentage position in the sequence (0 to 1)
|
||||||
let color;
|
const position = i / (steps - 1 || 1);
|
||||||
if (i % 3 === 0) {
|
|
||||||
color = base.clone().lighten(15);
|
let color = base.clone();
|
||||||
} else if (i % 3 === 1) {
|
|
||||||
color = base.clone().darken(10);
|
// Modify hue - shift around the color wheel based on size
|
||||||
|
// Smaller items (position closer to 0): shift hue towards cooler colors (-30 degrees)
|
||||||
|
// Larger items (position closer to 1): shift hue towards warmer colors (+30 degrees)
|
||||||
|
const hueShift = 15-(position*15); // Ranges from -30 to +30
|
||||||
|
|
||||||
|
// Apply HSL transformations
|
||||||
|
const hsl = color.toHsl();
|
||||||
|
//hsl.h = (baseHue + hueShift) % 360; // Keep hue within 0-360 range
|
||||||
|
|
||||||
|
// Also adjust saturation and lightness for even more distinction
|
||||||
|
/* if (position < 0.5) {
|
||||||
|
// Smaller items: more saturated, darker
|
||||||
|
hsl.s = Math.min(1, hsl.s * (1.3 - position * 0.6)); // Increase saturation up to 30%
|
||||||
|
hsl.l = Math.max(0.2, hsl.l * (0.85 + position * 0.3)); // Slightly darker
|
||||||
} else {
|
} else {
|
||||||
color = base.clone().saturate(20);
|
// Larger items: slightly less saturated, brighter
|
||||||
}
|
hsl.s = Math.min(1, hsl.s * (0.9 + position * 0.2)); // Slightly reduce saturation
|
||||||
result.push(color.toString());
|
hsl.l = Math.min(0.9, hsl.l * (1.1 + (position - 0.5) * 0.2)); // Brighter
|
||||||
|
}*/
|
||||||
|
|
||||||
|
result.push(tinycolor(hsl).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -600,64 +653,182 @@ window.addEventListener('resize', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add mouseover handler to update details box
|
// Add mouseover handler to update details box
|
||||||
function setupHoverEvents() {
|
function setupHoverEvents(sunburstData) {
|
||||||
const hoverNameElement = document.getElementById('hover-name');
|
|
||||||
const hoverAmountElement = document.getElementById('hover-amount');
|
|
||||||
const topItemsElement = document.getElementById('top-items');
|
const topItemsElement = document.getElementById('top-items');
|
||||||
|
|
||||||
// Add mouseover event listener
|
// Create a container for details header with name and amount
|
||||||
myChart.on('mouseover', function(params) {
|
const detailsHeader = document.getElementById('details-header') || document.createElement('div');
|
||||||
// Only process data nodes, not empty areas
|
detailsHeader.id = 'details-header';
|
||||||
if (params.data) {
|
detailsHeader.className = 'details-header';
|
||||||
// Set the name and amount
|
|
||||||
hoverNameElement.textContent = params.name;
|
|
||||||
hoverAmountElement.textContent = params.value.toLocaleString() + ' RUB';
|
|
||||||
|
|
||||||
|
if (!document.getElementById('details-header')) {
|
||||||
|
const detailsBox = document.querySelector('.details-box');
|
||||||
|
detailsBox.insertBefore(detailsHeader, detailsBox.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to display items in the details box
|
||||||
|
function displayDetailsItems(items, parentValue) {
|
||||||
// Clear previous top items
|
// Clear previous top items
|
||||||
topItemsElement.innerHTML = '';
|
topItemsElement.innerHTML = '';
|
||||||
|
|
||||||
// Find top items if there are children
|
// If we have items to show
|
||||||
if (params.data.children && params.data.children.length > 0) {
|
if (items.length > 0) {
|
||||||
// Sort children by value
|
// Create elements for each item
|
||||||
const sortedChildren = [...params.data.children].sort((a, b) => b.value - a.value);
|
items.forEach(item => {
|
||||||
|
|
||||||
// Display top 10 or fewer
|
|
||||||
const topChildren = sortedChildren.slice(0, 10);
|
|
||||||
|
|
||||||
// Create elements for each top item
|
|
||||||
topChildren.forEach(child => {
|
|
||||||
const itemDiv = document.createElement('div');
|
const itemDiv = document.createElement('div');
|
||||||
itemDiv.className = 'top-item';
|
itemDiv.className = 'top-item';
|
||||||
|
|
||||||
const nameSpan = document.createElement('span');
|
const nameSpan = document.createElement('span');
|
||||||
nameSpan.className = 'top-item-name';
|
nameSpan.className = 'top-item-name';
|
||||||
nameSpan.textContent = child.name;
|
|
||||||
|
// Add colored circle
|
||||||
|
const colorCircle = document.createElement('span');
|
||||||
|
colorCircle.className = 'color-circle';
|
||||||
|
|
||||||
|
// Use the item's color if available, otherwise use a default
|
||||||
|
if (item.itemStyle && item.itemStyle.color) {
|
||||||
|
colorCircle.style.backgroundColor = item.itemStyle.color;
|
||||||
|
} else if (item.color) {
|
||||||
|
colorCircle.style.backgroundColor = item.color;
|
||||||
|
} else {
|
||||||
|
colorCircle.style.backgroundColor = '#cccccc';
|
||||||
|
}
|
||||||
|
|
||||||
|
nameSpan.appendChild(colorCircle);
|
||||||
|
nameSpan.appendChild(document.createTextNode(item.name));
|
||||||
itemDiv.appendChild(nameSpan);
|
itemDiv.appendChild(nameSpan);
|
||||||
|
|
||||||
const amountSpan = document.createElement('span');
|
const amountSpan = document.createElement('span');
|
||||||
amountSpan.className = 'top-item-amount';
|
amountSpan.className = 'top-item-amount';
|
||||||
amountSpan.textContent = child.value.toLocaleString() + ' ₽';
|
amountSpan.textContent = item.value.toLocaleString() + ' ₽';
|
||||||
itemDiv.appendChild(amountSpan);
|
|
||||||
|
|
||||||
// Add percentage
|
// Add percentage if we have a parent value
|
||||||
const percentage = ((child.value / params.value) * 100).toFixed(1);
|
if (parentValue) {
|
||||||
|
const percentage = ((item.value / parentValue) * 100).toFixed(1);
|
||||||
amountSpan.textContent += ` (${percentage}%)`;
|
amountSpan.textContent += ` (${percentage}%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemDiv.appendChild(amountSpan);
|
||||||
topItemsElement.appendChild(itemDiv);
|
topItemsElement.appendChild(itemDiv);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// No children, show a message
|
// No items to show
|
||||||
topItemsElement.innerHTML = '<div>No subcategories available</div>';
|
topItemsElement.innerHTML = '<div>No details available</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show the default view with top categories
|
||||||
|
function showDefaultView() {
|
||||||
|
detailsHeader.innerHTML = `
|
||||||
|
<span class="hover-name">
|
||||||
|
<span class="color-circle" style="background-color: #ffffff;"></span>
|
||||||
|
Total
|
||||||
|
</span>
|
||||||
|
<span class="hover-amount">${sunburstData.total.toLocaleString()} ₽</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Show top categories as default items
|
||||||
|
displayDetailsItems(sunburstData.data, sunburstData.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show default view initially
|
||||||
|
showDefaultView();
|
||||||
|
|
||||||
|
// Track if we're inside a section to handle sector exit properly
|
||||||
|
let isInsideSection = false;
|
||||||
|
|
||||||
|
// Variables to track the circular boundary
|
||||||
|
const chartCenter = option.series.center;
|
||||||
|
const chartCenterX = chartDom.offsetWidth * (parseFloat(chartCenter[0]) / 100);
|
||||||
|
const chartCenterY = chartDom.offsetHeight * (parseFloat(chartCenter[1]) / 100);
|
||||||
|
const chartRadius = Math.min(chartDom.offsetWidth, chartDom.offsetHeight) *
|
||||||
|
(parseFloat(option.series.radius[1]) / 100);
|
||||||
|
|
||||||
|
// Check if point is inside the sunburst chart circle
|
||||||
|
function isInsideChart(x, y) {
|
||||||
|
const dx = x - chartCenterX;
|
||||||
|
const dy = y - chartCenterY;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
return distance <= chartRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add general mousemove event listener to detect when outside chart circle
|
||||||
|
chartDom.addEventListener('mousemove', function(e) {
|
||||||
|
// Get mouse position relative to chart container
|
||||||
|
const rect = chartDom.getBoundingClientRect();
|
||||||
|
const mouseX = e.clientX - rect.left;
|
||||||
|
const mouseY = e.clientY - rect.top;
|
||||||
|
|
||||||
|
// If not inside the chart circle and we were inside a section, show default view
|
||||||
|
if (!isInsideChart(mouseX, mouseY) && isInsideSection) {
|
||||||
|
isInsideSection = false;
|
||||||
|
showDefaultView();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset on mouseout from the chart
|
// Add mouseover event listener for sectors
|
||||||
myChart.on('mouseout', function(params) {
|
myChart.on('mouseover', function(params) {
|
||||||
if (!params.data) {
|
// Only process data nodes, not empty areas
|
||||||
hoverNameElement.textContent = 'Hover over a segment to see details';
|
if (params.data) {
|
||||||
hoverAmountElement.textContent = '';
|
isInsideSection = true;
|
||||||
topItemsElement.innerHTML = '';
|
|
||||||
|
// Set up the details header with name and amount
|
||||||
|
const itemColor = params.color || (params.data.itemStyle ? params.data.itemStyle.color : '#cccccc');
|
||||||
|
|
||||||
|
detailsHeader.innerHTML = `
|
||||||
|
<span class="hover-name">
|
||||||
|
<span class="color-circle" style="background-color: ${itemColor};"></span>
|
||||||
|
${params.name}
|
||||||
|
</span>
|
||||||
|
<span class="hover-amount">${params.value.toLocaleString()} ₽</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Number of items to show in the details box
|
||||||
|
const MAX_DETAIL_ITEMS = 5;
|
||||||
|
let itemsToShow = [];
|
||||||
|
|
||||||
|
// Check if the node has children (subcategories or microcategories)
|
||||||
|
if (params.data.children && params.data.children.length > 0) {
|
||||||
|
// Sort children by value
|
||||||
|
const sortedChildren = [...params.data.children].sort((a, b) => b.value - a.value);
|
||||||
|
itemsToShow = sortedChildren.slice(0, MAX_DETAIL_ITEMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have transaction data and not enough children, fill with transactions
|
||||||
|
if (itemsToShow.length < MAX_DETAIL_ITEMS && params.data.transactions) {
|
||||||
|
// Sort transactions by value
|
||||||
|
const sortedTransactions = [...params.data.transactions].sort((a, b) => b.value - a.value);
|
||||||
|
|
||||||
|
// Add color to transactions
|
||||||
|
const coloredTransactions = sortedTransactions.map(t => {
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
color: params.color || (params.data.itemStyle ? params.data.itemStyle.color : '#cccccc')
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Take only what we need to reach MAX_DETAIL_ITEMS
|
||||||
|
const neededTransactions = coloredTransactions.slice(0, MAX_DETAIL_ITEMS - itemsToShow.length);
|
||||||
|
itemsToShow = [...itemsToShow, ...neededTransactions];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the items
|
||||||
|
displayDetailsItems(itemsToShow, params.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// When mouse leaves a section but is still within the chart, we'll handle it with mousemove
|
||||||
|
myChart.on('mouseout', function(params) {
|
||||||
|
if (params.data) {
|
||||||
|
isInsideSection = false;
|
||||||
|
// Reset details immediately when leaving a section
|
||||||
|
showDefaultView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add back the downplay event handler - this is triggered when sections lose emphasis
|
||||||
|
myChart.on('downplay', function(params) {
|
||||||
|
// Reset to default view when a section is no longer emphasized
|
||||||
|
showDefaultView();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
@ -19,10 +19,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<div id="chart-container"></div>
|
<div id="chart-container"></div>
|
||||||
<div id="details-box">
|
<div id="details-box" class="details-box">
|
||||||
<h3>Details</h3>
|
<h3>Details</h3>
|
||||||
<div id="hover-name">Hover over a segment to see details</div>
|
<div id="details-header" class="details-header">
|
||||||
<div id="hover-amount"></div>
|
<span class="hover-name">Hover over a segment to see details</span>
|
||||||
|
<span class="hover-amount"></span>
|
||||||
|
</div>
|
||||||
<h4>Top Items:</h4>
|
<h4>Top Items:</h4>
|
||||||
<div id="top-items"></div>
|
<div id="top-items"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
44
styles.css
44
styles.css
@ -79,17 +79,41 @@ body {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#hover-name {
|
.details-header {
|
||||||
font-weight: bold;
|
display: flex;
|
||||||
margin-bottom: 5px;
|
justify-content: space-between;
|
||||||
color: #333;
|
align-items: center;
|
||||||
word-break: break-word;
|
margin-bottom: 15px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
#hover-amount {
|
.hover-name {
|
||||||
font-size: 18px;
|
font-weight: bold;
|
||||||
margin-bottom: 10px;
|
color: #333;
|
||||||
|
word-break: break-word;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-amount {
|
||||||
|
font-size: 16px;
|
||||||
color: #0066cc;
|
color: #0066cc;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: left;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-circle {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#top-items {
|
#top-items {
|
||||||
@ -112,9 +136,13 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-item-amount {
|
.top-item-amount {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
min-width: 100px;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user