[color] configure default color handling

This commit is contained in:
Prateeksha Singh 2018-03-20 22:20:55 +05:30
parent 98e5280888
commit 786b8b1ecf
15 changed files with 166 additions and 201 deletions

View File

@ -206,6 +206,24 @@ class SvgTip {
} }
} }
const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
const COMPATIBLE_CHARTS = {
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};
const DATA_COLOR_DIVISIONS = {
bar: 'datasets',
line: 'datasets',
pie: 'labels',
percentage: 'labels',
heatmap: HEATMAP_DISTRIBUTION_SIZE
};
const VERT_SPACE_OUTSIDE_BASE_CHART = 50; const VERT_SPACE_OUTSIDE_BASE_CHART = 50;
const TRANSLATE_Y_BASE_CHART = 20; const TRANSLATE_Y_BASE_CHART = 20;
const LEFT_MARGIN_BASE_CHART = 60; const LEFT_MARGIN_BASE_CHART = 60;
@ -234,6 +252,18 @@ const FULL_ANGLE = 360;
// More colors are difficult to parse visually // More colors are difficult to parse visually
const HEATMAP_DISTRIBUTION_SIZE = 5; const HEATMAP_DISTRIBUTION_SIZE = 5;
const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
const DEFAULT_COLORS = {
bar: DEFAULT_CHART_COLORS,
line: DEFAULT_CHART_COLORS,
pie: DEFAULT_CHART_COLORS,
percentage: DEFAULT_CHART_COLORS,
heatmap: HEATMAP_COLORS
};
function floatTwo(d) { function floatTwo(d) {
return parseFloat(d.toFixed(2)); return parseFloat(d.toFixed(2));
} }
@ -891,9 +921,6 @@ const PRESET_COLOR_MAP = {
'dark-grey': '#b8c2cc' 'dark-grey': '#b8c2cc'
}; };
const DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
function limitColor(r){ function limitColor(r){
if (r > 255) return 255; if (r > 255) return 255;
else if (r < 0) return 0; else if (r < 0) return 0;
@ -923,51 +950,6 @@ const getColor = (color) => {
return PRESET_COLOR_MAP[color] || color; return PRESET_COLOR_MAP[color] || color;
}; };
const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
const COMPATIBLE_CHARTS = {
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
scatter: ['line', 'bar', 'percentage', 'pie'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};
// Needs structure as per only labels/datasets
const COLOR_COMPATIBLE_CHARTS = {
bar: ['line', 'scatter'],
line: ['scatter', 'bar'],
pie: ['percentage'],
scatter: ['line', 'bar'],
percentage: ['pie'],
heatmap: []
};
function getDifferentChart(type, current_type, parent, args) {
if(type === current_type) return;
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[current_type].includes(type)) {
console.error(`'${current_type}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const useColor = COLOR_COMPATIBLE_CHARTS[current_type].includes(type);
// Okay, this is anticlimactic
// this function will need to actually be 'changeChartType(type)'
// that will update only the required elements, but for now ...
args.type = type;
args.colors = useColor ? args.colors : undefined;
return new Chart(parent, args);
}
const UNIT_ANIM_DUR = 350; const UNIT_ANIM_DUR = 350;
const PATH_ANIM_DUR = 350; const PATH_ANIM_DUR = 350;
const MARKER_LINE_ANIM_DUR = UNIT_ANIM_DUR; const MARKER_LINE_ANIM_DUR = UNIT_ANIM_DUR;
@ -1188,13 +1170,13 @@ function runSMILAnimation(parent, svgElement, elementsToAnimate) {
class BaseChart { class BaseChart {
constructor(parent, options) { constructor(parent, options) {
this.rawChartArgs = options;
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent; this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
if (!(this.parent instanceof HTMLElement)) { if (!(this.parent instanceof HTMLElement)) {
throw new Error('No `parent` element to render on was provided.'); throw new Error('No `parent` element to render on was provided.');
} }
this.rawChartArgs = options;
this.title = options.title || ''; this.title = options.title || '';
this.subtitle = options.subtitle || ''; this.subtitle = options.subtitle || '';
this.argHeight = options.height || 240; this.argHeight = options.height || 240;
@ -1202,7 +1184,10 @@ class BaseChart {
this.realData = this.prepareData(options.data); this.realData = this.prepareData(options.data);
this.data = this.prepareFirstData(this.realData); this.data = this.prepareFirstData(this.realData);
this.colors = [];
this.colors = this.validateColors(options.colors)
.concat(DEFAULT_COLORS[this.type]);
this.config = { this.config = {
showTooltip: 1, // calculate showTooltip: 1, // calculate
showLegend: options.showLegend || 1, showLegend: options.showLegend || 1,
@ -1221,8 +1206,7 @@ class BaseChart {
this.configure(options); this.configure(options);
} }
configure(args) { configure() {
this.setColors(args);
this.setMargins(); this.setMargins();
// Bind window events // Bind window events
@ -1230,21 +1214,17 @@ class BaseChart {
window.addEventListener('orientationchange', () => this.draw(true)); window.addEventListener('orientationchange', () => this.draw(true));
} }
setColors() { validateColors(colors = []) {
let args = this.rawChartArgs; const validColors = [];
colors.forEach((string) => {
// Needs structure as per only labels/datasets, from config const color = getColor(string);
const list = args.type === 'percentage' || args.type === 'pie' if(!isValidColor(color)) {
? args.data.labels console.warn('"' + string + '" is not a valid color.');
: args.data.datasets; } else {
validColors.push(color);
if(!args.colors || (list && args.colors.length < list.length)) { }
this.colors = DEFAULT_COLORS; });
} else { return validColors;
this.colors = args.colors;
}
this.colors = this.colors.map(color => getColor(color));
} }
setMargins() { setMargins() {
@ -1456,7 +1436,29 @@ class BaseChart {
updateDataset() {} updateDataset() {}
getDifferentChart(type) { getDifferentChart(type) {
return getDifferentChart(type, this.type, this.parent, this.rawChartArgs); const currentType = this.type;
let args = this.rawChartArgs;
if(type === currentType) return;
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[currentType].includes(type)) {
console.error(`'${currentType}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const useColor = DATA_COLOR_DIVISIONS[currentType] === DATA_COLOR_DIVISIONS[type];
// Okay, this is anticlimactic
// this function will need to actually be 'changeChartType(type)'
// that will update only the required elements, but for now ...
args.type = type;
args.colors = useColor ? args.colors : undefined;
return new Chart(this.parent, args);
} }
unbindWindowEvents(){ unbindWindowEvents(){
@ -2383,11 +2385,6 @@ class Heatmap extends BaseChart {
let today = new Date(); let today = new Date();
this.start = options.start || addDays(today, 365); this.start = options.start || addDays(today, 365);
let legendColors = (options.legendColors || []).slice(0, 5);
this.legendColors = this.validate_colors(legendColors)
? legendColors
: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
this.translateX = 0; this.translateX = 0;
this.setup(); this.setup();
} }
@ -2398,22 +2395,8 @@ class Heatmap extends BaseChart {
this.translateY = 10; this.translateY = 10;
} }
validate_colors(colors) { configure(args) {
if(colors.length < 5) return 0; super.configure(args);
let valid = 1;
colors.forEach(function(string) {
if(!isValidColor(string)) {
valid = 0;
console.warn('"' + string + '" is not a valid color.');
}
}, this);
return valid;
}
configure() {
super.configure();
this.today = new Date(); this.today = new Date();
if(!this.start) { if(!this.start) {
@ -2542,7 +2525,7 @@ class Heatmap extends BaseChart {
}; };
let heatSquare = makeHeatSquare('day', x, y, squareSide, let heatSquare = makeHeatSquare('day', x, y, squareSide,
this.legendColors[colorIndex], dataAttr); this.colors[colorIndex], dataAttr);
dataGroup.appendChild(heatSquare); dataGroup.appendChild(heatSquare);
@ -2758,7 +2741,7 @@ class AxisChart extends BaseChart {
} }
configure(args) { configure(args) {
super.configure(); super.configure(args);
args.axisOptions = args.axisOptions || {}; args.axisOptions = args.axisOptions || {};
args.tooltipOptions = args.tooltipOptions || {}; args.tooltipOptions = args.tooltipOptions || {};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -455,7 +455,7 @@ let heatmap = new frappe.Chart("#chart-heatmap", {
legendScale: [0, 1, 2, 4, 5], legendScale: [0, 1, 2, 4, 5],
height: 115, height: 115,
discreteDomains: 1, discreteDomains: 1,
legendColors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'] colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c']
}); });
console.log(heatmapData, heatmap); console.log(heatmapData, heatmap);
@ -486,7 +486,7 @@ Array.prototype.slice.call(
legendScale: [0, 1, 2, 4, 5], legendScale: [0, 1, 2, 4, 5],
height: 115, height: 115,
discreteDomains: discreteDomains, discreteDomains: discreteDomains,
legendColors: colors colors: colors
}); });
Array.prototype.slice.call( Array.prototype.slice.call(
@ -524,7 +524,7 @@ Array.prototype.slice.call(
legendScale: [0, 1, 2, 4, 5], legendScale: [0, 1, 2, 4, 5],
height: 115, height: 115,
discreteDomains: discreteDomains, discreteDomains: discreteDomains,
legendColors: colors colors: colors
}); });
Array.prototype.slice.call( Array.prototype.slice.call(

View File

@ -203,7 +203,7 @@
// default: today's date in past year // default: today's date in past year
// for an annual heatmap // for an annual heatmap
legendColors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'], colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'],
// Set of five incremental colors, // Set of five incremental colors,
// beginning with a low-saturation color for zero data; // beginning with a low-saturation color for zero data;
// default: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'] // default: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
@ -264,7 +264,7 @@
discreteDomains: 1, // default: 1 discreteDomains: 1, // default: 1
start: startDate, // Date object start: startDate, // Date object
legendColors: [] colors: []
} }
... ...

