[heatmap] subdomains and legend, biased random, blue theme
This commit is contained in:
parent
3877357bb3
commit
eb3041ae4f
131
dist/frappe-charts.esm.js
vendored
131
dist/frappe-charts.esm.js
vendored
@ -130,6 +130,9 @@ const PERCENTAGE_BAR_DEFAULT_DEPTH = 2;
|
||||
// More colors are difficult to parse visually
|
||||
const HEATMAP_DISTRIBUTION_SIZE = 5;
|
||||
|
||||
const HEATMAP_LEFT_MARGIN = 50;
|
||||
const HEATMAP_TOP_MARGIN = 25;
|
||||
|
||||
const HEATMAP_SQUARE_SIZE = 10;
|
||||
const HEATMAP_GUTTER_SIZE = 2;
|
||||
|
||||
@ -137,16 +140,18 @@ const DEFAULT_CHAR_WIDTH = 7;
|
||||
|
||||
const TOOLTIP_POINTER_TRIANGLE_HEIGHT = 5;
|
||||
|
||||
const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
|
||||
const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
||||
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
|
||||
const HEATMAP_COLORS_GREEN = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
|
||||
|
||||
|
||||
|
||||
const DEFAULT_COLORS = {
|
||||
bar: DEFAULT_CHART_COLORS,
|
||||
line: DEFAULT_CHART_COLORS,
|
||||
pie: DEFAULT_CHART_COLORS,
|
||||
percentage: DEFAULT_CHART_COLORS,
|
||||
heatmap: HEATMAP_COLORS
|
||||
heatmap: HEATMAP_COLORS_GREEN
|
||||
};
|
||||
|
||||
// Universal constants
|
||||
@ -277,6 +282,10 @@ class SvgTip {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a number upto 2 decimal places.
|
||||
* @param {Number} d Any number
|
||||
*/
|
||||
function floatTwo(d) {
|
||||
return parseFloat(d.toFixed(2));
|
||||
}
|
||||
@ -321,10 +330,13 @@ function getStringWidth(string, charWidth) {
|
||||
|
||||
|
||||
|
||||
// https://stackoverflow.com/a/29325222
|
||||
|
||||
|
||||
function getPositionByAngle(angle, radius) {
|
||||
return {
|
||||
x:Math.sin(angle * ANGLE_RATIO) * radius,
|
||||
y:Math.cos(angle * ANGLE_RATIO) * radius,
|
||||
x: Math.sin(angle * ANGLE_RATIO) * radius,
|
||||
y: Math.cos(angle * ANGLE_RATIO) * radius,
|
||||
};
|
||||
}
|
||||
|
||||
@ -524,7 +536,7 @@ function makeGradient(svgDefElem, color, lighter = false) {
|
||||
}
|
||||
|
||||
function percentageBar(x, y, width, height,
|
||||
depth=PERCENTAGE_BAR_DEFAULT_DEPTH, fill='none') {
|
||||
depth=PERCENTAGE_BAR_DEFAULT_DEPTH, fill='none') {
|
||||
|
||||
let args = {
|
||||
className: 'percentage-bar',
|
||||
@ -620,14 +632,19 @@ function legendDot(x, y, size, fill='none', label) {
|
||||
return group;
|
||||
}
|
||||
|
||||
function makeText(className, x, y, content, fontSize = FONT_SIZE) {
|
||||
function makeText(className, x, y, content, options = {}) {
|
||||
let fontSize = options.fontSize || FONT_SIZE;
|
||||
let dy = options.dy !== undefined ? options.dy : (fontSize / 2);
|
||||
let fill = options.fill || FONT_FILL;
|
||||
let textAnchor = options.textAnchor || 'start';
|
||||
return createSVG('text', {
|
||||
className: className,
|
||||
x: x,
|
||||
y: y,
|
||||
dy: (fontSize / 2) + 'px',
|
||||
dy: dy + 'px',
|
||||
'font-size': fontSize + 'px',
|
||||
fill: FONT_FILL,
|
||||
fill: fill,
|
||||
'text-anchor': textAnchor,
|
||||
innerHTML: content
|
||||
});
|
||||
}
|
||||
@ -1449,7 +1466,7 @@ class BaseChart {
|
||||
let titleAreaHeight = 0;
|
||||
let legendAreaHeight = 0;
|
||||
if(this.title.length) {
|
||||
titleAreaHeight = 30;
|
||||
titleAreaHeight = 40;
|
||||
}
|
||||
if(this.config.showLegend) {
|
||||
legendAreaHeight = 30;
|
||||
@ -1468,10 +1485,13 @@ class BaseChart {
|
||||
if(this.title.length) {
|
||||
this.titleEL = makeText(
|
||||
'title',
|
||||
this.leftMargin - AXIS_TICK_LENGTH,
|
||||
this.leftMargin - AXIS_TICK_LENGTH * 6,
|
||||
this.topMargin,
|
||||
this.title,
|
||||
11
|
||||
{
|
||||
fontSize: 12,
|
||||
fill: '#666666'
|
||||
}
|
||||
);
|
||||
this.svg.appendChild(this.titleEL);
|
||||
}
|
||||
@ -1664,6 +1684,9 @@ const MONTH_NAMES = ["January", "February", "March", "April", "May", "June",
|
||||
|
||||
|
||||
|
||||
|
||||
const DAY_NAMES_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
|
||||
// https://stackoverflow.com/a/11252167/6495043
|
||||
function treatAsUtc(date) {
|
||||
let result = new Date(date);
|
||||
@ -1689,7 +1712,7 @@ function clone(date) {
|
||||
|
||||
|
||||
|
||||
|
||||
// export function getMonthsBetween(startDate, endDate) {}
|
||||
|
||||
function getWeeksBetween(startDate, endDate) {
|
||||
let weekStartDate = setDayToSunday(startDate);
|
||||
@ -1821,7 +1844,9 @@ let componentConfigs = {
|
||||
});
|
||||
},
|
||||
|
||||
animateElements(newData) { }
|
||||
animateElements(newData) {
|
||||
if(newData) return [];
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
layerClass: 'y axis',
|
||||
@ -1957,16 +1982,20 @@ let componentConfigs = {
|
||||
heatDomain: {
|
||||
layerClass: function() { return 'heat-domain domain-' + this.constants.index; },
|
||||
makeElements(data) {
|
||||
let {index, colWidth, rowHeight, squareSize, xTranslate, discreteDomains} = this.constants;
|
||||
let monthNameHeight = 12;
|
||||
let x = xTranslate, y = monthNameHeight;
|
||||
let {index, colWidth, rowHeight, squareSize, xTranslate} = this.constants;
|
||||
let monthNameHeight = -12;
|
||||
let x = xTranslate, y = 0;
|
||||
|
||||
this.serializedSubDomains = [];
|
||||
|
||||
data.cols.map((week, weekNo) => {
|
||||
if(weekNo === 1) {
|
||||
this.labels.push(
|
||||
makeText('domain-name', x, 0, getMonthName(index, true), 11)
|
||||
makeText('domain-name', x, monthNameHeight, getMonthName(index, true),
|
||||
{
|
||||
fontSize: 11
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
week.map((day, i) => {
|
||||
@ -1981,14 +2010,16 @@ let componentConfigs = {
|
||||
}
|
||||
y += rowHeight;
|
||||
});
|
||||
y = monthNameHeight;
|
||||
y = 0;
|
||||
x += colWidth;
|
||||
});
|
||||
|
||||
return this.serializedSubDomains;
|
||||
},
|
||||
|
||||
animateElements(newData) { }
|
||||
animateElements(newData) {
|
||||
if(newData) return [];
|
||||
}
|
||||
},
|
||||
|
||||
barGraph: {
|
||||
@ -2192,7 +2223,7 @@ class PercentageChart extends AggregationChart {
|
||||
s.widths = [];
|
||||
|
||||
let xPos = 0;
|
||||
s.sliceTotals.map((value, i) => {
|
||||
s.sliceTotals.map((value) => {
|
||||
let width = this.width * value / s.grandTotal;
|
||||
s.widths.push(width);
|
||||
s.xPositions.push(xPos);
|
||||
@ -2612,6 +2643,12 @@ class Heatmap extends BaseChart {
|
||||
this.setup();
|
||||
}
|
||||
|
||||
setMargins() {
|
||||
super.setMargins();
|
||||
this.leftMargin = HEATMAP_LEFT_MARGIN;
|
||||
this.topMargin = HEATMAP_TOP_MARGIN;
|
||||
}
|
||||
|
||||
updateWidth() {
|
||||
this.baseWidth = (this.state.noOfWeeks + 99) * COL_WIDTH;
|
||||
|
||||
@ -2687,6 +2724,21 @@ class Heatmap extends BaseChart {
|
||||
let component = getComponent(...args);
|
||||
return [args[0] + '-' + i, component];
|
||||
}));
|
||||
|
||||
let y = 0;
|
||||
DAY_NAMES_SHORT.forEach((dayName, i) => {
|
||||
if([1, 3, 5].includes(i)) {
|
||||
let dayText = makeText('subdomain-name', -COL_WIDTH/2, y, dayName,
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE,
|
||||
dy: 8,
|
||||
textAnchor: 'end'
|
||||
}
|
||||
);
|
||||
this.drawArea.appendChild(dayText);
|
||||
}
|
||||
y += ROW_HEIGHT;
|
||||
});
|
||||
}
|
||||
|
||||
update(data) {
|
||||
@ -2714,8 +2766,8 @@ class Heatmap extends BaseChart {
|
||||
let gOff = this.container.getBoundingClientRect(), pOff = daySquare.getBoundingClientRect();
|
||||
|
||||
let width = parseInt(e.target.getAttribute('width'));
|
||||
let x = pOff.left - gOff.left + (width+2)/2;
|
||||
let y = pOff.top - gOff.top - (width+2)/2;
|
||||
let x = pOff.left - gOff.left + width/2;
|
||||
let y = pOff.top - gOff.top;
|
||||
let value = count + ' ' + this.countLabel;
|
||||
let name = ' on ' + month + ' ' + dateParts[0] + ', ' + dateParts[2];
|
||||
|
||||
@ -2726,6 +2778,36 @@ class Heatmap extends BaseChart {
|
||||
});
|
||||
}
|
||||
|
||||
renderLegend() {
|
||||
this.legendArea.textContent = '';
|
||||
let x = 0;
|
||||
let y = ROW_HEIGHT;
|
||||
|
||||
let lessText = makeText('subdomain-name', x, y, 'Less',
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE + 1,
|
||||
dy: 9
|
||||
}
|
||||
);
|
||||
x = (COL_WIDTH * 2) + COL_WIDTH/2;
|
||||
this.legendArea.appendChild(lessText);
|
||||
|
||||
this.colors.slice(0, HEATMAP_DISTRIBUTION_SIZE).map((color, i) => {
|
||||
const square = heatSquare('heatmap-legend-unit', x + (COL_WIDTH + 3) * i,
|
||||
y, HEATMAP_SQUARE_SIZE, color);
|
||||
this.legendArea.appendChild(square);
|
||||
});
|
||||
|
||||
let moreTextX = x + HEATMAP_DISTRIBUTION_SIZE * (COL_WIDTH + 3) + COL_WIDTH/4;
|
||||
let moreText = makeText('subdomain-name', moreTextX, y, 'More',
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE + 1,
|
||||
dy: 9
|
||||
}
|
||||
);
|
||||
this.legendArea.appendChild(moreText);
|
||||
}
|
||||
|
||||
getDomains() {
|
||||
let s = this.state;
|
||||
const [startMonth, startYear] = [s.start.getMonth(), s.start.getFullYear()];
|
||||
@ -3275,7 +3357,7 @@ class AxisChart extends BaseChart {
|
||||
|
||||
let s = this.state;
|
||||
|
||||
let formatY = this.config.formatTooltipY;
|
||||
// let formatY = this.config.formatTooltipY;
|
||||
let formatX = this.config.formatTooltipX;
|
||||
|
||||
let titles = s.xAxis.labels;
|
||||
@ -3283,7 +3365,7 @@ class AxisChart extends BaseChart {
|
||||
titles = titles.map(d=>formatX(d));
|
||||
}
|
||||
|
||||
formatY = formatY && formatY(s.yAxis.labels[0]) ? formatY : 0;
|
||||
// formatY = formatY && formatY(s.yAxis.labels[0]) ? formatY : 0;
|
||||
|
||||
// yVal = formatY ? formatY(set.values[i]) : set.values[i]
|
||||
}
|
||||
@ -3495,6 +3577,7 @@ class AxisChart extends BaseChart {
|
||||
// removeDataPoint(index = 0) {}
|
||||
}
|
||||
|
||||
// import MultiAxisChart from './charts/MultiAxisChart';
|
||||
const chartTypes = {
|
||||
// multiaxis: MultiAxisChart,
|
||||
percentage: PercentageChart,
|
||||
|
||||
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
dist/frappe-charts.min.iife.js.map
vendored
2
dist/frappe-charts.min.iife.js.map
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
import { DAYS_IN_YEAR, SEC_IN_DAY, MONTH_NAMES_SHORT, clone, timestampToMidnight, timestampSec, addDays } from '../../../src/js/utils/date-utils';
|
||||
import { SEC_IN_DAY, MONTH_NAMES_SHORT, clone, timestampToMidnight, timestampSec, addDays } from '../../../src/js/utils/date-utils';
|
||||
import { getRandomBias } from '../../../src/js/utils/helpers';
|
||||
|
||||
// Composite Chart
|
||||
// ================================================================================
|
||||
@ -176,8 +177,8 @@ export const moonData = {
|
||||
|
||||
let today = new Date();
|
||||
let start = clone(today);
|
||||
addDays(start, 1);
|
||||
let end = clone(today);
|
||||
addDays(start, 5);
|
||||
let end = clone(start);
|
||||
start.setFullYear( start.getFullYear() - 2 );
|
||||
end.setFullYear( end.getFullYear() - 1 );
|
||||
|
||||
@ -190,7 +191,7 @@ startTs = timestampToMidnight(startTs);
|
||||
endTs = timestampToMidnight(endTs, true);
|
||||
|
||||
while (startTs < endTs) {
|
||||
dataPoints[parseInt(startTs)] = Math.floor(Math.random() * 17);
|
||||
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
|
||||
startTs += SEC_IN_DAY;
|
||||
}
|
||||
|
||||
|
||||
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
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
||||
import { shuffle } from '../../../src/js/utils/helpers';
|
||||
import { HEATMAP_COLORS_YELLOW, HEATMAP_COLORS_BLUE } from '../../../src/js/utils/constants';
|
||||
import { fireballOver25, fireball_2_5, fireball_5_25, lineCompositeData,
|
||||
barCompositeData, typeData, trendsData, moonData, heatmapData } from './data';
|
||||
|
||||
@ -263,15 +264,16 @@ eventsChart.parent.addEventListener('data-select', (e) => {
|
||||
// Heatmap
|
||||
// ================================================================================
|
||||
|
||||
let heatmap = new Chart("#chart-heatmap", {
|
||||
let heatmapArgs = {
|
||||
title: "Monthly Distribution",
|
||||
data: heatmapData,
|
||||
type: 'heatmap',
|
||||
height: 115,
|
||||
discreteDomains: 1,
|
||||
colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c']
|
||||
});
|
||||
|
||||
console.log('heatmapData', Object.assign({}, heatmapData));
|
||||
colors: HEATMAP_COLORS_BLUE,
|
||||
legendScale: [0, 1, 2, 4, 5]
|
||||
};
|
||||
new Chart("#chart-heatmap", heatmapArgs);
|
||||
|
||||
Array.prototype.slice.call(
|
||||
document.querySelectorAll('.heatmap-mode-buttons button')
|
||||
@ -290,17 +292,14 @@ Array.prototype.slice.call(
|
||||
.querySelector('.heatmap-color-buttons .active')
|
||||
.getAttribute('data-color');
|
||||
if(colors_mode === 'halloween') {
|
||||
colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
|
||||
colors = HEATMAP_COLORS_YELLOW;
|
||||
} else if (colors_mode === 'blue') {
|
||||
colors = HEATMAP_COLORS_BLUE;
|
||||
}
|
||||
|
||||
new Chart("#chart-heatmap", {
|
||||
data: heatmapData,
|
||||
type: 'heatmap',
|
||||
legendScale: [0, 1, 2, 4, 5],
|
||||
height: 115,
|
||||
discreteDomains: discreteDomains,
|
||||
colors: colors
|
||||
});
|
||||
heatmapArgs.discreteDomains = discreteDomains;
|
||||
heatmapArgs.colors = colors;
|
||||
new Chart("#chart-heatmap", heatmapArgs);
|
||||
|
||||
Array.prototype.slice.call(
|
||||
btn.parentNode.querySelectorAll('button')).map(el => {
|
||||
@ -319,7 +318,9 @@ Array.prototype.slice.call(
|
||||
let colors = [];
|
||||
|
||||
if(colors_mode === 'halloween') {
|
||||
colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
|
||||
colors = HEATMAP_COLORS_YELLOW;
|
||||
} else if (colors_mode === 'blue') {
|
||||
colors = HEATMAP_COLORS_BLUE;
|
||||
}
|
||||
|
||||
let discreteDomains = 1;
|
||||
@ -331,14 +332,9 @@ Array.prototype.slice.call(
|
||||
discreteDomains = 0;
|
||||
}
|
||||
|
||||
new Chart("#chart-heatmap", {
|
||||
data: heatmapData,
|
||||
type: 'heatmap',
|
||||
legendScale: [0, 1, 2, 4, 5],
|
||||
height: 115,
|
||||
discreteDomains: discreteDomains,
|
||||
colors: colors
|
||||
});
|
||||
heatmapArgs.discreteDomains = discreteDomains;
|
||||
heatmapArgs.colors = colors;
|
||||
new Chart("#chart-heatmap", heatmapArgs);
|
||||
|
||||
Array.prototype.slice.call(
|
||||
btn.parentNode.querySelectorAll('button')).map(el => {
|
||||
|
||||
74
docs/assets/js/index.min.js
vendored
74
docs/assets/js/index.min.js
vendored
@ -41,6 +41,12 @@ function __$styleInject(css, ref) {
|
||||
|
||||
|
||||
|
||||
|
||||
var HEATMAP_COLORS_BLUE = ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'];
|
||||
var HEATMAP_COLORS_YELLOW = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
|
||||
|
||||
|
||||
|
||||
// Universal constants
|
||||
|
||||
/**
|
||||
@ -84,6 +90,19 @@ function shuffle(array) {
|
||||
* @param {Number} charWidth Width of single char in pixels
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// https://stackoverflow.com/a/29325222
|
||||
function getRandomBias(min, max, bias, influence) {
|
||||
var range = max - min;
|
||||
var biasValue = range * bias + min;
|
||||
var rnd = Math.random() * range + min,
|
||||
// random in range
|
||||
mix = Math.random() * influence; // random mixer
|
||||
return rnd * (1 - mix) + biasValue * mix; // mix full range and bias
|
||||
}
|
||||
|
||||
// Playing around with dates
|
||||
|
||||
|
||||
@ -98,6 +117,10 @@ var MONTH_NAMES_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
|
||||
|
||||
|
||||
|
||||
|
||||
// https://stackoverflow.com/a/11252167/6495043
|
||||
|
||||
|
||||
function clone(date) {
|
||||
return new Date(date.getTime());
|
||||
}
|
||||
@ -116,7 +139,7 @@ function timestampToMidnight(timestamp) {
|
||||
return midnightTs;
|
||||
}
|
||||
|
||||
|
||||
// export function getMonthsBetween(startDate, endDate) {}
|
||||
|
||||
|
||||
|
||||
@ -136,6 +159,8 @@ function addDays(date, numberOfDays) {
|
||||
date.setDate(date.getDate() + numberOfDays);
|
||||
}
|
||||
|
||||
// Composite Chart
|
||||
// ================================================================================
|
||||
var reportCountList = [152, 222, 199, 287, 534, 709, 1179, 1256, 1632, 1856, 1850];
|
||||
|
||||
var lineCompositeData = {
|
||||
@ -247,8 +272,8 @@ var moonData = {
|
||||
|
||||
var today = new Date();
|
||||
var start = clone(today);
|
||||
addDays(start, 1);
|
||||
var end = clone(today);
|
||||
addDays(start, 5);
|
||||
var end = clone(start);
|
||||
start.setFullYear(start.getFullYear() - 2);
|
||||
end.setFullYear(end.getFullYear() - 1);
|
||||
|
||||
@ -261,7 +286,7 @@ startTs = timestampToMidnight(startTs);
|
||||
endTs = timestampToMidnight(endTs, true);
|
||||
|
||||
while (startTs < endTs) {
|
||||
dataPoints[parseInt(startTs)] = Math.floor(Math.random() * 17);
|
||||
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
|
||||
startTs += SEC_IN_DAY;
|
||||
}
|
||||
|
||||
@ -520,15 +545,16 @@ eventsChart.parent.addEventListener('data-select', function (e) {
|
||||
// Heatmap
|
||||
// ================================================================================
|
||||
|
||||
var heatmap = new Chart("#chart-heatmap", {
|
||||
var heatmapArgs = {
|
||||
title: "Monthly Distribution",
|
||||
data: heatmapData,
|
||||
type: 'heatmap',
|
||||
height: 115,
|
||||
discreteDomains: 1,
|
||||
colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c']
|
||||
});
|
||||
|
||||
console.log('heatmapData', Object.assign({}, heatmapData));
|
||||
colors: HEATMAP_COLORS_BLUE,
|
||||
legendScale: [0, 1, 2, 4, 5]
|
||||
};
|
||||
new Chart("#chart-heatmap", heatmapArgs);
|
||||
|
||||
Array.prototype.slice.call(document.querySelectorAll('.heatmap-mode-buttons button')).map(function (el) {
|
||||
el.addEventListener('click', function (e) {
|
||||
@ -543,17 +569,14 @@ Array.prototype.slice.call(document.querySelectorAll('.heatmap-mode-buttons butt
|
||||
var colors = [];
|
||||
var colors_mode = document.querySelector('.heatmap-color-buttons .active').getAttribute('data-color');
|
||||
if (colors_mode === 'halloween') {
|
||||
colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
|
||||
colors = HEATMAP_COLORS_YELLOW;
|
||||
} else if (colors_mode === 'blue') {
|
||||
colors = HEATMAP_COLORS_BLUE;
|
||||
}
|
||||
|
||||
new Chart("#chart-heatmap", {
|
||||
data: heatmapData,
|
||||
type: 'heatmap',
|
||||
legendScale: [0, 1, 2, 4, 5],
|
||||
height: 115,
|
||||
discreteDomains: discreteDomains,
|
||||
colors: colors
|
||||
});
|
||||
heatmapArgs.discreteDomains = discreteDomains;
|
||||
heatmapArgs.colors = colors;
|
||||
new Chart("#chart-heatmap", heatmapArgs);
|
||||
|
||||
Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
|
||||
el.classList.remove('active');
|
||||
@ -569,7 +592,9 @@ Array.prototype.slice.call(document.querySelectorAll('.heatmap-color-buttons but
|
||||
var colors = [];
|
||||
|
||||
if (colors_mode === 'halloween') {
|
||||
colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
|
||||
colors = HEATMAP_COLORS_YELLOW;
|
||||
} else if (colors_mode === 'blue') {
|
||||
colors = HEATMAP_COLORS_BLUE;
|
||||
}
|
||||
|
||||
var discreteDomains = 1;
|
||||
@ -579,14 +604,9 @@ Array.prototype.slice.call(document.querySelectorAll('.heatmap-color-buttons but
|
||||
discreteDomains = 0;
|
||||
}
|
||||
|
||||
new Chart("#chart-heatmap", {
|
||||
data: heatmapData,
|
||||
type: 'heatmap',
|
||||
legendScale: [0, 1, 2, 4, 5],
|
||||
height: 115,
|
||||
discreteDomains: discreteDomains,
|
||||
colors: colors
|
||||
});
|
||||
heatmapArgs.discreteDomains = discreteDomains;
|
||||
heatmapArgs.colors = colors;
|
||||
new Chart("#chart-heatmap", heatmapArgs);
|
||||
|
||||
Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
|
||||
el.classList.remove('active');
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -188,7 +188,8 @@
|
||||
</div>
|
||||
<div class="heatmap-color-buttons btn-group mt-1 mx-auto" role="group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-color="default">Default green</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary active" data-color="halloween">GitHub's Halloween</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary active" data-color="blue">Blue</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-color="halloween">GitHub's Halloween</button>
|
||||
</div>
|
||||
<pre><code class="hljs javascript margin-vertical-px"> let heatmap = new Chart("#heatmap", {
|
||||
type: 'heatmap',
|
||||
@ -202,7 +203,7 @@
|
||||
// default: today's date in past year
|
||||
// for an annual heatmap
|
||||
|
||||
colors: ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'],
|
||||
colors: ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'],
|
||||
// Set of five incremental colors,
|
||||
// beginning with a low-saturation color for zero data;
|
||||
// default: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
|
||||
|
||||
@ -348,7 +348,7 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
let s = this.state;
|
||||
|
||||
let formatY = this.config.formatTooltipY;
|
||||
// let formatY = this.config.formatTooltipY;
|
||||
let formatX = this.config.formatTooltipX;
|
||||
|
||||
let titles = s.xAxis.labels;
|
||||
@ -356,7 +356,7 @@ export default class AxisChart extends BaseChart {
|
||||
titles = titles.map(d=>formatX(d));
|
||||
}
|
||||
|
||||
formatY = formatY && formatY(s.yAxis.labels[0]) ? formatY : 0;
|
||||
// formatY = formatY && formatY(s.yAxis.labels[0]) ? formatY : 0;
|
||||
|
||||
// yVal = formatY ? formatY(set.values[i]) : set.values[i]
|
||||
}
|
||||
@ -377,7 +377,7 @@ export default class AxisChart extends BaseChart {
|
||||
}
|
||||
|
||||
mapTooltipXPosition(relX) {
|
||||
let s = this.state, d = this.data;
|
||||
let s = this.state;
|
||||
if(!s.yExtremes) return;
|
||||
|
||||
let index = getClosestInArray(relX, s.xAxis.positions, true);
|
||||
|
||||
@ -194,7 +194,7 @@ export default class BaseChart {
|
||||
let titleAreaHeight = 0;
|
||||
let legendAreaHeight = 0;
|
||||
if(this.title.length) {
|
||||
titleAreaHeight = 30;
|
||||
titleAreaHeight = 40;
|
||||
}
|
||||
if(this.config.showLegend) {
|
||||
legendAreaHeight = 30;
|
||||
@ -213,10 +213,13 @@ export default class BaseChart {
|
||||
if(this.title.length) {
|
||||
this.titleEL = makeText(
|
||||
'title',
|
||||
this.leftMargin - AXIS_TICK_LENGTH,
|
||||
this.leftMargin - AXIS_TICK_LENGTH * 6,
|
||||
this.topMargin,
|
||||
this.title,
|
||||
11
|
||||
{
|
||||
fontSize: 12,
|
||||
fill: '#666666'
|
||||
}
|
||||
);
|
||||
this.svg.appendChild(this.titleEL);
|
||||
}
|
||||
@ -242,7 +245,7 @@ export default class BaseChart {
|
||||
this.tip.offset = {
|
||||
x: x,
|
||||
y: y
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
renderLegend() {}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import BaseChart from './BaseChart';
|
||||
import { getComponent } from '../objects/ChartComponents';
|
||||
import { addDays, areInSameMonth, getLastDateInMonth, setDayToSunday, getYyyyMmDd, getWeeksBetween, getMonthName, clone,
|
||||
import { makeText, heatSquare } from '../utils/draw';
|
||||
import { DAY_NAMES_SHORT, addDays, areInSameMonth, getLastDateInMonth, setDayToSunday, getYyyyMmDd, getWeeksBetween, getMonthName, clone,
|
||||
NO_OF_MILLIS, NO_OF_YEAR_MONTHS, NO_OF_DAYS_IN_WEEK } from '../utils/date-utils';
|
||||
import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
|
||||
import { HEATMAP_DISTRIBUTION_SIZE, HEATMAP_SQUARE_SIZE,
|
||||
import { HEATMAP_TOP_MARGIN, HEATMAP_LEFT_MARGIN, HEATMAP_DISTRIBUTION_SIZE, HEATMAP_SQUARE_SIZE,
|
||||
HEATMAP_GUTTER_SIZE } from '../utils/constants';
|
||||
|
||||
const COL_WIDTH = HEATMAP_SQUARE_SIZE + HEATMAP_GUTTER_SIZE;
|
||||
@ -26,6 +27,12 @@ export default class Heatmap extends BaseChart {
|
||||
this.setup();
|
||||
}
|
||||
|
||||
setMargins() {
|
||||
super.setMargins();
|
||||
this.leftMargin = HEATMAP_LEFT_MARGIN;
|
||||
this.topMargin = HEATMAP_TOP_MARGIN;
|
||||
}
|
||||
|
||||
updateWidth() {
|
||||
this.baseWidth = (this.state.noOfWeeks + 99) * COL_WIDTH;
|
||||
|
||||
@ -101,6 +108,21 @@ export default class Heatmap extends BaseChart {
|
||||
let component = getComponent(...args);
|
||||
return [args[0] + '-' + i, component];
|
||||
}));
|
||||
|
||||
let y = 0;
|
||||
DAY_NAMES_SHORT.forEach((dayName, i) => {
|
||||
if([1, 3, 5].includes(i)) {
|
||||
let dayText = makeText('subdomain-name', -COL_WIDTH/2, y, dayName,
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE,
|
||||
dy: 8,
|
||||
textAnchor: 'end'
|
||||
}
|
||||
);
|
||||
this.drawArea.appendChild(dayText);
|
||||
}
|
||||
y += ROW_HEIGHT;
|
||||
});
|
||||
}
|
||||
|
||||
update(data) {
|
||||
@ -128,8 +150,8 @@ export default class Heatmap extends BaseChart {
|
||||
let gOff = this.container.getBoundingClientRect(), pOff = daySquare.getBoundingClientRect();
|
||||
|
||||
let width = parseInt(e.target.getAttribute('width'));
|
||||
let x = pOff.left - gOff.left + (width+2)/2;
|
||||
let y = pOff.top - gOff.top - (width+2)/2;
|
||||
let x = pOff.left - gOff.left + width/2;
|
||||
let y = pOff.top - gOff.top;
|
||||
let value = count + ' ' + this.countLabel;
|
||||
let name = ' on ' + month + ' ' + dateParts[0] + ', ' + dateParts[2];
|
||||
|
||||
@ -140,6 +162,36 @@ export default class Heatmap extends BaseChart {
|
||||
});
|
||||
}
|
||||
|
||||
renderLegend() {
|
||||
this.legendArea.textContent = '';
|
||||
let x = 0;
|
||||
let y = ROW_HEIGHT;
|
||||
|
||||
let lessText = makeText('subdomain-name', x, y, 'Less',
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE + 1,
|
||||
dy: 9
|
||||
}
|
||||
);
|
||||
x = (COL_WIDTH * 2) + COL_WIDTH/2;
|
||||
this.legendArea.appendChild(lessText);
|
||||
|
||||
this.colors.slice(0, HEATMAP_DISTRIBUTION_SIZE).map((color, i) => {
|
||||
const square = heatSquare('heatmap-legend-unit', x + (COL_WIDTH + 3) * i,
|
||||
y, HEATMAP_SQUARE_SIZE, color);
|
||||
this.legendArea.appendChild(square);
|
||||
});
|
||||
|
||||
let moreTextX = x + HEATMAP_DISTRIBUTION_SIZE * (COL_WIDTH + 3) + COL_WIDTH/4;
|
||||
let moreText = makeText('subdomain-name', moreTextX, y, 'More',
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE + 1,
|
||||
dy: 9
|
||||
}
|
||||
);
|
||||
this.legendArea.appendChild(moreText);
|
||||
}
|
||||
|
||||
getDomains() {
|
||||
let s = this.state;
|
||||
const [startMonth, startYear] = [s.start.getMonth(), s.start.getFullYear()];
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import AggregationChart from './AggregationChart';
|
||||
import { $, getOffset } from '../utils/dom';
|
||||
import { getOffset } from '../utils/dom';
|
||||
import { getComponent } from '../objects/ChartComponents';
|
||||
import { PERCENTAGE_BAR_DEFAULT_HEIGHT, PERCENTAGE_BAR_DEFAULT_DEPTH } from '../utils/constants';
|
||||
|
||||
@ -52,7 +52,7 @@ export default class PercentageChart extends AggregationChart {
|
||||
s.widths = [];
|
||||
|
||||
let xPos = 0;
|
||||
s.sliceTotals.map((value, i) => {
|
||||
s.sliceTotals.map((value) => {
|
||||
let width = this.width * value / s.grandTotal;
|
||||
s.widths.push(width);
|
||||
s.xPositions.push(xPos);
|
||||
|
||||
@ -96,7 +96,9 @@ let componentConfigs = {
|
||||
});
|
||||
},
|
||||
|
||||
animateElements(newData) { }
|
||||
animateElements(newData) {
|
||||
if(newData) return [];
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
layerClass: 'y axis',
|
||||
@ -232,16 +234,20 @@ let componentConfigs = {
|
||||
heatDomain: {
|
||||
layerClass: function() { return 'heat-domain domain-' + this.constants.index; },
|
||||
makeElements(data) {
|
||||
let {index, colWidth, rowHeight, squareSize, xTranslate, discreteDomains} = this.constants;
|
||||
let monthNameHeight = 12;
|
||||
let x = xTranslate, y = monthNameHeight;
|
||||
let {index, colWidth, rowHeight, squareSize, xTranslate} = this.constants;
|
||||
let monthNameHeight = -12;
|
||||
let x = xTranslate, y = 0;
|
||||
|
||||
this.serializedSubDomains = [];
|
||||
|
||||
data.cols.map((week, weekNo) => {
|
||||
if(weekNo === 1) {
|
||||
this.labels.push(
|
||||
makeText('domain-name', x, 0, getMonthName(index, true), 11)
|
||||
makeText('domain-name', x, monthNameHeight, getMonthName(index, true),
|
||||
{
|
||||
fontSize: 11
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
week.map((day, i) => {
|
||||
@ -255,15 +261,17 @@ let componentConfigs = {
|
||||
this.serializedSubDomains.push(square);
|
||||
}
|
||||
y += rowHeight;
|
||||
})
|
||||
y = monthNameHeight;
|
||||
});
|
||||
y = 0;
|
||||
x += colWidth;
|
||||
});
|
||||
|
||||
return this.serializedSubDomains;
|
||||
},
|
||||
|
||||
animateElements(newData) { }
|
||||
animateElements(newData) {
|
||||
if(newData) return [];
|
||||
}
|
||||
},
|
||||
|
||||
barGraph: {
|
||||
|
||||
@ -44,6 +44,9 @@ export const PERCENTAGE_BAR_DEFAULT_DEPTH = 2;
|
||||
// More colors are difficult to parse visually
|
||||
export const HEATMAP_DISTRIBUTION_SIZE = 5;
|
||||
|
||||
export const HEATMAP_LEFT_MARGIN = 50;
|
||||
export const HEATMAP_TOP_MARGIN = 25;
|
||||
|
||||
export const HEATMAP_SQUARE_SIZE = 10;
|
||||
export const HEATMAP_GUTTER_SIZE = 2;
|
||||
|
||||
@ -51,16 +54,18 @@ export const DEFAULT_CHAR_WIDTH = 7;
|
||||
|
||||
export const TOOLTIP_POINTER_TRIANGLE_HEIGHT = 5;
|
||||
|
||||
const HEATMAP_COLORS = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
|
||||
const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
||||
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
|
||||
const HEATMAP_COLORS_GREEN = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
|
||||
export const HEATMAP_COLORS_BLUE = ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'];
|
||||
export const HEATMAP_COLORS_YELLOW = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
|
||||
|
||||
export const DEFAULT_COLORS = {
|
||||
bar: DEFAULT_CHART_COLORS,
|
||||
line: DEFAULT_CHART_COLORS,
|
||||
pie: DEFAULT_CHART_COLORS,
|
||||
percentage: DEFAULT_CHART_COLORS,
|
||||
heatmap: HEATMAP_COLORS
|
||||
heatmap: HEATMAP_COLORS_GREEN
|
||||
};
|
||||
|
||||
// Universal constants
|
||||
|
||||
@ -11,6 +11,9 @@ export const MONTH_NAMES = ["January", "February", "March", "April", "May", "Jun
|
||||
|
||||
export const MONTH_NAMES_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
|
||||
export const DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||
export const DAY_NAMES_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
|
||||
// https://stackoverflow.com/a/11252167/6495043
|
||||
function treatAsUtc(date) {
|
||||
let result = new Date(date);
|
||||
@ -44,7 +47,7 @@ export function timestampToMidnight(timestamp, roundAhead = false) {
|
||||
return midnightTs;
|
||||
}
|
||||
|
||||
export function getMonthsBetween(startDate, endDate) {}
|
||||
// export function getMonthsBetween(startDate, endDate) {}
|
||||
|
||||
export function getWeeksBetween(startDate, endDate) {
|
||||
let weekStartDate = setDayToSunday(startDate);
|
||||
|
||||
@ -124,7 +124,7 @@ export function activate($parent, $child, commonClass, activeClass='active', ind
|
||||
forEachNode($children, (node, i) => {
|
||||
if(index >= 0 && i <= index) return;
|
||||
node.classList.remove(activeClass);
|
||||
})
|
||||
});
|
||||
|
||||
$child.classList.add(activeClass);
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ export function makeGradient(svgDefElem, color, lighter = false) {
|
||||
}
|
||||
|
||||
export function percentageBar(x, y, width, height,
|
||||
depth=PERCENTAGE_BAR_DEFAULT_DEPTH, fill='none') {
|
||||
depth=PERCENTAGE_BAR_DEFAULT_DEPTH, fill='none') {
|
||||
|
||||
let args = {
|
||||
className: 'percentage-bar',
|
||||
@ -230,14 +230,19 @@ export function legendDot(x, y, size, fill='none', label) {
|
||||
return group;
|
||||
}
|
||||
|
||||
export function makeText(className, x, y, content, fontSize = FONT_SIZE) {
|
||||
export function makeText(className, x, y, content, options = {}) {
|
||||
let fontSize = options.fontSize || FONT_SIZE;
|
||||
let dy = options.dy !== undefined ? options.dy : (fontSize / 2);
|
||||
let fill = options.fill || FONT_FILL;
|
||||
let textAnchor = options.textAnchor || 'start';
|
||||
return createSVG('text', {
|
||||
className: className,
|
||||
x: x,
|
||||
y: y,
|
||||
dy: (fontSize / 2) + 'px',
|
||||
dy: dy + 'px',
|
||||
'font-size': fontSize + 'px',
|
||||
fill: FONT_FILL,
|
||||
fill: fill,
|
||||
'text-anchor': textAnchor,
|
||||
innerHTML: content
|
||||
});
|
||||
}
|
||||
|
||||
@ -77,9 +77,18 @@ export function bindChange(obj, getFn, setFn) {
|
||||
});
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/29325222
|
||||
export function getRandomBias(min, max, bias, influence) {
|
||||
const range = max - min;
|
||||
const biasValue = range * bias + min;
|
||||
var rnd = Math.random() * range + min, // random in range
|
||||
mix = Math.random() * influence; // random mixer
|
||||
return rnd * (1 - mix) + biasValue * mix; // mix full range and bias
|
||||
}
|
||||
|
||||
export function getPositionByAngle(angle, radius) {
|
||||
return {
|
||||
x:Math.sin(angle * ANGLE_RATIO) * radius,
|
||||
y:Math.cos(angle * ANGLE_RATIO) * radius,
|
||||
x: Math.sin(angle * ANGLE_RATIO) * radius,
|
||||
y: Math.cos(angle * ANGLE_RATIO) * radius,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user