From a5de8c76d6bc22b93a1177b218a76d3160156a10 Mon Sep 17 00:00:00 2001 From: James Morris Date: Tue, 26 Apr 2022 18:54:52 +0100 Subject: [PATCH] Working on custom tooltips for new Dashboard refs: https://github.com/TryGhost/Team/issues/1534 --- .../app/components/dashboard/dashboard-v5.hbs | 2 +- .../components/dashboard/v5/charts/anchor.hbs | 10 ++ .../components/dashboard/v5/charts/anchor.js | 69 ++++---- .../components/dashboard/v5/charts/mrr.hbs | 9 + .../app/components/dashboard/v5/charts/mrr.js | 42 +++-- .../dashboard/v5/charts/paid-breakdown.hbs | 11 ++ .../dashboard/v5/charts/paid-breakdown.js | 53 +++--- .../dashboard/v5/charts/paid-mix.hbs | 8 +- .../dashboard/v5/charts/paid-mix.js | 41 +++-- ghost/admin/app/styles/app-dark.css | 4 + .../admin/app/styles/layouts/dashboard-v5.css | 164 +++++++++++------- 11 files changed, 258 insertions(+), 155 deletions(-) diff --git a/ghost/admin/app/components/dashboard/dashboard-v5.hbs b/ghost/admin/app/components/dashboard/dashboard-v5.hbs index 4806282372..da01619c32 100644 --- a/ghost/admin/app/components/dashboard/dashboard-v5.hbs +++ b/ghost/admin/app/components/dashboard/dashboard-v5.hbs @@ -31,4 +31,4 @@ - \ No newline at end of file + diff --git a/ghost/admin/app/components/dashboard/v5/charts/anchor.hbs b/ghost/admin/app/components/dashboard/v5/charts/anchor.hbs index 25b17f927c..7e1fe65ccf 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/anchor.hbs +++ b/ghost/admin/app/components/dashboard/v5/charts/anchor.hbs @@ -36,6 +36,15 @@ @data={{this.chartData}} @options={{this.chartOptions}} @height={{if this.hasPaidTiers this.chartHeight this.chartHeightSmall}} /> + +
+
+ - +
+
+ - +
+
@@ -68,5 +77,6 @@ {{#if option.name}}{{option.name}}{{else}}Unknown option{{/if}}
+ diff --git a/ghost/admin/app/components/dashboard/v5/charts/anchor.js b/ghost/admin/app/components/dashboard/v5/charts/anchor.js index d44787af05..58461d5f3d 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/anchor.js +++ b/ghost/admin/app/components/dashboard/v5/charts/anchor.js @@ -2,7 +2,6 @@ import Component from '@glimmer/component'; import moment from 'moment'; import {action} from '@ember/object'; import {getSymbol} from 'ghost-admin/utils/currency'; -import {ghPriceAmount} from '../../../../helpers/gh-price-amount'; import {inject as service} from '@ember/service'; import {tracked} from '@glimmer/tracking'; @@ -117,6 +116,18 @@ export default class Anchor extends Component { return 'line'; } + get chartTitle() { + // paid + if (this.chartDisplay === 'paid') { + return 'Paid members'; + // free + } else if (this.chartDisplay === 'free') { + return 'Free members'; + } + // total + return 'Total members'; + } + get chartData() { let stats; let labels; @@ -210,43 +221,35 @@ export default class Anchor extends Component { } }, tooltips: { + enabled: false, intersect: false, mode: 'index', - displayColors: false, - backgroundColor: '#15171A', - xPadding: 7, - yPadding: 7, - cornerRadius: 5, - caretSize: 7, - caretPadding: 5, - bodyFontSize: 12.5, - titleFontSize: 12, - titleFontStyle: 'normal', - titleFontColor: 'rgba(255, 255, 255, 0.7)', - titleMarginBottom: 3, + custom: function (tooltip) { + // get tooltip element + const tooltipEl = document.getElementById('gh-dashboard5-anchor-tooltip'); + + // only show tooltip when active + if (tooltip.opacity === 0) { + tooltipEl.style.display = 'none'; + tooltipEl.style.opacity = 0; + return; + } + + // update tooltip styles + tooltipEl.style.display = 'block'; + tooltipEl.style.opacity = 1; + tooltipEl.style.position = 'absolute'; + tooltipEl.style.left = tooltip.x + 'px'; + tooltipEl.style.top = tooltip.y + 'px'; + }, callbacks: { label: (tooltipItems, data) => { - if (this.chartDisplay === 'mrr') { - // Convert integer in cents to value in USD/other currency. - const valueText = ghPriceAmount(data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index]); - return `MRR: ${this.mrrCurrencySymbol}${valueText}`; - } - - let valueText = data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); - let returnable = valueText; - - if (this.chartDisplay === 'total') { - if (tooltipItems.datasetIndex === 0) { - returnable = `Total members: ${valueText}`; - } else { - returnable = `Paid members: ${valueText}`; - } - } - - return returnable; + const value = data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + document.querySelector('#gh-dashboard5-anchor-tooltip .gh-dashboard5-tooltip-value').innerHTML = value; }, title: (tooltipItems) => { - return moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + const value = moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + document.querySelector('#gh-dashboard5-anchor-tooltip .gh-dashboard5-tooltip-label').innerHTML = value; } } }, @@ -303,7 +306,7 @@ export default class Anchor extends Component { } get chartHeightSmall() { - return 200; + return 180; } calculatePercentage(from, to) { diff --git a/ghost/admin/app/components/dashboard/v5/charts/mrr.hbs b/ghost/admin/app/components/dashboard/v5/charts/mrr.hbs index 693f07d494..46dd699141 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/mrr.hbs +++ b/ghost/admin/app/components/dashboard/v5/charts/mrr.hbs @@ -14,6 +14,15 @@ @type={{this.chartType}} @data={{this.chartData}} @options={{this.chartOptions}} /> + +
+
+ - +
+
+ - +
+
{{/if}} diff --git a/ghost/admin/app/components/dashboard/v5/charts/mrr.js b/ghost/admin/app/components/dashboard/v5/charts/mrr.js index 77f4c9e738..592d60a01a 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/mrr.js +++ b/ghost/admin/app/components/dashboard/v5/charts/mrr.js @@ -88,7 +88,8 @@ export default class Mrr extends Component { } get chartOptions() { - let barColor = this.feature.nightShift ? 'rgba(200, 204, 217, 0.25)' : 'rgba(200, 204, 217, 0.65)'; + const that = this; + const barColor = this.feature.nightShift ? 'rgba(200, 204, 217, 0.25)' : 'rgba(200, 204, 217, 0.65)'; return { responsive: true, @@ -117,28 +118,35 @@ export default class Mrr extends Component { }, responsiveAnimationDuration: 0, tooltips: { + enabled: false, intersect: false, mode: 'index', - displayColors: false, - backgroundColor: '#15171A', - xPadding: 7, - yPadding: 7, - cornerRadius: 5, - caretSize: 7, - caretPadding: 5, - bodyFontSize: 12.5, - titleFontSize: 12, - titleFontStyle: 'normal', - titleFontColor: 'rgba(255, 255, 255, 0.7)', - titleMarginBottom: 3, + custom: function (tooltip) { + // get tooltip element + const tooltipEl = document.getElementById('gh-dashboard5-mrr-tooltip'); + + // only show tooltip when active + if (tooltip.opacity === 0) { + tooltipEl.style.display = 'none'; + tooltipEl.style.opacity = 0; + return; + } + + // update tooltip styles + tooltipEl.style.display = 'block'; + tooltipEl.style.opacity = 1; + tooltipEl.style.position = 'absolute'; + tooltipEl.style.left = tooltip.x + 'px'; + tooltipEl.style.top = tooltip.y + 'px'; + }, callbacks: { label: (tooltipItems, data) => { - // Convert integer in cents to value in USD/other currency. - const valueText = ghPriceAmount(data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index]); - return `MRR: ${this.mrrCurrencySymbol}${valueText}`; + const value = `${that.mrrCurrencySymbol}${ghPriceAmount(data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index])}`; + document.querySelector('#gh-dashboard5-mrr-tooltip .gh-dashboard5-tooltip-value').innerHTML = value; }, title: (tooltipItems) => { - return moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + const value = moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + document.querySelector('#gh-dashboard5-mrr-tooltip .gh-dashboard5-tooltip-label').innerHTML = value; } } }, diff --git a/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.hbs b/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.hbs index ab295a2d35..b923c84a1b 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.hbs +++ b/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.hbs @@ -18,6 +18,17 @@ @type={{this.chartType}} @data={{this.chartData}} @options={{this.chartOptions}} /> + +
+
+ - +
+
+
+
+
+
+
{{/if}} diff --git a/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.js b/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.js index aa8decb280..7731ae7011 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.js +++ b/ghost/admin/app/components/dashboard/v5/charts/paid-breakdown.js @@ -72,7 +72,7 @@ export default class PaidBreakdown extends Component { } get chartOptions() { - let barColor = this.feature.nightShift ? 'rgba(200, 204, 217, 0.25)' : 'rgba(200, 204, 217, 0.65)'; + const barColor = this.feature.nightShift ? 'rgba(200, 204, 217, 0.25)' : 'rgba(200, 204, 217, 0.65)'; return { responsive: true, @@ -101,39 +101,38 @@ export default class PaidBreakdown extends Component { }, responsiveAnimationDuration: 0, tooltips: { + enabled: false, intersect: false, mode: 'index', - displayColors: false, - backgroundColor: '#15171A', - xPadding: 7, - yPadding: 7, - cornerRadius: 5, - caretSize: 7, - caretPadding: 5, - bodyFontSize: 12.5, - titleFontSize: 12, - titleFontStyle: 'normal', - titleFontColor: 'rgba(255, 255, 255, 0.7)', - titleMarginBottom: 3, - yAlign: 'center', + custom: function (tooltip) { + // get tooltip element + const tooltipEl = document.getElementById('gh-dashboard5-breakdown-tooltip'); + + // only show tooltip when active + if (tooltip.opacity === 0) { + tooltipEl.style.display = 'none'; + tooltipEl.style.opacity = 0; + return; + } + + // update tooltip styles + tooltipEl.style.display = 'block'; + tooltipEl.style.opacity = 1; + tooltipEl.style.position = 'absolute'; + tooltipEl.style.left = tooltip.x + 'px'; + tooltipEl.style.top = tooltip.y + 'px'; + }, callbacks: { label: (tooltipItems, data) => { - let valueText = data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + let newValue = data.datasets[1].data[tooltipItems.index].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + document.querySelector('#gh-dashboard5-breakdown-tooltip .gh-dashboard5-tooltip-value-2').innerHTML = `New ${newValue}`; - if (tooltipItems.datasetIndex === 0) { - return `Net: ${valueText}`; - } - - if (tooltipItems.datasetIndex === 1) { - return `New paid: ${valueText}`; - } - - if (tooltipItems.datasetIndex === 2) { - return `Canceled paid: ${Math.abs(valueText)}`; - } + let canceldValue = data.datasets[2].data[tooltipItems.index].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + document.querySelector('#gh-dashboard5-breakdown-tooltip .gh-dashboard5-tooltip-value-3').innerHTML = `Canceled ${canceldValue}`; }, title: (tooltipItems) => { - return moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + const value = moment(tooltipItems[0].xLabel).format(DATE_FORMAT); + document.querySelector('#gh-dashboard5-breakdown-tooltip .gh-dashboard5-tooltip-label').innerHTML = value; } } }, diff --git a/ghost/admin/app/components/dashboard/v5/charts/paid-mix.hbs b/ghost/admin/app/components/dashboard/v5/charts/paid-mix.hbs index c8ef3907fb..292774e7d7 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/paid-mix.hbs +++ b/ghost/admin/app/components/dashboard/v5/charts/paid-mix.hbs @@ -19,7 +19,13 @@ + @options={{this.chartOptions}} /> + +
+
+ - +
+
{{/if}} diff --git a/ghost/admin/app/components/dashboard/v5/charts/paid-mix.js b/ghost/admin/app/components/dashboard/v5/charts/paid-mix.js index 90731daad1..d23f420a45 100644 --- a/ghost/admin/app/components/dashboard/v5/charts/paid-mix.js +++ b/ghost/admin/app/components/dashboard/v5/charts/paid-mix.js @@ -140,27 +140,32 @@ export default class PaidMix extends Component { } }, tooltips: { - enabled: true, + enabled: false, intersect: false, mode: 'single', - displayColors: false, - backgroundColor: '#15171A', - yPadding: 7, - cornerRadius: 5, - caretSize: 7, - caretPadding: 5, - bodyFontSize: 12.5, - titleFontSize: 12, - titleFontStyle: 'normal', - titleFontColor: 'rgba(255, 255, 255, 0.7)', - titleMarginBottom: 3, - yAlign: 'bottom', - xAlign: 'center', + custom: function (tooltip) { + // get tooltip element + const tooltipEl = document.getElementById('gh-dashboard5-mix-tooltip'); + + // only show tooltip when active + if (tooltip.opacity === 0) { + tooltipEl.style.display = 'none'; + tooltipEl.style.opacity = 0; + return; + } + + // update tooltip styles + tooltipEl.style.display = 'block'; + tooltipEl.style.opacity = 1; + tooltipEl.style.position = 'absolute'; + tooltipEl.style.left = tooltip.x + 'px'; + tooltipEl.style.top = tooltip.y + 'px'; + }, callbacks: { - label: function (tooltipItem, data) { - const label = data.datasets[tooltipItem.datasetIndex].label || ''; - const value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] || 0; - return `${label}: ${value}%`; + label: (tooltipItems, data) => { + const label = data.datasets[tooltipItems.datasetIndex].label || ''; + const value = data.datasets[tooltipItems.datasetIndex].data[tooltipItems.index] || 0; + document.querySelector('#gh-dashboard5-mix-tooltip .gh-dashboard5-tooltip-value').innerHTML = `${label} ${value}%`; }, title: () => { return null; diff --git a/ghost/admin/app/styles/app-dark.css b/ghost/admin/app/styles/app-dark.css index 75351a7f26..31e20a604b 100644 --- a/ghost/admin/app/styles/app-dark.css +++ b/ghost/admin/app/styles/app-dark.css @@ -1122,6 +1122,10 @@ kbd { border-color: #394047; } +.gh-dashboard5 .gh-dashboard5-box.is-secondary { + border: 1px solid #2b2d31; +} + .gh-dashboard5 .gh-dashboard5-box.is-secondary, .gh-dashboard5 .gh-dashboard5-box.is-faded { background: transparent; diff --git a/ghost/admin/app/styles/layouts/dashboard-v5.css b/ghost/admin/app/styles/layouts/dashboard-v5.css index d816d2bff2..b18c95627c 100644 --- a/ghost/admin/app/styles/layouts/dashboard-v5.css +++ b/ghost/admin/app/styles/layouts/dashboard-v5.css @@ -165,11 +165,11 @@ Dashboard v5 Layout */ flex-direction: column; position: relative; align-items: stretch; - box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.01); } .gh-dashboard5-box.is-secondary { - background: #fcfcfc; + background: #f5f6f6; + border: transparent; } .gh-dashboard5-box.is-faded { @@ -357,6 +357,34 @@ Dashboard v5 Layout */ transition: none; } +.gh-dashboard5-triple { + display: flex; + flex-direction: column; +} + +.gh-dashboard5-articles { + flex: 1; + display: flex; + flex-direction: row; +} + +.gh-dashboard5-breakout { + width: calc(100% + 48px + 48px); + background: #fcfcfc; + padding: 24px 0 50vh; + margin-left: -48px; + margin-right: -48px; +} + +.gh-dashboard5-inner { + max-width: 1230px; + margin: 0 auto; +} + +.gh-dashboard5-subhead { + padding: 0 24px 24px; +} + /* --------------------------------- Dashboard v5 Chart */ @@ -504,6 +532,15 @@ Dashboard v5 Metric */ margin: 0 0 8px; } +.gh-dashboard5-metric.is-tooltip .gh-dashboard5-metric-label { + font-size: 1.3rem; + margin-bottom: 8px; +} + +.gh-dashboard5-metric.is-tooltip .gh-dashboard5-metric-value{ + font-size: 2rem; +} + .gh-dashboard5-metric-minivalue { font-size: 1.5rem; font-weight: 700; @@ -619,7 +656,7 @@ Dashboard v5 List */ /* --------------------------------- -Dashboard v5 Section Overview */ +Dashboard v5 Overview */ .gh-dashboard5-overview { position: relative; @@ -649,7 +686,7 @@ Dashboard v5 Section Overview */ /* --------------------------------- -Dashboard v5 Section Anchor */ +Dashboard v5 Anchor */ .gh-dashboard5-anchor { position: relative; @@ -777,7 +814,7 @@ Dashboard v5 Section Anchor */ /* --------------------------------- -Dashboard v5 Section Engagement */ +Dashboard v5 Engagement */ .gh-dashboard5-engagement { position: relative; @@ -793,7 +830,7 @@ Dashboard v5 Section Engagement */ /* --------------------------------- -Dashboard v5 Section Recent Posts */ +Dashboard v5 Recent Posts */ .gh-dashboard5-recent-posts { position: relative; @@ -853,7 +890,7 @@ Dashboard v5 Section Recent Posts */ /* --------------------------------- -Dashboard v5 Section Recent Activity */ +Dashboard v5 Recent Activity */ .gh-dashboard5-recent-activity { position: relative; @@ -906,7 +943,7 @@ Dashboard v5 Section Recent Activity */ /* --------------------------------- -Dashboard v5 Section Recents */ +Dashboard v5 Recents */ .gh-dashboard5-recents { position: relative; @@ -1003,7 +1040,7 @@ Dashboard v5 Section Recents */ /* --------------------------------- -Dashboard v5 Section What's New */ +Dashboard v5 What's New */ .gh-dashboard5-whats-new { position: relative; @@ -1047,7 +1084,7 @@ Dashboard v5 Section What's New */ /* --------------------------------- -Dashboard v5 Section Resources */ +Dashboard v5 Resources */ .gh-dashboard5-resources { position: relative; @@ -1097,7 +1134,7 @@ Dashboard v5 Section Resources */ /* --------------------------------- -Dashboard v5 Section Multi */ +Dashboard v5 Multi */ .gh-dashboard5-multi { position: relative; @@ -1153,7 +1190,7 @@ Dashboard v5 Section Multi */ /* --------------------------------- -Dashboard v5 Section Staff Picks */ +Dashboard v5 Staff Picks */ .gh-dashboard5-staff-picks { position: relative; @@ -1187,6 +1224,7 @@ Dashboard v5 Section Staff Picks */ line-height: 1.4em; margin-bottom: 8px; color: var(--black); + padding: 0; } .gh-dashboard5-staff-picks .gh-dashboard5-list-body { @@ -1202,7 +1240,7 @@ Dashboard v5 Section Staff Picks */ /* --------------------------------- -Dashboard v5 Section Latest Newsletters */ +Dashboard v5 Latest Newsletters */ .gh-dashboard5-newsletter { position: relative; @@ -1217,8 +1255,6 @@ Dashboard v5 Section Latest Newsletters */ flex: 1; } -.gh-dashboard5-newsletter .gh-dashboard5-newsletter-item {} - .gh-dashboard5-newsletter .gh-dashboard5-newsletter-item h5 { font-size: 1.4rem; padding: 0 32px 0 0; @@ -1226,7 +1262,7 @@ Dashboard v5 Section Latest Newsletters */ /* --------------------------------- -Dashboard v5 Section Community */ +Dashboard v5 Community */ .gh-dashboard5-community { position: relative; @@ -1319,48 +1355,6 @@ Dashboard v5 Misc */ } - - -.gh-dashboard5-triple { - display: flex; - flex-direction: column; -} - -.gh-dashboard5-articles { - flex: 1; - display: flex; - flex-direction: row; -} - -.gh-dashboard5-article { - -} - - - - - - - - -.gh-dashboard5-breakout { - width: calc(100% + 48px + 48px); - background: #fcfcfc; - padding: 24px 0 50vh; - margin-left: -48px; - margin-right: -48px; -} - -.gh-dashboard5-inner { - max-width: 1230px; - margin: 0 auto; -} - -.gh-dashboard5-subhead { - padding: 0 24px 24px; -} - - /* --------------------------------- Dashboard v5 Resources */ @@ -1482,3 +1476,57 @@ Dashboard v5 Resources */ padding: 3px 0 0 0; white-space: nowrap; } + + +/* --------------------------------- +Dashboard v5 Tooltips */ + +.gh-dashboard5-tooltip { + opacity: 0; + position: absolute; + top: 0; + left: 0; + background: var(--white); + border: 1px solid var(--whitegrey); + border-radius: 8px; + padding: 12px; + box-shadow: 0 2px 12px rgba(0,0,0,0.05); + z-index: 9999; + pointer-events: none; + transition: top 50ms ease-out, left 50ms ease-out; +} + +.gh-dashboard5-tooltip .gh-dashboard5-metric-value { + margin: 0; +} + +.gh-dashboard5-tooltip-label { + font-size: 1.2rem; + font-weight: 500; + line-height: 1em; + color: var(--midlightgrey); + white-space: nowrap; + letter-spacing: .2px; + margin: 0 0 8px; +} + +.gh-dashboard5-tooltip-value { + font-size: 2rem; + font-weight: 700; + letter-spacing: -.1px; + line-height: 1em; + white-space: nowrap; + color: var(--black); +} + +.gh-dashboard5-breakdown .gh-dashboard5-tooltip-value { + font-size: 1.4rem; +} + +.gh-dashboard5-breakdown .gh-dashboard5-tooltip-value-1 { + margin: 0 0 4px; +} + +.gh-dashboard5-mix .gh-dashboard5-tooltip-value { + font-size: 1.4rem; +}