chartjs-chart-treemap
Advanced tools
Comparing version 1.0.0-alpha to 1.0.0-beta
/*! | ||
* chartjs-chart-treemap v1.0.0-alpha | ||
* chartjs-chart-treemap v1.0.0-beta | ||
* https://github.com/kurkle/chartjs-chart-treemap#readme | ||
@@ -7,798 +7,791 @@ * (c) 2020 Jukka Kurkela | ||
*/ | ||
import Chart from 'chart.js'; | ||
import { DatasetController, Element } from 'chart.js'; | ||
import { toFont } from 'chart.js/helpers'; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
function _extends() { | ||
_extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
} | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat | ||
function flatten(input) { | ||
var stack = [...input]; | ||
var res = []; | ||
const stack = [...input]; | ||
const res = []; | ||
while (stack.length) { | ||
// pop value from stack | ||
const next = stack.pop(); | ||
if (Array.isArray(next)) { | ||
// push back array items, won't modify the original input | ||
stack.push(...next); | ||
} else { | ||
res.push(next); | ||
} | ||
} | ||
// reverse to restore input order | ||
return res.reverse(); | ||
} | ||
while (stack.length) { | ||
// pop value from stack | ||
var next = stack.pop(); | ||
/** | ||
* @param {[]} values | ||
* @param {string} grp | ||
* @param {string} key | ||
* @param {string} [mainGrp] | ||
* @param {*} [mainValue] | ||
*/ | ||
function group(values, grp, key, mainGrp, mainValue) { | ||
const tmp = Object.create(null); | ||
const data = Object.create(null); | ||
const ret = []; | ||
let g, i, n, v; | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
v = values[i]; | ||
if (mainGrp && v[mainGrp] !== mainValue) { | ||
continue; | ||
} | ||
g = v[grp] || ''; | ||
if (!(g in tmp)) { | ||
tmp[g] = 0; | ||
data[g] = []; | ||
} | ||
tmp[g] += +v[key]; | ||
data[g].push(v); | ||
} | ||
if (Array.isArray(next)) { | ||
// push back array items, won't modify the original input | ||
stack.push(...next); | ||
} else { | ||
res.push(next); | ||
} | ||
} // reverse to restore input order | ||
Object.keys(tmp).forEach((k) => { | ||
v = {children: data[k]}; | ||
v[key] = +tmp[k]; | ||
v[grp] = k; | ||
if (mainGrp) { | ||
v[mainGrp] = mainValue; | ||
} | ||
ret.push(v); | ||
}); | ||
return res.reverse(); | ||
return ret; | ||
} | ||
function group(values, grp, key, mainGrp, mainValue) { | ||
var tmp = Object.create(null); | ||
var data = Object.create(null); | ||
var ret = []; | ||
var g, i, n, v; | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
v = values[i]; | ||
if (mainGrp && v[mainGrp] !== mainValue) { | ||
continue; | ||
} | ||
g = v[grp] || ''; | ||
if (!(g in tmp)) { | ||
tmp[g] = 0; | ||
data[g] = []; | ||
} | ||
tmp[g] += +v[key]; | ||
data[g].push(v); | ||
} | ||
Object.keys(tmp).forEach(k => { | ||
v = { | ||
children: data[k] | ||
}; | ||
v[key] = +tmp[k]; | ||
v[grp] = k; | ||
if (mainGrp) { | ||
v[mainGrp] = mainValue; | ||
} | ||
ret.push(v); | ||
}); | ||
return ret; | ||
} | ||
function isObject(obj) { | ||
var type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
const type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
} | ||
function index(values, key) { | ||
var n = values.length; | ||
var i; | ||
let n = values.length; | ||
let i; | ||
if (!n) { | ||
return key; | ||
} | ||
if (!n) { | ||
return key; | ||
} | ||
var obj = isObject(values[0]); | ||
key = obj ? key : 'v'; | ||
const obj = isObject(values[0]); | ||
key = obj ? key : 'v'; | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
if (obj) { | ||
values[i]._idx = i; | ||
} else { | ||
values[i] = { | ||
v: values[i], | ||
_idx: i | ||
}; | ||
} | ||
} | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
if (obj) { | ||
values[i]._idx = i; | ||
} else { | ||
values[i] = {v: values[i], _idx: i}; | ||
} | ||
} | ||
return key; | ||
} | ||
return key; | ||
} | ||
function sort(values, key) { | ||
if (key) { | ||
values.sort((a, b) => +b[key] - +a[key]); | ||
} else { | ||
values.sort((a, b) => +b - +a); | ||
} | ||
if (key) { | ||
values.sort((a, b) => +b[key] - +a[key]); | ||
} else { | ||
values.sort((a, b) => +b - +a); | ||
} | ||
} | ||
function sum(values, key) { | ||
var s, i, n; | ||
let s, i, n; | ||
for (s = 0, i = 0, n = values.length; i < n; ++i) { | ||
s += key ? +values[i][key] : +values[i]; | ||
} | ||
for (s = 0, i = 0, n = values.length; i < n; ++i) { | ||
s += key ? +values[i][key] : +values[i]; | ||
} | ||
return s; | ||
return s; | ||
} | ||
function round(v, n) { | ||
return +(Math.round(v + 'e+' + n) + 'e-' + n); | ||
return +(Math.round(v + 'e+' + n) + 'e-' + n); | ||
} | ||
function getDims(itm, w2, s2, key) { | ||
var a = itm._normalized; | ||
var ar = w2 * a / s2; | ||
var d1 = Math.sqrt(a * ar); | ||
var d2 = a / d1; | ||
var w = key === '_ix' ? d1 : d2; | ||
var h = key === '_ix' ? d2 : d1; | ||
return { | ||
d1, | ||
d2, | ||
w, | ||
h | ||
}; | ||
const a = itm._normalized; | ||
const ar = w2 * a / s2; | ||
const d1 = Math.sqrt(a * ar); | ||
const d2 = a / d1; | ||
const w = key === '_ix' ? d1 : d2; | ||
const h = key === '_ix' ? d2 : d1; | ||
return {d1, d2, w, h}; | ||
} | ||
function buildRow(rect, itm, dims, sum) { | ||
var r = { | ||
x: round(rect.x + rect._ix, 4), | ||
y: round(rect.y + rect._iy, 4), | ||
w: round(dims.w, 4), | ||
h: round(dims.h, 4), | ||
a: itm._normalized, | ||
v: itm.value, | ||
s: sum, | ||
_data: itm._data | ||
}; | ||
if (itm.group) { | ||
r.g = itm.group; | ||
r.l = itm.level; | ||
r.gs = itm.groupSum; | ||
} | ||
return r; | ||
const r = { | ||
x: round(rect.x + rect._ix, 4), | ||
y: round(rect.y + rect._iy, 4), | ||
w: round(dims.w, 4), | ||
h: round(dims.h, 4), | ||
a: round(itm._normalized, 4), | ||
v: itm.value, | ||
s: sum, | ||
_data: itm._data | ||
}; | ||
if (itm.group) { | ||
r.g = itm.group; | ||
r.l = itm.level; | ||
r.gs = itm.groupSum; | ||
} | ||
return r; | ||
} | ||
class Rect { | ||
constructor(r) { | ||
var me = this; | ||
me.x = r.x || r.left || 0; | ||
me.y = r.y || r.top || 0; | ||
me._ix = 0; | ||
me._iy = 0; | ||
me.w = r.w || r.width || r.right - r.left; | ||
me.h = r.h || r.height || r.bottom - r.top; | ||
} | ||
constructor(r) { | ||
const me = this; | ||
r = r || {w: 1, h: 1}; | ||
me.x = r.x || r.left || 0; | ||
me.y = r.y || r.top || 0; | ||
me._ix = 0; | ||
me._iy = 0; | ||
me.w = r.w || r.width || (r.right - r.left); | ||
me.h = r.h || r.height || (r.bottom - r.top); | ||
} | ||
get area() { | ||
return this.w * this.h; | ||
} | ||
get area() { | ||
return this.w * this.h; | ||
} | ||
get iw() { | ||
return this.w - this._ix; | ||
} | ||
get iw() { | ||
return this.w - this._ix; | ||
} | ||
get ih() { | ||
return this.h - this._iy; | ||
} | ||
get ih() { | ||
return this.h - this._iy; | ||
} | ||
get dir() { | ||
var ih = this.ih; | ||
return ih <= this.iw && ih > 0 ? 'y' : 'x'; | ||
} | ||
get dir() { | ||
const ih = this.ih; | ||
return ih <= this.iw && ih > 0 ? 'y' : 'x'; | ||
} | ||
get side() { | ||
return this.dir === 'x' ? this.iw : this.ih; | ||
} | ||
get side() { | ||
return this.dir === 'x' ? this.iw : this.ih; | ||
} | ||
map(arr) { | ||
var me = this; | ||
var ret = []; | ||
var sum = arr.nsum; | ||
var row = arr.get(); | ||
var n = row.length; | ||
var dir = me.dir; | ||
var side = me.side; | ||
var w2 = side * side; | ||
var key = dir === 'x' ? '_ix' : '_iy'; | ||
var s2 = sum * sum; | ||
var maxd2 = 0; | ||
var totd1 = 0; | ||
var i, itm, dims; | ||
for (i = 0; i < n; ++i) { | ||
itm = row[i]; | ||
dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
if (dims.d2 > maxd2) { | ||
maxd2 = dims.d2; | ||
} | ||
ret.push(buildRow(me, itm, dims, arr.sum)); | ||
me[key] += dims.d1; | ||
} | ||
me[dir === 'y' ? '_ix' : '_iy'] += maxd2; | ||
me[key] -= totd1; | ||
return ret; | ||
} | ||
map(arr) { | ||
const me = this; | ||
const ret = []; | ||
const sum = arr.nsum; | ||
const row = arr.get(); | ||
const n = row.length; | ||
const dir = me.dir; | ||
const side = me.side; | ||
const w2 = side * side; | ||
const key = dir === 'x' ? '_ix' : '_iy'; | ||
const s2 = sum * sum; | ||
let maxd2 = 0; | ||
let totd1 = 0; | ||
let i, itm, dims; | ||
for (i = 0; i < n; ++i) { | ||
itm = row[i]; | ||
dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
if (dims.d2 > maxd2) { | ||
maxd2 = dims.d2; | ||
} | ||
ret.push(buildRow(me, itm, dims, arr.sum)); | ||
me[key] += dims.d1; | ||
} | ||
me[dir === 'y' ? '_ix' : '_iy'] += maxd2; | ||
me[key] -= totd1; | ||
return ret; | ||
} | ||
} | ||
var min = Math.min; | ||
var max = Math.max; | ||
const min = Math.min; | ||
const max = Math.max; | ||
function getStat(sa) { | ||
return { | ||
min: sa.min, | ||
max: sa.max, | ||
sum: sa.sum, | ||
nmin: sa.nmin, | ||
nmax: sa.nmax, | ||
nsum: sa.nsum | ||
}; | ||
return { | ||
min: sa.min, | ||
max: sa.max, | ||
sum: sa.sum, | ||
nmin: sa.nmin, | ||
nmax: sa.nmax, | ||
nsum: sa.nsum | ||
}; | ||
} | ||
function getNewStat(sa, o) { | ||
var v = +o[sa.key]; | ||
var n = v * sa.ratio; | ||
o._normalized = n; | ||
return { | ||
min: min(sa.min, v), | ||
max: max(sa.max, v), | ||
sum: sa.sum + v, | ||
nmin: min(sa.nmin, n), | ||
nmax: max(sa.nmax, n), | ||
nsum: sa.nsum + n | ||
}; | ||
const v = +o[sa.key]; | ||
const n = v * sa.ratio; | ||
o._normalized = n; | ||
return { | ||
min: min(sa.min, v), | ||
max: max(sa.max, v), | ||
sum: sa.sum + v, | ||
nmin: min(sa.nmin, n), | ||
nmax: max(sa.nmax, n), | ||
nsum: sa.nsum + n | ||
}; | ||
} | ||
function setStat(sa, stat) { | ||
_extends(sa, stat); | ||
Object.assign(sa, stat); | ||
} | ||
function push(sa, o, stat) { | ||
sa._arr.push(o); | ||
setStat(sa, stat); | ||
sa._arr.push(o); | ||
setStat(sa, stat); | ||
} | ||
class statArray { | ||
constructor(key, ratio) { | ||
var me = this; | ||
me.key = key; | ||
me.ratio = ratio; | ||
me.reset(); | ||
} | ||
class StatArray { | ||
constructor(key, ratio) { | ||
const me = this; | ||
me.key = key; | ||
me.ratio = ratio; | ||
me.reset(); | ||
} | ||
get length() { | ||
return this._arr.length; | ||
} | ||
get length() { | ||
return this._arr.length; | ||
} | ||
reset() { | ||
var me = this; | ||
me._arr = []; | ||
me._hist = []; | ||
me.sum = 0; | ||
me.nsum = 0; | ||
me.min = Infinity; | ||
me.max = -Infinity; | ||
me.nmin = Infinity; | ||
me.nmax = -Infinity; | ||
} | ||
reset() { | ||
const me = this; | ||
me._arr = []; | ||
me._hist = []; | ||
me.sum = 0; | ||
me.nsum = 0; | ||
me.min = Infinity; | ||
me.max = -Infinity; | ||
me.nmin = Infinity; | ||
me.nmax = -Infinity; | ||
} | ||
push(o) { | ||
push(this, o, getNewStat(this, o)); | ||
} | ||
push(o) { | ||
push(this, o, getNewStat(this, o)); | ||
} | ||
pushIf(o, fn) { | ||
var nstat = getNewStat(this, o); | ||
pushIf(o, fn, ...args) { | ||
const nstat = getNewStat(this, o); | ||
if (!fn(getStat(this), nstat, args)) { | ||
return o; | ||
} | ||
push(this, o, nstat); | ||
} | ||
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { | ||
args[_key - 2] = arguments[_key]; | ||
} | ||
if (!fn(getStat(this), nstat, args)) { | ||
return o; | ||
} | ||
push(this, o, nstat); | ||
} | ||
get() { | ||
return this._arr; | ||
} | ||
get() { | ||
return this._arr; | ||
} | ||
} | ||
function compareAspectRatio(oldStat, newStat, args) { | ||
if (oldStat.sum === 0) { | ||
return true; | ||
} | ||
if (oldStat.sum === 0) { | ||
return true; | ||
} | ||
var [length] = args; | ||
var os2 = oldStat.nsum * oldStat.nsum; | ||
var ns2 = newStat.nsum * newStat.nsum; | ||
var l2 = length * length; | ||
var or = Math.max(l2 * oldStat.nmax / os2, os2 / (l2 * oldStat.nmin)); | ||
var nr = Math.max(l2 * newStat.nmax / ns2, ns2 / (l2 * newStat.nmin)); | ||
return nr <= or; | ||
const [length] = args; | ||
const os2 = oldStat.nsum * oldStat.nsum; | ||
const ns2 = newStat.nsum * newStat.nsum; | ||
const l2 = length * length; | ||
const or = Math.max(l2 * oldStat.nmax / os2, os2 / (l2 * oldStat.nmin)); | ||
const nr = Math.max(l2 * newStat.nmax / ns2, ns2 / (l2 * newStat.nmin)); | ||
return nr <= or; | ||
} | ||
function squarify(values, r, key, grp, lvl, gsum) { | ||
var rows = []; | ||
var rect = new Rect(r); | ||
var row = new statArray('value', rect.area / sum(values, key)); | ||
var length = rect.side; | ||
var n = values.length; | ||
var i, o; | ||
/** | ||
* | ||
* @param {number[]|object[]} values | ||
* @param {object} rectangle | ||
* @param {string} key | ||
* @param {*} grp | ||
* @param {*} lvl | ||
* @param {*} gsum | ||
*/ | ||
function squarify(values, rectangle, key, grp, lvl, gsum) { | ||
values = values || []; | ||
const rows = []; | ||
const rect = new Rect(rectangle); | ||
const row = new StatArray('value', rect.area / sum(values, key)); | ||
let length = rect.side; | ||
const n = values.length; | ||
let i, o; | ||
if (!n) { | ||
return rows; | ||
} | ||
if (!n) { | ||
return rows; | ||
} | ||
var tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
const tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
function val(idx) { | ||
return key ? +tmp[idx][key] : +tmp[idx]; | ||
} | ||
const val = (idx) => key ? +tmp[idx][key] : +tmp[idx]; | ||
const gval = (idx) => grp && tmp[idx][grp]; | ||
function gval(idx) { | ||
return grp && tmp[idx][grp]; | ||
} | ||
for (i = 0; i < n; ++i) { | ||
o = {value: val(i), groupSum: gsum, _data: values[tmp[i]._idx]}; | ||
if (grp) { | ||
o.level = lvl; | ||
o.group = gval(i); | ||
} | ||
o = row.pushIf(o, compareAspectRatio, length); | ||
if (o) { | ||
rows.push(rect.map(row)); | ||
length = rect.side; | ||
row.reset(); | ||
row.push(o); | ||
} | ||
} | ||
if (row.length) { | ||
rows.push(rect.map(row)); | ||
} | ||
return flatten(rows); | ||
} | ||
for (i = 0; i < n; ++i) { | ||
o = { | ||
value: val(i), | ||
groupSum: gsum, | ||
_data: values[tmp[i]._idx] | ||
}; | ||
function rectNotEqual(r1, r2) { | ||
return !r1 || !r2 | ||
|| r1.x !== r2.x | ||
|| r1.y !== r2.y | ||
|| r1.w !== r2.w | ||
|| r1.h !== r2.h; | ||
} | ||
if (grp) { | ||
o.level = lvl; | ||
o.group = gval(i); | ||
} | ||
function arrayNotEqual(a1, a2) { | ||
let i, n; | ||
o = row.pushIf(o, compareAspectRatio, length); | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
if (o) { | ||
rows.push(rect.map(row)); | ||
length = rect.side; | ||
row.reset(); | ||
row.push(o); | ||
} | ||
} | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
if (row.length) { | ||
rows.push(rect.map(row)); | ||
} | ||
function shouldDrawCaption(rect, font) { | ||
if (!font) { | ||
return false; | ||
} | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
const min = font.lineHeight * 2; | ||
return w > min && h > min; | ||
} | ||
return flatten(rows); | ||
function drawCaption(ctx, rect, item, opts, levels) { | ||
ctx.save(); | ||
ctx.fillStyle = opts.font.color; | ||
ctx.font = opts.font.string; | ||
ctx.beginPath(); | ||
ctx.rect(rect.x, rect.y, rect.width, rect.height); | ||
ctx.clip(); | ||
if (!('l' in item) || item.l === levels) { | ||
ctx.textAlign = 'center'; | ||
ctx.textBaseline = 'middle'; | ||
drawLabels(ctx, item, rect); | ||
} else if (opts.groupLabels) { | ||
ctx.textAlign = 'start'; | ||
ctx.textBaseline = 'top'; | ||
ctx.fillText(item.g, rect.x + opts.borderWidth + 3, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
} | ||
/** | ||
* Helper function to get the bounds of the rect | ||
* @param {Rectangle} rect the rect | ||
* @param {boolean} [useFinalPosition] | ||
* @return {object} bounds of the rect | ||
* @private | ||
*/ | ||
function drawDivider(ctx, rect) { | ||
const opts = rect.options; | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
function getBounds(rect, useFinalPosition) { | ||
var { | ||
x, | ||
y, | ||
width, | ||
height | ||
} = rect.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
left: x, | ||
top: y, | ||
right: x + width, | ||
bottom: y + height | ||
}; | ||
ctx.save(); | ||
ctx.strokeStyle = opts.dividerColor || 'black'; | ||
ctx.lineCap = opts.dividerCapStyle; | ||
ctx.setLineDash(opts.dividerDash || []); | ||
ctx.lineDashOffset = opts.dividerDashOffset; | ||
ctx.lineWidth = opts.dividerWidth; | ||
ctx.beginPath(); | ||
if (w > h) { | ||
const w2 = w / 2; | ||
ctx.moveTo(rect.x + w2, rect.y); | ||
ctx.lineTo(rect.x + w2, rect.y + h); | ||
} else { | ||
const h2 = h / 2; | ||
ctx.moveTo(rect.x, rect.y + h2); | ||
ctx.lineTo(rect.x + w, rect.y + h2); | ||
} | ||
ctx.stroke(); | ||
ctx.restore(); | ||
} | ||
function limit(value, min, max) { | ||
return Math.max(Math.min(value, max), min); | ||
} | ||
function buildData(dataset, mainRect, font) { | ||
const key = dataset.key || ''; | ||
let tree = dataset.tree || []; | ||
const groups = dataset.groups || []; | ||
const glen = groups.length; | ||
const sp = (dataset.spacing || 0) + (dataset.borderWidth || 0); | ||
function parseBorderWidth(rect, maxW, maxH) { | ||
var value = rect.options.borderWidth; | ||
var t, r, b, l; | ||
function recur(gidx, rect, parent, gs) { | ||
const g = groups[gidx]; | ||
const pg = (gidx > 0) && groups[gidx - 1]; | ||
const gdata = group(tree, g, key, pg, parent); | ||
const gsq = squarify(gdata, rect, key, g, gidx, gs); | ||
const ret = gsq.slice(); | ||
let subRect; | ||
if (gidx < glen - 1) { | ||
gsq.forEach((sq) => { | ||
subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp}; | ||
if (Chart.helpers.isObject(value)) { | ||
t = +value.top || 0; | ||
r = +value.right || 0; | ||
b = +value.bottom || 0; | ||
l = +value.left || 0; | ||
} else { | ||
t = r = b = l = +value || 0; | ||
} | ||
if (dataset.groupLabels && shouldDrawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return ret; | ||
} | ||
return { | ||
t: limit(t, 0, maxH), | ||
r: limit(r, 0, maxW), | ||
b: limit(b, 0, maxH), | ||
l: limit(l, 0, maxW) | ||
}; | ||
} | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
function boundingRects(rect) { | ||
var bounds = getBounds(rect); | ||
var width = bounds.right - bounds.left; | ||
var height = bounds.bottom - bounds.top; | ||
var border = parseBorderWidth(rect, width / 2, height / 2); | ||
return { | ||
outer: { | ||
x: bounds.left, | ||
y: bounds.top, | ||
w: width, | ||
h: height | ||
}, | ||
inner: { | ||
x: bounds.left + border.l, | ||
y: bounds.top + border.t, | ||
w: width - border.l - border.r, | ||
h: height - border.t - border.b | ||
} | ||
}; | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key); | ||
} | ||
function inRange(rect, x, y, useFinalPosition) { | ||
var skipX = x === null; | ||
var skipY = y === null; | ||
var bounds = !rect || skipX && skipY ? false : getBounds(rect, useFinalPosition); | ||
return bounds && (skipX || x >= bounds.left && x <= bounds.right) && (skipY || y >= bounds.top && y <= bounds.bottom); | ||
function drawLabels(ctx, item, rect) { | ||
const opts = rect.options; | ||
const lh = opts.font.lineHeight; | ||
const labels = (opts.label || item.g + '\n' + item.v).split('\n'); | ||
const y = rect.y + rect.height / 2 - labels.length * lh / 4; | ||
labels.forEach((l, i) => ctx.fillText(l, rect.x + rect.width / 2, y + i * lh)); | ||
} | ||
class Rectangle extends Chart.Element { | ||
constructor(cfg) { | ||
super(); | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
class TreemapController extends DatasetController { | ||
initialize() { | ||
this.enableOptionSharing = true; | ||
super.initialize(); | ||
} | ||
if (cfg) { | ||
_extends(this, cfg); | ||
} | ||
} | ||
update(mode) { | ||
const me = this; | ||
const meta = me.getMeta(); | ||
const dataset = me.getDataset(); | ||
const groups = dataset.groups || (dataset.groups = []); | ||
const font = toFont(dataset.font, me.chart.options.font); | ||
const area = me.chart.chartArea; | ||
const key = dataset.key || ''; | ||
draw(ctx) { | ||
var options = this.options; | ||
var { | ||
inner, | ||
outer | ||
} = boundingRects(this); | ||
ctx.save(); | ||
const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top}; | ||
if (outer.w !== inner.w || outer.h !== inner.h) { | ||
ctx.beginPath(); | ||
ctx.rect(outer.x, outer.y, outer.w, outer.h); | ||
ctx.clip(); | ||
ctx.rect(inner.x, inner.y, inner.w, inner.h); | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fill(); | ||
ctx.fillStyle = options.borderColor; | ||
ctx.fill('evenodd'); | ||
} else { | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fillRect(inner.x, inner.y, inner.w, inner.h); | ||
} | ||
if (mode === 'reset' || rectNotEqual(me._rect, mainRect) || me._key !== key || arrayNotEqual(me._groups, groups)) { | ||
me._rect = mainRect; | ||
me._groups = groups.slice(); | ||
me._key = key; | ||
dataset.data = buildData(dataset, mainRect, font); | ||
me._dataCheck(); | ||
me._resyncElements(); | ||
} | ||
ctx.restore(); | ||
} | ||
me.updateElements(meta.data, 0, meta.data.length, mode); | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
_updateOptionsWithDefaults(options) { | ||
const me = this; | ||
const dataset = me.getDataset(); | ||
const treemapDefaults = me.chart.options.treemap.datasets; | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
options.dividerColor = dataset.dividerColor || treemapDefaults.dividerColor; | ||
options.dividerDash = dataset.dividerDash || treemapDefaults.dividerDash; | ||
options.dividerDashOffset = dataset.dividerDashOffset || treemapDefaults.dividerDashOffset; | ||
options.dividerWidth = dataset.dividerWidth || treemapDefaults.dividerWidth; | ||
options.font = toFont(options.font, me.chart.options.font); | ||
} | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
updateElements(rects, start, count, mode) { | ||
const me = this; | ||
const reset = mode === 'reset'; | ||
const dataset = me.getDataset(); | ||
const firstOpts = me._rect.options = me.resolveDataElementOptions(start, mode); | ||
const sharedOptions = me.getSharedOptions(firstOpts); | ||
const includeOptions = me.includeOptions(mode, sharedOptions); | ||
// me._updateOptionsWithDefaults(firstOpts); | ||
getCenterPoint(useFinalPosition) { | ||
var { | ||
x, | ||
y, | ||
width, | ||
height | ||
} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
for (let i = start; i < start + count; i++) { | ||
const sq = dataset.data[i]; | ||
const options = sharedOptions || me.resolveDataElementOptions(i, mode); | ||
const height = reset ? 0 : sq.h - options.spacing * 2; | ||
const width = reset ? 0 : sq.w - options.spacing * 2; | ||
const x = sq.x + options.spacing; | ||
const y = sq.y + options.spacing; | ||
const properties = { | ||
x, | ||
y, | ||
width, | ||
height | ||
}; | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
if (includeOptions) { | ||
properties.options = options; | ||
} | ||
// me._updateOptionsWithDefaults(options); | ||
me.updateElement(rects[i], i, properties, mode); | ||
} | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
me.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
} | ||
} | ||
_drawDividers(ctx, data, metadata) { | ||
for (let i = 0, ilen = metadata.length; i < ilen; ++i) { | ||
const rect = metadata[i]; | ||
const item = data[i]; | ||
if (rect.options.groupDividers && item._data.children.length > 1) { | ||
drawDivider(ctx, rect); | ||
} | ||
} | ||
if (this.getDataset().groupDividers) { | ||
drawDivider(ctx, this._rect); | ||
} | ||
} | ||
_defineProperty(Rectangle, "_type", 'rectangle'); | ||
_drawRects(ctx, data, metadata, levels) { | ||
for (let i = 0, ilen = metadata.length; i < ilen; ++i) { | ||
const rect = metadata[i]; | ||
const item = data[i]; | ||
if (!rect.hidden) { | ||
rect.draw(ctx); | ||
const opts = rect.options; | ||
if (shouldDrawCaption(rect, opts.font) && item.g) { | ||
drawCaption(ctx, rect, item, opts, levels); | ||
} | ||
} | ||
} | ||
} | ||
var defaults = Chart.defaults; | ||
var helpers = Chart.helpers; | ||
var optionHelpers = helpers.options; | ||
var parseFont = optionHelpers._parseFont; | ||
var resolve = optionHelpers.resolve; | ||
var valueOrDefault = helpers.valueOrDefault; | ||
draw() { | ||
const me = this; | ||
const ctx = me.chart.ctx; | ||
const metadata = me.getMeta().data || []; | ||
const dataset = me.getDataset(); | ||
const levels = (dataset.groups || []).length - 1; | ||
const data = dataset.data || []; | ||
function rectNotEqual(r1, r2) { | ||
return !r1 || !r2 || r1.x !== r2.x || r1.y !== r2.y || r1.w !== r2.w || r1.h !== r2.h; | ||
me._drawRects(ctx, data, metadata, levels); | ||
me._drawDividers(ctx, data, metadata); | ||
} | ||
} | ||
function arrayNotEqual(a1, a2) { | ||
var i, n; | ||
TreemapController.id = 'treemap'; | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
TreemapController.defaults = { | ||
dataElementType: 'treemap', | ||
dataElementOptions: [ | ||
'backgroundColor', | ||
'borderColor', | ||
'borderSkipped', | ||
'borderWidth', | ||
'dividerColor', | ||
'dividerDash', | ||
'dividerDashOffset', | ||
'dividerWidth', | ||
'font', | ||
'groupLabels', | ||
'groupDividers', | ||
'spacing', | ||
'label' | ||
], | ||
hover: { | ||
mode: 'point', | ||
intersect: true | ||
}, | ||
tooltips: { | ||
mode: 'point', | ||
position: 'treemap', | ||
intersect: true, | ||
callbacks: { | ||
title(items) { | ||
if (items.length) { | ||
const item = items[0]; | ||
return item.dataset.key || ''; | ||
} | ||
return ''; | ||
}, | ||
label(item) { | ||
const dataset = item.dataset; | ||
const dataItem = dataset.data[item.dataIndex]; | ||
return dataset.label + ': ' + dataItem.v; | ||
} | ||
} | ||
}, | ||
datasets: { | ||
groupLabels: true, | ||
borderWidth: 0, | ||
spacing: 0.5, | ||
groupDividers: false, | ||
dividerWidth: 1 | ||
}, | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
display: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
display: false | ||
} | ||
}, | ||
}; | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
/** | ||
* Helper function to get the bounds of the rect | ||
* @param {TreemapElement} rect the rect | ||
* @param {boolean} [useFinalPosition] | ||
* @return {object} bounds of the rect | ||
* @private | ||
*/ | ||
function getBounds(rect, useFinalPosition) { | ||
const {x, y, width, height} = rect.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return {left: x, top: y, right: x + width, bottom: y + height}; | ||
} | ||
function drawCaption(rect, font) { | ||
var w = rect.width || rect.w; | ||
var h = rect.height || rect.h; | ||
var min = font.lineHeight * 2; | ||
return w > min && h > min; | ||
function limit(value, min, max) { | ||
return Math.max(Math.min(value, max), min); | ||
} | ||
function buildData(dataset, mainRect, font) { | ||
var key = dataset.key || ''; | ||
var tree = dataset.tree || []; | ||
var groups = dataset.groups || []; | ||
var glen = groups.length; | ||
var sp = (dataset.spacing || 0) + (dataset.borderWidth || 0); | ||
function parseBorderWidth(value, maxW, maxH) { | ||
let t, r, b, l; | ||
function recur(gidx, rect, parent, gs) { | ||
var g = groups[gidx]; | ||
var pg = gidx > 0 && groups[gidx - 1]; | ||
var gdata = group(tree, g, key, pg, parent); | ||
var gsq = squarify(gdata, rect, key, g, gidx, gs); | ||
var ret = gsq.slice(); | ||
var subRect; | ||
if (isObject(value)) { | ||
t = +value.top || 0; | ||
r = +value.right || 0; | ||
b = +value.bottom || 0; | ||
l = +value.left || 0; | ||
} else { | ||
t = r = b = l = +value || 0; | ||
} | ||
if (gidx < glen - 1) { | ||
gsq.forEach(sq => { | ||
subRect = { | ||
x: sq.x + sp, | ||
y: sq.y + sp, | ||
w: sq.w - 2 * sp, | ||
h: sq.h - 2 * sp | ||
}; | ||
return { | ||
t: limit(t, 0, maxH), | ||
r: limit(r, 0, maxW), | ||
b: limit(b, 0, maxH), | ||
l: limit(l, 0, maxW) | ||
}; | ||
} | ||
if (drawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
function boundingRects(rect) { | ||
const bounds = getBounds(rect); | ||
const width = bounds.right - bounds.left; | ||
const height = bounds.bottom - bounds.top; | ||
const border = parseBorderWidth(rect.options.borderWidth, width / 2, height / 2); | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return { | ||
outer: { | ||
x: bounds.left, | ||
y: bounds.top, | ||
w: width, | ||
h: height | ||
}, | ||
inner: { | ||
x: bounds.left + border.l, | ||
y: bounds.top + border.t, | ||
w: width - border.l - border.r, | ||
h: height - border.t - border.b | ||
} | ||
}; | ||
} | ||
return ret; | ||
} | ||
function inRange(rect, x, y, useFinalPosition) { | ||
const skipX = x === null; | ||
const skipY = y === null; | ||
const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition); | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
return glen ? recur(0, mainRect) : squarify(tree, mainRect, key); | ||
return bounds | ||
&& (skipX || x >= bounds.left && x <= bounds.right) | ||
&& (skipY || y >= bounds.top && y <= bounds.bottom); | ||
} | ||
function parseFontOptions(options) { | ||
return _extends(parseFont({ | ||
fontFamily: valueOrDefault(options.fontFamily, defaults.fontFamily), | ||
fontSize: valueOrDefault(options.fontSize, defaults.fontSize), | ||
fontStyle: valueOrDefault(options.fontStyle, defaults.fontStyle), | ||
lineHeight: valueOrDefault(options.lineHeight, defaults.lineHeight) | ||
}), { | ||
color: resolve([options.fontColor, defaults.fontColor]) | ||
}); | ||
} | ||
class TreemapElement extends Element { | ||
class TreemapController extends Chart.DatasetController { | ||
update(mode) { | ||
var me = this; | ||
var meta = me.getMeta(); | ||
var dataset = me.getDataset(); | ||
var groups = dataset.groups || (dataset.groups = []); | ||
var font = parseFontOptions(dataset); | ||
var area = me.chart.chartArea; | ||
var key = dataset.key || ''; | ||
var mainRect = { | ||
x: area.left, | ||
y: area.top, | ||
w: area.right - area.left, | ||
h: area.bottom - area.top | ||
}; | ||
constructor(cfg) { | ||
super(); | ||
if (mode === 'reset' || rectNotEqual(me._rect, mainRect) || me._key !== key || arrayNotEqual(me._groups, groups)) { | ||
me._rect = mainRect; | ||
me._groups = groups.slice(); | ||
me._key = key; | ||
dataset.data = buildData(dataset, mainRect, font); | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
me._dataCheck(); | ||
if (cfg) { | ||
Object.assign(this, cfg); | ||
} | ||
} | ||
me._resyncElements(); | ||
} | ||
draw(ctx) { | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this); | ||
me.updateElements(meta.data, 0, mode); | ||
} | ||
ctx.save(); | ||
updateElements(rects, start, mode) { | ||
var me = this; | ||
var reset = mode === 'reset'; | ||
var dataset = me.getDataset(); | ||
var firstOpts = me.resolveDataElementOptions(start, mode); | ||
var sharedOptions = me.getSharedOptions(mode, rects[start], firstOpts); | ||
if (outer.w !== inner.w || outer.h !== inner.h) { | ||
ctx.beginPath(); | ||
ctx.rect(outer.x, outer.y, outer.w, outer.h); | ||
ctx.clip(); | ||
ctx.rect(inner.x, inner.y, inner.w, inner.h); | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fill(); | ||
ctx.fillStyle = options.borderColor; | ||
ctx.fill('evenodd'); | ||
} else { | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fillRect(inner.x, inner.y, inner.w, inner.h); | ||
} | ||
for (var i = 0; i < rects.length; i++) { | ||
var index = start + i; | ||
var sq = dataset.data[index]; | ||
var options = me.resolveDataElementOptions(i, mode); | ||
var height = reset ? 0 : sq.h - options.spacing * 2; | ||
var width = reset ? 0 : sq.w - options.spacing * 2; | ||
var x = sq.x + options.spacing; | ||
var y = sq.y + options.spacing; | ||
var properties = { | ||
x, | ||
y, | ||
width, | ||
height, | ||
options | ||
}; | ||
options.font = parseFont(options); | ||
me.updateElement(rects[i], index, properties, mode); | ||
} | ||
ctx.restore(); | ||
} | ||
me.updateSharedOptions(sharedOptions, mode); | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
draw() { | ||
var me = this; | ||
var metadata = me.getMeta().data || []; | ||
var dataset = me.getDataset(); | ||
var levels = (dataset.groups || []).length - 1; | ||
var data = dataset.data || []; | ||
var ctx = me.chart.ctx; | ||
var i, ilen, rect, item; | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
for (i = 0, ilen = metadata.length; i < ilen; ++i) { | ||
rect = metadata[i]; | ||
item = data[i]; | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
if (!rect.hidden) { | ||
rect.draw(ctx); | ||
var opts = rect.options; | ||
getCenterPoint(useFinalPosition) { | ||
const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
if (drawCaption(rect, opts.font) && item.g) { | ||
ctx.save(); | ||
ctx.fillStyle = opts.fontColor; | ||
ctx.font = opts.font.string; | ||
ctx.beginPath(); | ||
ctx.rect(rect.x, rect.y, rect.width, rect.height); | ||
ctx.clip(); | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
if (!('l' in item) || item.l === levels) { | ||
ctx.textAlign = 'center'; | ||
ctx.textBaseline = 'middle'; | ||
ctx.fillText(item.g, rect.x + rect.width / 2, rect.y + rect.height / 2); | ||
} else { | ||
ctx.textAlign = 'start'; | ||
ctx.textBaseline = 'top'; | ||
ctx.fillText(item.g, rect.x + opts.borderWidth + 3, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
} | ||
} | ||
} | ||
} | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
} | ||
TreemapController.prototype.dataElementType = Rectangle; | ||
TreemapController.prototype.dataElementOptions = ['backgroundColor', 'borderColor', 'borderSkipped', 'borderWidth', 'fontColor', 'fontFamily', 'fontSize', 'fontStyle', 'spacing']; | ||
var defaults$1 = { | ||
hover: { | ||
mode: 'point', | ||
intersect: true | ||
}, | ||
tooltips: { | ||
mode: 'point', | ||
position: 'treemap', | ||
intersect: true, | ||
callbacks: { | ||
title(item, data) { | ||
return data.datasets[item[0].datasetIndex].key; | ||
}, | ||
TreemapElement.id = 'treemap'; | ||
label(item, data) { | ||
var dataset = data.datasets[item.datasetIndex]; | ||
var dataItem = dataset.data[item.index]; | ||
return dataset.label + ': ' + dataItem.v; | ||
} | ||
TreemapElement.defaults = { | ||
dividerCapStyle: 'butt', | ||
dividerColor: 'black', | ||
dividerDash: undefined, | ||
dividerDashOffset: 0, | ||
dividerWidth: 0, | ||
groupDividers: false, | ||
}; | ||
} | ||
}, | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
display: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
display: false | ||
} | ||
}, | ||
elements: { | ||
rectangle: { | ||
borderWidth: 0, | ||
spacing: 0.5 | ||
} | ||
} | ||
TreemapElement.defaultRoutes = { | ||
backgroundColor: 'color', | ||
borderColor: 'color' | ||
}; | ||
Chart.controllers.treemap = TreemapController; | ||
Chart.defaults.treemap = defaults$1; | ||
var tooltipPlugin = Chart.plugins.getAll().find(p => p.id === 'tooltip'); | ||
tooltipPlugin.positioners.treemap = function (active) { | ||
if (!active.length) { | ||
return false; | ||
} | ||
var item = active[active.length - 1]; | ||
var el = item.element; | ||
return { | ||
x: el.x + el.width / 2, | ||
y: el.y + el.height / 2 | ||
}; | ||
}; | ||
export { Rect, TreemapElement as Rectangle, StatArray, TreemapController, flatten, group, index, isObject, sort, squarify, sum }; |
/*! | ||
* chartjs-chart-treemap v1.0.0-alpha | ||
* chartjs-chart-treemap v1.0.0-beta | ||
* https://github.com/kurkle/chartjs-chart-treemap#readme | ||
@@ -8,625 +8,646 @@ * (c) 2020 Jukka Kurkela | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js')) : | ||
typeof define === 'function' && define.amd ? define(['chart.js'], factory) : | ||
(global = global || self, factory(global.Chart)); | ||
}(this, (function (Chart) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js'), require('chart.js/helpers')) : | ||
typeof define === 'function' && define.amd ? define(['chart.js', 'chart.js/helpers'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Chart, global.Chart.helpers)); | ||
}(this, (function (chart_js, helpers) { 'use strict'; | ||
Chart = Chart && Object.prototype.hasOwnProperty.call(Chart, 'default') ? Chart['default'] : Chart; | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat | ||
function flatten(input) { | ||
const stack = [...input]; | ||
const res = []; | ||
while (stack.length) { | ||
// pop value from stack | ||
const next = stack.pop(); | ||
if (Array.isArray(next)) { | ||
// push back array items, won't modify the original input | ||
stack.push(...next); | ||
} else { | ||
res.push(next); | ||
} | ||
} | ||
// reverse to restore input order | ||
return res.reverse(); | ||
} | ||
function _typeof(obj) { | ||
"@babel/helpers - typeof"; | ||
/** | ||
* @param {[]} values | ||
* @param {string} grp | ||
* @param {string} key | ||
* @param {string} [mainGrp] | ||
* @param {*} [mainValue] | ||
*/ | ||
function group(values, grp, key, mainGrp, mainValue) { | ||
const tmp = Object.create(null); | ||
const data = Object.create(null); | ||
const ret = []; | ||
let g, i, n, v; | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
v = values[i]; | ||
if (mainGrp && v[mainGrp] !== mainValue) { | ||
continue; | ||
} | ||
g = v[grp] || ''; | ||
if (!(g in tmp)) { | ||
tmp[g] = 0; | ||
data[g] = []; | ||
} | ||
tmp[g] += +v[key]; | ||
data[g].push(v); | ||
} | ||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { | ||
_typeof = function (obj) { | ||
return typeof obj; | ||
}; | ||
} else { | ||
_typeof = function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
} | ||
Object.keys(tmp).forEach((k) => { | ||
v = {children: data[k]}; | ||
v[key] = +tmp[k]; | ||
v[grp] = k; | ||
if (mainGrp) { | ||
v[mainGrp] = mainValue; | ||
} | ||
ret.push(v); | ||
}); | ||
return _typeof(obj); | ||
return ret; | ||
} | ||
function _classCallCheck(instance, Constructor) { | ||
if (!(instance instanceof Constructor)) { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
function isObject(obj) { | ||
const type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
function index(values, key) { | ||
let n = values.length; | ||
let i; | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
if (!n) { | ||
return key; | ||
} | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
const obj = isObject(values[0]); | ||
key = obj ? key : 'v'; | ||
return obj; | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
if (obj) { | ||
values[i]._idx = i; | ||
} else { | ||
values[i] = {v: values[i], _idx: i}; | ||
} | ||
} | ||
return key; | ||
} | ||
function _extends() { | ||
_extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
function sort(values, key) { | ||
if (key) { | ||
values.sort((a, b) => +b[key] - +a[key]); | ||
} else { | ||
values.sort((a, b) => +b - +a); | ||
} | ||
} | ||
function _inherits(subClass, superClass) { | ||
if (typeof superClass !== "function" && superClass !== null) { | ||
throw new TypeError("Super expression must either be null or a function"); | ||
} | ||
function sum(values, key) { | ||
let s, i, n; | ||
subClass.prototype = Object.create(superClass && superClass.prototype, { | ||
constructor: { | ||
value: subClass, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
if (superClass) _setPrototypeOf(subClass, superClass); | ||
} | ||
for (s = 0, i = 0, n = values.length; i < n; ++i) { | ||
s += key ? +values[i][key] : +values[i]; | ||
} | ||
function _getPrototypeOf(o) { | ||
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { | ||
return o.__proto__ || Object.getPrototypeOf(o); | ||
}; | ||
return _getPrototypeOf(o); | ||
return s; | ||
} | ||
function _setPrototypeOf(o, p) { | ||
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { | ||
o.__proto__ = p; | ||
return o; | ||
}; | ||
return _setPrototypeOf(o, p); | ||
function round(v, n) { | ||
return +(Math.round(v + 'e+' + n) + 'e-' + n); | ||
} | ||
function _isNativeReflectConstruct() { | ||
if (typeof Reflect === "undefined" || !Reflect.construct) return false; | ||
if (Reflect.construct.sham) return false; | ||
if (typeof Proxy === "function") return true; | ||
function getDims(itm, w2, s2, key) { | ||
const a = itm._normalized; | ||
const ar = w2 * a / s2; | ||
const d1 = Math.sqrt(a * ar); | ||
const d2 = a / d1; | ||
const w = key === '_ix' ? d1 : d2; | ||
const h = key === '_ix' ? d2 : d1; | ||
try { | ||
Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
return {d1, d2, w, h}; | ||
} | ||
function _assertThisInitialized(self) { | ||
if (self === void 0) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
} | ||
return self; | ||
function buildRow(rect, itm, dims, sum) { | ||
const r = { | ||
x: round(rect.x + rect._ix, 4), | ||
y: round(rect.y + rect._iy, 4), | ||
w: round(dims.w, 4), | ||
h: round(dims.h, 4), | ||
a: round(itm._normalized, 4), | ||
v: itm.value, | ||
s: sum, | ||
_data: itm._data | ||
}; | ||
if (itm.group) { | ||
r.g = itm.group; | ||
r.l = itm.level; | ||
r.gs = itm.groupSum; | ||
} | ||
return r; | ||
} | ||
class Rect { | ||
constructor(r) { | ||
const me = this; | ||
r = r || {w: 1, h: 1}; | ||
me.x = r.x || r.left || 0; | ||
me.y = r.y || r.top || 0; | ||
me._ix = 0; | ||
me._iy = 0; | ||
me.w = r.w || r.width || (r.right - r.left); | ||
me.h = r.h || r.height || (r.bottom - r.top); | ||
} | ||
function _possibleConstructorReturn(self, call) { | ||
if (call && (typeof call === "object" || typeof call === "function")) { | ||
return call; | ||
} | ||
get area() { | ||
return this.w * this.h; | ||
} | ||
return _assertThisInitialized(self); | ||
} | ||
get iw() { | ||
return this.w - this._ix; | ||
} | ||
function _createSuper(Derived) { | ||
return function () { | ||
var Super = _getPrototypeOf(Derived), | ||
result; | ||
get ih() { | ||
return this.h - this._iy; | ||
} | ||
if (_isNativeReflectConstruct()) { | ||
var NewTarget = _getPrototypeOf(this).constructor; | ||
get dir() { | ||
const ih = this.ih; | ||
return ih <= this.iw && ih > 0 ? 'y' : 'x'; | ||
} | ||
result = Reflect.construct(Super, arguments, NewTarget); | ||
} else { | ||
result = Super.apply(this, arguments); | ||
} | ||
get side() { | ||
return this.dir === 'x' ? this.iw : this.ih; | ||
} | ||
return _possibleConstructorReturn(this, result); | ||
}; | ||
map(arr) { | ||
const me = this; | ||
const ret = []; | ||
const sum = arr.nsum; | ||
const row = arr.get(); | ||
const n = row.length; | ||
const dir = me.dir; | ||
const side = me.side; | ||
const w2 = side * side; | ||
const key = dir === 'x' ? '_ix' : '_iy'; | ||
const s2 = sum * sum; | ||
let maxd2 = 0; | ||
let totd1 = 0; | ||
let i, itm, dims; | ||
for (i = 0; i < n; ++i) { | ||
itm = row[i]; | ||
dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
if (dims.d2 > maxd2) { | ||
maxd2 = dims.d2; | ||
} | ||
ret.push(buildRow(me, itm, dims, arr.sum)); | ||
me[key] += dims.d1; | ||
} | ||
me[dir === 'y' ? '_ix' : '_iy'] += maxd2; | ||
me[key] -= totd1; | ||
return ret; | ||
} | ||
} | ||
function _slicedToArray(arr, i) { | ||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); | ||
} | ||
const min = Math.min; | ||
const max = Math.max; | ||
function _toConsumableArray(arr) { | ||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); | ||
function getStat(sa) { | ||
return { | ||
min: sa.min, | ||
max: sa.max, | ||
sum: sa.sum, | ||
nmin: sa.nmin, | ||
nmax: sa.nmax, | ||
nsum: sa.nsum | ||
}; | ||
} | ||
function _arrayWithoutHoles(arr) { | ||
if (Array.isArray(arr)) return _arrayLikeToArray(arr); | ||
} | ||
function getNewStat(sa, o) { | ||
const v = +o[sa.key]; | ||
const n = v * sa.ratio; | ||
o._normalized = n; | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
return { | ||
min: min(sa.min, v), | ||
max: max(sa.max, v), | ||
sum: sa.sum + v, | ||
nmin: min(sa.nmin, n), | ||
nmax: max(sa.nmax, n), | ||
nsum: sa.nsum + n | ||
}; | ||
} | ||
function _iterableToArray(iter) { | ||
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); | ||
function setStat(sa, stat) { | ||
Object.assign(sa, stat); | ||
} | ||
function _iterableToArrayLimit(arr, i) { | ||
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; | ||
var _arr = []; | ||
var _n = true; | ||
var _d = false; | ||
var _e = undefined; | ||
try { | ||
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
} | ||
} catch (err) { | ||
_d = true; | ||
_e = err; | ||
} finally { | ||
try { | ||
if (!_n && _i["return"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
function push(sa, o, stat) { | ||
sa._arr.push(o); | ||
setStat(sa, stat); | ||
} | ||
function _unsupportedIterableToArray(o, minLen) { | ||
if (!o) return; | ||
if (typeof o === "string") return _arrayLikeToArray(o, minLen); | ||
var n = Object.prototype.toString.call(o).slice(8, -1); | ||
if (n === "Object" && o.constructor) n = o.constructor.name; | ||
if (n === "Map" || n === "Set") return Array.from(n); | ||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); | ||
} | ||
class StatArray { | ||
constructor(key, ratio) { | ||
const me = this; | ||
me.key = key; | ||
me.ratio = ratio; | ||
me.reset(); | ||
} | ||
function _arrayLikeToArray(arr, len) { | ||
if (len == null || len > arr.length) len = arr.length; | ||
get length() { | ||
return this._arr.length; | ||
} | ||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | ||
reset() { | ||
const me = this; | ||
me._arr = []; | ||
me._hist = []; | ||
me.sum = 0; | ||
me.nsum = 0; | ||
me.min = Infinity; | ||
me.max = -Infinity; | ||
me.nmin = Infinity; | ||
me.nmax = -Infinity; | ||
} | ||
return arr2; | ||
} | ||
push(o) { | ||
push(this, o, getNewStat(this, o)); | ||
} | ||
function _nonIterableSpread() { | ||
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
} | ||
pushIf(o, fn, ...args) { | ||
const nstat = getNewStat(this, o); | ||
if (!fn(getStat(this), nstat, args)) { | ||
return o; | ||
} | ||
push(this, o, nstat); | ||
} | ||
function _nonIterableRest() { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
get() { | ||
return this._arr; | ||
} | ||
} | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat | ||
function flatten(input) { | ||
var stack = _toConsumableArray(input); | ||
function compareAspectRatio(oldStat, newStat, args) { | ||
if (oldStat.sum === 0) { | ||
return true; | ||
} | ||
var res = []; | ||
while (stack.length) { | ||
// pop value from stack | ||
var next = stack.pop(); | ||
if (Array.isArray(next)) { | ||
// push back array items, won't modify the original input | ||
stack.push.apply(stack, _toConsumableArray(next)); | ||
} else { | ||
res.push(next); | ||
} | ||
} // reverse to restore input order | ||
return res.reverse(); | ||
const [length] = args; | ||
const os2 = oldStat.nsum * oldStat.nsum; | ||
const ns2 = newStat.nsum * newStat.nsum; | ||
const l2 = length * length; | ||
const or = Math.max(l2 * oldStat.nmax / os2, os2 / (l2 * oldStat.nmin)); | ||
const nr = Math.max(l2 * newStat.nmax / ns2, ns2 / (l2 * newStat.nmin)); | ||
return nr <= or; | ||
} | ||
function group(values, grp, key, mainGrp, mainValue) { | ||
var tmp = Object.create(null); | ||
var data = Object.create(null); | ||
var ret = []; | ||
var g, i, n, v; | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
v = values[i]; | ||
/** | ||
* | ||
* @param {number[]|object[]} values | ||
* @param {object} rectangle | ||
* @param {string} key | ||
* @param {*} grp | ||
* @param {*} lvl | ||
* @param {*} gsum | ||
*/ | ||
function squarify(values, rectangle, key, grp, lvl, gsum) { | ||
values = values || []; | ||
const rows = []; | ||
const rect = new Rect(rectangle); | ||
const row = new StatArray('value', rect.area / sum(values, key)); | ||
let length = rect.side; | ||
const n = values.length; | ||
let i, o; | ||
if (mainGrp && v[mainGrp] !== mainValue) { | ||
continue; | ||
} | ||
if (!n) { | ||
return rows; | ||
} | ||
g = v[grp] || ''; | ||
const tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
if (!(g in tmp)) { | ||
tmp[g] = 0; | ||
data[g] = []; | ||
} | ||
const val = (idx) => key ? +tmp[idx][key] : +tmp[idx]; | ||
const gval = (idx) => grp && tmp[idx][grp]; | ||
tmp[g] += +v[key]; | ||
data[g].push(v); | ||
} | ||
Object.keys(tmp).forEach(function (k) { | ||
v = { | ||
children: data[k] | ||
}; | ||
v[key] = +tmp[k]; | ||
v[grp] = k; | ||
if (mainGrp) { | ||
v[mainGrp] = mainValue; | ||
} | ||
ret.push(v); | ||
}); | ||
return ret; | ||
for (i = 0; i < n; ++i) { | ||
o = {value: val(i), groupSum: gsum, _data: values[tmp[i]._idx]}; | ||
if (grp) { | ||
o.level = lvl; | ||
o.group = gval(i); | ||
} | ||
o = row.pushIf(o, compareAspectRatio, length); | ||
if (o) { | ||
rows.push(rect.map(row)); | ||
length = rect.side; | ||
row.reset(); | ||
row.push(o); | ||
} | ||
} | ||
if (row.length) { | ||
rows.push(rect.map(row)); | ||
} | ||
return flatten(rows); | ||
} | ||
function isObject(obj) { | ||
var type = _typeof(obj); | ||
return type === 'function' || type === 'object' && !!obj; | ||
function rectNotEqual(r1, r2) { | ||
return !r1 || !r2 | ||
|| r1.x !== r2.x | ||
|| r1.y !== r2.y | ||
|| r1.w !== r2.w | ||
|| r1.h !== r2.h; | ||
} | ||
function index(values, key) { | ||
var n = values.length; | ||
var i; | ||
if (!n) { | ||
return key; | ||
} | ||
function arrayNotEqual(a1, a2) { | ||
let i, n; | ||
var obj = isObject(values[0]); | ||
key = obj ? key : 'v'; | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
for (i = 0, n = values.length; i < n; ++i) { | ||
if (obj) { | ||
values[i]._idx = i; | ||
} else { | ||
values[i] = { | ||
v: values[i], | ||
_idx: i | ||
}; | ||
} | ||
} | ||
return key; | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
function sort(values, key) { | ||
if (key) { | ||
values.sort(function (a, b) { | ||
return +b[key] - +a[key]; | ||
}); | ||
} else { | ||
values.sort(function (a, b) { | ||
return +b - +a; | ||
}); | ||
} | ||
} | ||
function sum(values, key) { | ||
var s, i, n; | ||
for (s = 0, i = 0, n = values.length; i < n; ++i) { | ||
s += key ? +values[i][key] : +values[i]; | ||
} | ||
return s; | ||
function shouldDrawCaption(rect, font) { | ||
if (!font) { | ||
return false; | ||
} | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
const min = font.lineHeight * 2; | ||
return w > min && h > min; | ||
} | ||
function round(v, n) { | ||
return +(Math.round(v + 'e+' + n) + 'e-' + n); | ||
function drawCaption(ctx, rect, item, opts, levels) { | ||
ctx.save(); | ||
ctx.fillStyle = opts.font.color; | ||
ctx.font = opts.font.string; | ||
ctx.beginPath(); | ||
ctx.rect(rect.x, rect.y, rect.width, rect.height); | ||
ctx.clip(); | ||
if (!('l' in item) || item.l === levels) { | ||
ctx.textAlign = 'center'; | ||
ctx.textBaseline = 'middle'; | ||
drawLabels(ctx, item, rect); | ||
} else if (opts.groupLabels) { | ||
ctx.textAlign = 'start'; | ||
ctx.textBaseline = 'top'; | ||
ctx.fillText(item.g, rect.x + opts.borderWidth + 3, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
} | ||
function getDims(itm, w2, s2, key) { | ||
var a = itm._normalized; | ||
var ar = w2 * a / s2; | ||
var d1 = Math.sqrt(a * ar); | ||
var d2 = a / d1; | ||
var w = key === '_ix' ? d1 : d2; | ||
var h = key === '_ix' ? d2 : d1; | ||
return { | ||
d1: d1, | ||
d2: d2, | ||
w: w, | ||
h: h | ||
}; | ||
} | ||
function drawDivider(ctx, rect) { | ||
const opts = rect.options; | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
function buildRow(rect, itm, dims, sum) { | ||
var r = { | ||
x: round(rect.x + rect._ix, 4), | ||
y: round(rect.y + rect._iy, 4), | ||
w: round(dims.w, 4), | ||
h: round(dims.h, 4), | ||
a: itm._normalized, | ||
v: itm.value, | ||
s: sum, | ||
_data: itm._data | ||
}; | ||
if (itm.group) { | ||
r.g = itm.group; | ||
r.l = itm.level; | ||
r.gs = itm.groupSum; | ||
} | ||
return r; | ||
ctx.save(); | ||
ctx.strokeStyle = opts.dividerColor || 'black'; | ||
ctx.lineCap = opts.dividerCapStyle; | ||
ctx.setLineDash(opts.dividerDash || []); | ||
ctx.lineDashOffset = opts.dividerDashOffset; | ||
ctx.lineWidth = opts.dividerWidth; | ||
ctx.beginPath(); | ||
if (w > h) { | ||
const w2 = w / 2; | ||
ctx.moveTo(rect.x + w2, rect.y); | ||
ctx.lineTo(rect.x + w2, rect.y + h); | ||
} else { | ||
const h2 = h / 2; | ||
ctx.moveTo(rect.x, rect.y + h2); | ||
ctx.lineTo(rect.x + w, rect.y + h2); | ||
} | ||
ctx.stroke(); | ||
ctx.restore(); | ||
} | ||
var Rect = /*#__PURE__*/function () { | ||
function Rect(r) { | ||
_classCallCheck(this, Rect); | ||
function buildData(dataset, mainRect, font) { | ||
const key = dataset.key || ''; | ||
let tree = dataset.tree || []; | ||
const groups = dataset.groups || []; | ||
const glen = groups.length; | ||
const sp = (dataset.spacing || 0) + (dataset.borderWidth || 0); | ||
var me = this; | ||
me.x = r.x || r.left || 0; | ||
me.y = r.y || r.top || 0; | ||
me._ix = 0; | ||
me._iy = 0; | ||
me.w = r.w || r.width || r.right - r.left; | ||
me.h = r.h || r.height || r.bottom - r.top; | ||
} | ||
function recur(gidx, rect, parent, gs) { | ||
const g = groups[gidx]; | ||
const pg = (gidx > 0) && groups[gidx - 1]; | ||
const gdata = group(tree, g, key, pg, parent); | ||
const gsq = squarify(gdata, rect, key, g, gidx, gs); | ||
const ret = gsq.slice(); | ||
let subRect; | ||
if (gidx < glen - 1) { | ||
gsq.forEach((sq) => { | ||
subRect = {x: sq.x + sp, y: sq.y + sp, w: sq.w - 2 * sp, h: sq.h - 2 * sp}; | ||
_createClass(Rect, [{ | ||
key: "map", | ||
value: function map(arr) { | ||
var me = this; | ||
var ret = []; | ||
var sum = arr.nsum; | ||
var row = arr.get(); | ||
var n = row.length; | ||
var dir = me.dir; | ||
var side = me.side; | ||
var w2 = side * side; | ||
var key = dir === 'x' ? '_ix' : '_iy'; | ||
var s2 = sum * sum; | ||
var maxd2 = 0; | ||
var totd1 = 0; | ||
var i, itm, dims; | ||
if (dataset.groupLabels && shouldDrawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return ret; | ||
} | ||
for (i = 0; i < n; ++i) { | ||
itm = row[i]; | ||
dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
if (dims.d2 > maxd2) { | ||
maxd2 = dims.d2; | ||
} | ||
ret.push(buildRow(me, itm, dims, arr.sum)); | ||
me[key] += dims.d1; | ||
} | ||
me[dir === 'y' ? '_ix' : '_iy'] += maxd2; | ||
me[key] -= totd1; | ||
return ret; | ||
} | ||
}, { | ||
key: "area", | ||
get: function get() { | ||
return this.w * this.h; | ||
} | ||
}, { | ||
key: "iw", | ||
get: function get() { | ||
return this.w - this._ix; | ||
} | ||
}, { | ||
key: "ih", | ||
get: function get() { | ||
return this.h - this._iy; | ||
} | ||
}, { | ||
key: "dir", | ||
get: function get() { | ||
var ih = this.ih; | ||
return ih <= this.iw && ih > 0 ? 'y' : 'x'; | ||
} | ||
}, { | ||
key: "side", | ||
get: function get() { | ||
return this.dir === 'x' ? this.iw : this.ih; | ||
} | ||
}]); | ||
return Rect; | ||
}(); | ||
var min = Math.min; | ||
var max = Math.max; | ||
function getStat(sa) { | ||
return { | ||
min: sa.min, | ||
max: sa.max, | ||
sum: sa.sum, | ||
nmin: sa.nmin, | ||
nmax: sa.nmax, | ||
nsum: sa.nsum | ||
}; | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key); | ||
} | ||
function getNewStat(sa, o) { | ||
var v = +o[sa.key]; | ||
var n = v * sa.ratio; | ||
o._normalized = n; | ||
return { | ||
min: min(sa.min, v), | ||
max: max(sa.max, v), | ||
sum: sa.sum + v, | ||
nmin: min(sa.nmin, n), | ||
nmax: max(sa.nmax, n), | ||
nsum: sa.nsum + n | ||
}; | ||
function drawLabels(ctx, item, rect) { | ||
const opts = rect.options; | ||
const lh = opts.font.lineHeight; | ||
const labels = (opts.label || item.g + '\n' + item.v).split('\n'); | ||
const y = rect.y + rect.height / 2 - labels.length * lh / 4; | ||
labels.forEach((l, i) => ctx.fillText(l, rect.x + rect.width / 2, y + i * lh)); | ||
} | ||
function setStat(sa, stat) { | ||
_extends(sa, stat); | ||
} | ||
class TreemapController extends chart_js.DatasetController { | ||
initialize() { | ||
this.enableOptionSharing = true; | ||
super.initialize(); | ||
} | ||
function _push(sa, o, stat) { | ||
sa._arr.push(o); | ||
update(mode) { | ||
const me = this; | ||
const meta = me.getMeta(); | ||
const dataset = me.getDataset(); | ||
const groups = dataset.groups || (dataset.groups = []); | ||
const font = helpers.toFont(dataset.font, me.chart.options.font); | ||
const area = me.chart.chartArea; | ||
const key = dataset.key || ''; | ||
setStat(sa, stat); | ||
} | ||
const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top}; | ||
var statArray = /*#__PURE__*/function () { | ||
function statArray(key, ratio) { | ||
_classCallCheck(this, statArray); | ||
if (mode === 'reset' || rectNotEqual(me._rect, mainRect) || me._key !== key || arrayNotEqual(me._groups, groups)) { | ||
me._rect = mainRect; | ||
me._groups = groups.slice(); | ||
me._key = key; | ||
dataset.data = buildData(dataset, mainRect, font); | ||
me._dataCheck(); | ||
me._resyncElements(); | ||
} | ||
var me = this; | ||
me.key = key; | ||
me.ratio = ratio; | ||
me.reset(); | ||
} | ||
me.updateElements(meta.data, 0, meta.data.length, mode); | ||
} | ||
_createClass(statArray, [{ | ||
key: "reset", | ||
value: function reset() { | ||
var me = this; | ||
me._arr = []; | ||
me._hist = []; | ||
me.sum = 0; | ||
me.nsum = 0; | ||
me.min = Infinity; | ||
me.max = -Infinity; | ||
me.nmin = Infinity; | ||
me.nmax = -Infinity; | ||
} | ||
}, { | ||
key: "push", | ||
value: function push(o) { | ||
_push(this, o, getNewStat(this, o)); | ||
} | ||
}, { | ||
key: "pushIf", | ||
value: function pushIf(o, fn) { | ||
var nstat = getNewStat(this, o); | ||
_updateOptionsWithDefaults(options) { | ||
const me = this; | ||
const dataset = me.getDataset(); | ||
const treemapDefaults = me.chart.options.treemap.datasets; | ||
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { | ||
args[_key - 2] = arguments[_key]; | ||
} | ||
options.dividerColor = dataset.dividerColor || treemapDefaults.dividerColor; | ||
options.dividerDash = dataset.dividerDash || treemapDefaults.dividerDash; | ||
options.dividerDashOffset = dataset.dividerDashOffset || treemapDefaults.dividerDashOffset; | ||
options.dividerWidth = dataset.dividerWidth || treemapDefaults.dividerWidth; | ||
options.font = helpers.toFont(options.font, me.chart.options.font); | ||
} | ||
if (!fn(getStat(this), nstat, args)) { | ||
return o; | ||
} | ||
updateElements(rects, start, count, mode) { | ||
const me = this; | ||
const reset = mode === 'reset'; | ||
const dataset = me.getDataset(); | ||
const firstOpts = me._rect.options = me.resolveDataElementOptions(start, mode); | ||
const sharedOptions = me.getSharedOptions(firstOpts); | ||
const includeOptions = me.includeOptions(mode, sharedOptions); | ||
// me._updateOptionsWithDefaults(firstOpts); | ||
_push(this, o, nstat); | ||
} | ||
}, { | ||
key: "get", | ||
value: function get() { | ||
return this._arr; | ||
} | ||
}, { | ||
key: "length", | ||
get: function get() { | ||
return this._arr.length; | ||
} | ||
}]); | ||
for (let i = start; i < start + count; i++) { | ||
const sq = dataset.data[i]; | ||
const options = sharedOptions || me.resolveDataElementOptions(i, mode); | ||
const height = reset ? 0 : sq.h - options.spacing * 2; | ||
const width = reset ? 0 : sq.w - options.spacing * 2; | ||
const x = sq.x + options.spacing; | ||
const y = sq.y + options.spacing; | ||
const properties = { | ||
x, | ||
y, | ||
width, | ||
height | ||
}; | ||
return statArray; | ||
}(); | ||
if (includeOptions) { | ||
properties.options = options; | ||
} | ||
// me._updateOptionsWithDefaults(options); | ||
me.updateElement(rects[i], i, properties, mode); | ||
} | ||
function compareAspectRatio(oldStat, newStat, args) { | ||
if (oldStat.sum === 0) { | ||
return true; | ||
} | ||
me.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
} | ||
var _args = _slicedToArray(args, 1), | ||
length = _args[0]; | ||
_drawDividers(ctx, data, metadata) { | ||
for (let i = 0, ilen = metadata.length; i < ilen; ++i) { | ||
const rect = metadata[i]; | ||
const item = data[i]; | ||
if (rect.options.groupDividers && item._data.children.length > 1) { | ||
drawDivider(ctx, rect); | ||
} | ||
} | ||
if (this.getDataset().groupDividers) { | ||
drawDivider(ctx, this._rect); | ||
} | ||
} | ||
var os2 = oldStat.nsum * oldStat.nsum; | ||
var ns2 = newStat.nsum * newStat.nsum; | ||
var l2 = length * length; | ||
var or = Math.max(l2 * oldStat.nmax / os2, os2 / (l2 * oldStat.nmin)); | ||
var nr = Math.max(l2 * newStat.nmax / ns2, ns2 / (l2 * newStat.nmin)); | ||
return nr <= or; | ||
} | ||
_drawRects(ctx, data, metadata, levels) { | ||
for (let i = 0, ilen = metadata.length; i < ilen; ++i) { | ||
const rect = metadata[i]; | ||
const item = data[i]; | ||
if (!rect.hidden) { | ||
rect.draw(ctx); | ||
const opts = rect.options; | ||
if (shouldDrawCaption(rect, opts.font) && item.g) { | ||
drawCaption(ctx, rect, item, opts, levels); | ||
} | ||
} | ||
} | ||
} | ||
function squarify(values, r, key, grp, lvl, gsum) { | ||
var rows = []; | ||
var rect = new Rect(r); | ||
var row = new statArray('value', rect.area / sum(values, key)); | ||
var length = rect.side; | ||
var n = values.length; | ||
var i, o; | ||
draw() { | ||
const me = this; | ||
const ctx = me.chart.ctx; | ||
const metadata = me.getMeta().data || []; | ||
const dataset = me.getDataset(); | ||
const levels = (dataset.groups || []).length - 1; | ||
const data = dataset.data || []; | ||
if (!n) { | ||
return rows; | ||
} | ||
me._drawRects(ctx, data, metadata, levels); | ||
me._drawDividers(ctx, data, metadata); | ||
} | ||
} | ||
var tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
TreemapController.id = 'treemap'; | ||
function val(idx) { | ||
return key ? +tmp[idx][key] : +tmp[idx]; | ||
} | ||
TreemapController.defaults = { | ||
dataElementType: 'treemap', | ||
dataElementOptions: [ | ||
'backgroundColor', | ||
'borderColor', | ||
'borderSkipped', | ||
'borderWidth', | ||
'dividerColor', | ||
'dividerDash', | ||
'dividerDashOffset', | ||
'dividerWidth', | ||
'font', | ||
'groupLabels', | ||
'groupDividers', | ||
'spacing', | ||
'label' | ||
], | ||
hover: { | ||
mode: 'point', | ||
intersect: true | ||
}, | ||
tooltips: { | ||
mode: 'point', | ||
position: 'treemap', | ||
intersect: true, | ||
callbacks: { | ||
title(items) { | ||
if (items.length) { | ||
const item = items[0]; | ||
return item.dataset.key || ''; | ||
} | ||
return ''; | ||
}, | ||
label(item) { | ||
const dataset = item.dataset; | ||
const dataItem = dataset.data[item.dataIndex]; | ||
return dataset.label + ': ' + dataItem.v; | ||
} | ||
} | ||
}, | ||
datasets: { | ||
groupLabels: true, | ||
borderWidth: 0, | ||
spacing: 0.5, | ||
groupDividers: false, | ||
dividerWidth: 1 | ||
}, | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
display: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
display: false | ||
} | ||
}, | ||
}; | ||
function gval(idx) { | ||
return grp && tmp[idx][grp]; | ||
} | ||
for (i = 0; i < n; ++i) { | ||
o = { | ||
value: val(i), | ||
groupSum: gsum, | ||
_data: values[tmp[i]._idx] | ||
}; | ||
if (grp) { | ||
o.level = lvl; | ||
o.group = gval(i); | ||
} | ||
o = row.pushIf(o, compareAspectRatio, length); | ||
if (o) { | ||
rows.push(rect.map(row)); | ||
length = rect.side; | ||
row.reset(); | ||
row.push(o); | ||
} | ||
} | ||
if (row.length) { | ||
rows.push(rect.map(row)); | ||
} | ||
return flatten(rows); | ||
} | ||
/** | ||
* Helper function to get the bounds of the rect | ||
* @param {Rectangle} rect the rect | ||
* @param {TreemapElement} rect the rect | ||
* @param {boolean} [useFinalPosition] | ||
@@ -636,431 +657,159 @@ * @return {object} bounds of the rect | ||
*/ | ||
function getBounds(rect, useFinalPosition) { | ||
var _rect$getProps = rect.getProps(['x', 'y', 'width', 'height'], useFinalPosition), | ||
x = _rect$getProps.x, | ||
y = _rect$getProps.y, | ||
width = _rect$getProps.width, | ||
height = _rect$getProps.height; | ||
return { | ||
left: x, | ||
top: y, | ||
right: x + width, | ||
bottom: y + height | ||
}; | ||
const {x, y, width, height} = rect.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return {left: x, top: y, right: x + width, bottom: y + height}; | ||
} | ||
function limit(value, min, max) { | ||
return Math.max(Math.min(value, max), min); | ||
return Math.max(Math.min(value, max), min); | ||
} | ||
function parseBorderWidth(rect, maxW, maxH) { | ||
var value = rect.options.borderWidth; | ||
var t, r, b, l; | ||
function parseBorderWidth(value, maxW, maxH) { | ||
let t, r, b, l; | ||
if (Chart.helpers.isObject(value)) { | ||
t = +value.top || 0; | ||
r = +value.right || 0; | ||
b = +value.bottom || 0; | ||
l = +value.left || 0; | ||
} else { | ||
t = r = b = l = +value || 0; | ||
} | ||
if (isObject(value)) { | ||
t = +value.top || 0; | ||
r = +value.right || 0; | ||
b = +value.bottom || 0; | ||
l = +value.left || 0; | ||
} else { | ||
t = r = b = l = +value || 0; | ||
} | ||
return { | ||
t: limit(t, 0, maxH), | ||
r: limit(r, 0, maxW), | ||
b: limit(b, 0, maxH), | ||
l: limit(l, 0, maxW) | ||
}; | ||
return { | ||
t: limit(t, 0, maxH), | ||
r: limit(r, 0, maxW), | ||
b: limit(b, 0, maxH), | ||
l: limit(l, 0, maxW) | ||
}; | ||
} | ||
function boundingRects(rect) { | ||
var bounds = getBounds(rect); | ||
var width = bounds.right - bounds.left; | ||
var height = bounds.bottom - bounds.top; | ||
var border = parseBorderWidth(rect, width / 2, height / 2); | ||
return { | ||
outer: { | ||
x: bounds.left, | ||
y: bounds.top, | ||
w: width, | ||
h: height | ||
}, | ||
inner: { | ||
x: bounds.left + border.l, | ||
y: bounds.top + border.t, | ||
w: width - border.l - border.r, | ||
h: height - border.t - border.b | ||
} | ||
}; | ||
} | ||
const bounds = getBounds(rect); | ||
const width = bounds.right - bounds.left; | ||
const height = bounds.bottom - bounds.top; | ||
const border = parseBorderWidth(rect.options.borderWidth, width / 2, height / 2); | ||
function _inRange(rect, x, y, useFinalPosition) { | ||
var skipX = x === null; | ||
var skipY = y === null; | ||
var bounds = !rect || skipX && skipY ? false : getBounds(rect, useFinalPosition); | ||
return bounds && (skipX || x >= bounds.left && x <= bounds.right) && (skipY || y >= bounds.top && y <= bounds.bottom); | ||
return { | ||
outer: { | ||
x: bounds.left, | ||
y: bounds.top, | ||
w: width, | ||
h: height | ||
}, | ||
inner: { | ||
x: bounds.left + border.l, | ||
y: bounds.top + border.t, | ||
w: width - border.l - border.r, | ||
h: height - border.t - border.b | ||
} | ||
}; | ||
} | ||
var Rectangle = /*#__PURE__*/function (_Chart$Element) { | ||
_inherits(Rectangle, _Chart$Element); | ||
function inRange(rect, x, y, useFinalPosition) { | ||
const skipX = x === null; | ||
const skipY = y === null; | ||
const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition); | ||
var _super = _createSuper(Rectangle); | ||
function Rectangle(cfg) { | ||
var _this; | ||
_classCallCheck(this, Rectangle); | ||
_this = _super.call(this); | ||
_this.options = undefined; | ||
_this.width = undefined; | ||
_this.height = undefined; | ||
if (cfg) { | ||
_extends(_assertThisInitialized(_this), cfg); | ||
} | ||
return _this; | ||
} | ||
_createClass(Rectangle, [{ | ||
key: "draw", | ||
value: function draw(ctx) { | ||
var options = this.options; | ||
var _boundingRects = boundingRects(this), | ||
inner = _boundingRects.inner, | ||
outer = _boundingRects.outer; | ||
ctx.save(); | ||
if (outer.w !== inner.w || outer.h !== inner.h) { | ||
ctx.beginPath(); | ||
ctx.rect(outer.x, outer.y, outer.w, outer.h); | ||
ctx.clip(); | ||
ctx.rect(inner.x, inner.y, inner.w, inner.h); | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fill(); | ||
ctx.fillStyle = options.borderColor; | ||
ctx.fill('evenodd'); | ||
} else { | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fillRect(inner.x, inner.y, inner.w, inner.h); | ||
} | ||
ctx.restore(); | ||
} | ||
}, { | ||
key: "inRange", | ||
value: function inRange(mouseX, mouseY, useFinalPosition) { | ||
return _inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
}, { | ||
key: "inXRange", | ||
value: function inXRange(mouseX, useFinalPosition) { | ||
return _inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
}, { | ||
key: "inYRange", | ||
value: function inYRange(mouseY, useFinalPosition) { | ||
return _inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
}, { | ||
key: "getCenterPoint", | ||
value: function getCenterPoint(useFinalPosition) { | ||
var _this$getProps = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition), | ||
x = _this$getProps.x, | ||
y = _this$getProps.y, | ||
width = _this$getProps.width, | ||
height = _this$getProps.height; | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
}, { | ||
key: "tooltipPosition", | ||
value: function tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
}, { | ||
key: "getRange", | ||
value: function getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
}]); | ||
return Rectangle; | ||
}(Chart.Element); | ||
_defineProperty(Rectangle, "_type", 'rectangle'); | ||
var defaults = Chart.defaults; | ||
var helpers = Chart.helpers; | ||
var optionHelpers = helpers.options; | ||
var parseFont = optionHelpers._parseFont; | ||
var resolve = optionHelpers.resolve; | ||
var valueOrDefault = helpers.valueOrDefault; | ||
function rectNotEqual(r1, r2) { | ||
return !r1 || !r2 || r1.x !== r2.x || r1.y !== r2.y || r1.w !== r2.w || r1.h !== r2.h; | ||
return bounds | ||
&& (skipX || x >= bounds.left && x <= bounds.right) | ||
&& (skipY || y >= bounds.top && y <= bounds.bottom); | ||
} | ||
function arrayNotEqual(a1, a2) { | ||
var i, n; | ||
class TreemapElement extends chart_js.Element { | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
constructor(cfg) { | ||
super(); | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
return false; | ||
} | ||
if (cfg) { | ||
Object.assign(this, cfg); | ||
} | ||
} | ||
function drawCaption(rect, font) { | ||
var w = rect.width || rect.w; | ||
var h = rect.height || rect.h; | ||
var min = font.lineHeight * 2; | ||
return w > min && h > min; | ||
} | ||
draw(ctx) { | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this); | ||
function buildData(dataset, mainRect, font) { | ||
var key = dataset.key || ''; | ||
var tree = dataset.tree || []; | ||
var groups = dataset.groups || []; | ||
var glen = groups.length; | ||
var sp = (dataset.spacing || 0) + (dataset.borderWidth || 0); | ||
ctx.save(); | ||
function recur(gidx, rect, parent, gs) { | ||
var g = groups[gidx]; | ||
var pg = gidx > 0 && groups[gidx - 1]; | ||
var gdata = group(tree, g, key, pg, parent); | ||
var gsq = squarify(gdata, rect, key, g, gidx, gs); | ||
var ret = gsq.slice(); | ||
var subRect; | ||
if (outer.w !== inner.w || outer.h !== inner.h) { | ||
ctx.beginPath(); | ||
ctx.rect(outer.x, outer.y, outer.w, outer.h); | ||
ctx.clip(); | ||
ctx.rect(inner.x, inner.y, inner.w, inner.h); | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fill(); | ||
ctx.fillStyle = options.borderColor; | ||
ctx.fill('evenodd'); | ||
} else { | ||
ctx.fillStyle = options.backgroundColor; | ||
ctx.fillRect(inner.x, inner.y, inner.w, inner.h); | ||
} | ||
if (gidx < glen - 1) { | ||
gsq.forEach(function (sq) { | ||
subRect = { | ||
x: sq.x + sp, | ||
y: sq.y + sp, | ||
w: sq.w - 2 * sp, | ||
h: sq.h - 2 * sp | ||
}; | ||
ctx.restore(); | ||
} | ||
if (drawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
ret.push.apply(ret, _toConsumableArray(recur(gidx + 1, subRect, sq.g, sq.s))); | ||
}); | ||
} | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
return ret; | ||
} | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
getCenterPoint(useFinalPosition) { | ||
const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
return glen ? recur(0, mainRect) : squarify(tree, mainRect, key); | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
function parseFontOptions(options) { | ||
return _extends(parseFont({ | ||
fontFamily: valueOrDefault(options.fontFamily, defaults.fontFamily), | ||
fontSize: valueOrDefault(options.fontSize, defaults.fontSize), | ||
fontStyle: valueOrDefault(options.fontStyle, defaults.fontStyle), | ||
lineHeight: valueOrDefault(options.lineHeight, defaults.lineHeight) | ||
}), { | ||
color: resolve([options.fontColor, defaults.fontColor]) | ||
}); | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
} | ||
var TreemapController = /*#__PURE__*/function (_Chart$DatasetControl) { | ||
_inherits(TreemapController, _Chart$DatasetControl); | ||
TreemapElement.id = 'treemap'; | ||
var _super = _createSuper(TreemapController); | ||
TreemapElement.defaults = { | ||
dividerCapStyle: 'butt', | ||
dividerColor: 'black', | ||
dividerDash: undefined, | ||
dividerDashOffset: 0, | ||
dividerWidth: 0, | ||
groupDividers: false, | ||
}; | ||
function TreemapController() { | ||
_classCallCheck(this, TreemapController); | ||
TreemapElement.defaultRoutes = { | ||
backgroundColor: 'color', | ||
borderColor: 'color' | ||
}; | ||
return _super.apply(this, arguments); | ||
} | ||
chart_js.Chart.register(TreemapController, TreemapElement); | ||
_createClass(TreemapController, [{ | ||
key: "update", | ||
value: function update(mode) { | ||
var me = this; | ||
var meta = me.getMeta(); | ||
var dataset = me.getDataset(); | ||
var groups = dataset.groups || (dataset.groups = []); | ||
var font = parseFontOptions(dataset); | ||
var area = me.chart.chartArea; | ||
var key = dataset.key || ''; | ||
var mainRect = { | ||
x: area.left, | ||
y: area.top, | ||
w: area.right - area.left, | ||
h: area.bottom - area.top | ||
}; | ||
const tooltipPlugin = chart_js.Chart.registry.plugins.get('tooltip'); | ||
tooltipPlugin.positioners.treemap = function(active) { | ||
if (!active.length) { | ||
return false; | ||
} | ||
if (mode === 'reset' || rectNotEqual(me._rect, mainRect) || me._key !== key || arrayNotEqual(me._groups, groups)) { | ||
me._rect = mainRect; | ||
me._groups = groups.slice(); | ||
me._key = key; | ||
dataset.data = buildData(dataset, mainRect, font); | ||
const item = active[active.length - 1]; | ||
const el = item.element; | ||
me._dataCheck(); | ||
me._resyncElements(); | ||
} | ||
me.updateElements(meta.data, 0, mode); | ||
} | ||
}, { | ||
key: "updateElements", | ||
value: function updateElements(rects, start, mode) { | ||
var me = this; | ||
var reset = mode === 'reset'; | ||
var dataset = me.getDataset(); | ||
var firstOpts = me.resolveDataElementOptions(start, mode); | ||
var sharedOptions = me.getSharedOptions(mode, rects[start], firstOpts); | ||
for (var i = 0; i < rects.length; i++) { | ||
var index = start + i; | ||
var sq = dataset.data[index]; | ||
var options = me.resolveDataElementOptions(i, mode); | ||
var height = reset ? 0 : sq.h - options.spacing * 2; | ||
var width = reset ? 0 : sq.w - options.spacing * 2; | ||
var x = sq.x + options.spacing; | ||
var y = sq.y + options.spacing; | ||
var properties = { | ||
x: x, | ||
y: y, | ||
width: width, | ||
height: height, | ||
options: options | ||
}; | ||
options.font = parseFont(options); | ||
me.updateElement(rects[i], index, properties, mode); | ||
} | ||
me.updateSharedOptions(sharedOptions, mode); | ||
} | ||
}, { | ||
key: "draw", | ||
value: function draw() { | ||
var me = this; | ||
var metadata = me.getMeta().data || []; | ||
var dataset = me.getDataset(); | ||
var levels = (dataset.groups || []).length - 1; | ||
var data = dataset.data || []; | ||
var ctx = me.chart.ctx; | ||
var i, ilen, rect, item; | ||
for (i = 0, ilen = metadata.length; i < ilen; ++i) { | ||
rect = metadata[i]; | ||
item = data[i]; | ||
if (!rect.hidden) { | ||
rect.draw(ctx); | ||
var opts = rect.options; | ||
if (drawCaption(rect, opts.font) && item.g) { | ||
ctx.save(); | ||
ctx.fillStyle = opts.fontColor; | ||
ctx.font = opts.font.string; | ||
ctx.beginPath(); | ||
ctx.rect(rect.x, rect.y, rect.width, rect.height); | ||
ctx.clip(); | ||
if (!('l' in item) || item.l === levels) { | ||
ctx.textAlign = 'center'; | ||
ctx.textBaseline = 'middle'; | ||
ctx.fillText(item.g, rect.x + rect.width / 2, rect.y + rect.height / 2); | ||
} else { | ||
ctx.textAlign = 'start'; | ||
ctx.textBaseline = 'top'; | ||
ctx.fillText(item.g, rect.x + opts.borderWidth + 3, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
} | ||
} | ||
} | ||
} | ||
}]); | ||
return TreemapController; | ||
}(Chart.DatasetController); | ||
TreemapController.prototype.dataElementType = Rectangle; | ||
TreemapController.prototype.dataElementOptions = ['backgroundColor', 'borderColor', 'borderSkipped', 'borderWidth', 'fontColor', 'fontFamily', 'fontSize', 'fontStyle', 'spacing']; | ||
var defaults$1 = { | ||
hover: { | ||
mode: 'point', | ||
intersect: true | ||
}, | ||
tooltips: { | ||
mode: 'point', | ||
position: 'treemap', | ||
intersect: true, | ||
callbacks: { | ||
title: function title(item, data) { | ||
return data.datasets[item[0].datasetIndex].key; | ||
}, | ||
label: function label(item, data) { | ||
var dataset = data.datasets[item.datasetIndex]; | ||
var dataItem = dataset.data[item.index]; | ||
return dataset.label + ': ' + dataItem.v; | ||
} | ||
} | ||
}, | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
display: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
display: false | ||
} | ||
}, | ||
elements: { | ||
rectangle: { | ||
borderWidth: 0, | ||
spacing: 0.5 | ||
} | ||
} | ||
return el.tooltipPosition(); | ||
}; | ||
Chart.controllers.treemap = TreemapController; | ||
Chart.defaults.treemap = defaults$1; | ||
var tooltipPlugin = Chart.plugins.getAll().find(function (p) { | ||
return p.id === 'tooltip'; | ||
}); | ||
tooltipPlugin.positioners.treemap = function (active) { | ||
if (!active.length) { | ||
return false; | ||
} | ||
var item = active[active.length - 1]; | ||
var el = item.element; | ||
return { | ||
x: el.x + el.width / 2, | ||
y: el.y + el.height / 2 | ||
}; | ||
}; | ||
}))); |
/*! | ||
* chartjs-chart-treemap v1.0.0-alpha | ||
* chartjs-chart-treemap v1.0.0-beta | ||
* https://github.com/kurkle/chartjs-chart-treemap#readme | ||
@@ -7,2 +7,2 @@ * (c) 2020 Jukka Kurkela | ||
*/ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("chart.js")):"function"==typeof define&&define.amd?define(["chart.js"],e):e((t=t||self).Chart)}(this,(function(t){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function i(t,e,n){return e&&r(t.prototype,e),n&&r(t,n),t}function o(){return(o=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t}).apply(this,arguments)}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&l(t,e)}function u(t){return(u=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function l(t,e){return(l=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function f(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(t){return!1}}function s(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function c(t,e){return!e||"object"!=typeof e&&"function"!=typeof e?s(t):e}function h(t){return function(){var e,n=u(t);if(f()){var r=u(this).constructor;e=Reflect.construct(n,arguments,r)}else e=n.apply(this,arguments);return c(this,e)}}function p(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var n=[],r=!0,i=!1,o=void 0;try{for(var a,u=t[Symbol.iterator]();!(r=(a=u.next()).done)&&(n.push(a.value),!e||n.length!==e);r=!0);}catch(t){i=!0,o=t}finally{try{r||null==u.return||u.return()}finally{if(i)throw o}}return n}(t,e)||d(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function y(t){return function(t){if(Array.isArray(t))return g(t)}(t)||function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||d(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function d(t,e){if(t){if("string"==typeof t)return g(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(n):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?g(t,e):void 0}}function g(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n<e;n++)r[n]=t[n];return r}function m(t,n){var r,i=t.length;if(!i)return n;var o=function(t){var n=e(t);return"function"===n||"object"===n&&!!t}(t[0]);for(n=o?n:"v",r=0,i=t.length;r<i;++r)o?t[r]._idx=r:t[r]={v:t[r],_idx:r};return n}function v(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)}function b(t,e,n,r){var i=t._normalized,o=e*i/n,a=Math.sqrt(i*o),u=i/a;return{d1:a,d2:u,w:"_ix"===r?a:u,h:"_ix"===r?u:a}}function x(t,e,n,r){var i={x:v(t.x+t._ix,4),y:v(t.y+t._iy,4),w:v(n.w,4),h:v(n.h,4),a:e._normalized,v:e.value,s:r,_data:e._data};return e.group&&(i.g=e.group,i.l=e.level,i.gs=e.groupSum),i}t=t&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t;var w=function(){function t(e){n(this,t);var r=this;r.x=e.x||e.left||0,r.y=e.y||e.top||0,r._ix=0,r._iy=0,r.w=e.w||e.width||e.right-e.left,r.h=e.h||e.height||e.bottom-e.top}return i(t,[{key:"map",value:function(t){var e,n,r,i=this,o=[],a=t.nsum,u=t.get(),l=u.length,f=i.dir,s=i.side,c=s*s,h="x"===f?"_ix":"_iy",p=a*a,y=0,d=0;for(e=0;e<l;++e)d+=(r=b(n=u[e],c,p,h)).d1,r.d2>y&&(y=r.d2),o.push(x(i,n,r,t.sum)),i[h]+=r.d1;return i["y"===f?"_ix":"_iy"]+=y,i[h]-=d,o}},{key:"area",get:function(){return this.w*this.h}},{key:"iw",get:function(){return this.w-this._ix}},{key:"ih",get:function(){return this.h-this._iy}},{key:"dir",get:function(){var t=this.ih;return t<=this.iw&&t>0?"y":"x"}},{key:"side",get:function(){return"x"===this.dir?this.iw:this.ih}}]),t}(),_=Math.min,k=Math.max;function S(t){return{min:t.min,max:t.max,sum:t.sum,nmin:t.nmin,nmax:t.nmax,nsum:t.nsum}}function O(t,e){var n=+e[t.key],r=n*t.ratio;return e._normalized=r,{min:_(t.min,n),max:k(t.max,n),sum:t.sum+n,nmin:_(t.nmin,r),nmax:k(t.nmax,r),nsum:t.nsum+r}}function j(t,e,n){t._arr.push(e),function(t,e){o(t,e)}(t,n)}var A=function(){function t(e,r){n(this,t);this.key=e,this.ratio=r,this.reset()}return i(t,[{key:"reset",value:function(){var t=this;t._arr=[],t._hist=[],t.sum=0,t.nsum=0,t.min=1/0,t.max=-1/0,t.nmin=1/0,t.nmax=-1/0}},{key:"push",value:function(t){j(this,t,O(this,t))}},{key:"pushIf",value:function(t,e){for(var n=O(this,t),r=arguments.length,i=new Array(r>2?r-2:0),o=2;o<r;o++)i[o-2]=arguments[o];if(!e(S(this),n,i))return t;j(this,t,n)}},{key:"get",value:function(){return this._arr}},{key:"length",get:function(){return this._arr.length}}]),t}();function C(t,e,n){if(0===t.sum)return!0;var r=p(n,1)[0],i=t.nsum*t.nsum,o=e.nsum*e.nsum,a=r*r,u=Math.max(a*t.nmax/i,i/(a*t.nmin));return Math.max(a*e.nmax/o,o/(a*e.nmin))<=u}function E(t,e,n,r,i,o){var a,u,l=[],f=new w(e),s=new A("value",f.area/function(t,e){var n,r,i;for(n=0,r=0,i=t.length;r<i;++r)n+=e?+t[r][e]:+t[r];return n}(t,n)),c=f.side,h=t.length;if(!h)return l;var p,d=t.slice();function g(t){return r&&d[t][r]}for(n=m(d,n),function(t,e){e?t.sort((function(t,n){return+n[e]-+t[e]})):t.sort((function(t,e){return+e-+t}))}(d,n),a=0;a<h;++a)u={value:(p=a,n?+d[p][n]:+d[p]),groupSum:o,_data:t[d[a]._idx]},r&&(u.level=i,u.group=g(a)),(u=s.pushIf(u,C,c))&&(l.push(f.map(s)),c=f.side,s.reset(),s.push(u));return s.length&&l.push(f.map(s)),function(t){for(var e=y(t),n=[];e.length;){var r=e.pop();Array.isArray(r)?e.push.apply(e,y(r)):n.push(r)}return n.reverse()}(l)}function P(t,e){var n=t.getProps(["x","y","width","height"],e),r=n.x,i=n.y;return{left:r,top:i,right:r+n.width,bottom:i+n.height}}function M(t,e,n){return Math.max(Math.min(t,n),e)}function R(e){var n=P(e),r=n.right-n.left,i=n.bottom-n.top,o=function(e,n,r){var i,o,a,u,l=e.options.borderWidth;return t.helpers.isObject(l)?(i=+l.top||0,o=+l.right||0,a=+l.bottom||0,u=+l.left||0):i=o=a=u=+l||0,{t:M(i,0,r),r:M(o,0,n),b:M(a,0,r),l:M(u,0,n)}}(e,r/2,i/2);return{outer:{x:n.left,y:n.top,w:r,h:i},inner:{x:n.left+o.l,y:n.top+o.t,w:r-o.l-o.r,h:i-o.t-o.b}}}function D(t,e,n,r){var i=null===e,o=null===n,a=!(!t||i&&o)&&P(t,r);return a&&(i||e>=a.left&&e<=a.right)&&(o||n>=a.top&&n<=a.bottom)}var I,z,T,H=function(t){a(r,t);var e=h(r);function r(t){var i;return n(this,r),(i=e.call(this)).options=void 0,i.width=void 0,i.height=void 0,t&&o(s(i),t),i}return i(r,[{key:"draw",value:function(t){var e=this.options,n=R(this),r=n.inner,i=n.outer;t.save(),i.w!==r.w||i.h!==r.h?(t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.rect(r.x,r.y,r.w,r.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(r.x,r.y,r.w,r.h)),t.restore()}},{key:"inRange",value:function(t,e,n){return D(this,t,e,n)}},{key:"inXRange",value:function(t,e){return D(this,t,null,e)}},{key:"inYRange",value:function(t,e){return D(this,null,t,e)}},{key:"getCenterPoint",value:function(t){var e=this.getProps(["x","y","width","height"],t),n=e.x,r=e.y;return{x:n+e.width/2,y:r+e.height/2}}},{key:"tooltipPosition",value:function(){return this.getCenterPoint()}},{key:"getRange",value:function(t){return"x"===t?this.width/2:this.height/2}}]),r}(t.Element);T="rectangle",(z="_type")in(I=H)?Object.defineProperty(I,z,{value:T,enumerable:!0,configurable:!0,writable:!0}):I[z]=T;var W=t.defaults,F=t.helpers,q=F.options,B=q._parseFont,U=q.resolve,X=F.valueOrDefault;function Y(t,e){var n=t.width||t.w,r=t.height||t.h,i=2*e.lineHeight;return n>i&&r>i}function $(t,e,n){var r=t.key||"",i=t.tree||[],o=t.groups||[],a=o.length,u=(t.spacing||0)+(t.borderWidth||0);return!i.length&&t.data.length&&(i=t.tree=t.data),a?function t(e,l,f,s){var c,h=o[e],p=e>0&&o[e-1],d=E(function(t,e,n,r,i){var o,a,u,l,f=Object.create(null),s=Object.create(null),c=[];for(a=0,u=t.length;a<u;++a)l=t[a],r&&l[r]!==i||((o=l[e]||"")in f||(f[o]=0,s[o]=[]),f[o]+=+l[n],s[o].push(l));return Object.keys(f).forEach((function(t){(l={children:s[t]})[n]=+f[t],l[e]=t,r&&(l[r]=i),c.push(l)})),c}(i,h,r,p,f),l,r,h,e,s),g=d.slice();return e<a-1&&d.forEach((function(r){c={x:r.x+u,y:r.y+u,w:r.w-2*u,h:r.h-2*u},Y(r,n)&&(c.y+=n.lineHeight,c.h-=n.lineHeight),g.push.apply(g,y(t(e+1,c,r.g,r.s)))})),g}(0,e):E(i,e,r)}var G=function(t){a(r,t);var e=h(r);function r(){return n(this,r),e.apply(this,arguments)}return i(r,[{key:"update",value:function(t){var e,n,r,i=this,a=i.getMeta(),u=i.getDataset(),l=u.groups||(u.groups=[]),f=o(B({fontFamily:X((e=u).fontFamily,W.fontFamily),fontSize:X(e.fontSize,W.fontSize),fontStyle:X(e.fontStyle,W.fontStyle),lineHeight:X(e.lineHeight,W.lineHeight)}),{color:U([e.fontColor,W.fontColor])}),s=i.chart.chartArea,c=u.key||"",h={x:s.left,y:s.top,w:s.right-s.left,h:s.bottom-s.top};"reset"!==t&&(n=i._rect,r=h,n&&r&&n.x===r.x&&n.y===r.y&&n.w===r.w&&n.h===r.h)&&i._key===c&&!function(t,e){var n,r;if(t.lenght!==e.length)return!0;for(n=0,r=t.length;n<r;++n)if(t[n]!==e[n])return!0;return!1}(i._groups,l)||(i._rect=h,i._groups=l.slice(),i._key=c,u.data=$(u,h,f),i._dataCheck(),i._resyncElements()),i.updateElements(a.data,0,t)}},{key:"updateElements",value:function(t,e,n){for(var r=this,i="reset"===n,o=r.getDataset(),a=r.resolveDataElementOptions(e,n),u=r.getSharedOptions(n,t[e],a),l=0;l<t.length;l++){var f=e+l,s=o.data[f],c=r.resolveDataElementOptions(l,n),h=i?0:s.h-2*c.spacing,p=i?0:s.w-2*c.spacing,y={x:s.x+c.spacing,y:s.y+c.spacing,width:p,height:h,options:c};c.font=B(c),r.updateElement(t[l],f,y,n)}r.updateSharedOptions(u,n)}},{key:"draw",value:function(){var t,e,n,r,i=this.getMeta().data||[],o=this.getDataset(),a=(o.groups||[]).length-1,u=o.data||[],l=this.chart.ctx;for(t=0,e=i.length;t<e;++t)if(n=i[t],r=u[t],!n.hidden){n.draw(l);var f=n.options;Y(n,f.font)&&r.g&&(l.save(),l.fillStyle=f.fontColor,l.font=f.font.string,l.beginPath(),l.rect(n.x,n.y,n.width,n.height),l.clip(),"l"in r&&r.l!==a?(l.textAlign="start",l.textBaseline="top",l.fillText(r.g,n.x+f.borderWidth+3,n.y+f.borderWidth+3)):(l.textAlign="center",l.textBaseline="middle",l.fillText(r.g,n.x+n.width/2,n.y+n.height/2)),l.restore())}}}]),r}(t.DatasetController);G.prototype.dataElementType=H,G.prototype.dataElementOptions=["backgroundColor","borderColor","borderSkipped","borderWidth","fontColor","fontFamily","fontSize","fontStyle","spacing"];t.controllers.treemap=G,t.defaults.treemap={hover:{mode:"point",intersect:!0},tooltips:{mode:"point",position:"treemap",intersect:!0,callbacks:{title:function(t,e){return e.datasets[t[0].datasetIndex].key},label:function(t,e){var n=e.datasets[t.datasetIndex],r=n.data[t.index];return n.label+": "+r.v}}},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}},elements:{rectangle:{borderWidth:0,spacing:.5}}},t.plugins.getAll().find((function(t){return"tooltip"===t.id})).positioners.treemap=function(t){if(!t.length)return!1;var e=t[t.length-1].element;return{x:e.x+e.width/2,y:e.y+e.height/2}}})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Chart,t.Chart.helpers)}(this,(function(t,e){"use strict";function i(t){const e=typeof t;return"function"===e||"object"===e&&!!t}function n(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)}function r(t,e,i,n){const r=t._normalized,o=e*r/i,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===n?s:a,h:"_ix"===n?a:s}}function o(t,e,i,r){const o={x:n(t.x+t._ix,4),y:n(t.y+t._iy,4),w:n(i.w,4),h:n(i.h,4),a:n(e._normalized,4),v:e.value,s:r,_data:e._data};return e.group&&(o.g=e.group,o.l=e.level,o.gs=e.groupSum),o}class s{constructor(t){const e=this;t=t||{w:1,h:1},e.x=t.x||t.left||0,e.y=t.y||t.top||0,e._ix=0,e._iy=0,e.w=t.w||t.width||t.right-t.left,e.h=t.h||t.height||t.bottom-t.top}get area(){return this.w*this.h}get iw(){return this.w-this._ix}get ih(){return this.h-this._iy}get dir(){const t=this.ih;return t<=this.iw&&t>0?"y":"x"}get side(){return"x"===this.dir?this.iw:this.ih}map(t){const e=this,i=[],n=t.nsum,s=t.get(),a=s.length,h=e.dir,l=e.side,d=l*l,u="x"===h?"_ix":"_iy",c=n*n;let g,p,f,m=0,x=0;for(g=0;g<a;++g)p=s[g],f=r(p,d,c,u),x+=f.d1,f.d2>m&&(m=f.d2),i.push(o(e,p,f,t.sum)),e[u]+=f.d1;return e["y"===h?"_ix":"_iy"]+=m,e[u]-=x,i}}const a=Math.min,h=Math.max;function l(t,e){const i=+e[t.key],n=i*t.ratio;return e._normalized=n,{min:a(t.min,i),max:h(t.max,i),sum:t.sum+i,nmin:a(t.nmin,n),nmax:h(t.nmax,n),nsum:t.nsum+n}}function d(t,e,i){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,i)}class u{constructor(t,e){const i=this;i.key=t,i.ratio=e,i.reset()}get length(){return this._arr.length}reset(){const t=this;t._arr=[],t._hist=[],t.sum=0,t.nsum=0,t.min=1/0,t.max=-1/0,t.nmin=1/0,t.nmax=-1/0}push(t){d(this,t,l(this,t))}pushIf(t,e,...i){const n=l(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),n,i))return t;var r;d(this,t,n)}get(){return this._arr}}function c(t,e,i){if(0===t.sum)return!0;const[n]=i,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=n*n,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function g(t,e,n,r,o,a){t=t||[];const h=[],l=new s(e),d=new u("value",l.area/function(t,e){let i,n,r;for(i=0,n=0,r=t.length;n<r;++n)i+=e?+t[n][e]:+t[n];return i}(t,n));let g=l.side;const p=t.length;let f,m;if(!p)return h;const x=t.slice();n=function(t,e){let n,r=t.length;if(!r)return e;const o=i(t[0]);for(e=o?e:"v",n=0,r=t.length;n<r;++n)o?t[n]._idx=n:t[n]={v:t[n],_idx:n};return e}(x,n),function(t,e){e?t.sort(((t,i)=>+i[e]-+t[e])):t.sort(((t,e)=>+e-+t))}(x,n);const y=t=>r&&x[t][r];for(f=0;f<p;++f)m={value:(v=f,n?+x[v][n]:+x[v]),groupSum:a,_data:t[x[f]._idx]},r&&(m.level=o,m.group=y(f)),m=d.pushIf(m,c,g),m&&(h.push(l.map(d)),g=l.side,d.reset(),d.push(m));var v;return d.length&&h.push(l.map(d)),function(t){const e=[...t],i=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):i.push(t)}return i.reverse()}(h)}function p(t,e){if(!e)return!1;const i=t.width||t.w,n=t.height||t.h,r=2*e.lineHeight;return i>r&&n>r}function f(t,e,i,n,r){t.save(),t.fillStyle=n.font.color,t.font=n.font.string,t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.clip(),"l"in i&&i.l!==r?n.groupLabels&&(t.textAlign="start",t.textBaseline="top",t.fillText(i.g,e.x+n.borderWidth+3,e.y+n.borderWidth+3)):(t.textAlign="center",t.textBaseline="middle",function(t,e,i){const n=i.options,r=n.font.lineHeight,o=(n.label||e.g+"\n"+e.v).split("\n"),s=i.y+i.height/2-o.length*r/4;o.forEach(((e,n)=>t.fillText(e,i.x+i.width/2,s+n*r)))}(t,i,e)),t.restore()}function m(t,e){const i=e.options,n=e.width||e.w,r=e.height||e.h;if(t.save(),t.strokeStyle=i.dividerColor||"black",t.lineCap=i.dividerCapStyle,t.setLineDash(i.dividerDash||[]),t.lineDashOffset=i.dividerDashOffset,t.lineWidth=i.dividerWidth,t.beginPath(),n>r){const i=n/2;t.moveTo(e.x+i,e.y),t.lineTo(e.x+i,e.y+r)}else{const i=r/2;t.moveTo(e.x,e.y+i),t.lineTo(e.x+n,e.y+i)}t.stroke(),t.restore()}function x(t,e,i){const n=t.key||"";let r=t.tree||[];const o=t.groups||[],s=o.length,a=(t.spacing||0)+(t.borderWidth||0);return!r.length&&t.data.length&&(r=t.tree=t.data),s?function e(h,l,d,u){const c=o[h],f=h>0&&o[h-1],m=g(function(t,e,i,n,r){const o=Object.create(null),s=Object.create(null),a=[];let h,l,d,u;for(l=0,d=t.length;l<d;++l)u=t[l],n&&u[n]!==r||(h=u[e]||"",h in o||(o[h]=0,s[h]=[]),o[h]+=+u[i],s[h].push(u));return Object.keys(o).forEach((t=>{u={children:s[t]},u[i]=+o[t],u[e]=t,n&&(u[n]=r),a.push(u)})),a}(r,c,n,f,d),l,n,c,h,u),x=m.slice();let y;return h<s-1&&m.forEach((n=>{y={x:n.x+a,y:n.y+a,w:n.w-2*a,h:n.h-2*a},t.groupLabels&&p(n,i)&&(y.y+=i.lineHeight,y.h-=i.lineHeight),x.push(...e(h+1,y,n.g,n.s))})),x}(0,e):g(r,e,n)}class y extends t.DatasetController{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const i=this,n=i.getMeta(),r=i.getDataset(),o=r.groups||(r.groups=[]),s=e.toFont(r.font,i.chart.options.font),a=i.chart.chartArea,h=r.key||"",l={x:a.left,y:a.top,w:a.right-a.left,h:a.bottom-a.top};var d,u;"reset"!==t&&(d=i._rect,u=l,d&&u&&d.x===u.x&&d.y===u.y&&d.w===u.w&&d.h===u.h)&&i._key===h&&!function(t,e){let i,n;if(t.lenght!==e.length)return!0;for(i=0,n=t.length;i<n;++i)if(t[i]!==e[i])return!0;return!1}(i._groups,o)||(i._rect=l,i._groups=o.slice(),i._key=h,r.data=x(r,l,s),i._dataCheck(),i._resyncElements()),i.updateElements(n.data,0,n.data.length,t)}_updateOptionsWithDefaults(t){const i=this,n=i.getDataset(),r=i.chart.options.treemap.datasets;t.dividerColor=n.dividerColor||r.dividerColor,t.dividerDash=n.dividerDash||r.dividerDash,t.dividerDashOffset=n.dividerDashOffset||r.dividerDashOffset,t.dividerWidth=n.dividerWidth||r.dividerWidth,t.font=e.toFont(t.font,i.chart.options.font)}updateElements(t,e,i,n){const r=this,o="reset"===n,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,n),h=r.getSharedOptions(a),l=r.includeOptions(n,h);for(let a=e;a<e+i;a++){const e=s.data[a],i=h||r.resolveDataElementOptions(a,n),d=o?0:e.h-2*i.spacing,u=o?0:e.w-2*i.spacing,c={x:e.x+i.spacing,y:e.y+i.spacing,width:u,height:d};l&&(c.options=i),r.updateElement(t[a],a,c,n)}r.updateSharedOptions(h,n,a)}_drawDividers(t,e,i){for(let n=0,r=i.length;n<r;++n){const r=i[n],o=e[n];r.options.groupDividers&&o._data.children.length>1&&m(t,r)}this.getDataset().groupDividers&&m(t,this._rect)}_drawRects(t,e,i,n){for(let r=0,o=i.length;r<o;++r){const o=i[r],s=e[r];if(!o.hidden){o.draw(t);const e=o.options;p(o,e.font)&&s.g&&f(t,o,s,e,n)}}}draw(){const t=this,e=t.chart.ctx,i=t.getMeta().data||[],n=t.getDataset(),r=(n.groups||[]).length-1,o=n.data||[];t._drawRects(e,o,i,r),t._drawDividers(e,o,i)}}function v(t,e){const{x:i,y:n,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:i,top:n,right:i+r,bottom:n+o}}function b(t,e,i){return Math.max(Math.min(t,i),e)}function w(t){const e=v(t),n=e.right-e.left,r=e.bottom-e.top,o=function(t,e,n){let r,o,s,a;return i(t)?(r=+t.top||0,o=+t.right||0,s=+t.bottom||0,a=+t.left||0):r=o=s=a=+t||0,{t:b(r,0,n),r:b(o,0,e),b:b(s,0,n),l:b(a,0,e)}}(t.options.borderWidth,n/2,r/2);return{outer:{x:e.left,y:e.top,w:n,h:r},inner:{x:e.left+o.l,y:e.top+o.t,w:n-o.l-o.r,h:r-o.t-o.b}}}function _(t,e,i,n){const r=null===e,o=null===i,s=!(!t||r&&o)&&v(t,n);return s&&(r||e>=s.left&&e<=s.right)&&(o||i>=s.top&&i<=s.bottom)}y.id="treemap",y.defaults={dataElementType:"treemap",dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","dividerColor","dividerDash","dividerDashOffset","dividerWidth","font","groupLabels","groupDividers","spacing","label"],hover:{mode:"point",intersect:!0},tooltips:{mode:"point",position:"treemap",intersect:!0,callbacks:{title(t){if(t.length){return t[0].dataset.key||""}return""},label(t){const e=t.dataset,i=e.data[t.dataIndex];return e.label+": "+i.v}}},datasets:{groupLabels:!0,borderWidth:0,spacing:.5,groupDividers:!1,dividerWidth:1},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}};class D extends t.Element{constructor(t){super(),this.options=void 0,this.width=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const e=this.options,{inner:i,outer:n}=w(this);t.save(),n.w!==i.w||n.h!==i.h?(t.beginPath(),t.rect(n.x,n.y,n.w,n.h),t.clip(),t.rect(i.x,i.y,i.w,i.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h)),t.restore()}inRange(t,e,i){return _(this,t,e,i)}inXRange(t,e){return _(this,t,null,e)}inYRange(t,e){return _(this,null,t,e)}getCenterPoint(t){const{x:e,y:i,width:n,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+n/2,y:i+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}D.id="treemap",D.defaults={dividerCapStyle:"butt",dividerColor:"black",dividerDash:void 0,dividerDashOffset:0,dividerWidth:0,groupDividers:!1},D.defaultRoutes={backgroundColor:"color",borderColor:"color"},t.Chart.register(y,D);t.Chart.registry.plugins.get("tooltip").positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()}})); |
{ | ||
"name": "chartjs-chart-treemap", | ||
"version": "1.0.0-alpha", | ||
"version": "1.0.0-beta", | ||
"description": "Chart.js module for creating treemap charts", | ||
@@ -8,3 +8,8 @@ "main": "dist/chartjs-chart-treemap.js", | ||
"scripts": { | ||
"test": "gulp test" | ||
"autobuild": "rollup -c -w", | ||
"build": "rollup -c", | ||
"dev": "karma start ---auto-watch --no-single-run --browsers chrome", | ||
"docs": "cd docs && npm install && npm run build && mkdir -p ../dist && cp -r build ../dist/docs", | ||
"lint": "eslint samples/**/*.html samples/**/*.js src/**/*.js test/**/*.js", | ||
"test": "npm run lint && karma start --no-auto-watch --single-run --coverage" | ||
}, | ||
@@ -30,38 +35,32 @@ "repository": { | ||
"devDependencies": { | ||
"@babel/core": "^7.9.0", | ||
"@babel/plugin-proposal-class-properties": "^7.8.3", | ||
"@babel/plugin-transform-object-assign": "^7.8.3", | ||
"@babel/preset-env": "^7.9.5", | ||
"@rollup/plugin-commonjs": "^11.1.0", | ||
"@rollup/plugin-node-resolve": "^7.1.3", | ||
"babel-eslint": "^10.1.0", | ||
"babel-plugin-istanbul": "^6.0.0", | ||
"chart.js": "^3.0.0-alpha", | ||
"eslint": "^6.8.0", | ||
"@babel/preset-env": "^7.11.5", | ||
"@rollup/plugin-babel": "^5.2.1", | ||
"@rollup/plugin-commonjs": "^15.1.0", | ||
"@rollup/plugin-node-resolve": "^9.0.0", | ||
"chart.js": "^3.0.0-beta.3", | ||
"eslint": "^7.10.0", | ||
"eslint-config-chartjs": "^0.2.0", | ||
"eslint-config-esnext": "^4.0.0", | ||
"eslint-plugin-html": "^6.0.1", | ||
"gulp": "^4.0.2", | ||
"gulp-eslint": "^6.0.0", | ||
"gulp-replace": "^1.0.0", | ||
"gulp-streamify": "^1.0.2", | ||
"gulp-zip": "^4.2.0", | ||
"jasmine-core": "^3.5.0", | ||
"karma": "^5.0.1", | ||
"karma-coverage": "^1.1.2", | ||
"eslint-config-esnext": "^4.1.0", | ||
"eslint-plugin-html": "^6.1.0", | ||
"jasmine": "^3.6.1", | ||
"jasmine-core": "^3.6.0", | ||
"karma": "^5.2.3", | ||
"karma-chrome-launcher": "^3.1.0", | ||
"karma-coverage-istanbul-instrumenter": "^1.0.3", | ||
"karma-coverage-istanbul-reporter": "^3.0.3", | ||
"karma-firefox-launcher": "^1.3.0", | ||
"karma-jasmine": "^2.0.1", | ||
"karma-jasmine-html-reporter": "^1.5.3", | ||
"karma-jasmine": "^4.0.1", | ||
"karma-jasmine-html-reporter": "^1.5.4", | ||
"karma-rollup-preprocessor": "^7.0.5", | ||
"karma-spec-reporter": "0.0.32", | ||
"merge2": "^1.3.0", | ||
"pixelmatch": "^4.0.2", | ||
"rollup": "^2.6.0", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"rollup-plugin-terser": "^5.3.0", | ||
"yargs": "^13.3.2" | ||
"karma-summary-reporter": "^1.8.0", | ||
"minimist": "^1.2.5", | ||
"pixelmatch": "^5.2.1", | ||
"rollup": "^2.28.2", | ||
"rollup-plugin-terser": "^7.0.2" | ||
}, | ||
"peerDependencies": { | ||
"chart.js": "^3.0.0-alpha" | ||
} | ||
"chart.js": "^3.0.0-beta.3" | ||
}, | ||
"dependencies": {} | ||
} |
@@ -32,6 +32,8 @@ # chartjs-chart-treemap | ||
tree: [6,6,5,4,3,2,2,1], | ||
fontColor: '#000', | ||
fontFamily: 'serif', | ||
fontSize: 12, | ||
fontStyle: 'normal', | ||
font: { | ||
color: '#000', | ||
family: 'serif', | ||
size: 12, | ||
style: 'normal', | ||
}, | ||
backgroundColor: function(ctx) { | ||
@@ -38,0 +40,0 @@ var value = ctx.dataset.data[ctx.dataIndex]; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
25
76
50572
6
1447