Skip to content

Commit 3ca406d

Browse files
authored
Bugfix/scrollable legends (#73)
* style: enhance chart layout and add custom scrollable legend for better data visualization * chore: bump version to 0.6.4
1 parent 858f530 commit 3ca406d

2 files changed

Lines changed: 131 additions & 50 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "simple-coding-time-tracker",
33
"displayName": "Simple Coding Time Tracker",
44
"description": "Track and visualize your coding time across projects",
5-
"version": "0.6.3",
5+
"version": "0.6.4",
66
"publisher": "noorashuvo",
77
"license": "MIT",
88
"icon": "icon-sctt.png",

src/summaryView.ts

Lines changed: 130 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -789,10 +789,58 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
789789
position: relative;
790790
height: 300px;
791791
width: 100%;
792+
display: flex;
793+
align-items: center;
792794
}
793795
.search-results-chart {
794796
height: 400px;
795797
}
798+
.search-results-chart .chart-wrapper {
799+
height: 350px;
800+
}
801+
.chart-canvas {
802+
flex: 1;
803+
max-width: 60%;
804+
}
805+
.custom-legend {
806+
max-height: 300px;
807+
overflow-y: auto;
808+
overflow-x: hidden;
809+
padding-right: 5px;
810+
padding-left: 10px;
811+
}
812+
.search-results-chart .custom-legend {
813+
max-height: 350px;
814+
}
815+
.custom-legend::-webkit-scrollbar {
816+
width: 6px;
817+
}
818+
.custom-legend::-webkit-scrollbar-track {
819+
background: var(--vscode-scrollbarSlider-background);
820+
border-radius: 3px;
821+
}
822+
.custom-legend::-webkit-scrollbar-thumb {
823+
background: var(--vscode-scrollbarSlider-hoverBackground);
824+
border-radius: 3px;
825+
}
826+
.custom-legend::-webkit-scrollbar-thumb:hover {
827+
background: var(--vscode-scrollbarSlider-activeBackground);
828+
}
829+
.legend-item {
830+
display: flex;
831+
align-items: center;
832+
margin-bottom: 8px;
833+
font-size: 12px;
834+
font-weight: 500;
835+
color: var(--vscode-foreground);
836+
}
837+
.legend-color {
838+
width: 12px;
839+
height: 12px;
840+
border-radius: 50%;
841+
margin-right: 8px;
842+
border: 1px solid var(--vscode-panel-border);
843+
}
796844
</style>
797845
</head>
798846
<body>
@@ -913,7 +961,9 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
913961
<div class="chart-container search-results-chart" style="display: none;">
914962
<div class="chart-title">Search Results</div>
915963
<div class="chart-wrapper">
916-
<canvas id="searchChart"></canvas>
964+
<div class="chart-canvas">
965+
<canvas id="searchChart"></canvas>
966+
</div>
917967
</div>
918968
</div>
919969
</div>
@@ -2122,7 +2172,9 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
21222172
<div class="chart-container search-results-chart">
21232173
<div class="chart-title">Project Distribution</div>
21242174
<div class="chart-wrapper">
2125-
<canvas id="searchChart"></canvas>
2175+
<div class="chart-canvas">
2176+
<canvas id="searchChart"></canvas>
2177+
</div>
21262178
</div>
21272179
</div>
21282180
</div>
@@ -2232,7 +2284,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
22322284
const allProjectData = Object.entries(data.projectSummary)
22332285
.sort((a, b) => b[1] - a[1]);
22342286
2235-
new Chart(searchCtx, {
2287+
const searchChart = new Chart(searchCtx, {
22362288
type: 'pie',
22372289
data: {
22382290
labels: allProjectData.map(([project]) => project),
@@ -2242,18 +2294,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
22422294
hours: Math.floor(minutes / 60),
22432295
mins: Math.round(minutes % 60)
22442296
})),
2245-
backgroundColor: [
2246-
'rgba(255, 99, 132, 0.7)', // Red
2247-
'rgba(54, 162, 235, 0.7)', // Blue
2248-
'rgba(255, 206, 86, 0.7)', // Yellow
2249-
'rgba(75, 192, 192, 0.7)', // Teal
2250-
'rgba(153, 102, 255, 0.7)', // Purple
2251-
'rgba(255, 159, 64, 0.7)', // Orange
2252-
'rgba(199, 199, 199, 0.7)', // Gray
2253-
'rgba(83, 102, 255, 0.7)', // Indigo
2254-
'rgba(40, 167, 69, 0.7)', // Green
2255-
'rgba(220, 53, 69, 0.7)' // Dark Red
2256-
],
2297+
backgroundColor: generateChartColors(allProjectData.length),
22572298
borderColor: chartColors.background,
22582299
borderWidth: 1
22592300
}]
@@ -2263,16 +2304,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
22632304
maintainAspectRatio: false,
22642305
plugins: {
22652306
legend: {
2266-
position: 'right',
2267-
labels: {
2268-
color: chartColors.text,
2269-
font: {
2270-
size: 12,
2271-
weight: '500'
2272-
},
2273-
padding: 20,
2274-
usePointStyle: true
2275-
}
2307+
display: false
22762308
},
22772309
tooltip: {
22782310
backgroundColor: isDarkTheme ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
@@ -2291,6 +2323,9 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
22912323
}
22922324
}
22932325
});
2326+
2327+
// Create custom scrollable legend
2328+
createCustomLegend(searchChart, allProjectData, chartColors);
22942329
}
22952330
22962331
function displaySearchResult(entries) {
@@ -2358,7 +2393,9 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
23582393
<div class="chart-container">
23592394
<div class="chart-title">Project Distribution (Total Time: \${formatTime(totalTime)})</div>
23602395
<div class="chart-wrapper">
2361-
<canvas id="searchChart"></canvas>
2396+
<div class="chart-canvas">
2397+
<canvas id="searchChart"></canvas>
2398+
</div>
23622399
</div>
23632400
</div>
23642401
\`;
@@ -2481,7 +2518,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
24812518
const searchChartData = Object.entries(projectData)
24822519
.sort((a, b) => b[1] - a[1]);
24832520
2484-
new Chart(searchCtx, {
2521+
const searchChart2 = new Chart(searchCtx, {
24852522
type: 'pie',
24862523
data: {
24872524
labels: searchChartData.map(([project]) => project),
@@ -2491,18 +2528,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
24912528
hours: Math.floor(minutes / 60),
24922529
mins: Math.round(minutes % 60)
24932530
})),
2494-
backgroundColor: [
2495-
'rgba(255, 99, 132, 0.7)', // Red
2496-
'rgba(54, 162, 235, 0.7)', // Blue
2497-
'rgba(255, 206, 86, 0.7)', // Yellow
2498-
'rgba(75, 192, 192, 0.7)', // Teal
2499-
'rgba(153, 102, 255, 0.7)', // Purple
2500-
'rgba(255, 159, 64, 0.7)', // Orange
2501-
'rgba(199, 199, 199, 0.7)', // Gray
2502-
'rgba(83, 102, 255, 0.7)', // Indigo
2503-
'rgba(40, 167, 69, 0.7)', // Green
2504-
'rgba(220, 53, 69, 0.7)' // Dark Red
2505-
],
2531+
backgroundColor: generateChartColors(searchChartData.length),
25062532
borderColor: chartColors.background,
25072533
borderWidth: 1
25082534
}]
@@ -2512,16 +2538,7 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
25122538
maintainAspectRatio: false,
25132539
plugins: {
25142540
legend: {
2515-
position: 'right',
2516-
labels: {
2517-
color: chartColors.text,
2518-
font: {
2519-
size: 12,
2520-
weight: '500'
2521-
},
2522-
padding: 20,
2523-
usePointStyle: true
2524-
}
2541+
display: false
25252542
},
25262543
tooltip: {
25272544
backgroundColor: isDarkTheme ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
@@ -2540,6 +2557,70 @@ export class SummaryViewProvider implements vscode.WebviewViewProvider {
25402557
}
25412558
}
25422559
});
2560+
2561+
// Create custom scrollable legend
2562+
createCustomLegend(searchChart2, searchChartData, chartColors);
2563+
}
2564+
2565+
function generateChartColors(count) {
2566+
const baseColors = [
2567+
'rgba(255, 99, 132, 0.7)', // Red
2568+
'rgba(54, 162, 235, 0.7)', // Blue
2569+
'rgba(255, 206, 86, 0.7)', // Yellow
2570+
'rgba(75, 192, 192, 0.7)', // Teal
2571+
'rgba(153, 102, 255, 0.7)', // Purple
2572+
'rgba(255, 159, 64, 0.7)', // Orange
2573+
'rgba(199, 199, 199, 0.7)', // Gray
2574+
'rgba(83, 102, 255, 0.7)', // Indigo
2575+
'rgba(40, 167, 69, 0.7)', // Green
2576+
'rgba(220, 53, 69, 0.7)' // Dark Red
2577+
];
2578+
2579+
const colors = [];
2580+
for (let i = 0; i < count; i++) {
2581+
if (i < baseColors.length) {
2582+
colors.push(baseColors[i]);
2583+
} else {
2584+
// Generate additional colors using HSL
2585+
const hue = (i * 137.5) % 360; // Golden angle approximation
2586+
const saturation = 70;
2587+
const lightness = 50;
2588+
colors.push(\`hsla(\${hue}, \${saturation}%, \${lightness}%, 0.7)\`);
2589+
}
2590+
}
2591+
return colors;
2592+
}
2593+
2594+
function createCustomLegend(chart, projectData, chartColors) {
2595+
// Find the chart wrapper (parent of chart-canvas)
2596+
const chartWrapper = chart.canvas.parentElement.parentElement;
2597+
2598+
// Create legend container
2599+
const legendContainer = document.createElement('div');
2600+
legendContainer.className = 'custom-legend';
2601+
2602+
// Create legend items
2603+
projectData.forEach(([project, minutes], index) => {
2604+
const hours = Math.floor(minutes / 60);
2605+
const mins = Math.round(minutes % 60);
2606+
2607+
const legendItem = document.createElement('div');
2608+
legendItem.className = 'legend-item';
2609+
2610+
const colorDot = document.createElement('div');
2611+
colorDot.className = 'legend-color';
2612+
colorDot.style.backgroundColor = chart.data.datasets[0].backgroundColor[index];
2613+
2614+
const label = document.createElement('span');
2615+
label.textContent = \`\${project}: \${hours}h \${mins}m\`;
2616+
2617+
legendItem.appendChild(colorDot);
2618+
legendItem.appendChild(label);
2619+
legendContainer.appendChild(legendItem);
2620+
});
2621+
2622+
// Insert legend into the chart wrapper
2623+
chartWrapper.appendChild(legendContainer);
25432624
}
25442625
25452626
function formatTime(minutes) {

0 commit comments

Comments
 (0)