ソースを参照

feat: new legends

* merge legendBar and legendDots
* use BaseChart to render legends
pull/347/head
Shivam Mishra 4年前
コミット
7cfa35a418
5個のファイルの変更96行の追加111行の削除
  1. +22
    -35
      src/js/charts/AggregationChart.js
  2. +22
    -18
      src/js/charts/AxisChart.js
  3. +27
    -5
      src/js/charts/BaseChart.js
  4. +1
    -1
      src/js/utils/constants.js
  5. +24
    -52
      src/js/utils/draw.js

+ 22
- 35
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
);
}
}

+ 22
- 18
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);


+ 27
- 5
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();


+ 1
- 1
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;


+ 24
- 52
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;
}


読み込み中…
キャンセル
保存