chartjs-chart-treemap
Advanced tools
Comparing version 1.0.0-beta.3 to 1.0.0-beta.4
/*! | ||
* chartjs-chart-treemap v1.0.0-beta.3 | ||
* chartjs-chart-treemap v1.0.0-beta.4 | ||
* https://github.com/kurkle/chartjs-chart-treemap#readme | ||
* (c) 2020 Jukka Kurkela | ||
* (c) 2021 Jukka Kurkela | ||
* Released under the MIT license | ||
@@ -12,16 +12,16 @@ */ | ||
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(); | ||
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(); | ||
} | ||
@@ -37,90 +37,90 @@ | ||
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); | ||
} | ||
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); | ||
} | ||
Object.keys(tmp).forEach((k) => { | ||
v = {children: data[k]}; | ||
v[key] = +tmp[k]; | ||
v[grp] = k; | ||
if (mainGrp) { | ||
v[mainGrp] = mainValue; | ||
} | ||
ret.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; | ||
return ret; | ||
} | ||
function isObject(obj) { | ||
const type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
const type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
} | ||
function index(values, key) { | ||
let n = values.length; | ||
let i; | ||
let n = values.length; | ||
let i; | ||
if (!n) { | ||
return key; | ||
} | ||
if (!n) { | ||
return key; | ||
} | ||
const 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}; | ||
} | ||
} | ||
return key; | ||
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 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) { | ||
let 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)) || 0; | ||
return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0; | ||
} | ||
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; | ||
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}; | ||
return {d1, d2, w, h}; | ||
} | ||
@@ -131,77 +131,77 @@ | ||
function buildRow(rect, itm, dims, sum) { | ||
const r = { | ||
x: getX(rect, dims.w), | ||
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; | ||
const r = { | ||
x: getX(rect, dims.w), | ||
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.rtl = !!r.rtl; | ||
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.rtl = !!r.rtl; | ||
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() { | ||
const 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) { | ||
const me = this; | ||
const ret = []; | ||
const sum = arr.nsum; | ||
const row = arr.get(); | ||
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; | ||
for (const itm of row) { | ||
const dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
maxd2 = Math.max(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 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; | ||
for (const itm of row) { | ||
const dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
maxd2 = Math.max(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; | ||
} | ||
} | ||
@@ -213,89 +213,89 @@ | ||
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) { | ||
const v = +o[sa.key]; | ||
const n = v * sa.ratio; | ||
o._normalized = 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 | ||
}; | ||
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) { | ||
Object.assign(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) { | ||
const me = this; | ||
me.key = key; | ||
me.ratio = ratio; | ||
me.reset(); | ||
} | ||
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() { | ||
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; | ||
} | ||
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, ...args) { | ||
const nstat = getNewStat(this, o); | ||
if (!fn(getStat(this), nstat, args)) { | ||
return o; | ||
} | ||
push(this, o, nstat); | ||
} | ||
pushIf(o, fn, ...args) { | ||
const nstat = getNewStat(this, o); | ||
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; | ||
} | ||
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; | ||
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; | ||
} | ||
@@ -313,43 +313,43 @@ | ||
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; | ||
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; | ||
} | ||
const tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
const tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
const val = (idx) => key ? +tmp[idx][key] : +tmp[idx]; | ||
const gval = (idx) => grp && tmp[idx][grp]; | ||
const val = (idx) => key ? +tmp[idx][key] : +tmp[idx]; | ||
const gval = (idx) => 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]}; | ||
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 rectNotEqual(r1, r2) { | ||
return !r1 || !r2 | ||
return !r1 || !r2 | ||
|| r1.x !== r2.x | ||
@@ -362,222 +362,222 @@ || r1.y !== r2.y | ||
function arrayNotEqual(a1, a2) { | ||
let i, n; | ||
let i, n; | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
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; | ||
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 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 = opts.rtl ? 'end' : 'start'; | ||
ctx.textBaseline = 'top'; | ||
const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3; | ||
ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
ctx.save(); | ||
ctx.fillStyle = opts.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 = opts.rtl ? 'end' : 'start'; | ||
ctx.textBaseline = 'top'; | ||
const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3; | ||
ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
} | ||
function drawDivider(ctx, rect) { | ||
const opts = rect.options; | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
const opts = rect.options; | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
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(); | ||
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 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); | ||
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 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}; | ||
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 (valueOrDefault(dataset.groupLabels, true) && shouldDrawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return ret; | ||
} | ||
if (valueOrDefault(dataset.groupLabels, true) && shouldDrawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return ret; | ||
} | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key); | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key); | ||
} | ||
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)); | ||
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 TreemapController extends DatasetController { | ||
initialize() { | ||
this.enableOptionSharing = true; | ||
super.initialize(); | ||
} | ||
initialize() { | ||
this.enableOptionSharing = true; | ||
super.initialize(); | ||
} | ||
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 || ''; | ||
const rtl = !!dataset.rtl; | ||
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 || ''; | ||
const rtl = !!dataset.rtl; | ||
const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top, rtl}; | ||
const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top, rtl}; | ||
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(); | ||
} | ||
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(); | ||
} | ||
me.updateElements(meta.data, 0, meta.data.length, mode); | ||
} | ||
me.updateElements(meta.data, 0, meta.data.length, mode); | ||
} | ||
resolveDataElementOptions(index, mode) { | ||
const options = super.resolveDataElementOptions(index, mode); | ||
const result = Object.isFrozen(options) ? Object.assign({}, options) : options; | ||
result.font = toFont(options.font, this.chart.options.font); | ||
return result; | ||
} | ||
resolveDataElementOptions(index, mode) { | ||
const options = super.resolveDataElementOptions(index, mode); | ||
const result = Object.isFrozen(options) ? Object.assign({}, options) : options; | ||
result.font = toFont(options.font, this.chart.options.font); | ||
return result; | ||
} | ||
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); | ||
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); | ||
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 | ||
}; | ||
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 | ||
}; | ||
if (includeOptions) { | ||
properties.options = options; | ||
} | ||
me.updateElement(rects[i], i, properties, mode); | ||
} | ||
if (includeOptions) { | ||
properties.options = options; | ||
} | ||
me.updateElement(rects[i], i, properties, mode); | ||
} | ||
me.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
} | ||
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); | ||
} | ||
} | ||
_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); | ||
} | ||
} | ||
_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); | ||
} | ||
} | ||
} | ||
} | ||
_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); | ||
} | ||
} | ||
} | ||
} | ||
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 || []; | ||
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 || []; | ||
me._drawRects(ctx, data, metadata, levels); | ||
me._drawDividers(ctx, data, metadata); | ||
} | ||
me._drawRects(ctx, data, metadata, levels); | ||
me._drawDividers(ctx, data, metadata); | ||
} | ||
} | ||
@@ -588,61 +588,52 @@ | ||
TreemapController.defaults = { | ||
dataElementType: 'treemap', | ||
dataElementOptions: [ | ||
'backgroundColor', | ||
'borderColor', | ||
'borderSkipped', | ||
'borderWidth', | ||
'dividerColor', | ||
'dividerDash', | ||
'dividerDashOffset', | ||
'dividerWidth', | ||
'font', | ||
'groupLabels', | ||
'groupDividers', | ||
'spacing', | ||
'label', | ||
'rtl' | ||
], | ||
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 | ||
} | ||
}, | ||
dataElementType: 'treemap', | ||
groupLabels: true, | ||
borderWidth: 0, | ||
spacing: 0.5, | ||
groupDividers: false, | ||
dividerWidth: 1 | ||
}; | ||
TreemapController.overrides = { | ||
interaction: { | ||
mode: 'point', | ||
intersect: true | ||
}, | ||
hover: {}, | ||
plugins: { | ||
tooltip: { | ||
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]; | ||
const label = dataItem.g || dataset.label; | ||
return (label ? label + ': ' : '') + dataItem.v; | ||
} | ||
} | ||
}, | ||
}, | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
display: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
display: false | ||
} | ||
}, | ||
}; | ||
/** | ||
@@ -656,58 +647,58 @@ * Helper function to get the bounds of the rect | ||
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}; | ||
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(value, maxW, maxH) { | ||
let t, r, b, l; | ||
let t, r, b, l; | ||
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 (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) { | ||
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); | ||
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); | ||
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 { | ||
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 | ||
} | ||
}; | ||
} | ||
function inRange(rect, x, y, useFinalPosition) { | ||
const skipX = x === null; | ||
const skipY = y === null; | ||
const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition); | ||
const skipX = x === null; | ||
const skipY = y === null; | ||
const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition); | ||
return bounds | ||
return bounds | ||
&& (skipX || x >= bounds.left && x <= bounds.right) | ||
@@ -719,64 +710,64 @@ && (skipY || y >= bounds.top && y <= bounds.bottom); | ||
constructor(cfg) { | ||
super(); | ||
constructor(cfg) { | ||
super(); | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
if (cfg) { | ||
Object.assign(this, cfg); | ||
} | ||
} | ||
if (cfg) { | ||
Object.assign(this, cfg); | ||
} | ||
} | ||
draw(ctx) { | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this); | ||
draw(ctx) { | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this); | ||
ctx.save(); | ||
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); | ||
} | ||
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(); | ||
} | ||
ctx.restore(); | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
getCenterPoint(useFinalPosition) { | ||
const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
getCenterPoint(useFinalPosition) { | ||
const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
} | ||
@@ -787,15 +778,23 @@ | ||
TreemapElement.defaults = { | ||
dividerCapStyle: 'butt', | ||
dividerColor: 'black', | ||
dividerDash: undefined, | ||
dividerDashOffset: 0, | ||
dividerWidth: 0, | ||
groupDividers: false, | ||
borderSkipped: undefined, | ||
borderWidth: undefined, | ||
color: undefined, | ||
dividerCapStyle: 'butt', | ||
dividerColor: 'black', | ||
dividerDash: undefined, | ||
dividerDashOffset: 0, | ||
dividerWidth: 0, | ||
font: {}, | ||
groupDividers: false, | ||
groupLabels: undefined, | ||
spacing: undefined, | ||
label: undefined, | ||
rtl: undefined | ||
}; | ||
TreemapElement.defaultRoutes = { | ||
backgroundColor: 'color', | ||
borderColor: 'color' | ||
backgroundColor: 'backgroundColor', | ||
borderColor: 'borderColor' | ||
}; | ||
export { Rect, TreemapElement as Rectangle, StatArray, TreemapController, flatten, group, index, isObject, sort, squarify, sum }; |
/*! | ||
* chartjs-chart-treemap v1.0.0-beta.3 | ||
* chartjs-chart-treemap v1.0.0-beta.4 | ||
* https://github.com/kurkle/chartjs-chart-treemap#readme | ||
* (c) 2020 Jukka Kurkela | ||
* (c) 2021 Jukka Kurkela | ||
* Released under the MIT license | ||
*/ | ||
(function (global, factory) { | ||
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'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('chart.js'), require('chart.js/helpers')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'chart.js', 'chart.js/helpers'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['chartjs-chart-treemap'] = {}, global.Chart, global.Chart.helpers)); | ||
}(this, (function (exports, chart_js, helpers) { 'use strict'; | ||
// 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(); | ||
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(); | ||
} | ||
@@ -39,90 +39,90 @@ | ||
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); | ||
} | ||
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); | ||
} | ||
Object.keys(tmp).forEach((k) => { | ||
v = {children: data[k]}; | ||
v[key] = +tmp[k]; | ||
v[grp] = k; | ||
if (mainGrp) { | ||
v[mainGrp] = mainValue; | ||
} | ||
ret.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; | ||
return ret; | ||
} | ||
function isObject(obj) { | ||
const type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
const type = typeof obj; | ||
return type === 'function' || type === 'object' && !!obj; | ||
} | ||
function index(values, key) { | ||
let n = values.length; | ||
let i; | ||
let n = values.length; | ||
let i; | ||
if (!n) { | ||
return key; | ||
} | ||
if (!n) { | ||
return key; | ||
} | ||
const 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}; | ||
} | ||
} | ||
return key; | ||
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 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) { | ||
let 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)) || 0; | ||
return (+(Math.round(v + 'e+' + n) + 'e-' + n)) || 0; | ||
} | ||
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; | ||
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}; | ||
return {d1, d2, w, h}; | ||
} | ||
@@ -133,77 +133,77 @@ | ||
function buildRow(rect, itm, dims, sum) { | ||
const r = { | ||
x: getX(rect, dims.w), | ||
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; | ||
const r = { | ||
x: getX(rect, dims.w), | ||
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.rtl = !!r.rtl; | ||
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.rtl = !!r.rtl; | ||
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() { | ||
const 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) { | ||
const me = this; | ||
const ret = []; | ||
const sum = arr.nsum; | ||
const row = arr.get(); | ||
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; | ||
for (const itm of row) { | ||
const dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
maxd2 = Math.max(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 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; | ||
for (const itm of row) { | ||
const dims = getDims(itm, w2, s2, key); | ||
totd1 += dims.d1; | ||
maxd2 = Math.max(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; | ||
} | ||
} | ||
@@ -215,89 +215,89 @@ | ||
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) { | ||
const v = +o[sa.key]; | ||
const n = v * sa.ratio; | ||
o._normalized = 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 | ||
}; | ||
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) { | ||
Object.assign(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) { | ||
const me = this; | ||
me.key = key; | ||
me.ratio = ratio; | ||
me.reset(); | ||
} | ||
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() { | ||
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; | ||
} | ||
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, ...args) { | ||
const nstat = getNewStat(this, o); | ||
if (!fn(getStat(this), nstat, args)) { | ||
return o; | ||
} | ||
push(this, o, nstat); | ||
} | ||
pushIf(o, fn, ...args) { | ||
const nstat = getNewStat(this, o); | ||
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; | ||
} | ||
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; | ||
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; | ||
} | ||
@@ -315,43 +315,43 @@ | ||
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; | ||
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; | ||
} | ||
const tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
const tmp = values.slice(); | ||
key = index(tmp, key); | ||
sort(tmp, key); | ||
const val = (idx) => key ? +tmp[idx][key] : +tmp[idx]; | ||
const gval = (idx) => grp && tmp[idx][grp]; | ||
const val = (idx) => key ? +tmp[idx][key] : +tmp[idx]; | ||
const gval = (idx) => 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]}; | ||
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 rectNotEqual(r1, r2) { | ||
return !r1 || !r2 | ||
return !r1 || !r2 | ||
|| r1.x !== r2.x | ||
@@ -364,222 +364,222 @@ || r1.y !== r2.y | ||
function arrayNotEqual(a1, a2) { | ||
let i, n; | ||
let i, n; | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
if (a1.lenght !== a2.length) { | ||
return true; | ||
} | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
for (i = 0, n = a1.length; i < n; ++i) { | ||
if (a1[i] !== a2[i]) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
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; | ||
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 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 = opts.rtl ? 'end' : 'start'; | ||
ctx.textBaseline = 'top'; | ||
const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3; | ||
ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
ctx.save(); | ||
ctx.fillStyle = opts.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 = opts.rtl ? 'end' : 'start'; | ||
ctx.textBaseline = 'top'; | ||
const x = opts.rtl ? rect.x + rect.width - opts.borderWidth - 3 : rect.x + opts.borderWidth + 3; | ||
ctx.fillText(item.g, x, rect.y + opts.borderWidth + 3); | ||
} | ||
ctx.restore(); | ||
} | ||
function drawDivider(ctx, rect) { | ||
const opts = rect.options; | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
const opts = rect.options; | ||
const w = rect.width || rect.w; | ||
const h = rect.height || rect.h; | ||
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(); | ||
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 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); | ||
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 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}; | ||
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 (helpers.valueOrDefault(dataset.groupLabels, true) && shouldDrawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return ret; | ||
} | ||
if (helpers.valueOrDefault(dataset.groupLabels, true) && shouldDrawCaption(sq, font)) { | ||
subRect.y += font.lineHeight; | ||
subRect.h -= font.lineHeight; | ||
} | ||
ret.push(...recur(gidx + 1, subRect, sq.g, sq.s)); | ||
}); | ||
} | ||
return ret; | ||
} | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
if (!tree.length && dataset.data.length) { | ||
tree = dataset.tree = dataset.data; | ||
} | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key); | ||
return glen | ||
? recur(0, mainRect) | ||
: squarify(tree, mainRect, key); | ||
} | ||
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)); | ||
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 TreemapController extends chart_js.DatasetController { | ||
initialize() { | ||
this.enableOptionSharing = true; | ||
super.initialize(); | ||
} | ||
initialize() { | ||
this.enableOptionSharing = true; | ||
super.initialize(); | ||
} | ||
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 || ''; | ||
const rtl = !!dataset.rtl; | ||
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 || ''; | ||
const rtl = !!dataset.rtl; | ||
const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top, rtl}; | ||
const mainRect = {x: area.left, y: area.top, w: area.right - area.left, h: area.bottom - area.top, rtl}; | ||
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(); | ||
} | ||
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(); | ||
} | ||
me.updateElements(meta.data, 0, meta.data.length, mode); | ||
} | ||
me.updateElements(meta.data, 0, meta.data.length, mode); | ||
} | ||
resolveDataElementOptions(index, mode) { | ||
const options = super.resolveDataElementOptions(index, mode); | ||
const result = Object.isFrozen(options) ? Object.assign({}, options) : options; | ||
result.font = helpers.toFont(options.font, this.chart.options.font); | ||
return result; | ||
} | ||
resolveDataElementOptions(index, mode) { | ||
const options = super.resolveDataElementOptions(index, mode); | ||
const result = Object.isFrozen(options) ? Object.assign({}, options) : options; | ||
result.font = helpers.toFont(options.font, this.chart.options.font); | ||
return result; | ||
} | ||
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); | ||
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); | ||
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 | ||
}; | ||
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 | ||
}; | ||
if (includeOptions) { | ||
properties.options = options; | ||
} | ||
me.updateElement(rects[i], i, properties, mode); | ||
} | ||
if (includeOptions) { | ||
properties.options = options; | ||
} | ||
me.updateElement(rects[i], i, properties, mode); | ||
} | ||
me.updateSharedOptions(sharedOptions, mode, firstOpts); | ||
} | ||
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); | ||
} | ||
} | ||
_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); | ||
} | ||
} | ||
_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); | ||
} | ||
} | ||
} | ||
} | ||
_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); | ||
} | ||
} | ||
} | ||
} | ||
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 || []; | ||
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 || []; | ||
me._drawRects(ctx, data, metadata, levels); | ||
me._drawDividers(ctx, data, metadata); | ||
} | ||
me._drawRects(ctx, data, metadata, levels); | ||
me._drawDividers(ctx, data, metadata); | ||
} | ||
} | ||
@@ -590,61 +590,52 @@ | ||
TreemapController.defaults = { | ||
dataElementType: 'treemap', | ||
dataElementOptions: [ | ||
'backgroundColor', | ||
'borderColor', | ||
'borderSkipped', | ||
'borderWidth', | ||
'dividerColor', | ||
'dividerDash', | ||
'dividerDashOffset', | ||
'dividerWidth', | ||
'font', | ||
'groupLabels', | ||
'groupDividers', | ||
'spacing', | ||
'label', | ||
'rtl' | ||
], | ||
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 | ||
} | ||
}, | ||
dataElementType: 'treemap', | ||
groupLabels: true, | ||
borderWidth: 0, | ||
spacing: 0.5, | ||
groupDividers: false, | ||
dividerWidth: 1 | ||
}; | ||
TreemapController.overrides = { | ||
interaction: { | ||
mode: 'point', | ||
intersect: true | ||
}, | ||
hover: {}, | ||
plugins: { | ||
tooltip: { | ||
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]; | ||
const label = dataItem.g || dataset.label; | ||
return (label ? label + ': ' : '') + dataItem.v; | ||
} | ||
} | ||
}, | ||
}, | ||
scales: { | ||
x: { | ||
type: 'linear', | ||
display: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
display: false | ||
} | ||
}, | ||
}; | ||
/** | ||
@@ -658,58 +649,58 @@ * Helper function to get the bounds of the rect | ||
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}; | ||
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(value, maxW, maxH) { | ||
let t, r, b, l; | ||
let t, r, b, l; | ||
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 (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) { | ||
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); | ||
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); | ||
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 { | ||
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 | ||
} | ||
}; | ||
} | ||
function inRange(rect, x, y, useFinalPosition) { | ||
const skipX = x === null; | ||
const skipY = y === null; | ||
const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition); | ||
const skipX = x === null; | ||
const skipY = y === null; | ||
const bounds = !rect || (skipX && skipY) ? false : getBounds(rect, useFinalPosition); | ||
return bounds | ||
return bounds | ||
&& (skipX || x >= bounds.left && x <= bounds.right) | ||
@@ -721,64 +712,64 @@ && (skipY || y >= bounds.top && y <= bounds.bottom); | ||
constructor(cfg) { | ||
super(); | ||
constructor(cfg) { | ||
super(); | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
this.options = undefined; | ||
this.width = undefined; | ||
this.height = undefined; | ||
if (cfg) { | ||
Object.assign(this, cfg); | ||
} | ||
} | ||
if (cfg) { | ||
Object.assign(this, cfg); | ||
} | ||
} | ||
draw(ctx) { | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this); | ||
draw(ctx) { | ||
const options = this.options; | ||
const {inner, outer} = boundingRects(this); | ||
ctx.save(); | ||
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); | ||
} | ||
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(); | ||
} | ||
ctx.restore(); | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
inRange(mouseX, mouseY, useFinalPosition) { | ||
return inRange(this, mouseX, mouseY, useFinalPosition); | ||
} | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
inXRange(mouseX, useFinalPosition) { | ||
return inRange(this, mouseX, null, useFinalPosition); | ||
} | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
inYRange(mouseY, useFinalPosition) { | ||
return inRange(this, null, mouseY, useFinalPosition); | ||
} | ||
getCenterPoint(useFinalPosition) { | ||
const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
getCenterPoint(useFinalPosition) { | ||
const {x, y, width, height} = this.getProps(['x', 'y', 'width', 'height'], useFinalPosition); | ||
return { | ||
x: x + width / 2, | ||
y: y + height / 2 | ||
}; | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
tooltipPosition() { | ||
return this.getCenterPoint(); | ||
} | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
getRange(axis) { | ||
return axis === 'x' ? this.width / 2 : this.height / 2; | ||
} | ||
} | ||
@@ -789,13 +780,21 @@ | ||
TreemapElement.defaults = { | ||
dividerCapStyle: 'butt', | ||
dividerColor: 'black', | ||
dividerDash: undefined, | ||
dividerDashOffset: 0, | ||
dividerWidth: 0, | ||
groupDividers: false, | ||
borderSkipped: undefined, | ||
borderWidth: undefined, | ||
color: undefined, | ||
dividerCapStyle: 'butt', | ||
dividerColor: 'black', | ||
dividerDash: undefined, | ||
dividerDashOffset: 0, | ||
dividerWidth: 0, | ||
font: {}, | ||
groupDividers: false, | ||
groupLabels: undefined, | ||
spacing: undefined, | ||
label: undefined, | ||
rtl: undefined | ||
}; | ||
TreemapElement.defaultRoutes = { | ||
backgroundColor: 'color', | ||
borderColor: 'color' | ||
backgroundColor: 'backgroundColor', | ||
borderColor: 'borderColor' | ||
}; | ||
@@ -807,12 +806,21 @@ | ||
tooltipPlugin.positioners.treemap = function(active) { | ||
if (!active.length) { | ||
return false; | ||
} | ||
if (!active.length) { | ||
return false; | ||
} | ||
const item = active[active.length - 1]; | ||
const el = item.element; | ||
const item = active[active.length - 1]; | ||
const el = item.element; | ||
return el.tooltipPosition(); | ||
return el.tooltipPosition(); | ||
}; | ||
exports.flatten = flatten; | ||
exports.group = group; | ||
exports.index = index; | ||
exports.isObject = isObject; | ||
exports.sort = sort; | ||
exports.sum = sum; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); |
/*! | ||
* chartjs-chart-treemap v1.0.0-beta.3 | ||
* chartjs-chart-treemap v1.0.0-beta.4 | ||
* https://github.com/kurkle/chartjs-chart-treemap#readme | ||
* (c) 2020 Jukka Kurkela | ||
* (c) 2021 Jukka Kurkela | ||
* Released under the MIT license | ||
*/ | ||
!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 n(t){const e=typeof t;return"function"===e||"object"===e&&!!t}function i(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)||0}function r(t,e,n,i){const r=t._normalized,o=e*r/n,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===i?s:a,h:"_ix"===i?a:s}}const o=(t,e)=>i(t.rtl?t.x+t.w-t._ix-e:t.x+t._ix,4);function s(t,e,n,r){const s={x:o(t,n.w),y:i(t.y+t._iy,4),w:i(n.w,4),h:i(n.h,4),a:i(e._normalized,4),v:e.value,s:r,_data:e._data};return e.group&&(s.g=e.group,s.l=e.level,s.gs=e.groupSum),s}class a{constructor(t){const e=this;t=t||{w:1,h:1},e.rtl=!!t.rtl,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,n=[],i=t.nsum,o=t.get(),a=e.dir,h=e.side,l=h*h,u="x"===a?"_ix":"_iy",d=i*i;let c=0,g=0;for(const i of o){const o=r(i,l,d,u);g+=o.d1,c=Math.max(c,o.d2),n.push(s(e,i,o,t.sum)),e[u]+=o.d1}return e["y"===a?"_ix":"_iy"]+=c,e[u]-=g,n}}const h=Math.min,l=Math.max;function u(t,e){const n=+e[t.key],i=n*t.ratio;return e._normalized=i,{min:h(t.min,n),max:l(t.max,n),sum:t.sum+n,nmin:h(t.nmin,i),nmax:l(t.nmax,i),nsum:t.nsum+i}}function d(t,e,n){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,n)}class c{constructor(t,e){const n=this;n.key=t,n.ratio=e,n.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,u(this,t))}pushIf(t,e,...n){const i=u(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),i,n))return t;var r;d(this,t,i)}get(){return this._arr}}function g(t,e,n){if(0===t.sum)return!0;const[i]=n,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=i*i,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function p(t,e,i,r,o,s){t=t||[];const h=[],l=new a(e),u=new c("value",l.area/function(t,e){let n,i,r;for(n=0,i=0,r=t.length;i<r;++i)n+=e?+t[i][e]:+t[i];return n}(t,i));let d=l.side;const p=t.length;let f,m;if(!p)return h;const x=t.slice();i=function(t,e){let i,r=t.length;if(!r)return e;const o=n(t[0]);for(e=o?e:"v",i=0,r=t.length;i<r;++i)o?t[i]._idx=i:t[i]={v:t[i],_idx:i};return e}(x,i),function(t,e){e?t.sort(((t,n)=>+n[e]-+t[e])):t.sort(((t,e)=>+e-+t))}(x,i);const y=t=>r&&x[t][r];for(f=0;f<p;++f)m={value:(v=f,i?+x[v][i]:+x[v]),groupSum:s,_data:t[x[f]._idx]},r&&(m.level=o,m.group=y(f)),m=u.pushIf(m,g,d),m&&(h.push(l.map(u)),d=l.side,u.reset(),u.push(m));var v;return u.length&&h.push(l.map(u)),function(t){const e=[...t],n=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):n.push(t)}return n.reverse()}(h)}function f(t,e){if(!e)return!1;const n=t.width||t.w,i=t.height||t.h,r=2*e.lineHeight;return n>r&&i>r}function m(t,e,n,i,r){if(t.save(),t.fillStyle=i.font.color,t.font=i.font.string,t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.clip(),"l"in n&&n.l!==r){if(i.groupLabels){t.textAlign=i.rtl?"end":"start",t.textBaseline="top";const r=i.rtl?e.x+e.width-i.borderWidth-3:e.x+i.borderWidth+3;t.fillText(n.g,r,e.y+i.borderWidth+3)}}else t.textAlign="center",t.textBaseline="middle",function(t,e,n){const i=n.options,r=i.font.lineHeight,o=(i.label||e.g+"\n"+e.v).split("\n"),s=n.y+n.height/2-o.length*r/4;o.forEach(((e,i)=>t.fillText(e,n.x+n.width/2,s+i*r)))}(t,n,e);t.restore()}function x(t,e){const n=e.options,i=e.width||e.w,r=e.height||e.h;if(t.save(),t.strokeStyle=n.dividerColor||"black",t.lineCap=n.dividerCapStyle,t.setLineDash(n.dividerDash||[]),t.lineDashOffset=n.dividerDashOffset,t.lineWidth=n.dividerWidth,t.beginPath(),i>r){const n=i/2;t.moveTo(e.x+n,e.y),t.lineTo(e.x+n,e.y+r)}else{const n=r/2;t.moveTo(e.x,e.y+n),t.lineTo(e.x+i,e.y+n)}t.stroke(),t.restore()}function y(t,n,i){const r=t.key||"";let o=t.tree||[];const s=t.groups||[],a=s.length,h=(t.spacing||0)+(t.borderWidth||0);return!o.length&&t.data.length&&(o=t.tree=t.data),a?function n(l,u,d,c){const g=s[l],m=l>0&&s[l-1],x=p(function(t,e,n,i,r){const o=Object.create(null),s=Object.create(null),a=[];let h,l,u,d;for(l=0,u=t.length;l<u;++l)d=t[l],i&&d[i]!==r||(h=d[e]||"",h in o||(o[h]=0,s[h]=[]),o[h]+=+d[n],s[h].push(d));return Object.keys(o).forEach((t=>{d={children:s[t]},d[n]=+o[t],d[e]=t,i&&(d[i]=r),a.push(d)})),a}(o,g,r,m,d),u,r,g,l,c),y=x.slice();let v;return l<a-1&&x.forEach((r=>{v={x:r.x+h,y:r.y+h,w:r.w-2*h,h:r.h-2*h},e.valueOrDefault(t.groupLabels,!0)&&f(r,i)&&(v.y+=i.lineHeight,v.h-=i.lineHeight),y.push(...n(l+1,v,r.g,r.s))})),y}(0,n):p(o,n,r)}class v extends t.DatasetController{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const n=this,i=n.getMeta(),r=n.getDataset(),o=r.groups||(r.groups=[]),s=e.toFont(r.font,n.chart.options.font),a=n.chart.chartArea,h=r.key||"",l=!!r.rtl,u={x:a.left,y:a.top,w:a.right-a.left,h:a.bottom-a.top,rtl:l};var d,c;"reset"!==t&&(d=n._rect,c=u,d&&c&&d.x===c.x&&d.y===c.y&&d.w===c.w&&d.h===c.h)&&n._key===h&&!function(t,e){let n,i;if(t.lenght!==e.length)return!0;for(n=0,i=t.length;n<i;++n)if(t[n]!==e[n])return!0;return!1}(n._groups,o)||(n._rect=u,n._groups=o.slice(),n._key=h,r.data=y(r,u,s),n._dataCheck(),n._resyncElements()),n.updateElements(i.data,0,i.data.length,t)}resolveDataElementOptions(t,n){const i=super.resolveDataElementOptions(t,n),r=Object.isFrozen(i)?Object.assign({},i):i;return r.font=e.toFont(i.font,this.chart.options.font),r}updateElements(t,e,n,i){const r=this,o="reset"===i,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,i),h=r.getSharedOptions(a),l=r.includeOptions(i,h);for(let a=e;a<e+n;a++){const e=s.data[a],n=h||r.resolveDataElementOptions(a,i),u=o?0:e.h-2*n.spacing,d=o?0:e.w-2*n.spacing,c={x:e.x+n.spacing,y:e.y+n.spacing,width:d,height:u};l&&(c.options=n),r.updateElement(t[a],a,c,i)}r.updateSharedOptions(h,i,a)}_drawDividers(t,e,n){for(let i=0,r=n.length;i<r;++i){const r=n[i],o=e[i];r.options.groupDividers&&o._data.children.length>1&&x(t,r)}this.getDataset().groupDividers&&x(t,this._rect)}_drawRects(t,e,n,i){for(let r=0,o=n.length;r<o;++r){const o=n[r],s=e[r];if(!o.hidden){o.draw(t);const e=o.options;f(o,e.font)&&s.g&&m(t,o,s,e,i)}}}draw(){const t=this,e=t.chart.ctx,n=t.getMeta().data||[],i=t.getDataset(),r=(i.groups||[]).length-1,o=i.data||[];t._drawRects(e,o,n,r),t._drawDividers(e,o,n)}}function b(t,e){const{x:n,y:i,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:n,top:i,right:n+r,bottom:i+o}}function w(t,e,n){return Math.max(Math.min(t,n),e)}function _(t){const e=b(t),i=e.right-e.left,r=e.bottom-e.top,o=function(t,e,i){let r,o,s,a;return n(t)?(r=+t.top||0,o=+t.right||0,s=+t.bottom||0,a=+t.left||0):r=o=s=a=+t||0,{t:w(r,0,i),r:w(o,0,e),b:w(s,0,i),l:w(a,0,e)}}(t.options.borderWidth,i/2,r/2);return{outer:{x:e.left,y:e.top,w:i,h:r},inner:{x:e.left+o.l,y:e.top+o.t,w:i-o.l-o.r,h:r-o.t-o.b}}}function D(t,e,n,i){const r=null===e,o=null===n,s=!(!t||r&&o)&&b(t,i);return s&&(r||e>=s.left&&e<=s.right)&&(o||n>=s.top&&n<=s.bottom)}v.id="treemap",v.defaults={dataElementType:"treemap",dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","dividerColor","dividerDash","dividerDashOffset","dividerWidth","font","groupLabels","groupDividers","spacing","label","rtl"],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,n=e.data[t.dataIndex];return e.label+": "+n.v}}},datasets:{groupLabels:!0,borderWidth:0,spacing:.5,groupDividers:!1,dividerWidth:1},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}};class C 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:n,outer:i}=_(this);t.save(),i.w!==n.w||i.h!==n.h?(t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.rect(n.x,n.y,n.w,n.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(n.x,n.y,n.w,n.h)),t.restore()}inRange(t,e,n){return D(this,t,e,n)}inXRange(t,e){return D(this,t,null,e)}inYRange(t,e){return D(this,null,t,e)}getCenterPoint(t){const{x:e,y:n,width:i,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+i/2,y:n+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}C.id="treemap",C.defaults={dividerCapStyle:"butt",dividerColor:"black",dividerDash:void 0,dividerDashOffset:0,dividerWidth:0,groupDividers:!1},C.defaultRoutes={backgroundColor:"color",borderColor:"color"},t.Chart.register(v,C);t.Chart.registry.plugins.get("tooltip").positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()}})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-treemap"]={},t.Chart,t.Chart.helpers)}(this,(function(t,e,n){"use strict";function i(t){const e=[...t],n=[];for(;e.length;){const t=e.pop();Array.isArray(t)?e.push(...t):n.push(t)}return n.reverse()}function r(t,e,n,i,r){const o=Object.create(null),s=Object.create(null),a=[];let h,l,u,d;for(l=0,u=t.length;l<u;++l)d=t[l],i&&d[i]!==r||(h=d[e]||"",h in o||(o[h]=0,s[h]=[]),o[h]+=+d[n],s[h].push(d));return Object.keys(o).forEach((t=>{d={children:s[t]},d[n]=+o[t],d[e]=t,i&&(d[i]=r),a.push(d)})),a}function o(t){const e=typeof t;return"function"===e||"object"===e&&!!t}function s(t,e){let n,i=t.length;if(!i)return e;const r=o(t[0]);for(e=r?e:"v",n=0,i=t.length;n<i;++n)r?t[n]._idx=n:t[n]={v:t[n],_idx:n};return e}function a(t,e){e?t.sort(((t,n)=>+n[e]-+t[e])):t.sort(((t,e)=>+e-+t))}function h(t,e){let n,i,r;for(n=0,i=0,r=t.length;i<r;++i)n+=e?+t[i][e]:+t[i];return n}function l(t,e){return+(Math.round(t+"e+"+e)+"e-"+e)||0}function u(t,e,n,i){const r=t._normalized,o=e*r/n,s=Math.sqrt(r*o),a=r/s;return{d1:s,d2:a,w:"_ix"===i?s:a,h:"_ix"===i?a:s}}const d=(t,e)=>l(t.rtl?t.x+t.w-t._ix-e:t.x+t._ix,4);function c(t,e,n,i){const r={x:d(t,n.w),y:l(t.y+t._iy,4),w:l(n.w,4),h:l(n.h,4),a:l(e._normalized,4),v:e.value,s:i,_data:e._data};return e.group&&(r.g=e.group,r.l=e.level,r.gs=e.groupSum),r}class g{constructor(t){const e=this;t=t||{w:1,h:1},e.rtl=!!t.rtl,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,n=[],i=t.nsum,r=t.get(),o=e.dir,s=e.side,a=s*s,h="x"===o?"_ix":"_iy",l=i*i;let d=0,g=0;for(const i of r){const r=u(i,a,l,h);g+=r.d1,d=Math.max(d,r.d2),n.push(c(e,i,r,t.sum)),e[h]+=r.d1}return e["y"===o?"_ix":"_iy"]+=d,e[h]-=g,n}}const p=Math.min,f=Math.max;function m(t,e){const n=+e[t.key],i=n*t.ratio;return e._normalized=i,{min:p(t.min,n),max:f(t.max,n),sum:t.sum+n,nmin:p(t.nmin,i),nmax:f(t.nmax,i),nsum:t.nsum+i}}function x(t,e,n){t._arr.push(e),function(t,e){Object.assign(t,e)}(t,n)}class y{constructor(t,e){const n=this;n.key=t,n.ratio=e,n.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){x(this,t,m(this,t))}pushIf(t,e,...n){const i=m(this,t);if(!e((r=this,{min:r.min,max:r.max,sum:r.sum,nmin:r.nmin,nmax:r.nmax,nsum:r.nsum}),i,n))return t;var r;x(this,t,i)}get(){return this._arr}}function v(t,e,n){if(0===t.sum)return!0;const[i]=n,r=t.nsum*t.nsum,o=e.nsum*e.nsum,s=i*i,a=Math.max(s*t.nmax/r,r/(s*t.nmin));return Math.max(s*e.nmax/o,o/(s*e.nmin))<=a}function b(t,e,n,r,o,l){t=t||[];const u=[],d=new g(e),c=new y("value",d.area/h(t,n));let p=d.side;const f=t.length;let m,x;if(!f)return u;const b=t.slice();n=s(b,n),a(b,n);const w=t=>r&&b[t][r];for(m=0;m<f;++m)x={value:(_=m,n?+b[_][n]:+b[_]),groupSum:l,_data:t[b[m]._idx]},r&&(x.level=o,x.group=w(m)),x=c.pushIf(x,v,p),x&&(u.push(d.map(c)),p=d.side,c.reset(),c.push(x));var _;return c.length&&u.push(d.map(c)),i(u)}function w(t,e){if(!e)return!1;const n=t.width||t.w,i=t.height||t.h,r=2*e.lineHeight;return n>r&&i>r}function _(t,e,n,i,r){if(t.save(),t.fillStyle=i.color,t.font=i.font.string,t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.clip(),"l"in n&&n.l!==r){if(i.groupLabels){t.textAlign=i.rtl?"end":"start",t.textBaseline="top";const r=i.rtl?e.x+e.width-i.borderWidth-3:e.x+i.borderWidth+3;t.fillText(n.g,r,e.y+i.borderWidth+3)}}else t.textAlign="center",t.textBaseline="middle",function(t,e,n){const i=n.options,r=i.font.lineHeight,o=(i.label||e.g+"\n"+e.v).split("\n"),s=n.y+n.height/2-o.length*r/4;o.forEach(((e,i)=>t.fillText(e,n.x+n.width/2,s+i*r)))}(t,n,e);t.restore()}function D(t,e){const n=e.options,i=e.width||e.w,r=e.height||e.h;if(t.save(),t.strokeStyle=n.dividerColor||"black",t.lineCap=n.dividerCapStyle,t.setLineDash(n.dividerDash||[]),t.lineDashOffset=n.dividerDashOffset,t.lineWidth=n.dividerWidth,t.beginPath(),i>r){const n=i/2;t.moveTo(e.x+n,e.y),t.lineTo(e.x+n,e.y+r)}else{const n=r/2;t.moveTo(e.x,e.y+n),t.lineTo(e.x+i,e.y+n)}t.stroke(),t.restore()}class O extends e.DatasetController{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const e=this,i=e.getMeta(),o=e.getDataset(),s=o.groups||(o.groups=[]),a=n.toFont(o.font,e.chart.options.font),h=e.chart.chartArea,l=o.key||"",u=!!o.rtl,d={x:h.left,y:h.top,w:h.right-h.left,h:h.bottom-h.top,rtl:u};var c,g;"reset"!==t&&(c=e._rect,g=d,c&&g&&c.x===g.x&&c.y===g.y&&c.w===g.w&&c.h===g.h)&&e._key===l&&!function(t,e){let n,i;if(t.lenght!==e.length)return!0;for(n=0,i=t.length;n<i;++n)if(t[n]!==e[n])return!0;return!1}(e._groups,s)||(e._rect=d,e._groups=s.slice(),e._key=l,o.data=function(t,e,i){const o=t.key||"";let s=t.tree||[];const a=t.groups||[],h=a.length,l=(t.spacing||0)+(t.borderWidth||0);return!s.length&&t.data.length&&(s=t.tree=t.data),h?function e(u,d,c,g){const p=a[u],f=u>0&&a[u-1],m=b(r(s,p,o,f,c),d,o,p,u,g),x=m.slice();let y;return u<h-1&&m.forEach((r=>{y={x:r.x+l,y:r.y+l,w:r.w-2*l,h:r.h-2*l},n.valueOrDefault(t.groupLabels,!0)&&w(r,i)&&(y.y+=i.lineHeight,y.h-=i.lineHeight),x.push(...e(u+1,y,r.g,r.s))})),x}(0,e):b(s,e,o)}(o,d,a),e._dataCheck(),e._resyncElements()),e.updateElements(i.data,0,i.data.length,t)}resolveDataElementOptions(t,e){const i=super.resolveDataElementOptions(t,e),r=Object.isFrozen(i)?Object.assign({},i):i;return r.font=n.toFont(i.font,this.chart.options.font),r}updateElements(t,e,n,i){const r=this,o="reset"===i,s=r.getDataset(),a=r._rect.options=r.resolveDataElementOptions(e,i),h=r.getSharedOptions(a),l=r.includeOptions(i,h);for(let a=e;a<e+n;a++){const e=s.data[a],n=h||r.resolveDataElementOptions(a,i),u=o?0:e.h-2*n.spacing,d=o?0:e.w-2*n.spacing,c={x:e.x+n.spacing,y:e.y+n.spacing,width:d,height:u};l&&(c.options=n),r.updateElement(t[a],a,c,i)}r.updateSharedOptions(h,i,a)}_drawDividers(t,e,n){for(let i=0,r=n.length;i<r;++i){const r=n[i],o=e[i];r.options.groupDividers&&o._data.children.length>1&&D(t,r)}this.getDataset().groupDividers&&D(t,this._rect)}_drawRects(t,e,n,i){for(let r=0,o=n.length;r<o;++r){const o=n[r],s=e[r];if(!o.hidden){o.draw(t);const e=o.options;w(o,e.font)&&s.g&&_(t,o,s,e,i)}}}draw(){const t=this,e=t.chart.ctx,n=t.getMeta().data||[],i=t.getDataset(),r=(i.groups||[]).length-1,o=i.data||[];t._drawRects(e,o,n,r),t._drawDividers(e,o,n)}}function C(t,e){const{x:n,y:i,width:r,height:o}=t.getProps(["x","y","width","height"],e);return{left:n,top:i,right:n+r,bottom:i+o}}function k(t,e,n){return Math.max(Math.min(t,n),e)}function j(t){const e=C(t),n=e.right-e.left,i=e.bottom-e.top,r=function(t,e,n){let i,r,s,a;return o(t)?(i=+t.top||0,r=+t.right||0,s=+t.bottom||0,a=+t.left||0):i=r=s=a=+t||0,{t:k(i,0,n),r:k(r,0,e),b:k(s,0,n),l:k(a,0,e)}}(t.options.borderWidth,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+r.l,y:e.top+r.t,w:n-r.l-r.r,h:i-r.t-r.b}}}function E(t,e,n,i){const r=null===e,o=null===n,s=!(!t||r&&o)&&C(t,i);return s&&(r||e>=s.left&&e<=s.right)&&(o||n>=s.top&&n<=s.bottom)}O.id="treemap",O.defaults={dataElementType:"treemap",groupLabels:!0,borderWidth:0,spacing:.5,groupDividers:!1,dividerWidth:1},O.overrides={interaction:{mode:"point",intersect:!0},hover:{},plugins:{tooltip:{position:"treemap",intersect:!0,callbacks:{title(t){if(t.length){return t[0].dataset.key||""}return""},label(t){const e=t.dataset,n=e.data[t.dataIndex],i=n.g||e.label;return(i?i+": ":"")+n.v}}}},scales:{x:{type:"linear",display:!1},y:{type:"linear",display:!1}}};class S extends e.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:n,outer:i}=j(this);t.save(),i.w!==n.w||i.h!==n.h?(t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.rect(n.x,n.y,n.w,n.h),t.fillStyle=e.backgroundColor,t.fill(),t.fillStyle=e.borderColor,t.fill("evenodd")):(t.fillStyle=e.backgroundColor,t.fillRect(n.x,n.y,n.w,n.h)),t.restore()}inRange(t,e,n){return E(this,t,e,n)}inXRange(t,e){return E(this,t,null,e)}inYRange(t,e){return E(this,null,t,e)}getCenterPoint(t){const{x:e,y:n,width:i,height:r}=this.getProps(["x","y","width","height"],t);return{x:e+i/2,y:n+r/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}S.id="treemap",S.defaults={borderSkipped:void 0,borderWidth:void 0,color:void 0,dividerCapStyle:"butt",dividerColor:"black",dividerDash:void 0,dividerDashOffset:0,dividerWidth:0,font:{},groupDividers:!1,groupLabels:void 0,spacing:void 0,label:void 0,rtl:void 0},S.defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"},e.Chart.register(O,S);e.Chart.registry.plugins.get("tooltip").positioners.treemap=function(t){if(!t.length)return!1;return t[t.length-1].element.tooltipPosition()},t.flatten=i,t.group=r,t.index=s,t.isObject=o,t.sort=a,t.sum=h,Object.defineProperty(t,"__esModule",{value:!0})})); |
{ | ||
"name": "chartjs-chart-treemap", | ||
"version": "1.0.0-beta.3", | ||
"version": "1.0.0-beta.4", | ||
"description": "Chart.js module for creating treemap charts", | ||
@@ -10,6 +10,8 @@ "main": "dist/chartjs-chart-treemap.js", | ||
"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" | ||
"dev": "karma start --no-signle-run --auto-watch --browsers chrome", | ||
"lint": "eslint --ignore-path .gitignore .", | ||
"lint-fix": "eslint --ignore-path .gitignore --fix .", | ||
"test": "cross-env NODE_ENV=test concurrently \"npm:test-*\"", | ||
"test-lint": "npm run lint", | ||
"test-karma": "karma start --auto-watch --single-run" | ||
}, | ||
@@ -35,30 +37,33 @@ "repository": { | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^16.0.0", | ||
"@rollup/plugin-node-resolve": "^10.0.0", | ||
"chart.js": "^3.0.0-beta.6", | ||
"eslint": "^7.13.0", | ||
"eslint-config-chartjs": "^0.2.0", | ||
"eslint-config-esnext": "^4.1.0", | ||
"eslint-plugin-html": "^6.1.1", | ||
"jasmine": "^3.6.3", | ||
"jasmine-core": "^3.6.0", | ||
"karma": "^5.2.3", | ||
"@rollup/plugin-commonjs": "^17.1.0", | ||
"@rollup/plugin-node-resolve": "^11.2.0", | ||
"chart.js": "^3.0.0-rc.2", | ||
"chartjs-adapter-date-fns": "^1.1.0-beta.1", | ||
"chartjs-test-utils": "^0.2.2", | ||
"concurrently": "^6.0.0", | ||
"cross-env": "^7.0.3", | ||
"date-fns": "^2.19.0", | ||
"eslint": "^7.22.0", | ||
"eslint-config-chartjs": "^0.3.0", | ||
"eslint-plugin-es": "^4.1.0", | ||
"eslint-plugin-html": "^6.1.2", | ||
"jasmine-core": "^3.7.1", | ||
"karma": "^6.2.0", | ||
"karma-chrome-launcher": "^3.1.0", | ||
"karma-coverage-istanbul-instrumenter": "^1.0.3", | ||
"karma-coverage-istanbul-reporter": "^3.0.3", | ||
"karma-coverage": "^2.0.3", | ||
"karma-firefox-launcher": "^2.1.0", | ||
"karma-jasmine": "^4.0.1", | ||
"karma-jasmine-html-reporter": "^1.5.4", | ||
"karma-rollup-preprocessor": "^7.0.5", | ||
"karma-rollup-preprocessor": "^7.0.7", | ||
"karma-spec-reporter": "0.0.32", | ||
"karma-summary-reporter": "^1.9.0", | ||
"minimist": "^1.2.5", | ||
"karma-summary-reporter": "^2.0.2", | ||
"pixelmatch": "^5.2.1", | ||
"rollup": "^2.33.2", | ||
"rollup": "^2.42.1", | ||
"rollup-plugin-istanbul": "^3.0.0", | ||
"rollup-plugin-terser": "^7.0.2" | ||
}, | ||
"peerDependencies": { | ||
"chart.js": "^3.0.0-beta.3" | ||
"chart.js": "^3.0.0-beta.10" | ||
}, | ||
"dependencies": {} | ||
} |
# chartjs-chart-treemap | ||
[Chart.js](https://www.chartjs.org/) **v3.0.0-beta.3** module for creating treemap charts. Implementation for Chart.js v2 is in [2.x branch](https://github.com/kurkle/chartjs-chart-treemap/tree/2.x) | ||
[Chart.js](https://www.chartjs.org/) **v3.0.0-beta.10** module for creating treemap charts. Implementation for Chart.js v2 is in [2.x branch](https://github.com/kurkle/chartjs-chart-treemap/tree/2.x) | ||
![npm](https://img.shields.io/npm/v/chartjs-chart-treemap.svg) [![release](https://img.shields.io/github/release/kurkle/chartjs-chart-treemap.svg?style=flat-square)](https://github.com/kurkle/chartjs-chart-treemap/releases/latest) [![travis](https://img.shields.io/travis/kurkle/chartjs-chart-treemap.svg?style=flat-square&maxAge=60)](https://travis-ci.org/kurkle/chartjs-chart-treemap) ![npm bundle size](https://img.shields.io/bundlephobia/min/chartjs-chart-treemap.svg) ![GitHub](https://img.shields.io/github/license/kurkle/chartjs-chart-treemap.svg) | ||
[![npm](https://img.shields.io/npm/v/chartjs-chart-treemap.svg)](https://www.npmjs.com/package/chartjs-chart-matrix) | ||
[![release](https://img.shields.io/github/release/kurkle/chartjs-chart-treemap.svg?style=flat-square)](https://github.com/kurkle/chartjs-chart-treemap/releases/latest) | ||
![npm bundle size](https://img.shields.io/bundlephobia/min/chartjs-chart-treemap.svg) | ||
![GitHub](https://img.shields.io/github/license/kurkle/chartjs-chart-treemap.svg) | ||
@@ -83,9 +86,7 @@ ## Documentation | ||
```bash | ||
> gulp build // build dist files | ||
> gulp build --watch // build and watch for changes | ||
> gulp test // run all tests | ||
> gulp test --watch // run all tests and watch for changes | ||
> gulp test --coverage // run all tests and generate code coverage | ||
> gulp lint // perform code linting | ||
> gulp package // create an archive with dist files and samples | ||
> npm run build // build dist files | ||
> npm run dev // build and watch for changes | ||
> npm test // run all tests | ||
> npm run lint // perform code linting | ||
> npm package // create an archive with dist files and samples | ||
``` | ||
@@ -95,2 +96,2 @@ | ||
chartjs-chart-treemap is available under the [MIT license](https://opensource.org/licenses/MIT). | ||
chartjs-chart-treemap is available under the [MIT license](https://opensource.org/licenses/MIT). |
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
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
53263
96
26
1432