diff --git a/website_example/index.html b/website_example/index.html
index dc18575..f2f37eb 100644
--- a/website_example/index.html
+++ b/website_example/index.html
@@ -10,6 +10,7 @@
+
diff --git a/website_example/js/common.js b/website_example/js/common.js
index a5804c2..5eab3a3 100644
--- a/website_example/js/common.js
+++ b/website_example/js/common.js
@@ -1664,21 +1664,155 @@ function workerstats_FetchAddressStats(longpoll, stats, api, xhrAddressPoll){
* Charts
**/
+let workerChartInstances = {};
+let homeChartInstances = {};
+
+function destroyApexChart(store, key) {
+ if (store[key]) {
+ try {
+ store[key].destroy();
+ } catch (e) {}
+ delete store[key];
+ }
+}
+
+function destroyApexChartsByPrefix(store, prefix) {
+ Object.keys(store).forEach(function(key) {
+ if (key.indexOf(prefix) === 0) {
+ destroyApexChart(store, key);
+ }
+ });
+}
+
+function createModernAreaChart(containerSelector, chartKey, categories, values, options) {
+ const chartContainer = document.querySelector(containerSelector);
+ if (!chartContainer || typeof ApexCharts === 'undefined') {
+ return;
+ }
+
+ destroyApexChart(options.store, chartKey);
+ chartContainer.innerHTML = '';
+
+ const chart = new ApexCharts(chartContainer, {
+ chart: {
+ type: 'area',
+ height: options.height || 220,
+ toolbar: { show: false },
+ zoom: { enabled: false },
+ foreColor: '#9db7b2',
+ animations: {
+ enabled: true,
+ easing: 'easeinout',
+ speed: 350
+ }
+ },
+ series: [{
+ name: options.label,
+ data: values
+ }],
+ xaxis: {
+ categories: categories,
+ labels: {
+ rotate: 0,
+ trim: true,
+ style: {
+ colors: '#59727a',
+ fontSize: '11px'
+ }
+ },
+ axisBorder: { show: false },
+ axisTicks: { show: false }
+ },
+ yaxis: {
+ labels: {
+ style: {
+ colors: '#59727a',
+ fontSize: '11px'
+ },
+ formatter: options.yFormatter
+ }
+ },
+ dataLabels: { enabled: false },
+ stroke: {
+ curve: 'smooth',
+ width: 3
+ },
+ fill: {
+ type: 'gradient',
+ gradient: {
+ shadeIntensity: 1,
+ opacityFrom: 0.34,
+ opacityTo: 0.04,
+ stops: [0, 95, 100]
+ }
+ },
+ colors: [options.color],
+ grid: {
+ borderColor: 'rgba(136, 255, 225, 0.08)',
+ strokeDashArray: 4
+ },
+ tooltip: {
+ theme: 'dark',
+ x: {
+ formatter: function(value, ctx) {
+ return categories[ctx.dataPointIndex];
+ }
+ },
+ y: {
+ formatter: options.tooltipFormatter || options.yFormatter
+ }
+ },
+ legend: { show: false }
+ });
+
+ chart.render();
+ options.store[chartKey] = chart;
+}
+
// Create charts
function workerstats_CreateCharts(data, stats) {
- if (data.hasOwnProperty("charts")) {
- var graphData = {
- hashrate: workerstats_GetGraphData(stats, data.charts.hashrate),
- payments: workerstats_GetGraphData(stats, data.charts.payments, true)
- };
+ if (!data.hasOwnProperty("charts")) {
+ return;
+ }
- for(var graphType in graphData) {
- if(graphData[graphType].values.length > 1) {
- var settings = jQuery.extend({}, graphSettings);
- settings.tooltipValueLookups = {names: graphData[graphType].names};
- var $chart = $(`[data-chart=user_${graphType}_${stats.config.coin}]`).show().find('.chart');
- $chart.sparkline(graphData[graphType].values, settings);
+ var graphData = {
+ hashrate: workerstats_GetGraphData(stats, data.charts.hashrate),
+ payments: workerstats_GetGraphData(stats, data.charts.payments, true)
+ };
+
+ var chartMeta = {
+ hashrate: {
+ label: 'Hashrate',
+ color: '#4cc8ff',
+ yFormatter: function(value) {
+ return getReadableHashRateString(value || 0) + '/s';
}
+ },
+ payments: {
+ label: 'Payments',
+ color: '#42f5c8',
+ yFormatter: function(value) {
+ return Number(value || 0).toFixed(4) + ' ' + stats.config.symbol;
+ }
+ }
+ };
+
+ for (var graphType in graphData) {
+ var graph = graphData[graphType];
+ var chartKey = stats.config.coin + '_' + graphType;
+ var selector = `[data-chart=user_${graphType}_${stats.config.coin}] .chart`;
+ if (graph.values.length > 1) {
+ $(`[data-chart=user_${graphType}_${stats.config.coin}]`).show();
+ createModernAreaChart(selector, chartKey, graph.names, graph.values, {
+ store: workerChartInstances,
+ label: chartMeta[graphType].label,
+ color: chartMeta[graphType].color,
+ yFormatter: chartMeta[graphType].yFormatter,
+ height: 180
+ });
+ } else {
+ destroyApexChart(workerChartInstances, chartKey);
+ $(`[data-chart=user_${graphType}_${stats.config.coin}]`).hide();
}
}
}
@@ -1934,39 +2068,65 @@ workerstats methods
***************************************************************
*/
-let home_GraphSettings = {
- type: 'line',
- width: '100%',
- height: '140',
- lineColor: '#03a9f4',
- fillColor: 'rgba(3, 169, 244, .4)',
- spotColor: null,
- minSpotColor: null,
- maxSpotColor: null,
- highlightLineColor: '#236d26',
- spotRadius: 3,
- chartRangeMin: 0,
- drawNormalOnTop: false,
- tooltipFormat: '{{y}} – {{offset:names}}'
-};
-
function home_CreateCharts(data) {
- if (data.hasOwnProperty("charts")) {
- var graphData = {
- hashrate: home_GetGraphData(data.charts.hashrate),
- diff: home_GetGraphData(data.charts.difficulty),
- miners: home_GetGraphData(data.charts.miners),
- workers: home_GetGraphData(data.charts.workers)
- };
+ if (!data.hasOwnProperty("charts")) {
+ return;
+ }
- for(var graphType in graphData) {
- if(graphData[graphType].values.length > 1) {
- var settings = jQuery.extend({}, home_GraphSettings);
- settings.tooltipValueLookups = {names: graphData[graphType].names};
- var $chart = $('[data-chart=' + graphType + '] .chart');
- $chart.closest('.poolChart').show();
- $chart.sparkline(graphData[graphType].values, settings);
+ var graphData = {
+ hashrate: home_GetGraphData(data.charts.hashrate),
+ diff: home_GetGraphData(data.charts.difficulty),
+ miners: home_GetGraphData(data.charts.miners),
+ workers: home_GetGraphData(data.charts.workers)
+ };
+
+ var chartMeta = {
+ hashrate: {
+ label: 'Hashrate',
+ color: '#4cc8ff',
+ yFormatter: function(value) {
+ return getReadableHashRateString(value || 0) + '/s';
}
+ },
+ diff: {
+ label: 'Difficulty',
+ color: '#d4ff69',
+ yFormatter: function(value) {
+ return formatNumber(Math.round(value || 0).toString(), ' ');
+ }
+ },
+ miners: {
+ label: 'Miners',
+ color: '#42f5c8',
+ yFormatter: function(value) {
+ return Math.round(value || 0).toString();
+ }
+ },
+ workers: {
+ label: 'Workers',
+ color: '#ff9eb8',
+ yFormatter: function(value) {
+ return Math.round(value || 0).toString();
+ }
+ }
+ };
+
+ for (var graphType in graphData) {
+ var graph = graphData[graphType];
+ if (graph.values.length > 1) {
+ var selector = '[data-chart=' + graphType + '] .chart';
+ var chartKey = 'home_' + graphType;
+ $(selector).closest('.poolChart').show();
+ createModernAreaChart(selector, chartKey, graph.names, graph.values, {
+ store: homeChartInstances,
+ label: chartMeta[graphType].label,
+ color: chartMeta[graphType].color,
+ yFormatter: chartMeta[graphType].yFormatter,
+ height: 220
+ });
+ } else {
+ destroyApexChart(homeChartInstances, 'home_' + graphType);
+ $('[data-chart=' + graphType + ']').closest('.poolChart').hide();
}
}
}
diff --git a/website_example/pages/home.html b/website_example/pages/home.html
index ca829bd..2986c62 100644
--- a/website_example/pages/home.html
+++ b/website_example/pages/home.html
@@ -564,6 +564,7 @@ currentPage = {
$('#networkLastBlockFound,#poolLastBlockFound').timeago('dispose');
if (xhrHomeTopMiners) xhrHomeTopMiners.abort();
if (xhrHomeDualMm) xhrHomeDualMm.abort();
+ destroyApexChartsByPrefix(homeChartInstances, 'home_');
},
update: function(updateKey){
if (lastStats)
@@ -609,6 +610,7 @@ currentPage = {
updateText('blockSolvedTime', getReadableTime(lastStats.network.difficulty / lastStats.pool.hashrate));
home_RenderPoolBlocks(stats);
home_RenderPayments(stats);
+ home_CreateCharts(stats);
}
}
};
diff --git a/website_example/pages/worker_stats.html b/website_example/pages/worker_stats.html
index f8eacc9..6f0a2d9 100644
--- a/website_example/pages/worker_stats.html
+++ b/website_example/pages/worker_stats.html
@@ -156,6 +156,7 @@ let ranOnce = false
currentPage = {
destroy: function(){
$(`#yourLastShare${lastStats.config.coin}`).timeago('dispose');
+ destroyApexChartsByPrefix(workerChartInstances, `${lastStats.config.coin}_`);
if (xhrAddressPoll[lastStats.config.coin])
xhrAddressPoll[lastStats.config.coin].abort();
if (xhrAddressPoll) {
diff --git a/website_example/themes/custom.css b/website_example/themes/custom.css
index 0ba544a..06ea5ae 100644
--- a/website_example/themes/custom.css
+++ b/website_example/themes/custom.css
@@ -684,6 +684,86 @@ textarea:focus {
text-transform: uppercase;
}
+@media (min-width: 992px) {
+ body {
+ margin-bottom: 64px;
+ }
+
+ .nav-side-menu {
+ position: sticky;
+ top: 0;
+ width: 100%;
+ height: auto;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+ padding: 0 24px;
+ border-right: none;
+ border-bottom: 1px solid var(--line);
+ z-index: 1100;
+ }
+
+ .nav-side-menu .brand {
+ flex: 0 0 auto;
+ background: transparent;
+ border-bottom: none;
+ line-height: 72px;
+ padding: 0;
+ }
+
+ .nav-side-menu .menu-list {
+ flex: 1 1 auto;
+ }
+
+ .nav-side-menu .menu-list .menu-content {
+ display: flex !important;
+ align-items: center;
+ justify-content: flex-end;
+ flex-wrap: wrap;
+ gap: 8px;
+ padding: 14px 0;
+ }
+
+ .nav-side-menu li {
+ border: none;
+ background: transparent;
+ }
+
+ .nav-side-menu li a {
+ width: auto;
+ padding: 10px 14px;
+ border-radius: 999px;
+ }
+
+ .nav-side-menu li a i {
+ padding-left: 0;
+ padding-right: 10px;
+ }
+
+ .nav-side-menu li:hover,
+ .nav-side-menu ul .active,
+ .nav-side-menu li .active,
+ .nav-side-menu li.active {
+ border-left-color: transparent;
+ }
+
+ #top-bar {
+ position: sticky;
+ top: 73px;
+ left: 0;
+ right: 0;
+ overflow: auto;
+ padding: 0 24px;
+ z-index: 1000;
+ }
+
+ #page-wrapper {
+ margin: 0;
+ padding: 34px 34px 0;
+ }
+}
+
@media (max-width: 991px) {
body {
margin-bottom: 86px;