* merge legendBar and legendDots * use BaseChart to render legendspull/347/head
@@ -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 | |||
); | |||
} | |||
} |
@@ -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); | |||
@@ -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(); | |||
@@ -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; | |||
@@ -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; | |||
} | |||