View File

@ -22,7 +22,7 @@ export default class AxisChart extends BaseChart {
} }
configure(args) { configure(args) {
super.configure(); super.configure(args);
args.axisOptions = args.axisOptions || {}; args.axisOptions = args.axisOptions || {};
args.tooltipOptions = args.tooltipOptions || {}; args.tooltipOptions = args.tooltipOptions || {};

View File

@ -2,20 +2,21 @@ import SvgTip from '../objects/SvgTip';
import { $, isElementInViewport, getElementContentWidth } from '../utils/dom'; import { $, isElementInViewport, getElementContentWidth } from '../utils/dom';
import { makeSVGContainer, makeSVGDefs, makeSVGGroup } from '../utils/draw'; import { makeSVGContainer, makeSVGDefs, makeSVGGroup } from '../utils/draw';
import { VERT_SPACE_OUTSIDE_BASE_CHART, TRANSLATE_Y_BASE_CHART, LEFT_MARGIN_BASE_CHART, import { VERT_SPACE_OUTSIDE_BASE_CHART, TRANSLATE_Y_BASE_CHART, LEFT_MARGIN_BASE_CHART,
RIGHT_MARGIN_BASE_CHART, INIT_CHART_UPDATE_TIMEOUT, CHART_POST_ANIMATE_TIMEOUT } from '../utils/constants'; RIGHT_MARGIN_BASE_CHART, INIT_CHART_UPDATE_TIMEOUT, CHART_POST_ANIMATE_TIMEOUT, DEFAULT_COLORS,
import { getColor, DEFAULT_COLORS } from '../utils/colors'; ALL_CHART_TYPES, COMPATIBLE_CHARTS, DATA_COLOR_DIVISIONS} from '../utils/constants';
import { getDifferentChart } from '../config'; import { getColor, isValidColor } from '../utils/colors';
import { runSMILAnimation } from '../utils/animation'; import { runSMILAnimation } from '../utils/animation';
import { Chart } from '../chart';
export default class BaseChart { export default class BaseChart {
constructor(parent, options) { constructor(parent, options) {
this.rawChartArgs = options;
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent; this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
if (!(this.parent instanceof HTMLElement)) { if (!(this.parent instanceof HTMLElement)) {
throw new Error('No `parent` element to render on was provided.'); throw new Error('No `parent` element to render on was provided.');
} }
this.rawChartArgs = options;
this.title = options.title || ''; this.title = options.title || '';
this.subtitle = options.subtitle || ''; this.subtitle = options.subtitle || '';
this.argHeight = options.height || 240; this.argHeight = options.height || 240;
@ -23,7 +24,10 @@ export default class BaseChart {
this.realData = this.prepareData(options.data); this.realData = this.prepareData(options.data);
this.data = this.prepareFirstData(this.realData); this.data = this.prepareFirstData(this.realData);
this.colors = [];
this.colors = this.validateColors(options.colors)
.concat(DEFAULT_COLORS[this.type]);
this.config = { this.config = {
showTooltip: 1, // calculate showTooltip: 1, // calculate
showLegend: options.showLegend || 1, showLegend: options.showLegend || 1,
@ -42,8 +46,7 @@ export default class BaseChart {
this.configure(options); this.configure(options);
} }
configure(args) { configure() {
this.setColors(args);
this.setMargins(); this.setMargins();
// Bind window events // Bind window events
@ -51,21 +54,17 @@ export default class BaseChart {
window.addEventListener('orientationchange', () => this.draw(true)); window.addEventListener('orientationchange', () => this.draw(true));
} }
setColors() { validateColors(colors = []) {
let args = this.rawChartArgs; const validColors = [];
colors.forEach((string) => {
// Needs structure as per only labels/datasets, from config const color = getColor(string);
const list = args.type === 'percentage' || args.type === 'pie' if(!isValidColor(color)) {
? args.data.labels console.warn('"' + string + '" is not a valid color.');
: args.data.datasets; } else {
validColors.push(color);
if(!args.colors || (list && args.colors.length < list.length)) { }
this.colors = DEFAULT_COLORS; });
} else { return validColors;
this.colors = args.colors;
}
this.colors = this.colors.map(color => getColor(color));
} }
setMargins() { setMargins() {
@ -277,7 +276,29 @@ export default class BaseChart {
updateDataset() {} updateDataset() {}
getDifferentChart(type) { getDifferentChart(type) {
return getDifferentChart(type, this.type, this.parent, this.rawChartArgs); const currentType = this.type;
let args = this.rawChartArgs;
if(type === currentType) return;
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[currentType].includes(type)) {
console.error(`'${currentType}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const useColor = DATA_COLOR_DIVISIONS[currentType] === DATA_COLOR_DIVISIONS[type];
// Okay, this is anticlimactic
// this function will need to actually be 'changeChartType(type)'
// that will update only the required elements, but for now ...
args.type = type;
args.colors = useColor ? args.colors : undefined;
return new Chart(this.parent, args);
} }
unbindWindowEvents(){ unbindWindowEvents(){

View File

@ -2,7 +2,6 @@ import BaseChart from './BaseChart';
import { makeSVGGroup, makeHeatSquare, makeText } from '../utils/draw'; import { makeSVGGroup, makeHeatSquare, makeText } from '../utils/draw';
import { addDays, getDdMmYyyy, getWeeksBetween } from '../utils/date-utils'; import { addDays, getDdMmYyyy, getWeeksBetween } from '../utils/date-utils';
import { calcDistribution, getMaxCheckpoint } from '../utils/intervals'; import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
import { isValidColor } from '../utils/colors';
import { HEATMAP_DISTRIBUTION_SIZE } from '../utils/constants'; import { HEATMAP_DISTRIBUTION_SIZE } from '../utils/constants';
export default class Heatmap extends BaseChart { export default class Heatmap extends BaseChart {
@ -17,11 +16,6 @@ export default class Heatmap extends BaseChart {
let today = new Date(); let today = new Date();
this.start = options.start || addDays(today, 365); this.start = options.start || addDays(today, 365);
let legendColors = (options.legendColors || []).slice(0, 5);
this.legendColors = this.validate_colors(legendColors)
? legendColors
: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
this.translateX = 0; this.translateX = 0;
this.setup(); this.setup();
} }
@ -32,22 +26,8 @@ export default class Heatmap extends BaseChart {
this.translateY = 10; this.translateY = 10;
} }
validate_colors(colors) { configure(args) {
if(colors.length < 5) return 0; super.configure(args);
let valid = 1;
colors.forEach(function(string) {
if(!isValidColor(string)) {
valid = 0;
console.warn('"' + string + '" is not a valid color.');
}
}, this);
return valid;
}
configure() {
super.configure();
this.today = new Date(); this.today = new Date();
if(!this.start) { if(!this.start) {
@ -176,7 +156,7 @@ export default class Heatmap extends BaseChart {
}; };
let heatSquare = makeHeatSquare('day', x, y, squareSide, let heatSquare = makeHeatSquare('day', x, y, squareSide,
this.legendColors[colorIndex], dataAttr); this.colors[colorIndex], dataAttr);
dataGroup.appendChild(heatSquare); dataGroup.appendChild(heatSquare);

View File

@ -1,46 +0,0 @@
import { Chart } from './chart';
const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
const COMPATIBLE_CHARTS = {
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
scatter: ['line', 'bar', 'percentage', 'pie'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};
// Needs structure as per only labels/datasets
const COLOR_COMPATIBLE_CHARTS = {
bar: ['line', 'scatter'],
line: ['scatter', 'bar'],
pie: ['percentage'],
scatter: ['line', 'bar'],
percentage: ['pie'],
heatmap: []
};
export function getDifferentChart(type, current_type, parent, args) {
if(type === current_type) return;
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[current_type].includes(type)) {
console.error(`'${current_type}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const useColor = COLOR_COMPATIBLE_CHARTS[current_type].includes(type);
// Okay, this is anticlimactic
// this function will need to actually be 'changeChartType(type)'
// that will update only the required elements, but for now ...
args.type = type;
args.colors = useColor ? args.colors : undefined;
return new Chart(parent, args);
}

View File

@ -15,9 +15,6 @@ const PRESET_COLOR_MAP = {
'dark-grey': '#b8c2cc' 'dark-grey': '#b8c2cc'
}; };
export const DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
function limitColor(r){ function limitColor(r){
if (r > 255) return 255; if (r > 255) return 255;
else if (r < 0) return 0; else if (r < 0) return 0;

View File

@ -1,3 +1,21 @@
export const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
export const COMPATIBLE_CHARTS = {
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};
export const DATA_COLOR_DIVISIONS = {
bar: 'datasets',
line: 'datasets',
pie: 'labels',
percentage: 'labels',
heatmap: HEATMAP_DISTRIBUTION_SIZE
}
export const VERT_SPACE_OUTSIDE_BASE_CHART = 50; export const VERT_SPACE_OUTSIDE_BASE_CHART = 50;
export const TRANSLATE_Y_BASE_CHART = 20; export const TRANSLATE_Y_BASE_CHART = 20;
export const LEFT_MARGIN_BASE_CHART = 60; export const LEFT_MARGIN_BASE_CHART = 60;
@ -25,3 +43,15 @@ export const FULL_ANGLE = 360;
// Fixed 5-color theme, // Fixed 5-color theme,
// More colors are difficult to parse visually // More colors are difficult to parse visually
export const HEATMAP_DISTRIBUTION_SIZE = 5; export const HEATMAP_DISTRIBUTION_SIZE = 5;
const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
export const DEFAULT_COLORS = {
bar: DEFAULT_CHART_COLORS,
line: DEFAULT_CHART_COLORS,
pie: DEFAULT_CHART_COLORS,
percentage: DEFAULT_CHART_COLORS,
heatmap: HEATMAP_COLORS
}