add color customization functionality

This commit is contained in:
Graeme Funk 2017-11-14 00:48:04 -08:00
parent a9c6bcbc97
commit e7df406b8c
16 changed files with 154 additions and 333 deletions

View File

@ -128,6 +128,52 @@ $.fire = (target, type, properties) => {
return target.dispatchEvent(evt);
};
function limit_color(r){
if (r > 255) return 255;
else if (r < 0) return 0;
return r;
}
function lighten_darken_color(color, amt) {
let col = get_color(color);
let usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
let num = parseInt(col,16);
let r = limit_color((num >> 16) + amt);
let b = limit_color(((num >> 8) & 0x00FF) + amt);
let g = limit_color((num & 0x0000FF) + amt);
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}
function is_valid_color(string) {
// https://stackoverflow.com/a/8027444/6495043
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(string);
}
const color_map = {
'light-blue': '#7cd6fd',
blue: '#5e64ff',
violet: '#743ee2',
red: '#ff5858',
orange: '#ffa00a',
yellow: '#feef72',
green: '#28a745',
'light-green': '#98d85b',
purple: '#b554ff',
magenta: '#ffa3ef',
black: '#36114C',
grey: '#bdd3e6',
'light-grey': '#f0f4f7',
'dark-grey': '#b8c2cc'
};
const get_color = (color) => {
return color_map[color] || color;
};
var UnitRenderer = (function() {
var UnitRenderer = function(total_height, zero_line, avg_unit_width) {
this.total_height = total_height;
@ -170,7 +216,8 @@ var UnitRenderer = (function() {
let [height, y] = get_bar_height_and_y_attr(y_top, this.zero_line, this.total_height);
return $.createSVG('rect', {
className: `bar mini fill ${color}`,
className: `bar mini`,
style: `fill: ${get_color(color)}`,
'data-point-index': index,
x: current_x,
y: y,
@ -181,7 +228,7 @@ var UnitRenderer = (function() {
draw_dot: function(x, y, args, color, index) {
return $.createSVG('circle', {
className: `fill ${color}`,
style: `fill: ${get_color(color)}`,
'data-point-index': index,
cx: x,
cy: y,
@ -643,8 +690,12 @@ class SvgTip {
this.data_point_list.innerHTML = '';
this.list_values.map((set) => {
const color = set.color ? get_color(set.color) : 'black';
let li = $.create('li', {
className: `border-top ${set.color || 'black'}`,
styles: {
'border-top': `3px solid ${color}`
},
innerHTML: `<strong style="display: block;">${ set.value === 0 || set.value ? set.value : '' }</strong>
${set.title ? set.title : '' }`
});
@ -734,7 +785,11 @@ class BaseChart {
this.has_legend = has_legend;
this.colors = colors;
if(!this.colors || (this.data.labels && this.colors.length < this.data.labels.length)) {
const list = type === 'percentage' || type === 'pie'
? this.data.labels
: this.data.datasets;
if(!this.colors || (list && this.colors.length < list.length)) {
this.colors = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta'];
}
@ -772,7 +827,8 @@ class BaseChart {
title: this.title,
data: this.raw_chart_args.data,
type: type,
height: this.raw_chart_args.height
height: this.raw_chart_args.height,
colors: this.colors
});
}
@ -900,7 +956,10 @@ class BaseChart {
this.summary.map(d => {
let stats = $.create('div', {
className: 'stats',
innerHTML: `<span class="indicator ${d.color}">${d.title}: ${d.value}</span>`
styles: {
background: d.color
},
innerHTML: `<span class="indicator">${d.title}: ${d.value}</span>`
});
this.stats_wrapper.appendChild(stats);
});
@ -1843,7 +1902,6 @@ class BarChart extends AxisChart {
if(this.overlay) {
this.overlay.parentNode.removeChild(this.overlay);
}
this.overlay = unit.cloneNode();
this.overlay.style.fill = '#000000';
this.overlay.style.opacity = '0.4';
@ -1875,6 +1933,9 @@ class BarChart extends AxisChart {
attributes.filter(attr => attr.specified).map(attr => {
this.overlay.setAttribute(attr.name, attr.nodeValue);
});
this.overlay.style.fill = '#000000';
this.overlay.style.opacity = '0.4';
}
on_left_arrow() {
@ -1960,7 +2021,7 @@ class LineChart extends AxisChart {
d.path = $.createSVG('path', {
inside: this.paths_groups[i],
className: `stroke ${color}`,
style: `stroke: ${get_color(color)}`,
d: "M"+points_str
});
@ -2001,8 +2062,8 @@ class LineChart extends AxisChart {
let set_gradient_stop = (grad_elem, offset, color, opacity) => {
$.createSVG('stop', {
'className': 'stop-color ' + color,
'inside': grad_elem,
style: `stop-color: ${color}`,
inside: grad_elem,
'offset': offset,
'stop-opacity': opacity
});
@ -2135,9 +2196,10 @@ class PercentageChart extends BaseChart {
this.slices = [];
this.slice_totals.map((total, i) => {
let slice = $.create('div', {
className: `progress-bar background ${this.colors[i]}`,
className: `progress-bar`,
inside: this.percentage_bar,
styles: {
background: get_color(this.colors[i]),
width: total*100/this.grand_total + "%"
}
});
@ -2171,7 +2233,8 @@ class PercentageChart extends BaseChart {
className: 'stats',
inside: this.stats_wrapper
});
stats.innerHTML = `<span class="indicator ${this.colors[i]}">
stats.innerHTML = `<span class="indicator">
<i style="background: ${get_color(this.colors[i])}"></i>
<span class="text-muted">${x_values[i]}:</span>
${d}
</span>`;
@ -2180,30 +2243,6 @@ class PercentageChart extends BaseChart {
}
}
function limit_color(r){
if (r > 255) return 255;
else if (r < 0) return 0;
return r;
}
function lighten_darken_color(col, amt) {
let usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
let num = parseInt(col,16);
let r = limit_color((num >> 16) + amt);
let b = limit_color(((num >> 8) & 0x00FF) + amt);
let g = limit_color((num & 0x0000FF) + amt);
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}
function is_valid_color(string) {
// https://stackoverflow.com/a/8027444/6495043
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(string);
}
const ANGLE_RATIO = Math.PI / 180;
const FULL_ANGLE = 360;
@ -2219,10 +2258,6 @@ class PieChart extends BaseChart {
this.colors = args.colors;
this.startAngle = args.startAngle || 0;
this.clockWise = args.clockWise || false;
if(!this.colors || this.colors.length < this.data.labels.length) {
this.colors = ['#7cd6fd', '#5e64ff', '#743ee2', '#ff5858', '#ffa00a',
'#FEEF72', '#28a745', '#98d85b', '#b554ff', '#ffa3ef'];
}
this.mouseMove = this.mouseMove.bind(this);
this.mouseLeave = this.mouseLeave.bind(this);
this.setup();
@ -2305,7 +2340,7 @@ class PieChart extends BaseChart {
className:'pie-path',
style:'transition:transform .3s;',
d:curPath,
fill:this.colors[i]
fill:get_color(this.colors[i])
});
this.slices.push(slice);
this.slicesProperties.push({
@ -2359,9 +2394,10 @@ class PieChart extends BaseChart {
}
hoverSlice(path,i,flag,e){
if(!path) return;
const color = get_color(this.colors[i]);
if(flag){
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
path.setAttribute('fill',lighten_darken_color(this.colors[i],50));
path.setAttribute('fill',lighten_darken_color(color,50));
let g_off = $.offset(this.svg);
let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10;
@ -2373,7 +2409,7 @@ class PieChart extends BaseChart {
}else{
transform(path,'translate3d(0,0,0)');
this.tip.hide_tip();
path.setAttribute('fill',this.colors[i]);
path.setAttribute('fill',color);
}
}
@ -2403,13 +2439,15 @@ class PieChart extends BaseChart {
let x_values = this.formatted_labels && this.formatted_labels.length > 0
? this.formatted_labels : this.labels;
this.legend_totals.map((d, i) => {
const color = get_color(this.colors[i]);
if(d) {
let stats = $.create('div', {
className: 'stats',
inside: this.stats_wrapper
});
stats.innerHTML = `<span class="indicator">
<i style="background-color:${this.colors[i]};"></i>
<i style="background-color:${color};"></i>
<span class="text-muted">${x_values[i]}:</span>
${d}
</span>`;

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

@ -83,6 +83,7 @@
title: "My Awesome Chart",
data: data,
type: 'bar', // or 'line', 'scatter', 'pie', 'percentage'
// colors: ['#000', '#ece7f2', 'light-blue'], // use direct hex code or preset color
height: 250,
format_tooltip_x: d => (d + '').toUpperCase(),
format_tooltip_y: d => d + ' pts'

View File

@ -30,7 +30,6 @@ export default class BarChart extends AxisChart {
if(this.overlay) {
this.overlay.parentNode.removeChild(this.overlay);
}
this.overlay = unit.cloneNode();
this.overlay.style.fill = '#000000';
this.overlay.style.opacity = '0.4';
@ -62,6 +61,9 @@ export default class BarChart extends AxisChart {
attributes.filter(attr => attr.specified).map(attr => {
this.overlay.setAttribute(attr.name, attr.nodeValue);
});
this.overlay.style.fill = '#000000';
this.overlay.style.opacity = '0.4';
}
on_left_arrow() {

View File

@ -38,7 +38,11 @@ export default class BaseChart {
this.has_legend = has_legend;
this.colors = colors;
if(!this.colors || (this.data.labels && this.colors.length < this.data.labels.length)) {
const list = type === 'percentage' || type === 'pie'
? this.data.labels
: this.data.datasets;
if(!this.colors || (list && this.colors.length < list.length)) {
this.colors = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta'];
}
@ -76,7 +80,8 @@ export default class BaseChart {
title: this.title,
data: this.raw_chart_args.data,
type: type,
height: this.raw_chart_args.height
height: this.raw_chart_args.height,
colors: this.colors
});
}
@ -204,7 +209,10 @@ export default class BaseChart {
this.summary.map(d => {
let stats = $.create('div', {
className: 'stats',
innerHTML: `<span class="indicator ${d.color}">${d.title}: ${d.value}</span>`
styles: {
background: d.color
},
innerHTML: `<span class="indicator">${d.title}: ${d.value}</span>`
});
this.stats_wrapper.appendChild(stats);
});

View File

@ -1,5 +1,6 @@
import AxisChart from './AxisChart';
import $ from '../utils/dom';
import { get_color } from '../utils/colors';
export default class LineChart extends AxisChart {
constructor(args) {
@ -70,7 +71,7 @@ export default class LineChart extends AxisChart {
d.path = $.createSVG('path', {
inside: this.paths_groups[i],
className: `stroke ${color}`,
style: `stroke: ${get_color(color)}`,
d: "M"+points_str
});
@ -111,8 +112,8 @@ export default class LineChart extends AxisChart {
let set_gradient_stop = (grad_elem, offset, color, opacity) => {
$.createSVG('stop', {
'className': 'stop-color ' + color,
'inside': grad_elem,
style: `stop-color: ${color}`,
inside: grad_elem,
'offset': offset,
'stop-opacity': opacity
});

View File

@ -1,5 +1,6 @@
import BaseChart from './BaseChart';
import $ from '../utils/dom';
import { get_color } from '../utils/colors';
export default class PercentageChart extends BaseChart {
constructor(args) {
@ -80,9 +81,10 @@ export default class PercentageChart extends BaseChart {
this.slices = [];
this.slice_totals.map((total, i) => {
let slice = $.create('div', {
className: `progress-bar background ${this.colors[i]}`,
className: `progress-bar`,
inside: this.percentage_bar,
styles: {
background: get_color(this.colors[i]),
width: total*100/this.grand_total + "%"
}
});
@ -116,7 +118,8 @@ export default class PercentageChart extends BaseChart {
className: 'stats',
inside: this.stats_wrapper
});
stats.innerHTML = `<span class="indicator ${this.colors[i]}">
stats.innerHTML = `<span class="indicator">
<i style="background: ${get_color(this.colors[i])}"></i>
<span class="text-muted">${x_values[i]}:</span>
${d}
</span>`;

View File

@ -1,6 +1,6 @@
import BaseChart from './BaseChart';
import $ from '../utils/dom';
import { lighten_darken_color } from '../utils/colors';
import { get_color, lighten_darken_color } from '../utils/colors';
import { runSVGAnimation, transform } from '../utils/animate';
const ANGLE_RATIO = Math.PI / 180;
const FULL_ANGLE = 360;
@ -17,10 +17,6 @@ export default class PieChart extends BaseChart {
this.colors = args.colors;
this.startAngle = args.startAngle || 0;
this.clockWise = args.clockWise || false;
if(!this.colors || this.colors.length < this.data.labels.length) {
this.colors = ['#7cd6fd', '#5e64ff', '#743ee2', '#ff5858', '#ffa00a',
'#FEEF72', '#28a745', '#98d85b', '#b554ff', '#ffa3ef'];
}
this.mouseMove = this.mouseMove.bind(this);
this.mouseLeave = this.mouseLeave.bind(this);
this.setup();
@ -103,7 +99,7 @@ export default class PieChart extends BaseChart {
className:'pie-path',
style:'transition:transform .3s;',
d:curPath,
fill:this.colors[i]
fill:get_color(this.colors[i])
});
this.slices.push(slice);
this.slicesProperties.push({
@ -157,9 +153,10 @@ export default class PieChart extends BaseChart {
}
hoverSlice(path,i,flag,e){
if(!path) return;
const color = get_color(this.colors[i]);
if(flag){
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
path.setAttribute('fill',lighten_darken_color(this.colors[i],50));
path.setAttribute('fill',lighten_darken_color(color,50));
let g_off = $.offset(this.svg);
let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10;
@ -171,7 +168,7 @@ export default class PieChart extends BaseChart {
}else{
transform(path,'translate3d(0,0,0)');
this.tip.hide_tip();
path.setAttribute('fill',this.colors[i]);
path.setAttribute('fill',color);
}
}
@ -201,13 +198,15 @@ export default class PieChart extends BaseChart {
let x_values = this.formatted_labels && this.formatted_labels.length > 0
? this.formatted_labels : this.labels;
this.legend_totals.map((d, i) => {
const color = get_color(this.colors[i]);
if(d) {
let stats = $.create('div', {
className: 'stats',
inside: this.stats_wrapper
});
stats.innerHTML = `<span class="indicator">
<i style="background-color:${this.colors[i]};"></i>
<i style="background-color:${color};"></i>
<span class="text-muted">${x_values[i]}:</span>
${d}
</span>`;

View File

@ -1,4 +1,5 @@
import $ from '../utils/dom';
import { get_color } from '../utils/colors';
export default class SvgTip {
constructor({
@ -58,8 +59,12 @@ export default class SvgTip {
this.data_point_list.innerHTML = '';
this.list_values.map((set) => {
const color = set.color ? get_color(set.color) : 'black';
let li = $.create('li', {
className: `border-top ${set.color || 'black'}`,
styles: {
'border-top': `3px solid ${color}`
},
innerHTML: `<strong style="display: block;">${ set.value === 0 || set.value ? set.value : '' }</strong>
${set.title ? set.title : '' }`
});

View File

@ -4,7 +4,8 @@ function limit_color(r){
return r;
}
export function lighten_darken_color(col, amt) {
export function lighten_darken_color(color, amt) {
let col = get_color(color);
let usePound = false;
if (col[0] == "#") {
col = col.slice(1);
@ -20,4 +21,25 @@ export function lighten_darken_color(col, amt) {
export function is_valid_color(string) {
// https://stackoverflow.com/a/8027444/6495043
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(string);
}
}
export const color_map = {
'light-blue': '#7cd6fd',
blue: '#5e64ff',
violet: '#743ee2',
red: '#ff5858',
orange: '#ffa00a',
yellow: '#feef72',
green: '#28a745',
'light-green': '#98d85b',
purple: '#b554ff',
magenta: '#ffa3ef',
black: '#36114C',
grey: '#bdd3e6',
'light-grey': '#f0f4f7',
'dark-grey': '#b8c2cc'
};
export const get_color = (color) => {
return color_map[color] || color;
};

View File

@ -1,4 +1,5 @@
import $ from './dom';
import { get_color } from './colors';
export var UnitRenderer = (function() {
var UnitRenderer = function(total_height, zero_line, avg_unit_width) {
@ -42,7 +43,8 @@ export var UnitRenderer = (function() {
let [height, y] = get_bar_height_and_y_attr(y_top, this.zero_line, this.total_height);
return $.createSVG('rect', {
className: `bar mini fill ${color}`,
className: `bar mini`,
style: `fill: ${get_color(color)}`,
'data-point-index': index,
x: current_x,
y: y,
@ -53,7 +55,7 @@ export var UnitRenderer = (function() {
draw_dot: function(x, y, args, color, index) {
return $.createSVG('circle', {
className: `fill ${color}`,
style: `fill: ${get_color(color)}`,
'data-point-index': index,
cx: x,
cy: y,

View File

@ -187,9 +187,7 @@
font-weight: bold;
color: #6c7680;
}
.indicator::before,
.indicator i ,
.indicator-right::after {
.indicator i {
content: '';
display: inline-block;
height: 8px;
@ -202,264 +200,6 @@
.indicator-right::after {
margin: 0 0 0 4px;
}
.background.grey,
.indicator.grey::before,
.indicator.grey i,
.indicator-right.grey::after {
background: #bdd3e6;
}
.background.light-grey,
.indicator.light-grey::before,
.indicator.light-grey i,
.indicator-right.light-grey::after {
background: #F0F4F7;
}
.background.blue,
.indicator.blue::before,
.indicator.blue i,
.indicator-right.blue::after {
background: #5e64ff;
}
.background.red,
.indicator.red::before,
.indicator.red i,
.indicator-right.red::after {
background: #ff5858;
}
.background.green,
.indicator.green::before,
.indicator.green i,
.indicator-right.green::after {
background: #28a745;
}
.background.light-green,
.indicator.light-green::before,
.indicator.light-green i,
.indicator-right.light-green::after {
background: #98d85b;
}
.background.orange,
.indicator.orange::before,
.indicator.orange i,
.indicator-right.orange::after {
background: #ffa00a;
}
.background.violet,
.indicator.violet::before,
.indicator.violet i,
.indicator-right.violet::after {
background: #743ee2;
}
.background.dark-grey,
.indicator.dark-grey::before,
.indicator.dark-grey i,
.indicator-right.dark-grey::after {
background: #b8c2cc;
}
.background.black,
.indicator.black::before,
.indicator.black i,
.indicator-right.black::after {
background: #36414C;
}
.background.yellow,
.indicator.yellow::before,
.indicator.yellow i,
.indicator-right.yellow::after {
background: #FEEF72;
}
.background.light-blue,
.indicator.light-blue::before,
.indicator.light-blue i,
.indicator-right.light-blue::after {
background: #7CD6FD;
}
.background.purple,
.indicator.purple::before,
.indicator.purple i,
.indicator-right.purple::after {
background: #b554ff;
}
.background.magenta,
.indicator.magenta::before,
.indicator.magenta i,
.indicator-right.magenta::after {
background: #ffa3ef;
}
/*Svg properties colors*/
.stroke.grey {
stroke: #bdd3e6;
}
.stroke.light-grey {
stroke: #F0F4F7;
}
.stroke.blue {
stroke: #5e64ff;
}
.stroke.red {
stroke: #ff5858;
}
.stroke.light-green {
stroke: #98d85b;
}
.stroke.green {
stroke: #28a745;
}
.stroke.orange {
stroke: #ffa00a;
}
.stroke.violet {
stroke: #743ee2;
}
.stroke.dark-grey {
stroke: #b8c2cc;
}
.stroke.black {
stroke: #36414C;
}
.stroke.yellow {
stroke: #FEEF72;
}
.stroke.light-blue {
stroke: #7CD6FD;
}
.stroke.purple {
stroke: #b554ff;
}
.stroke.magenta {
stroke: #ffa3ef;
}
.fill.grey {
fill: #bdd3e6;
}
.fill.light-grey {
fill: #F0F4F7;
}
.fill.blue {
fill: #5e64ff;
}
.fill.red {
fill: #ff5858;
}
.fill.light-green {
fill: #98d85b;
}
.fill.green {
fill: #28a745;
}
.fill.orange {
fill: #ffa00a;
}
.fill.violet {
fill: #743ee2;
}
.fill.dark-grey {
fill: #b8c2cc;
}
.fill.black {
fill: #36414C;
}
.fill.yellow {
fill: #FEEF72;
}
.fill.light-blue {
fill: #7CD6FD;
}
.fill.purple {
fill: #b554ff;
}
.fill.magenta {
fill: #ffa3ef;
}
.border-top.grey {
border-top: 3px solid #bdd3e6;
}
.border-top.light-grey {
border-top: 3px solid #F0F4F7;
}
.border-top.blue {
border-top: 3px solid #5e64ff;
}
.border-top.red {
border-top: 3px solid #ff5858;
}
.border-top.light-green {
border-top: 3px solid #98d85b;
}
.border-top.green {
border-top: 3px solid #28a745;
}
.border-top.orange {
border-top: 3px solid #ffa00a;
}
.border-top.violet {
border-top: 3px solid #743ee2;
}
.border-top.dark-grey {
border-top: 3px solid #b8c2cc;
}
.border-top.black {
border-top: 3px solid #36414C;
}
.border-top.yellow {
border-top: 3px solid #FEEF72;
}
.border-top.light-blue {
border-top: 3px solid #7CD6FD;
}
.border-top.purple {
border-top: 3px solid #b554ff;
}
.border-top.magenta {
border-top: 3px solid #ffa3ef;
}
.stop-color.grey {
stop-color: #bdd3e6;
}
.stop-color.light-grey {
stop-color: #F0F4F7;
}
.stop-color.blue {
stop-color: #5e64ff;
}
.stop-color.red {
stop-color: #ff5858;
}
.stop-color.light-green {
stop-color: #98d85b;
}
.stop-color.green {
stop-color: #28a745;
}
.stop-color.orange {
stop-color: #ffa00a;
}
.stop-color.violet {
stop-color: #743ee2;
}
.stop-color.dark-grey {
stop-color: #b8c2cc;
}
.stop-color.black {
stop-color: #36414C;
}
.stop-color.yellow {
stop-color: #FEEF72;
}
.stop-color.light-blue {
stop-color: #7CD6FD;
}
.stop-color.purple {
stop-color: #b554ff;
}
.stop-color.magenta {
stop-color: #ffa3ef;
}
}