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;