abstract out createSVG() to draw.js :D
This commit is contained in:
parent
eeefeeb54d
commit
cd8d750152
592
dist/frappe-charts.esm.js
vendored
592
dist/frappe-charts.esm.js
vendored
@ -25,6 +25,7 @@ $.create = (tag, o) => {
|
|||||||
var ref = $(val);
|
var ref = $(val);
|
||||||
ref.parentNode.insertBefore(element, ref);
|
ref.parentNode.insertBefore(element, ref);
|
||||||
element.appendChild(ref);
|
element.appendChild(ref);
|
||||||
|
|
||||||
} else if (i === "styles") {
|
} else if (i === "styles") {
|
||||||
if(typeof val === "object") {
|
if(typeof val === "object") {
|
||||||
Object.keys(val).map(prop => {
|
Object.keys(val).map(prop => {
|
||||||
@ -42,33 +43,6 @@ $.create = (tag, o) => {
|
|||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
$.createSVG = (tag, o) => {
|
|
||||||
var element = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
||||||
|
|
||||||
for (var i in o) {
|
|
||||||
var val = o[i];
|
|
||||||
|
|
||||||
if (i === "inside") {
|
|
||||||
$(val).appendChild(element);
|
|
||||||
}
|
|
||||||
else if (i === "around") {
|
|
||||||
var ref = $(val);
|
|
||||||
ref.parentNode.insertBefore(element, ref);
|
|
||||||
element.appendChild(ref);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(i === "className") { i = "class"; }
|
|
||||||
if(i === "innerHTML") {
|
|
||||||
element['textContent'] = val;
|
|
||||||
} else {
|
|
||||||
element.setAttribute(i, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
|
|
||||||
$.offset = (element) => {
|
$.offset = (element) => {
|
||||||
let rect = element.getBoundingClientRect();
|
let rect = element.getBoundingClientRect();
|
||||||
return {
|
return {
|
||||||
@ -128,6 +102,201 @@ $.fire = (target, type, properties) => {
|
|||||||
return target.dispatchEvent(evt);
|
return target.dispatchEvent(evt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Constants used
|
||||||
|
|
||||||
|
function $$1(expr, con) {
|
||||||
|
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSVG(tag, o) {
|
||||||
|
var element = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
||||||
|
|
||||||
|
for (var i in o) {
|
||||||
|
var val = o[i];
|
||||||
|
|
||||||
|
if (i === "inside") {
|
||||||
|
$$1(val).appendChild(element);
|
||||||
|
}
|
||||||
|
else if (i === "around") {
|
||||||
|
var ref = $$1(val);
|
||||||
|
ref.parentNode.insertBefore(element, ref);
|
||||||
|
element.appendChild(ref);
|
||||||
|
|
||||||
|
} else if (i === "styles") {
|
||||||
|
if(typeof val === "object") {
|
||||||
|
Object.keys(val).map(prop => {
|
||||||
|
element.style[prop] = val[prop];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(i === "className") { i = "class"; }
|
||||||
|
if(i === "innerHTML") {
|
||||||
|
element['textContent'] = val;
|
||||||
|
} else {
|
||||||
|
element.setAttribute(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVerticalGradient(svgDefElem, gradientId) {
|
||||||
|
return createSVG('linearGradient', {
|
||||||
|
inside: svgDefElem,
|
||||||
|
id: gradientId,
|
||||||
|
x1: 0,
|
||||||
|
x2: 0,
|
||||||
|
y1: 0,
|
||||||
|
y2: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGradientStop(gradElem, offset, color, opacity) {
|
||||||
|
createSVG('stop', {
|
||||||
|
'inside': gradElem,
|
||||||
|
'style': `stop-color: ${color}`,
|
||||||
|
'offset': offset,
|
||||||
|
'stop-opacity': opacity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSVGContainer(parent, className, width, height) {
|
||||||
|
return createSVG('svg', {
|
||||||
|
className: className,
|
||||||
|
inside: parent,
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSVGDefs(svgContainer) {
|
||||||
|
return createSVG('defs', {
|
||||||
|
inside: svgContainer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSVGGroup(parent, className, transform='') {
|
||||||
|
return createSVG('g', {
|
||||||
|
className: className,
|
||||||
|
inside: parent,
|
||||||
|
transform: transform
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makePath(pathStr, className='', stroke='none', fill='none') {
|
||||||
|
return createSVG('path', {
|
||||||
|
className: className,
|
||||||
|
d: pathStr,
|
||||||
|
styles: {
|
||||||
|
stroke: stroke,
|
||||||
|
fill: fill
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
|
let gradientId ='path-fill-gradient' + '-' + color;
|
||||||
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
|
let opacities = [1, 0.6, 0.2];
|
||||||
|
if(lighter) {
|
||||||
|
opacities = [0.4, 0.2, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
setGradientStop(gradientDef, "0%", color, opacities[0]);
|
||||||
|
setGradientStop(gradientDef, "50%", color, opacities[1]);
|
||||||
|
setGradientStop(gradientDef, "100%", color, opacities[2]);
|
||||||
|
|
||||||
|
return gradientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeHeatSquare(className, x, y, size, fill='none', data={}) {
|
||||||
|
let args = {
|
||||||
|
className: className,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
fill: fill
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(data).map(key => {
|
||||||
|
args[key] = data[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return createSVG("rect", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeText(className, x, y, content) {
|
||||||
|
return createSVG('text', {
|
||||||
|
className: className,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
dy: '.32em',
|
||||||
|
innerHTML: content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeXLine(height, textStartAt, point, labelClass, axisLineClass, xPos) {
|
||||||
|
let line = createSVG('line', {
|
||||||
|
x1: 0,
|
||||||
|
x2: 0,
|
||||||
|
y1: 0,
|
||||||
|
y2: height
|
||||||
|
});
|
||||||
|
|
||||||
|
let text = createSVG('text', {
|
||||||
|
className: labelClass,
|
||||||
|
x: 0,
|
||||||
|
y: textStartAt,
|
||||||
|
dy: '.71em',
|
||||||
|
innerHTML: point
|
||||||
|
});
|
||||||
|
|
||||||
|
let xLine = createSVG('g', {
|
||||||
|
className: `tick ${axisLineClass}`,
|
||||||
|
transform: `translate(${ xPos }, 0)`
|
||||||
|
});
|
||||||
|
|
||||||
|
xLine.appendChild(line);
|
||||||
|
xLine.appendChild(text);
|
||||||
|
|
||||||
|
return xLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeYLine(startAt, width, textEndAt, point, labelClass, axisLineClass, yPos, darker=false, lineType="") {
|
||||||
|
let line = createSVG('line', {
|
||||||
|
className: lineType === "dashed" ? "dashed": "",
|
||||||
|
x1: startAt,
|
||||||
|
x2: width,
|
||||||
|
y1: 0,
|
||||||
|
y2: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
let text = createSVG('text', {
|
||||||
|
className: labelClass,
|
||||||
|
x: textEndAt,
|
||||||
|
y: 0,
|
||||||
|
dy: '.32em',
|
||||||
|
innerHTML: point+""
|
||||||
|
});
|
||||||
|
|
||||||
|
let yLine = createSVG('g', {
|
||||||
|
className: `tick ${axisLineClass}`,
|
||||||
|
transform: `translate(0, ${yPos})`,
|
||||||
|
'stroke-opacity': 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if(darker) {
|
||||||
|
line.style.stroke = "rgba(27, 31, 35, 0.6)";
|
||||||
|
}
|
||||||
|
|
||||||
|
yLine.appendChild(line);
|
||||||
|
yLine.appendChild(text);
|
||||||
|
|
||||||
|
return yLine;
|
||||||
|
}
|
||||||
|
|
||||||
var UnitRenderer = (function() {
|
var UnitRenderer = (function() {
|
||||||
var UnitRenderer = function(total_height, zero_line, avg_unit_width) {
|
var UnitRenderer = function(total_height, zero_line, avg_unit_width) {
|
||||||
this.total_height = total_height;
|
this.total_height = total_height;
|
||||||
@ -169,7 +338,7 @@ var UnitRenderer = (function() {
|
|||||||
|
|
||||||
let [height, y] = get_bar_height_and_y_attr(y_top, this.zero_line, this.total_height);
|
let [height, y] = get_bar_height_and_y_attr(y_top, this.zero_line, this.total_height);
|
||||||
|
|
||||||
return $.createSVG('rect', {
|
return createSVG('rect', {
|
||||||
className: `bar mini`,
|
className: `bar mini`,
|
||||||
style: `fill: ${color}`,
|
style: `fill: ${color}`,
|
||||||
'data-point-index': index,
|
'data-point-index': index,
|
||||||
@ -181,7 +350,7 @@ var UnitRenderer = (function() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
draw_dot: function(x, y, args, color, index) {
|
draw_dot: function(x, y, args, color, index) {
|
||||||
return $.createSVG('circle', {
|
return createSVG('circle', {
|
||||||
style: `fill: ${color}`,
|
style: `fill: ${color}`,
|
||||||
'data-point-index': index,
|
'data-point-index': index,
|
||||||
cx: x,
|
cx: x,
|
||||||
@ -210,67 +379,6 @@ var UnitRenderer = (function() {
|
|||||||
return UnitRenderer;
|
return UnitRenderer;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
function make_x_line(height, text_start_at, point, label_class, axis_line_class, x_pos) {
|
|
||||||
let line = $.createSVG('line', {
|
|
||||||
x1: 0,
|
|
||||||
x2: 0,
|
|
||||||
y1: 0,
|
|
||||||
y2: height
|
|
||||||
});
|
|
||||||
|
|
||||||
let text = $.createSVG('text', {
|
|
||||||
className: label_class,
|
|
||||||
x: 0,
|
|
||||||
y: text_start_at,
|
|
||||||
dy: '.71em',
|
|
||||||
innerHTML: point
|
|
||||||
});
|
|
||||||
|
|
||||||
let x_line = $.createSVG('g', {
|
|
||||||
className: `tick ${axis_line_class}`,
|
|
||||||
transform: `translate(${ x_pos }, 0)`
|
|
||||||
});
|
|
||||||
|
|
||||||
x_line.appendChild(line);
|
|
||||||
x_line.appendChild(text);
|
|
||||||
|
|
||||||
return x_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
function make_y_line(start_at, width, text_end_at, point, label_class, axis_line_class, y_pos, darker=false, line_type="") {
|
|
||||||
let line = $.createSVG('line', {
|
|
||||||
className: line_type === "dashed" ? "dashed": "",
|
|
||||||
x1: start_at,
|
|
||||||
x2: width,
|
|
||||||
y1: 0,
|
|
||||||
y2: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
let text = $.createSVG('text', {
|
|
||||||
className: label_class,
|
|
||||||
x: text_end_at,
|
|
||||||
y: 0,
|
|
||||||
dy: '.32em',
|
|
||||||
innerHTML: point+""
|
|
||||||
});
|
|
||||||
|
|
||||||
let y_line = $.createSVG('g', {
|
|
||||||
className: `tick ${axis_line_class}`,
|
|
||||||
transform: `translate(0, ${y_pos})`,
|
|
||||||
'stroke-opacity': 1
|
|
||||||
});
|
|
||||||
|
|
||||||
if(darker) {
|
|
||||||
line.style.stroke = "rgba(27, 31, 35, 0.6)";
|
|
||||||
}
|
|
||||||
|
|
||||||
y_line.appendChild(line);
|
|
||||||
y_line.appendChild(text);
|
|
||||||
|
|
||||||
return y_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leveraging SMIL Animations
|
// Leveraging SMIL Animations
|
||||||
|
|
||||||
const EASING = {
|
const EASING = {
|
||||||
@ -706,6 +814,26 @@ class SvgTip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PRESET_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 DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
||||||
|
'yellow', 'green', 'light-green', 'purple', 'magenta'];
|
||||||
|
|
||||||
function limit_color(r){
|
function limit_color(r){
|
||||||
if (r > 255) return 255;
|
if (r > 255) return 255;
|
||||||
else if (r < 0) return 0;
|
else if (r < 0) return 0;
|
||||||
@ -731,25 +859,29 @@ function is_valid_color(string) {
|
|||||||
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(string);
|
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
const color_map = {
|
const get_color = (color) => {
|
||||||
'light-blue': '#7cd6fd',
|
return PRESET_COLOR_MAP[color] || color;
|
||||||
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) => {
|
const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
|
||||||
return color_map[color] || color;
|
|
||||||
|
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: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: 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: []
|
||||||
};
|
};
|
||||||
|
|
||||||
class BaseChart {
|
class BaseChart {
|
||||||
@ -764,7 +896,7 @@ class BaseChart {
|
|||||||
is_navigable = 0,
|
is_navigable = 0,
|
||||||
has_legend = 0,
|
has_legend = 0,
|
||||||
|
|
||||||
type = '', // eslint-disable-line no-unused-vars
|
type = '',
|
||||||
|
|
||||||
parent,
|
parent,
|
||||||
data
|
data
|
||||||
@ -785,55 +917,24 @@ class BaseChart {
|
|||||||
this.current_index = 0;
|
this.current_index = 0;
|
||||||
}
|
}
|
||||||
this.has_legend = has_legend;
|
this.has_legend = has_legend;
|
||||||
this.colors = colors;
|
|
||||||
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
this.colors = this.colors.map(color => get_color(color));
|
|
||||||
|
|
||||||
this.chart_types = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
|
|
||||||
|
|
||||||
|
this.setColors(colors, type);
|
||||||
this.set_margins(height);
|
this.set_margins(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
get_different_chart(type) {
|
get_different_chart(type) {
|
||||||
if(!this.chart_types.includes(type)) {
|
|
||||||
console.error(`'${type}' is not a valid chart type.`);
|
|
||||||
}
|
|
||||||
if(type === this.type) return;
|
if(type === this.type) return;
|
||||||
|
|
||||||
// Only across compatible types
|
if(!ALL_CHART_TYPES.includes(type)) {
|
||||||
let compatible_types = {
|
console.error(`'${type}' is not a valid chart type.`);
|
||||||
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: []
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only across compatible colors types
|
if(!COMPATIBLE_CHARTS[this.type].includes(type)) {
|
||||||
let color_compatible_types = {
|
|
||||||
bar: ['line', 'scatter'],
|
|
||||||
line: ['scatter', 'bar'],
|
|
||||||
pie: ['percentage'],
|
|
||||||
scatter: ['line', 'bar'],
|
|
||||||
percentage: ['pie'],
|
|
||||||
heatmap: []
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!compatible_types[this.type].includes(type)) {
|
|
||||||
console.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);
|
console.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// whether the new chart can use the existing colors
|
// whether the new chart can use the existing colors
|
||||||
const use_color = color_compatible_types[this.type].includes(type);
|
const use_color = COLOR_COMPATIBLE_CHARTS[this.type].includes(type);
|
||||||
|
|
||||||
// Okay, this is anticlimactic
|
// Okay, this is anticlimactic
|
||||||
// this function will need to actually be 'change_chart_type(type)'
|
// this function will need to actually be 'change_chart_type(type)'
|
||||||
@ -848,6 +949,21 @@ class BaseChart {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setColors(colors, type) {
|
||||||
|
this.colors = colors;
|
||||||
|
|
||||||
|
// TODO: Needs structure as per only labels/datasets
|
||||||
|
const list = type === 'percentage' || type === 'pie'
|
||||||
|
? this.data.labels
|
||||||
|
: this.data.datasets;
|
||||||
|
|
||||||
|
if(!this.colors || (list && this.colors.length < list.length)) {
|
||||||
|
this.colors = DEFAULT_COLORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.colors = this.colors.map(color => get_color(color));
|
||||||
|
}
|
||||||
|
|
||||||
set_margins(height) {
|
set_margins(height) {
|
||||||
this.base_height = height;
|
this.base_height = height;
|
||||||
this.height = height - 40;
|
this.height = height - 40;
|
||||||
@ -935,26 +1051,22 @@ class BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make_chart_area() {
|
make_chart_area() {
|
||||||
this.svg = $.createSVG('svg', {
|
this.svg = makeSVGContainer(
|
||||||
className: 'chart',
|
this.chart_wrapper,
|
||||||
inside: this.chart_wrapper,
|
'chart',
|
||||||
width: this.base_width,
|
this.base_width,
|
||||||
height: this.base_height
|
this.base_height
|
||||||
});
|
);
|
||||||
|
this.svg_defs = makeSVGDefs(this.svg);
|
||||||
this.svg_defs = $.createSVG('defs', {
|
|
||||||
inside: this.svg,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.svg;
|
return this.svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_draw_area() {
|
make_draw_area() {
|
||||||
this.draw_area = $.createSVG("g", {
|
this.draw_area = makeSVGGroup(
|
||||||
className: this.type + '-chart',
|
this.svg,
|
||||||
inside: this.svg,
|
this.type + '-chart',
|
||||||
transform: `translate(${this.translate_x}, ${this.translate_y})`
|
`translate(${this.translate_x}, ${this.translate_y})`
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_components() { }
|
setup_components() { }
|
||||||
@ -1043,6 +1155,10 @@ class BaseChart {
|
|||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
setup_utils() { }
|
setup_utils() { }
|
||||||
|
|
||||||
|
makeDrawAreaComponent(className, transform='') {
|
||||||
|
return makeSVGGroup(this.draw_area, className, transform);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AxisChart extends BaseChart {
|
class AxisChart extends BaseChart {
|
||||||
@ -1146,23 +1262,21 @@ class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_marker_components() {
|
setup_marker_components() {
|
||||||
this.y_axis_group = $.createSVG('g', {className: 'y axis', inside: this.draw_area});
|
this.y_axis_group = this.makeDrawAreaComponent('y axis');
|
||||||
this.x_axis_group = $.createSVG('g', {className: 'x axis', inside: this.draw_area});
|
this.x_axis_group = this.makeDrawAreaComponent('x axis');
|
||||||
this.specific_y_group = $.createSVG('g', {className: 'specific axis', inside: this.draw_area});
|
this.specific_y_group = this.makeDrawAreaComponent('specific axis');
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_aggregation_components() {
|
setup_aggregation_components() {
|
||||||
this.sum_group = $.createSVG('g', {className: 'data-points', inside: this.draw_area});
|
this.sum_group = this.makeDrawAreaComponent('data-points');
|
||||||
this.average_group = $.createSVG('g', {className: 'chart-area', inside: this.draw_area});
|
this.average_group = this.makeDrawAreaComponent('chart-area');
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_graph_components() {
|
setup_graph_components() {
|
||||||
this.svg_units_groups = [];
|
this.svg_units_groups = [];
|
||||||
this.y.map((d, i) => {
|
this.y.map((d, i) => {
|
||||||
this.svg_units_groups[i] = $.createSVG('g', {
|
this.svg_units_groups[i] = this.makeDrawAreaComponent(
|
||||||
className: 'data-points data-points-' + i,
|
'data-points data-points-' + i);
|
||||||
inside: this.draw_area
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1216,7 +1330,7 @@ class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.x_axis_group.appendChild(
|
this.x_axis_group.appendChild(
|
||||||
make_x_line(
|
makeXLine(
|
||||||
height,
|
height,
|
||||||
text_start_at,
|
text_start_at,
|
||||||
point,
|
point,
|
||||||
@ -1241,7 +1355,7 @@ class AxisChart extends BaseChart {
|
|||||||
this.y_axis_group.textContent = '';
|
this.y_axis_group.textContent = '';
|
||||||
this.y_axis_values.map((value, i) => {
|
this.y_axis_values.map((value, i) => {
|
||||||
this.y_axis_group.appendChild(
|
this.y_axis_group.appendChild(
|
||||||
make_y_line(
|
makeYLine(
|
||||||
start_at,
|
start_at,
|
||||||
width,
|
width,
|
||||||
text_end_at,
|
text_end_at,
|
||||||
@ -1365,7 +1479,7 @@ class AxisChart extends BaseChart {
|
|||||||
this.specific_y_group.textContent = '';
|
this.specific_y_group.textContent = '';
|
||||||
this.specific_values.map(d => {
|
this.specific_values.map(d => {
|
||||||
this.specific_y_group.appendChild(
|
this.specific_y_group.appendChild(
|
||||||
make_y_line(
|
makeYLine(
|
||||||
0,
|
0,
|
||||||
this.width,
|
this.width,
|
||||||
this.width + 5,
|
this.width + 5,
|
||||||
@ -1448,7 +1562,7 @@ class AxisChart extends BaseChart {
|
|||||||
this.make_new_units_for_dataset(
|
this.make_new_units_for_dataset(
|
||||||
this.x_axis_positions,
|
this.x_axis_positions,
|
||||||
this.y_sums.map( val => float_2(this.zero_line - val * this.multiplier)),
|
this.y_sums.map( val => float_2(this.zero_line - val * this.multiplier)),
|
||||||
'light-grey',
|
'#f0f4f7',
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
this.sum_group,
|
this.sum_group,
|
||||||
@ -1706,7 +1820,7 @@ class AxisChart extends BaseChart {
|
|||||||
if(typeof new_pos === 'string') {
|
if(typeof new_pos === 'string') {
|
||||||
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
|
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
|
||||||
}
|
}
|
||||||
const x_line = make_x_line(
|
const x_line = makeXLine(
|
||||||
height,
|
height,
|
||||||
text_start_at,
|
text_start_at,
|
||||||
value, // new value
|
value, // new value
|
||||||
@ -1832,7 +1946,7 @@ class AxisChart extends BaseChart {
|
|||||||
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);
|
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);
|
||||||
let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
|
let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
|
||||||
value = !specific ? value : (value+"").toUpperCase();
|
value = !specific ? value : (value+"").toUpperCase();
|
||||||
const y_line = make_y_line(
|
const y_line = makeYLine(
|
||||||
start_at,
|
start_at,
|
||||||
width,
|
width,
|
||||||
text_end_at,
|
text_end_at,
|
||||||
@ -2001,10 +2115,10 @@ class LineChart extends AxisChart {
|
|||||||
setup_path_groups() {
|
setup_path_groups() {
|
||||||
this.paths_groups = [];
|
this.paths_groups = [];
|
||||||
this.y.map((d, i) => {
|
this.y.map((d, i) => {
|
||||||
this.paths_groups[i] = $.createSVG('g', {
|
this.paths_groups[i] = makeSVGGroup(
|
||||||
className: 'path-group path-group-' + i,
|
this.draw_area,
|
||||||
inside: this.draw_area
|
'path-group path-group-' + i
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2036,14 +2150,11 @@ class LineChart extends AxisChart {
|
|||||||
|
|
||||||
this.paths_groups[i].textContent = '';
|
this.paths_groups[i].textContent = '';
|
||||||
|
|
||||||
d.path = $.createSVG('path', {
|
d.path = makePath("M"+points_str, 'line-graph-path', color);
|
||||||
inside: this.paths_groups[i],
|
this.paths_groups[i].appendChild(d.path);
|
||||||
style: `stroke: ${color}`,
|
|
||||||
d: "M"+points_str
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.heatline) {
|
if(this.heatline) {
|
||||||
let gradient_id = this.make_gradient(color);
|
let gradient_id = makeGradient(this.svg_defs, color);
|
||||||
d.path.style.stroke = `url(#${gradient_id})`;
|
d.path.style.stroke = `url(#${gradient_id})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2053,50 +2164,11 @@ class LineChart extends AxisChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fill_region_for_dataset(d, i, color, points_str) {
|
fill_region_for_dataset(d, i, color, points_str) {
|
||||||
let gradient_id = this.make_gradient(color, true);
|
let gradient_id = makeGradient(this.svg_defs, color, true);
|
||||||
|
let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`;
|
||||||
|
|
||||||
d.region_path = $.createSVG('path', {
|
d.region_path = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`);
|
||||||
inside: this.paths_groups[i],
|
this.paths_groups[i].appendChild(d.region_path);
|
||||||
className: `region-fill`,
|
|
||||||
d: "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
d.region_path.style.stroke = "none";
|
|
||||||
d.region_path.style.fill = `url(#${gradient_id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
make_gradient(color, lighter = false) {
|
|
||||||
let gradient_id ='path-fill-gradient' + '-' + color;
|
|
||||||
|
|
||||||
let gradient_def = $.createSVG('linearGradient', {
|
|
||||||
inside: this.svg_defs,
|
|
||||||
id: gradient_id,
|
|
||||||
x1: 0,
|
|
||||||
x2: 0,
|
|
||||||
y1: 0,
|
|
||||||
y2: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
let set_gradient_stop = (grad_elem, offset, color, opacity) => {
|
|
||||||
$.createSVG('stop', {
|
|
||||||
style: `stop-color: ${color}`,
|
|
||||||
inside: grad_elem,
|
|
||||||
'offset': offset,
|
|
||||||
'stop-opacity': opacity
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let opacities = [1, 0.6, 0.2];
|
|
||||||
|
|
||||||
if(lighter) {
|
|
||||||
opacities = [0.4, 0.2, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
set_gradient_stop(gradient_def, "0%", color, opacities[0]);
|
|
||||||
set_gradient_stop(gradient_def, "50%", color, opacities[1]);
|
|
||||||
set_gradient_stop(gradient_def, "100%", color, opacities[2]);
|
|
||||||
|
|
||||||
return gradient_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2351,13 +2423,10 @@ class PieChart extends BaseChart {
|
|||||||
curEnd = endPosition;
|
curEnd = endPosition;
|
||||||
}
|
}
|
||||||
const curPath = this.makeArcPath(curStart,curEnd);
|
const curPath = this.makeArcPath(curStart,curEnd);
|
||||||
let slice = $.createSVG('path',{
|
let slice = makePath(curPath, 'pie-path', 'none', this.colors[i]);
|
||||||
inside: this.draw_area,
|
slice.style.transition = 'transform .3s;';
|
||||||
className: 'pie-path',
|
this.draw_area.appendChild(slice);
|
||||||
style: 'transition:transform .3s;',
|
|
||||||
d: curPath,
|
|
||||||
fill: this.colors[i]
|
|
||||||
});
|
|
||||||
this.slices.push(slice);
|
this.slices.push(slice);
|
||||||
this.slicesProperties.push({
|
this.slicesProperties.push({
|
||||||
startPosition,
|
startPosition,
|
||||||
@ -2413,7 +2482,7 @@ class PieChart extends BaseChart {
|
|||||||
const color = this.colors[i];
|
const color = this.colors[i];
|
||||||
if(flag){
|
if(flag){
|
||||||
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
|
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
|
||||||
path.setAttribute('fill',lighten_darken_color(color,50));
|
path.style.fill = lighten_darken_color(color,50);
|
||||||
let g_off = $.offset(this.svg);
|
let g_off = $.offset(this.svg);
|
||||||
let x = e.pageX - g_off.left + 10;
|
let x = e.pageX - g_off.left + 10;
|
||||||
let y = e.pageY - g_off.top - 10;
|
let y = e.pageY - g_off.top - 10;
|
||||||
@ -2425,7 +2494,7 @@ class PieChart extends BaseChart {
|
|||||||
}else{
|
}else{
|
||||||
transform(path,'translate3d(0,0,0)');
|
transform(path,'translate3d(0,0,0)');
|
||||||
this.tip.hide_tip();
|
this.tip.hide_tip();
|
||||||
path.setAttribute('fill',color);
|
path.style.fill = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2584,15 +2653,13 @@ class Heatmap extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_components() {
|
setup_components() {
|
||||||
this.domain_label_group = $.createSVG("g", {
|
this.domain_label_group = this.makeDrawAreaComponent(
|
||||||
className: "domain-label-group chart-label",
|
'domain-label-group chart-label');
|
||||||
inside: this.draw_area
|
|
||||||
});
|
this.data_groups = this.makeDrawAreaComponent(
|
||||||
this.data_groups = $.createSVG("g", {
|
'data-groups',
|
||||||
className: "data-groups",
|
`translate(0, 20)`
|
||||||
inside: this.draw_area,
|
);
|
||||||
transform: `translate(0, 20)`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_values() {
|
setup_values() {
|
||||||
@ -2647,10 +2714,7 @@ class Heatmap extends BaseChart {
|
|||||||
let month_change = 0;
|
let month_change = 0;
|
||||||
let week_col_change = 0;
|
let week_col_change = 0;
|
||||||
|
|
||||||
let data_group = $.createSVG("g", {
|
let data_group = makeSVGGroup(this.data_groups, 'data-group');
|
||||||
className: "data-group",
|
|
||||||
inside: this.data_groups
|
|
||||||
});
|
|
||||||
|
|
||||||
for(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {
|
for(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {
|
||||||
let data_value = 0;
|
let data_value = 0;
|
||||||
@ -2673,18 +2737,15 @@ class Heatmap extends BaseChart {
|
|||||||
|
|
||||||
let x = 13 + (index + week_col_change) * 12;
|
let x = 13 + (index + week_col_change) * 12;
|
||||||
|
|
||||||
$.createSVG("rect", {
|
let dataAttr = {
|
||||||
className: 'day',
|
|
||||||
inside: data_group,
|
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
width: square_side,
|
|
||||||
height: square_side,
|
|
||||||
fill: this.legend_colors[color_index],
|
|
||||||
'data-date': get_dd_mm_yyyy(current_date),
|
'data-date': get_dd_mm_yyyy(current_date),
|
||||||
'data-value': data_value,
|
'data-value': data_value,
|
||||||
'data-day': current_date.getDay()
|
'data-day': current_date.getDay()
|
||||||
});
|
};
|
||||||
|
let heatSquare = makeHeatSquare('day', x, y, square_side,
|
||||||
|
this.legend_colors[color_index], dataAttr);
|
||||||
|
|
||||||
|
data_group.appendChild(heatSquare);
|
||||||
|
|
||||||
let next_date = new Date(current_date);
|
let next_date = new Date(current_date);
|
||||||
add_days(next_date, 1);
|
add_days(next_date, 1);
|
||||||
@ -2727,15 +2788,8 @@ class Heatmap extends BaseChart {
|
|||||||
|
|
||||||
this.month_start_points.map((start, i) => {
|
this.month_start_points.map((start, i) => {
|
||||||
let month_name = this.month_names[this.months[i]].substring(0, 3);
|
let month_name = this.month_names[this.months[i]].substring(0, 3);
|
||||||
|
let text = makeText('y-value-text', start+12, 10, month_name);
|
||||||
$.createSVG('text', {
|
this.domain_label_group.appendChild(text);
|
||||||
className: 'y-value-text',
|
|
||||||
inside: this.domain_label_group,
|
|
||||||
x: start + 12,
|
|
||||||
y: 10,
|
|
||||||
dy: '.32em',
|
|
||||||
innerHTML: month_name
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
dist/frappe-charts.min.cjs.js
vendored
2
dist/frappe-charts.min.cjs.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.esm.js
vendored
2
dist/frappe-charts.min.esm.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.iife.js
vendored
2
dist/frappe-charts.min.iife.js
vendored
File diff suppressed because one or more lines are too long
2
docs/assets/js/frappe-charts.min.js
vendored
2
docs/assets/js/frappe-charts.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
import $ from '../utils/dom';
|
import $ from '../utils/dom';
|
||||||
import { UnitRenderer, make_x_line, make_y_line } from '../utils/draw';
|
import { UnitRenderer, makeXLine, makeYLine } from '../utils/draw';
|
||||||
import { runSVGAnimation } from '../utils/animate';
|
import { runSVGAnimation } from '../utils/animation';
|
||||||
import { calcIntervals } from '../utils/intervals';
|
import { calcIntervals } from '../utils/intervals';
|
||||||
import { float_2, arrays_equal, get_string_width } from '../utils/helpers';
|
import { float_2, arrays_equal, get_string_width } from '../utils/helpers';
|
||||||
import BaseChart from './BaseChart';
|
import BaseChart from './BaseChart';
|
||||||
@ -106,23 +106,21 @@ export default class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_marker_components() {
|
setup_marker_components() {
|
||||||
this.y_axis_group = $.createSVG('g', {className: 'y axis', inside: this.draw_area});
|
this.y_axis_group = this.makeDrawAreaComponent('y axis');
|
||||||
this.x_axis_group = $.createSVG('g', {className: 'x axis', inside: this.draw_area});
|
this.x_axis_group = this.makeDrawAreaComponent('x axis');
|
||||||
this.specific_y_group = $.createSVG('g', {className: 'specific axis', inside: this.draw_area});
|
this.specific_y_group = this.makeDrawAreaComponent('specific axis');
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_aggregation_components() {
|
setup_aggregation_components() {
|
||||||
this.sum_group = $.createSVG('g', {className: 'data-points', inside: this.draw_area});
|
this.sum_group = this.makeDrawAreaComponent('data-points');
|
||||||
this.average_group = $.createSVG('g', {className: 'chart-area', inside: this.draw_area});
|
this.average_group = this.makeDrawAreaComponent('chart-area');
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_graph_components() {
|
setup_graph_components() {
|
||||||
this.svg_units_groups = [];
|
this.svg_units_groups = [];
|
||||||
this.y.map((d, i) => {
|
this.y.map((d, i) => {
|
||||||
this.svg_units_groups[i] = $.createSVG('g', {
|
this.svg_units_groups[i] = this.makeDrawAreaComponent(
|
||||||
className: 'data-points data-points-' + i,
|
'data-points data-points-' + i);
|
||||||
inside: this.draw_area
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +174,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.x_axis_group.appendChild(
|
this.x_axis_group.appendChild(
|
||||||
make_x_line(
|
makeXLine(
|
||||||
height,
|
height,
|
||||||
text_start_at,
|
text_start_at,
|
||||||
point,
|
point,
|
||||||
@ -201,7 +199,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.y_axis_group.textContent = '';
|
this.y_axis_group.textContent = '';
|
||||||
this.y_axis_values.map((value, i) => {
|
this.y_axis_values.map((value, i) => {
|
||||||
this.y_axis_group.appendChild(
|
this.y_axis_group.appendChild(
|
||||||
make_y_line(
|
makeYLine(
|
||||||
start_at,
|
start_at,
|
||||||
width,
|
width,
|
||||||
text_end_at,
|
text_end_at,
|
||||||
@ -325,7 +323,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.specific_y_group.textContent = '';
|
this.specific_y_group.textContent = '';
|
||||||
this.specific_values.map(d => {
|
this.specific_values.map(d => {
|
||||||
this.specific_y_group.appendChild(
|
this.specific_y_group.appendChild(
|
||||||
make_y_line(
|
makeYLine(
|
||||||
0,
|
0,
|
||||||
this.width,
|
this.width,
|
||||||
this.width + 5,
|
this.width + 5,
|
||||||
@ -408,7 +406,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.make_new_units_for_dataset(
|
this.make_new_units_for_dataset(
|
||||||
this.x_axis_positions,
|
this.x_axis_positions,
|
||||||
this.y_sums.map( val => float_2(this.zero_line - val * this.multiplier)),
|
this.y_sums.map( val => float_2(this.zero_line - val * this.multiplier)),
|
||||||
'light-grey',
|
'#f0f4f7',
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
this.sum_group,
|
this.sum_group,
|
||||||
@ -666,7 +664,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
if(typeof new_pos === 'string') {
|
if(typeof new_pos === 'string') {
|
||||||
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
|
new_pos = parseInt(new_pos.substring(0, new_pos.length-1));
|
||||||
}
|
}
|
||||||
const x_line = make_x_line(
|
const x_line = makeXLine(
|
||||||
height,
|
height,
|
||||||
text_start_at,
|
text_start_at,
|
||||||
value, // new value
|
value, // new value
|
||||||
@ -792,7 +790,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);
|
let [width, text_end_at, axis_line_class, start_at] = this.get_y_axis_line_props(specific);
|
||||||
let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
|
let axis_label_class = !specific ? 'y-value-text' : 'specific-value';
|
||||||
value = !specific ? value : (value+"").toUpperCase();
|
value = !specific ? value : (value+"").toUpperCase();
|
||||||
const y_line = make_y_line(
|
const y_line = makeYLine(
|
||||||
start_at,
|
start_at,
|
||||||
width,
|
width,
|
||||||
text_end_at,
|
text_end_at,
|
||||||
|
|||||||
@ -1,9 +1,31 @@
|
|||||||
import SvgTip from '../objects/SvgTip';
|
import SvgTip from '../objects/SvgTip';
|
||||||
import $ from '../utils/dom';
|
import $ from '../utils/dom';
|
||||||
|
import { makeSVGContainer, makeSVGDefs, makeSVGGroup } from '../utils/draw';
|
||||||
import { get_string_width } from '../utils/helpers';
|
import { get_string_width } from '../utils/helpers';
|
||||||
import { get_color } from '../utils/colors';
|
import { get_color, DEFAULT_COLORS } from '../utils/colors';
|
||||||
import Chart from '../charts';
|
import Chart from '../charts';
|
||||||
|
|
||||||
|
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: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: 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 default class BaseChart {
|
export default class BaseChart {
|
||||||
constructor({
|
constructor({
|
||||||
height = 240,
|
height = 240,
|
||||||
@ -16,7 +38,7 @@ export default class BaseChart {
|
|||||||
is_navigable = 0,
|
is_navigable = 0,
|
||||||
has_legend = 0,
|
has_legend = 0,
|
||||||
|
|
||||||
type = '', // eslint-disable-line no-unused-vars
|
type = '',
|
||||||
|
|
||||||
parent,
|
parent,
|
||||||
data
|
data
|
||||||
@ -37,55 +59,24 @@ export default class BaseChart {
|
|||||||
this.current_index = 0;
|
this.current_index = 0;
|
||||||
}
|
}
|
||||||
this.has_legend = has_legend;
|
this.has_legend = has_legend;
|
||||||
this.colors = colors;
|
|
||||||
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
this.colors = this.colors.map(color => get_color(color));
|
|
||||||
|
|
||||||
this.chart_types = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
|
|
||||||
|
|
||||||
|
this.setColors(colors, type);
|
||||||
this.set_margins(height);
|
this.set_margins(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
get_different_chart(type) {
|
get_different_chart(type) {
|
||||||
if(!this.chart_types.includes(type)) {
|
|
||||||
console.error(`'${type}' is not a valid chart type.`);
|
|
||||||
}
|
|
||||||
if(type === this.type) return;
|
if(type === this.type) return;
|
||||||
|
|
||||||
// Only across compatible types
|
if(!ALL_CHART_TYPES.includes(type)) {
|
||||||
let compatible_types = {
|
console.error(`'${type}' is not a valid chart type.`);
|
||||||
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: []
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only across compatible colors types
|
if(!COMPATIBLE_CHARTS[this.type].includes(type)) {
|
||||||
let color_compatible_types = {
|
|
||||||
bar: ['line', 'scatter'],
|
|
||||||
line: ['scatter', 'bar'],
|
|
||||||
pie: ['percentage'],
|
|
||||||
scatter: ['line', 'bar'],
|
|
||||||
percentage: ['pie'],
|
|
||||||
heatmap: []
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!compatible_types[this.type].includes(type)) {
|
|
||||||
console.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);
|
console.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// whether the new chart can use the existing colors
|
// whether the new chart can use the existing colors
|
||||||
const use_color = color_compatible_types[this.type].includes(type);
|
const use_color = COLOR_COMPATIBLE_CHARTS[this.type].includes(type);
|
||||||
|
|
||||||
// Okay, this is anticlimactic
|
// Okay, this is anticlimactic
|
||||||
// this function will need to actually be 'change_chart_type(type)'
|
// this function will need to actually be 'change_chart_type(type)'
|
||||||
@ -100,6 +91,21 @@ export default class BaseChart {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setColors(colors, type) {
|
||||||
|
this.colors = colors;
|
||||||
|
|
||||||
|
// TODO: Needs structure as per only labels/datasets
|
||||||
|
const list = type === 'percentage' || type === 'pie'
|
||||||
|
? this.data.labels
|
||||||
|
: this.data.datasets;
|
||||||
|
|
||||||
|
if(!this.colors || (list && this.colors.length < list.length)) {
|
||||||
|
this.colors = DEFAULT_COLORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.colors = this.colors.map(color => get_color(color));
|
||||||
|
}
|
||||||
|
|
||||||
set_margins(height) {
|
set_margins(height) {
|
||||||
this.base_height = height;
|
this.base_height = height;
|
||||||
this.height = height - 40;
|
this.height = height - 40;
|
||||||
@ -187,26 +193,22 @@ export default class BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
make_chart_area() {
|
make_chart_area() {
|
||||||
this.svg = $.createSVG('svg', {
|
this.svg = makeSVGContainer(
|
||||||
className: 'chart',
|
this.chart_wrapper,
|
||||||
inside: this.chart_wrapper,
|
'chart',
|
||||||
width: this.base_width,
|
this.base_width,
|
||||||
height: this.base_height
|
this.base_height
|
||||||
});
|
);
|
||||||
|
this.svg_defs = makeSVGDefs(this.svg);
|
||||||
this.svg_defs = $.createSVG('defs', {
|
|
||||||
inside: this.svg,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.svg;
|
return this.svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_draw_area() {
|
make_draw_area() {
|
||||||
this.draw_area = $.createSVG("g", {
|
this.draw_area = makeSVGGroup(
|
||||||
className: this.type + '-chart',
|
this.svg,
|
||||||
inside: this.svg,
|
this.type + '-chart',
|
||||||
transform: `translate(${this.translate_x}, ${this.translate_y})`
|
`translate(${this.translate_x}, ${this.translate_y})`
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_components() { }
|
setup_components() { }
|
||||||
@ -295,4 +297,8 @@ export default class BaseChart {
|
|||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
setup_utils() { }
|
setup_utils() { }
|
||||||
|
|
||||||
|
makeDrawAreaComponent(className, transform='') {
|
||||||
|
return makeSVGGroup(this.draw_area, className, transform);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import BaseChart from './BaseChart';
|
import BaseChart from './BaseChart';
|
||||||
import $ from '../utils/dom';
|
import { makeSVGGroup, makeHeatSquare, makeText } from '../utils/draw';
|
||||||
import { add_days, get_dd_mm_yyyy, get_weeks_between } from '../utils/date-utils';
|
import { add_days, get_dd_mm_yyyy, get_weeks_between } from '../utils/date-utils';
|
||||||
import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
|
import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
|
||||||
import { is_valid_color } from '../utils/colors';
|
import { is_valid_color } from '../utils/colors';
|
||||||
@ -81,15 +81,13 @@ export default class Heatmap extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_components() {
|
setup_components() {
|
||||||
this.domain_label_group = $.createSVG("g", {
|
this.domain_label_group = this.makeDrawAreaComponent(
|
||||||
className: "domain-label-group chart-label",
|
'domain-label-group chart-label');
|
||||||
inside: this.draw_area
|
|
||||||
});
|
this.data_groups = this.makeDrawAreaComponent(
|
||||||
this.data_groups = $.createSVG("g", {
|
'data-groups',
|
||||||
className: "data-groups",
|
`translate(0, 20)`
|
||||||
inside: this.draw_area,
|
);
|
||||||
transform: `translate(0, 20)`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_values() {
|
setup_values() {
|
||||||
@ -144,10 +142,7 @@ export default class Heatmap extends BaseChart {
|
|||||||
let month_change = 0;
|
let month_change = 0;
|
||||||
let week_col_change = 0;
|
let week_col_change = 0;
|
||||||
|
|
||||||
let data_group = $.createSVG("g", {
|
let data_group = makeSVGGroup(this.data_groups, 'data-group');
|
||||||
className: "data-group",
|
|
||||||
inside: this.data_groups
|
|
||||||
});
|
|
||||||
|
|
||||||
for(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {
|
for(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {
|
||||||
let data_value = 0;
|
let data_value = 0;
|
||||||
@ -170,18 +165,15 @@ export default class Heatmap extends BaseChart {
|
|||||||
|
|
||||||
let x = 13 + (index + week_col_change) * 12;
|
let x = 13 + (index + week_col_change) * 12;
|
||||||
|
|
||||||
$.createSVG("rect", {
|
let dataAttr = {
|
||||||
className: 'day',
|
|
||||||
inside: data_group,
|
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
width: square_side,
|
|
||||||
height: square_side,
|
|
||||||
fill: this.legend_colors[color_index],
|
|
||||||
'data-date': get_dd_mm_yyyy(current_date),
|
'data-date': get_dd_mm_yyyy(current_date),
|
||||||
'data-value': data_value,
|
'data-value': data_value,
|
||||||
'data-day': current_date.getDay()
|
'data-day': current_date.getDay()
|
||||||
});
|
};
|
||||||
|
let heatSquare = makeHeatSquare('day', x, y, square_side,
|
||||||
|
this.legend_colors[color_index], dataAttr);
|
||||||
|
|
||||||
|
data_group.appendChild(heatSquare);
|
||||||
|
|
||||||
let next_date = new Date(current_date);
|
let next_date = new Date(current_date);
|
||||||
add_days(next_date, 1);
|
add_days(next_date, 1);
|
||||||
@ -224,15 +216,8 @@ export default class Heatmap extends BaseChart {
|
|||||||
|
|
||||||
this.month_start_points.map((start, i) => {
|
this.month_start_points.map((start, i) => {
|
||||||
let month_name = this.month_names[this.months[i]].substring(0, 3);
|
let month_name = this.month_names[this.months[i]].substring(0, 3);
|
||||||
|
let text = makeText('y-value-text', start+12, 10, month_name);
|
||||||
$.createSVG('text', {
|
this.domain_label_group.appendChild(text);
|
||||||
className: 'y-value-text',
|
|
||||||
inside: this.domain_label_group,
|
|
||||||
x: start + 12,
|
|
||||||
y: 10,
|
|
||||||
dy: '.32em',
|
|
||||||
innerHTML: month_name
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import AxisChart from './AxisChart';
|
import AxisChart from './AxisChart';
|
||||||
import $ from '../utils/dom';
|
import { makeSVGGroup, makePath, makeGradient } from '../utils/draw';
|
||||||
|
|
||||||
export default class LineChart extends AxisChart {
|
export default class LineChart extends AxisChart {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
@ -33,10 +33,10 @@ export default class LineChart extends AxisChart {
|
|||||||
setup_path_groups() {
|
setup_path_groups() {
|
||||||
this.paths_groups = [];
|
this.paths_groups = [];
|
||||||
this.y.map((d, i) => {
|
this.y.map((d, i) => {
|
||||||
this.paths_groups[i] = $.createSVG('g', {
|
this.paths_groups[i] = makeSVGGroup(
|
||||||
className: 'path-group path-group-' + i,
|
this.draw_area,
|
||||||
inside: this.draw_area
|
'path-group path-group-' + i
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,14 +68,11 @@ export default class LineChart extends AxisChart {
|
|||||||
|
|
||||||
this.paths_groups[i].textContent = '';
|
this.paths_groups[i].textContent = '';
|
||||||
|
|
||||||
d.path = $.createSVG('path', {
|
d.path = makePath("M"+points_str, 'line-graph-path', color);
|
||||||
inside: this.paths_groups[i],
|
this.paths_groups[i].appendChild(d.path);
|
||||||
style: `stroke: ${color}`,
|
|
||||||
d: "M"+points_str
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.heatline) {
|
if(this.heatline) {
|
||||||
let gradient_id = this.make_gradient(color);
|
let gradient_id = makeGradient(this.svg_defs, color);
|
||||||
d.path.style.stroke = `url(#${gradient_id})`;
|
d.path.style.stroke = `url(#${gradient_id})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,49 +82,10 @@ export default class LineChart extends AxisChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fill_region_for_dataset(d, i, color, points_str) {
|
fill_region_for_dataset(d, i, color, points_str) {
|
||||||
let gradient_id = this.make_gradient(color, true);
|
let gradient_id = makeGradient(this.svg_defs, color, true);
|
||||||
|
let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`;
|
||||||
|
|
||||||
d.region_path = $.createSVG('path', {
|
d.region_path = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`);
|
||||||
inside: this.paths_groups[i],
|
this.paths_groups[i].appendChild(d.region_path);
|
||||||
className: `region-fill`,
|
|
||||||
d: "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
d.region_path.style.stroke = "none";
|
|
||||||
d.region_path.style.fill = `url(#${gradient_id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
make_gradient(color, lighter = false) {
|
|
||||||
let gradient_id ='path-fill-gradient' + '-' + color;
|
|
||||||
|
|
||||||
let gradient_def = $.createSVG('linearGradient', {
|
|
||||||
inside: this.svg_defs,
|
|
||||||
id: gradient_id,
|
|
||||||
x1: 0,
|
|
||||||
x2: 0,
|
|
||||||
y1: 0,
|
|
||||||
y2: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
let set_gradient_stop = (grad_elem, offset, color, opacity) => {
|
|
||||||
$.createSVG('stop', {
|
|
||||||
style: `stop-color: ${color}`,
|
|
||||||
inside: grad_elem,
|
|
||||||
'offset': offset,
|
|
||||||
'stop-opacity': opacity
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let opacities = [1, 0.6, 0.2];
|
|
||||||
|
|
||||||
if(lighter) {
|
|
||||||
opacities = [0.4, 0.2, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
set_gradient_stop(gradient_def, "0%", color, opacities[0]);
|
|
||||||
set_gradient_stop(gradient_def, "50%", color, opacities[1]);
|
|
||||||
set_gradient_stop(gradient_def, "100%", color, opacities[2]);
|
|
||||||
|
|
||||||
return gradient_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import BaseChart from './BaseChart';
|
import BaseChart from './BaseChart';
|
||||||
import $ from '../utils/dom';
|
import $ from '../utils/dom';
|
||||||
|
import { makePath } from '../utils/draw';
|
||||||
import { lighten_darken_color } from '../utils/colors';
|
import { lighten_darken_color } from '../utils/colors';
|
||||||
import { runSVGAnimation, transform } from '../utils/animate';
|
import { runSVGAnimation, transform } from '../utils/animation';
|
||||||
const ANGLE_RATIO = Math.PI / 180;
|
const ANGLE_RATIO = Math.PI / 180;
|
||||||
const FULL_ANGLE = 360;
|
const FULL_ANGLE = 360;
|
||||||
|
|
||||||
@ -93,13 +94,10 @@ export default class PieChart extends BaseChart {
|
|||||||
curEnd = endPosition;
|
curEnd = endPosition;
|
||||||
}
|
}
|
||||||
const curPath = this.makeArcPath(curStart,curEnd);
|
const curPath = this.makeArcPath(curStart,curEnd);
|
||||||
let slice = $.createSVG('path',{
|
let slice = makePath(curPath, 'pie-path', 'none', this.colors[i]);
|
||||||
inside: this.draw_area,
|
slice.style.transition = 'transform .3s;';
|
||||||
className: 'pie-path',
|
this.draw_area.appendChild(slice);
|
||||||
style: 'transition:transform .3s;',
|
|
||||||
d: curPath,
|
|
||||||
fill: this.colors[i]
|
|
||||||
});
|
|
||||||
this.slices.push(slice);
|
this.slices.push(slice);
|
||||||
this.slicesProperties.push({
|
this.slicesProperties.push({
|
||||||
startPosition,
|
startPosition,
|
||||||
@ -155,7 +153,7 @@ export default class PieChart extends BaseChart {
|
|||||||
const color = this.colors[i];
|
const color = this.colors[i];
|
||||||
if(flag){
|
if(flag){
|
||||||
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
|
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
|
||||||
path.setAttribute('fill',lighten_darken_color(color,50));
|
path.style.fill = lighten_darken_color(color,50);
|
||||||
let g_off = $.offset(this.svg);
|
let g_off = $.offset(this.svg);
|
||||||
let x = e.pageX - g_off.left + 10;
|
let x = e.pageX - g_off.left + 10;
|
||||||
let y = e.pageY - g_off.top - 10;
|
let y = e.pageY - g_off.top - 10;
|
||||||
@ -167,7 +165,7 @@ export default class PieChart extends BaseChart {
|
|||||||
}else{
|
}else{
|
||||||
transform(path,'translate3d(0,0,0)');
|
transform(path,'translate3d(0,0,0)');
|
||||||
this.tip.hide_tip();
|
this.tip.hide_tip();
|
||||||
path.setAttribute('fill',color);
|
path.style.fill = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,101 +1,4 @@
|
|||||||
// Leveraging SMIL Animations
|
|
||||||
|
|
||||||
const EASING = {
|
export function getAnimXLine() {}
|
||||||
ease: "0.25 0.1 0.25 1",
|
|
||||||
linear: "0 0 1 1",
|
|
||||||
// easein: "0.42 0 1 1",
|
|
||||||
easein: "0.1 0.8 0.2 1",
|
|
||||||
easeout: "0 0 0.58 1",
|
|
||||||
easeinout: "0.42 0 0.58 1"
|
|
||||||
};
|
|
||||||
|
|
||||||
function animateSVG(element, props, dur, easing_type="linear", type=undefined, old_values={}) {
|
export function getAnimYLine() {}
|
||||||
|
|
||||||
let anim_element = element.cloneNode(true);
|
|
||||||
let new_element = element.cloneNode(true);
|
|
||||||
|
|
||||||
for(var attributeName in props) {
|
|
||||||
let animate_element;
|
|
||||||
if(attributeName === 'transform') {
|
|
||||||
animate_element = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
|
|
||||||
} else {
|
|
||||||
animate_element = document.createElementNS("http://www.w3.org/2000/svg", "animate");
|
|
||||||
}
|
|
||||||
let current_value = old_values[attributeName] || element.getAttribute(attributeName);
|
|
||||||
let value = props[attributeName];
|
|
||||||
|
|
||||||
let anim_attr = {
|
|
||||||
attributeName: attributeName,
|
|
||||||
from: current_value,
|
|
||||||
to: value,
|
|
||||||
begin: "0s",
|
|
||||||
dur: dur/1000 + "s",
|
|
||||||
values: current_value + ";" + value,
|
|
||||||
keySplines: EASING[easing_type],
|
|
||||||
keyTimes: "0;1",
|
|
||||||
calcMode: "spline",
|
|
||||||
fill: 'freeze'
|
|
||||||
};
|
|
||||||
|
|
||||||
if(type) {
|
|
||||||
anim_attr["type"] = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i in anim_attr) {
|
|
||||||
animate_element.setAttribute(i, anim_attr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
anim_element.appendChild(animate_element);
|
|
||||||
|
|
||||||
if(type) {
|
|
||||||
new_element.setAttribute(attributeName, `translate(${value})`);
|
|
||||||
} else {
|
|
||||||
new_element.setAttribute(attributeName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [anim_element, new_element];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transform(element, style) { // eslint-disable-line no-unused-vars
|
|
||||||
element.style.transform = style;
|
|
||||||
element.style.webkitTransform = style;
|
|
||||||
element.style.msTransform = style;
|
|
||||||
element.style.mozTransform = style;
|
|
||||||
element.style.oTransform = style;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function runSVGAnimation(svg_container, elements) {
|
|
||||||
let new_elements = [];
|
|
||||||
let anim_elements = [];
|
|
||||||
|
|
||||||
elements.map(element => {
|
|
||||||
let obj = element[0];
|
|
||||||
let parent = obj.unit.parentNode;
|
|
||||||
|
|
||||||
let anim_element, new_element;
|
|
||||||
|
|
||||||
element[0] = obj.unit;
|
|
||||||
[anim_element, new_element] = animateSVG(...element);
|
|
||||||
|
|
||||||
new_elements.push(new_element);
|
|
||||||
anim_elements.push([anim_element, parent]);
|
|
||||||
|
|
||||||
parent.replaceChild(anim_element, obj.unit);
|
|
||||||
|
|
||||||
if(obj.array) {
|
|
||||||
obj.array[obj.index] = new_element;
|
|
||||||
} else {
|
|
||||||
obj.object[obj.key] = new_element;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let anim_svg = svg_container.cloneNode(true);
|
|
||||||
|
|
||||||
anim_elements.map((anim_element, i) => {
|
|
||||||
anim_element[1].replaceChild(new_elements[i], anim_element[0]);
|
|
||||||
elements[i][0] = new_elements[i];
|
|
||||||
});
|
|
||||||
|
|
||||||
return anim_svg;
|
|
||||||
}
|
|
||||||
101
src/scripts/utils/animation.js
Normal file
101
src/scripts/utils/animation.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Leveraging SMIL Animations
|
||||||
|
|
||||||
|
const EASING = {
|
||||||
|
ease: "0.25 0.1 0.25 1",
|
||||||
|
linear: "0 0 1 1",
|
||||||
|
// easein: "0.42 0 1 1",
|
||||||
|
easein: "0.1 0.8 0.2 1",
|
||||||
|
easeout: "0 0 0.58 1",
|
||||||
|
easeinout: "0.42 0 0.58 1"
|
||||||
|
};
|
||||||
|
|
||||||
|
function animateSVG(element, props, dur, easing_type="linear", type=undefined, old_values={}) {
|
||||||
|
|
||||||
|
let anim_element = element.cloneNode(true);
|
||||||
|
let new_element = element.cloneNode(true);
|
||||||
|
|
||||||
|
for(var attributeName in props) {
|
||||||
|
let animate_element;
|
||||||
|
if(attributeName === 'transform') {
|
||||||
|
animate_element = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
|
||||||
|
} else {
|
||||||
|
animate_element = document.createElementNS("http://www.w3.org/2000/svg", "animate");
|
||||||
|
}
|
||||||
|
let current_value = old_values[attributeName] || element.getAttribute(attributeName);
|
||||||
|
let value = props[attributeName];
|
||||||
|
|
||||||
|
let anim_attr = {
|
||||||
|
attributeName: attributeName,
|
||||||
|
from: current_value,
|
||||||
|
to: value,
|
||||||
|
begin: "0s",
|
||||||
|
dur: dur/1000 + "s",
|
||||||
|
values: current_value + ";" + value,
|
||||||
|
keySplines: EASING[easing_type],
|
||||||
|
keyTimes: "0;1",
|
||||||
|
calcMode: "spline",
|
||||||
|
fill: 'freeze'
|
||||||
|
};
|
||||||
|
|
||||||
|
if(type) {
|
||||||
|
anim_attr["type"] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i in anim_attr) {
|
||||||
|
animate_element.setAttribute(i, anim_attr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
anim_element.appendChild(animate_element);
|
||||||
|
|
||||||
|
if(type) {
|
||||||
|
new_element.setAttribute(attributeName, `translate(${value})`);
|
||||||
|
} else {
|
||||||
|
new_element.setAttribute(attributeName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [anim_element, new_element];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transform(element, style) { // eslint-disable-line no-unused-vars
|
||||||
|
element.style.transform = style;
|
||||||
|
element.style.webkitTransform = style;
|
||||||
|
element.style.msTransform = style;
|
||||||
|
element.style.mozTransform = style;
|
||||||
|
element.style.oTransform = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runSVGAnimation(svg_container, elements) {
|
||||||
|
let new_elements = [];
|
||||||
|
let anim_elements = [];
|
||||||
|
|
||||||
|
elements.map(element => {
|
||||||
|
let obj = element[0];
|
||||||
|
let parent = obj.unit.parentNode;
|
||||||
|
|
||||||
|
let anim_element, new_element;
|
||||||
|
|
||||||
|
element[0] = obj.unit;
|
||||||
|
[anim_element, new_element] = animateSVG(...element);
|
||||||
|
|
||||||
|
new_elements.push(new_element);
|
||||||
|
anim_elements.push([anim_element, parent]);
|
||||||
|
|
||||||
|
parent.replaceChild(anim_element, obj.unit);
|
||||||
|
|
||||||
|
if(obj.array) {
|
||||||
|
obj.array[obj.index] = new_element;
|
||||||
|
} else {
|
||||||
|
obj.object[obj.key] = new_element;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let anim_svg = svg_container.cloneNode(true);
|
||||||
|
|
||||||
|
anim_elements.map((anim_element, i) => {
|
||||||
|
anim_element[1].replaceChild(new_elements[i], anim_element[0]);
|
||||||
|
elements[i][0] = new_elements[i];
|
||||||
|
});
|
||||||
|
|
||||||
|
return anim_svg;
|
||||||
|
}
|
||||||
@ -1,3 +1,23 @@
|
|||||||
|
const PRESET_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 DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
||||||
|
'yellow', 'green', 'light-green', 'purple', 'magenta'];
|
||||||
|
|
||||||
function limit_color(r){
|
function limit_color(r){
|
||||||
if (r > 255) return 255;
|
if (r > 255) return 255;
|
||||||
else if (r < 0) return 0;
|
else if (r < 0) return 0;
|
||||||
@ -23,23 +43,6 @@ export function is_valid_color(string) {
|
|||||||
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(string);
|
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) => {
|
export const get_color = (color) => {
|
||||||
return color_map[color] || color;
|
return PRESET_COLOR_MAP[color] || color;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -25,6 +25,7 @@ $.create = (tag, o) => {
|
|||||||
var ref = $(val);
|
var ref = $(val);
|
||||||
ref.parentNode.insertBefore(element, ref);
|
ref.parentNode.insertBefore(element, ref);
|
||||||
element.appendChild(ref);
|
element.appendChild(ref);
|
||||||
|
|
||||||
} else if (i === "styles") {
|
} else if (i === "styles") {
|
||||||
if(typeof val === "object") {
|
if(typeof val === "object") {
|
||||||
Object.keys(val).map(prop => {
|
Object.keys(val).map(prop => {
|
||||||
@ -42,33 +43,6 @@ $.create = (tag, o) => {
|
|||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
$.createSVG = (tag, o) => {
|
|
||||||
var element = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
||||||
|
|
||||||
for (var i in o) {
|
|
||||||
var val = o[i];
|
|
||||||
|
|
||||||
if (i === "inside") {
|
|
||||||
$(val).appendChild(element);
|
|
||||||
}
|
|
||||||
else if (i === "around") {
|
|
||||||
var ref = $(val);
|
|
||||||
ref.parentNode.insertBefore(element, ref);
|
|
||||||
element.appendChild(ref);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(i === "className") { i = "class"; }
|
|
||||||
if(i === "innerHTML") {
|
|
||||||
element['textContent'] = val;
|
|
||||||
} else {
|
|
||||||
element.setAttribute(i, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
|
|
||||||
$.offset = (element) => {
|
$.offset = (element) => {
|
||||||
let rect = element.getBoundingClientRect();
|
let rect = element.getBoundingClientRect();
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,4 +1,197 @@
|
|||||||
import $ from './dom';
|
// Constants used
|
||||||
|
|
||||||
|
function $(expr, con) {
|
||||||
|
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSVG(tag, o) {
|
||||||
|
var element = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
||||||
|
|
||||||
|
for (var i in o) {
|
||||||
|
var val = o[i];
|
||||||
|
|
||||||
|
if (i === "inside") {
|
||||||
|
$(val).appendChild(element);
|
||||||
|
}
|
||||||
|
else if (i === "around") {
|
||||||
|
var ref = $(val);
|
||||||
|
ref.parentNode.insertBefore(element, ref);
|
||||||
|
element.appendChild(ref);
|
||||||
|
|
||||||
|
} else if (i === "styles") {
|
||||||
|
if(typeof val === "object") {
|
||||||
|
Object.keys(val).map(prop => {
|
||||||
|
element.style[prop] = val[prop];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(i === "className") { i = "class"; }
|
||||||
|
if(i === "innerHTML") {
|
||||||
|
element['textContent'] = val;
|
||||||
|
} else {
|
||||||
|
element.setAttribute(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVerticalGradient(svgDefElem, gradientId) {
|
||||||
|
return createSVG('linearGradient', {
|
||||||
|
inside: svgDefElem,
|
||||||
|
id: gradientId,
|
||||||
|
x1: 0,
|
||||||
|
x2: 0,
|
||||||
|
y1: 0,
|
||||||
|
y2: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGradientStop(gradElem, offset, color, opacity) {
|
||||||
|
createSVG('stop', {
|
||||||
|
'inside': gradElem,
|
||||||
|
'style': `stop-color: ${color}`,
|
||||||
|
'offset': offset,
|
||||||
|
'stop-opacity': opacity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeSVGContainer(parent, className, width, height) {
|
||||||
|
return createSVG('svg', {
|
||||||
|
className: className,
|
||||||
|
inside: parent,
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeSVGDefs(svgContainer) {
|
||||||
|
return createSVG('defs', {
|
||||||
|
inside: svgContainer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeSVGGroup(parent, className, transform='') {
|
||||||
|
return createSVG('g', {
|
||||||
|
className: className,
|
||||||
|
inside: parent,
|
||||||
|
transform: transform
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makePath(pathStr, className='', stroke='none', fill='none') {
|
||||||
|
return createSVG('path', {
|
||||||
|
className: className,
|
||||||
|
d: pathStr,
|
||||||
|
styles: {
|
||||||
|
stroke: stroke,
|
||||||
|
fill: fill
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
|
let gradientId ='path-fill-gradient' + '-' + color;
|
||||||
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
|
let opacities = [1, 0.6, 0.2];
|
||||||
|
if(lighter) {
|
||||||
|
opacities = [0.4, 0.2, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
setGradientStop(gradientDef, "0%", color, opacities[0]);
|
||||||
|
setGradientStop(gradientDef, "50%", color, opacities[1]);
|
||||||
|
setGradientStop(gradientDef, "100%", color, opacities[2]);
|
||||||
|
|
||||||
|
return gradientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeHeatSquare(className, x, y, size, fill='none', data={}) {
|
||||||
|
let args = {
|
||||||
|
className: className,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
fill: fill
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(data).map(key => {
|
||||||
|
args[key] = data[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return createSVG("rect", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeText(className, x, y, content) {
|
||||||
|
return createSVG('text', {
|
||||||
|
className: className,
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
dy: '.32em',
|
||||||
|
innerHTML: content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeXLine(height, textStartAt, point, labelClass, axisLineClass, xPos) {
|
||||||
|
let line = createSVG('line', {
|
||||||
|
x1: 0,
|
||||||
|
x2: 0,
|
||||||
|
y1: 0,
|
||||||
|
y2: height
|
||||||
|
});
|
||||||
|
|
||||||
|
let text = createSVG('text', {
|
||||||
|
className: labelClass,
|
||||||
|
x: 0,
|
||||||
|
y: textStartAt,
|
||||||
|
dy: '.71em',
|
||||||
|
innerHTML: point
|
||||||
|
});
|
||||||
|
|
||||||
|
let xLine = createSVG('g', {
|
||||||
|
className: `tick ${axisLineClass}`,
|
||||||
|
transform: `translate(${ xPos }, 0)`
|
||||||
|
});
|
||||||
|
|
||||||
|
xLine.appendChild(line);
|
||||||
|
xLine.appendChild(text);
|
||||||
|
|
||||||
|
return xLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeYLine(startAt, width, textEndAt, point, labelClass, axisLineClass, yPos, darker=false, lineType="") {
|
||||||
|
let line = createSVG('line', {
|
||||||
|
className: lineType === "dashed" ? "dashed": "",
|
||||||
|
x1: startAt,
|
||||||
|
x2: width,
|
||||||
|
y1: 0,
|
||||||
|
y2: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
let text = createSVG('text', {
|
||||||
|
className: labelClass,
|
||||||
|
x: textEndAt,
|
||||||
|
y: 0,
|
||||||
|
dy: '.32em',
|
||||||
|
innerHTML: point+""
|
||||||
|
});
|
||||||
|
|
||||||
|
let yLine = createSVG('g', {
|
||||||
|
className: `tick ${axisLineClass}`,
|
||||||
|
transform: `translate(0, ${yPos})`,
|
||||||
|
'stroke-opacity': 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if(darker) {
|
||||||
|
line.style.stroke = "rgba(27, 31, 35, 0.6)";
|
||||||
|
}
|
||||||
|
|
||||||
|
yLine.appendChild(line);
|
||||||
|
yLine.appendChild(text);
|
||||||
|
|
||||||
|
return yLine;
|
||||||
|
}
|
||||||
|
|
||||||
export var UnitRenderer = (function() {
|
export var UnitRenderer = (function() {
|
||||||
var UnitRenderer = function(total_height, zero_line, avg_unit_width) {
|
var UnitRenderer = function(total_height, zero_line, avg_unit_width) {
|
||||||
@ -41,7 +234,7 @@ export var UnitRenderer = (function() {
|
|||||||
|
|
||||||
let [height, y] = get_bar_height_and_y_attr(y_top, this.zero_line, this.total_height);
|
let [height, y] = get_bar_height_and_y_attr(y_top, this.zero_line, this.total_height);
|
||||||
|
|
||||||
return $.createSVG('rect', {
|
return createSVG('rect', {
|
||||||
className: `bar mini`,
|
className: `bar mini`,
|
||||||
style: `fill: ${color}`,
|
style: `fill: ${color}`,
|
||||||
'data-point-index': index,
|
'data-point-index': index,
|
||||||
@ -53,7 +246,7 @@ export var UnitRenderer = (function() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
draw_dot: function(x, y, args, color, index) {
|
draw_dot: function(x, y, args, color, index) {
|
||||||
return $.createSVG('circle', {
|
return createSVG('circle', {
|
||||||
style: `fill: ${color}`,
|
style: `fill: ${color}`,
|
||||||
'data-point-index': index,
|
'data-point-index': index,
|
||||||
cx: x,
|
cx: x,
|
||||||
@ -81,69 +274,3 @@ export var UnitRenderer = (function() {
|
|||||||
|
|
||||||
return UnitRenderer;
|
return UnitRenderer;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
export function make_x_line(height, text_start_at, point, label_class, axis_line_class, x_pos) {
|
|
||||||
let line = $.createSVG('line', {
|
|
||||||
x1: 0,
|
|
||||||
x2: 0,
|
|
||||||
y1: 0,
|
|
||||||
y2: height
|
|
||||||
});
|
|
||||||
|
|
||||||
let text = $.createSVG('text', {
|
|
||||||
className: label_class,
|
|
||||||
x: 0,
|
|
||||||
y: text_start_at,
|
|
||||||
dy: '.71em',
|
|
||||||
innerHTML: point
|
|
||||||
});
|
|
||||||
|
|
||||||
let x_line = $.createSVG('g', {
|
|
||||||
className: `tick ${axis_line_class}`,
|
|
||||||
transform: `translate(${ x_pos }, 0)`
|
|
||||||
});
|
|
||||||
|
|
||||||
x_line.appendChild(line);
|
|
||||||
x_line.appendChild(text);
|
|
||||||
|
|
||||||
return x_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function make_y_line(start_at, width, text_end_at, point, label_class, axis_line_class, y_pos, darker=false, line_type="") {
|
|
||||||
let line = $.createSVG('line', {
|
|
||||||
className: line_type === "dashed" ? "dashed": "",
|
|
||||||
x1: start_at,
|
|
||||||
x2: width,
|
|
||||||
y1: 0,
|
|
||||||
y2: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
let text = $.createSVG('text', {
|
|
||||||
className: label_class,
|
|
||||||
x: text_end_at,
|
|
||||||
y: 0,
|
|
||||||
dy: '.32em',
|
|
||||||
innerHTML: point+""
|
|
||||||
});
|
|
||||||
|
|
||||||
let y_line = $.createSVG('g', {
|
|
||||||
className: `tick ${axis_line_class}`,
|
|
||||||
transform: `translate(0, ${y_pos})`,
|
|
||||||
'stroke-opacity': 1
|
|
||||||
});
|
|
||||||
|
|
||||||
if(darker) {
|
|
||||||
line.style.stroke = "rgba(27, 31, 35, 0.6)";
|
|
||||||
}
|
|
||||||
|
|
||||||
y_line.appendChild(line);
|
|
||||||
y_line.appendChild(text);
|
|
||||||
|
|
||||||
return y_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get_anim_x_line() {}
|
|
||||||
|
|
||||||
export function get_anim_y_line() {}
|
|
||||||
|
|
||||||
|
|||||||
@ -45,8 +45,6 @@
|
|||||||
color: #98d85b;
|
color: #98d85b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.axis, .chart-label {
|
.axis, .chart-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user