Browse Source

[heatmap] subdomains and legend, biased random, blue theme

tags/1.2.0
Prateeksha Singh 7 years ago
parent
commit
eb3041ae4f
22 changed files with 304 additions and 118 deletions
  1. +107
    -24
      dist/frappe-charts.esm.js
  2. +1
    -1
      dist/frappe-charts.min.cjs.js
  3. +1
    -1
      dist/frappe-charts.min.esm.js
  4. +1
    -1
      dist/frappe-charts.min.iife.js
  5. +1
    -1
      dist/frappe-charts.min.iife.js.map
  6. +5
    -4
      docs/assets/js/data.js
  7. +1
    -1
      docs/assets/js/frappe-charts.min.js
  8. +1
    -1
      docs/assets/js/frappe-charts.min.js.map
  9. +19
    -23
      docs/assets/js/index.js
  10. +47
    -27
      docs/assets/js/index.min.js
  11. +1
    -1
      docs/assets/js/index.min.js.map
  12. +3
    -2
      docs/index.html
  13. +3
    -3
      src/js/charts/AxisChart.js
  14. +7
    -4
      src/js/charts/BaseChart.js
  15. +56
    -4
      src/js/charts/Heatmap.js
  16. +2
    -2
      src/js/charts/PercentageChart.js
  17. +16
    -8
      src/js/objects/ChartComponents.js
  18. +7
    -2
      src/js/utils/constants.js
  19. +4
    -1
      src/js/utils/date-utils.js
  20. +1
    -1
      src/js/utils/dom.js
  21. +9
    -4
      src/js/utils/draw.js
  22. +11
    -2
      src/js/utils/helpers.js

+ 107
- 24
dist/frappe-charts.esm.js View File

@@ -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,


+ 1
- 1
dist/frappe-charts.min.cjs.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/frappe-charts.min.esm.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/frappe-charts.min.iife.js
File diff suppressed because it is too large
View File


+ 1
- 1
dist/frappe-charts.min.iife.js.map
File diff suppressed because it is too large
View File


+ 5
- 4
docs/assets/js/data.js View File

@@ -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;
}



+ 1
- 1
docs/assets/js/frappe-charts.min.js
File diff suppressed because it is too large
View File


+ 1
- 1
docs/assets/js/frappe-charts.min.js.map
File diff suppressed because it is too large
View File


+ 19
- 23
docs/assets/js/index.js View File

@@ -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 => {


+ 47
- 27
docs/assets/js/index.min.js View File

@@ -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');


+ 1
- 1
docs/assets/js/index.min.js.map
File diff suppressed because it is too large
View File


+ 3
- 2
docs/index.html View File

@@ -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']


+ 3
- 3
src/js/charts/AxisChart.js View File

@@ -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);


+ 7
- 4
src/js/charts/BaseChart.js View File

@@ -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() {}


+ 56
- 4
src/js/charts/Heatmap.js View File

@@ -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()];


+ 2
- 2
src/js/charts/PercentageChart.js View File

@@ -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);


+ 16
- 8
src/js/objects/ChartComponents.js View File

@@ -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: {


+ 7
- 2
src/js/utils/constants.js View File

@@ -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


+ 4
- 1
src/js/utils/date-utils.js View File

@@ -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);


+ 1
- 1
src/js/utils/dom.js View File

@@ -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);
}

+ 9
- 4
src/js/utils/draw.js View File

@@ -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
});
}


+ 11
- 2
src/js/utils/helpers.js View File

@@ -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…
Cancel
Save