diff --git a/src/js/charts/AggregationChart.js b/src/js/charts/AggregationChart.js index fb5d91b..4432efb 100644 --- a/src/js/charts/AggregationChart.js +++ b/src/js/charts/AggregationChart.js @@ -15,6 +15,7 @@ export default class AggregationChart extends BaseChart { this.config.formatTooltipY = (args.tooltipOptions || {}).formatTooltipY; this.config.maxSlices = args.maxSlices || 20; this.config.maxLegendPoints = args.maxLegendPoints || 20; + this.config.legendRowHeight = 60; } calc() { @@ -31,17 +32,17 @@ export default class AggregationChart extends BaseChart { }).filter(d => { return d[0] >= 0; }); // keep only positive results let totals = allTotals; - if(allTotals.length > maxSlices) { + if (allTotals.length > maxSlices) { // Prune and keep a grey area for rest as per maxSlices allTotals.sort((a, b) => { return b[0] - a[0]; }); - totals = allTotals.slice(0, maxSlices-1); - let remaining = allTotals.slice(maxSlices-1); + totals = allTotals.slice(0, maxSlices - 1); + let remaining = allTotals.slice(maxSlices - 1); let sumOfRemaining = 0; - remaining.map(d => {sumOfRemaining += d[0];}); + remaining.map(d => { sumOfRemaining += d[0]; }); totals.push([sumOfRemaining, 'Rest']); - this.colors[maxSlices-1] = 'grey'; + this.colors[maxSlices - 1] = 'grey'; } s.labels = []; @@ -62,36 +63,22 @@ export default class AggregationChart extends BaseChart { let s = this.state; this.legendArea.textContent = ''; this.legendTotals = s.sliceTotals.slice(0, this.config.maxLegendPoints); + super.renderLegend(this.legendTotals); + } - let count = 0; - let y = 0; - this.legendTotals.map((d, i) => { - let barWidth = 120; - let divisor = Math.floor( - (this.width - getExtraWidth(this.measures))/barWidth - ); - if (this.legendTotals.length < divisor) { - barWidth = this.width/this.legendTotals.length; - } - if(count > divisor) { - count = 0; - y += 60; - } - let x = barWidth * count + 5; - let label = this.config.truncateLegends ? truncateString(s.labels[i], barWidth/10) : s.labels[i]; - let formatted = this.config.formatTooltipY ? this.config.formatTooltipY(d) : d; - let dot = legendDot( - x, - y, - 12, - 3, - this.colors[i], - `${label}: ${formatted}`, - d, - false - ); - this.legendArea.appendChild(dot); - count++; - }); + makeLegend(data, index, x_pos, y_pos) { + let formatted = this.config.formatTooltipY ? this.config.formatTooltipY(data) : data; + + return legendDot( + x_pos, + y_pos, + 12, // size + 3, // dot radius + this.colors[index], // fill + this.state.labels[index], // label + formatted, // value + null, // base_font_size + this.config.truncateLegends // truncate_legends + ); } } diff --git a/src/js/charts/AxisChart.js b/src/js/charts/AxisChart.js index c5a3a59..6a814d8 100644 --- a/src/js/charts/AxisChart.js +++ b/src/js/charts/AxisChart.js @@ -1,13 +1,14 @@ import BaseChart from './BaseChart'; import { dataPrep, zeroDataPrep, getShortenedLabels } from '../utils/axis-chart-utils'; -import { AXIS_LEGEND_BAR_SIZE } from '../utils/constants'; import { getComponent } from '../objects/ChartComponents'; import { getOffset, fire } from '../utils/dom'; import { calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex, scale, getClosestInArray } from '../utils/intervals'; import { floatTwo } from '../utils/helpers'; -import { makeOverlay, updateOverlay, legendBar } from '../utils/draw'; -import { getTopOffset, getLeftOffset, MIN_BAR_PERCENT_HEIGHT, BAR_CHART_SPACE_RATIO, - LINE_CHART_DOT_SIZE } from '../utils/constants'; +import { makeOverlay, updateOverlay, legendDot } from '../utils/draw'; +import { + getTopOffset, getLeftOffset, MIN_BAR_PERCENT_HEIGHT, BAR_CHART_SPACE_RATIO, + LINE_CHART_DOT_SIZE +} from '../utils/constants'; export default class AxisChart extends BaseChart { constructor(parent, args) { @@ -44,6 +45,7 @@ export default class AxisChart extends BaseChart { this.config.formatTooltipY = options.tooltipOptions.formatTooltipY; this.config.valuesOverPoints = options.valuesOverPoints; + this.config.legendRowHeight = 30; } prepareData(data=this.data) { @@ -423,29 +425,31 @@ export default class AxisChart extends BaseChart { renderLegend() { let s = this.data; if (s.datasets.length > 1) { - this.legendArea.textContent = ''; - let barWidth = AXIS_LEGEND_BAR_SIZE; - s.datasets.map((d, i) => { - let rect = legendBar( - barWidth * i, - '0', - this.colors[i], - d.name, - this.config.truncateLegends); - this.legendArea.appendChild(rect); - }); + super.renderLegend(s.datasets); } } - + makeLegend(data, index, x_pos, y_pos) { + return legendDot( + x_pos, + y_pos + 5, // Extra offset + 12, // size + 3, // dot radius + this.colors[index], // fill + data.name, //label + null, // value + 8.75, // base_font_size + this.config.truncateLegends // truncate legends + ); + } // Overlay makeOverlay() { - if(this.init) { + if (this.init) { this.init = 0; return; } - if(this.overlayGuides) { + if (this.overlayGuides) { this.overlayGuides.forEach(g => { let o = g.overlay; o.parentNode.removeChild(o); diff --git a/src/js/charts/BaseChart.js b/src/js/charts/BaseChart.js index 6c266e7..084380a 100644 --- a/src/js/charts/BaseChart.js +++ b/src/js/charts/BaseChart.js @@ -1,8 +1,11 @@ import SvgTip from '../objects/SvgTip'; import { $, isElementInViewport, getElementContentWidth, isHidden } from '../utils/dom'; import { makeSVGContainer, makeSVGDefs, makeSVGGroup, makeText } from '../utils/draw'; -import { BASE_MEASURES, getExtraHeight, getExtraWidth, getTopOffset, getLeftOffset, - INIT_CHART_UPDATE_TIMEOUT, CHART_POST_ANIMATE_TIMEOUT, DEFAULT_COLORS} from '../utils/constants'; +import { LEGEND_ITEM_WIDTH } from '../utils/constants'; +import { + BASE_MEASURES, getExtraHeight, getExtraWidth, getTopOffset, getLeftOffset, + INIT_CHART_UPDATE_TIMEOUT, CHART_POST_ANIMATE_TIMEOUT, DEFAULT_COLORS +} from '../utils/constants'; import { getColor, isValidColor } from '../utils/colors'; import { runSMILAnimation } from '../utils/animation'; import { downloadFile, prepareForExport } from '../utils/export'; @@ -262,10 +265,29 @@ export default class BaseChart { } } - renderLegend() {} + renderLegend(dataset) { + this.legendArea.textContent = ''; + let count = 0; + let y = 0; - setupNavigation(init=false) { - if(!this.config.isNavigable) return; + dataset.map((data, index) => { + let divisor = Math.floor(this.width / LEGEND_ITEM_WIDTH); + if (count > divisor) { + count = 0; + y += this.config.legendRowHeight; + } + let x = LEGEND_ITEM_WIDTH * count; + let dot = this.makeLegend(data, index, x, y); + this.legendArea.appendChild(dot); + count++; + }); + } + + makeLegend() { } + + + setupNavigation(init = false) { + if (!this.config.isNavigable) return; if(init) { this.bindOverlay(); diff --git a/src/js/utils/constants.js b/src/js/utils/constants.js index d03184d..8efe287 100644 --- a/src/js/utils/constants.js +++ b/src/js/utils/constants.js @@ -65,7 +65,7 @@ export const CHART_POST_ANIMATE_TIMEOUT = 400; export const DEFAULT_AXIS_CHART_TYPE = 'line'; export const AXIS_DATASET_CHART_TYPES = ['line', 'bar']; -export const AXIS_LEGEND_BAR_SIZE = 100; +export const LEGEND_ITEM_WIDTH = 150; export const SERIES_LABEL_SPACE_RATIO = 0.6; export const BAR_CHART_SPACE_RATIO = 0.5; diff --git a/src/js/utils/draw.js b/src/js/utils/draw.js index 3537424..9320a8b 100644 --- a/src/js/utils/draw.js +++ b/src/js/utils/draw.js @@ -4,7 +4,7 @@ import { DOT_OVERLAY_SIZE_INCR } from './constants'; export const AXIS_TICK_LENGTH = 6; const LABEL_MARGIN = 4; -const LABEL_MAX_CHARS = 15; +const LABEL_MAX_CHARS = 18; export const FONT_SIZE = 10; const BASE_LINE_COLOR = '#E2E6E9'; const FONT_FILL = '#313B44'; @@ -223,43 +223,9 @@ export function heatSquare(className, x, y, size, radius, fill='none', data={}) return createSVG("rect", args); } -export function legendBar(x, y, fill = 'none', label, truncate = true) { - label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label; - - let args = { - className: 'legend-bar', - x: 0, - y: 0, - width: '12px', - height: '12px', - rx: '2px', - ry: '2px', - fill: fill - }; - - let text = createSVG('text', { - className: 'legend-dataset-text', - x: 0, - y: 0, - dy: (FONT_SIZE) + 'px', - dx: (FONT_SIZE * 1.5) + 'px', - 'font-size': (FONT_SIZE * 1.2) + 'px', - 'text-anchor': 'start', - fill: FONT_FILL, - innerHTML: label - }); - - let group = createSVG('g', { - transform: `translate(${x}, ${y})` - }); - group.appendChild(createSVG("rect", args)); - group.appendChild(text); - - return group; -} - -export function legendDot(x, y, size, radius, fill='none', label, value, truncate=false) { +export function legendDot(x, y, size, radius, fill = 'none', label, value, font_size = null, truncate = false) { label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label; + if (!font_size) font_size = FONT_SIZE; let args = { className: 'legend-dot', @@ -275,32 +241,38 @@ export function legendDot(x, y, size, radius, fill='none', label, value, truncat className: 'legend-dataset-label', x: size, y: 0, - dx: (FONT_SIZE) + 'px', - dy: (FONT_SIZE/3) + 'px', - 'font-size': (FONT_SIZE * 1.6) + 'px', + dx: (font_size) + 'px', + dy: (font_size / 3) + 'px', + 'font-size': (font_size * 1.6) + 'px', 'text-anchor': 'start', fill: FONT_FILL, innerHTML: label }); - let textValue = createSVG('text', { - className: 'legend-dataset-value', - x: size, - y: FONT_SIZE + 10, - dx: (FONT_SIZE) + 'px', - dy: (FONT_SIZE/3) + 'px', - 'font-size': (FONT_SIZE * 1.2) + 'px', - 'text-anchor': 'start', - fill: FONT_FILL, - innerHTML: value - }); + let textValue = null; + if (value) { + textValue = createSVG('text', { + className: 'legend-dataset-value', + x: size, + y: FONT_SIZE + 10, + dx: (FONT_SIZE) + 'px', + dy: (FONT_SIZE / 3) + 'px', + 'font-size': (FONT_SIZE * 1.2) + 'px', + 'text-anchor': 'start', + fill: FONT_FILL, + innerHTML: value + }); + } let group = createSVG('g', { transform: `translate(${x}, ${y})` }); group.appendChild(createSVG("rect", args)); group.appendChild(textLabel); - group.appendChild(textValue); + + if (value && textValue) { + group.appendChild(textValue); + } return group; }