chartjs-chart-sankey
Advanced tools
Comparing version 0.5.0 to 0.6.0
/*! | ||
* chartjs-chart-sankey v0.5.0 | ||
* chartjs-chart-sankey v0.6.0 | ||
* https://github.com/kurkle/chartjs-chart-sankey#readme | ||
@@ -137,10 +137,2 @@ * (c) 2021 Jukka Kurkela | ||
function maxRows(nodeArray, maxX) { | ||
let max = 0; | ||
for (let i = 0; i <= maxX; i++) { | ||
max = Math.max(max, nodeArray.filter(n => n.x === i).length); | ||
} | ||
return max; | ||
} | ||
function addPadding(nodeArray, padding) { | ||
@@ -150,2 +142,3 @@ let i = 1; | ||
let prev = 0; | ||
let maxY = 0; | ||
const rows = []; | ||
@@ -170,5 +163,7 @@ nodeArray.sort(nodeByXY).forEach(node => { | ||
node.y += i * padding; | ||
maxY = Math.max(maxY, node.y + Math.max(node.in, node.out)); | ||
i++; | ||
} | ||
}); | ||
return maxY; | ||
} | ||
@@ -195,9 +190,8 @@ | ||
const maxY = priority ? calculateYUsingPriority(nodeArray, maxX) : calculateY(nodeArray, maxX); | ||
const rows = maxRows(nodeArray, maxX); | ||
const padding = maxY * 0.03; // rows; | ||
addPadding(nodeArray, padding); | ||
const maxYWithPadding = addPadding(nodeArray, padding); | ||
sortFlows(nodeArray); | ||
return {maxX, maxY: maxY + rows * padding}; | ||
return {maxX, maxY: maxYWithPadding}; | ||
} | ||
@@ -280,4 +274,4 @@ | ||
xScale.options.max = maxX; | ||
yScale.options.max = maxY; | ||
me._maxX = maxX; | ||
me._maxY = maxY; | ||
@@ -305,2 +299,10 @@ for (let i = 0, ilen = data.length; i < ilen; ++i) { | ||
getMinMax(scale) { | ||
const me = this; | ||
return { | ||
min: 0, | ||
max: scale === this._cachedMeta.xScale ? this._maxX : me._maxY | ||
}; | ||
} | ||
update(mode) { | ||
@@ -496,12 +498,14 @@ const me = this; | ||
type: 'linear', | ||
bounds: 'data', | ||
display: false, | ||
min: 0, | ||
offset: true | ||
offset: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
bounds: 'data', | ||
display: false, | ||
min: 0, | ||
reverse: true, | ||
offset: true | ||
offset: false | ||
} | ||
@@ -511,3 +515,6 @@ }, | ||
padding: { | ||
right: 10 | ||
top: 3, | ||
left: 3, | ||
right: 13, | ||
bottom: 3 | ||
} | ||
@@ -561,5 +568,14 @@ } | ||
const fill = ctx.createLinearGradient(x, 0, x2, 0); | ||
fill.addColorStop(0, color(options.colorFrom).alpha(0.5).rgbString()); | ||
fill.addColorStop(1, color(options.colorTo).alpha(0.5).rgbString()); | ||
let fill; | ||
if (options.colorMode === 'from') { | ||
fill = color(options.colorFrom).alpha(0.5).rgbString(); | ||
} else if (options.colorMode === 'to') { | ||
fill = color(options.colorTo).alpha(0.5).rgbString(); | ||
} else { | ||
fill = ctx.createLinearGradient(x, 0, x2, 0); | ||
fill.addColorStop(0, color(options.colorFrom).alpha(0.5).rgbString()); | ||
fill.addColorStop(1, color(options.colorTo).alpha(0.5).rgbString()); | ||
} | ||
ctx.fillStyle = fill; | ||
@@ -633,5 +649,6 @@ ctx.strokeStyle = fill; | ||
colorFrom: 'red', | ||
colorTo: 'green' | ||
colorTo: 'green', | ||
colorMode: 'gradient' | ||
}; | ||
export { Flow, SankeyController }; |
/*! | ||
* chartjs-chart-sankey v0.5.0 | ||
* chartjs-chart-sankey v0.6.0 | ||
* https://github.com/kurkle/chartjs-chart-sankey#readme | ||
@@ -144,10 +144,2 @@ * (c) 2021 Jukka Kurkela | ||
function maxRows(nodeArray, maxX) { | ||
let max = 0; | ||
for (let i = 0; i <= maxX; i++) { | ||
max = Math.max(max, nodeArray.filter(n => n.x === i).length); | ||
} | ||
return max; | ||
} | ||
function addPadding(nodeArray, padding) { | ||
@@ -157,2 +149,3 @@ let i = 1; | ||
let prev = 0; | ||
let maxY = 0; | ||
const rows = []; | ||
@@ -177,5 +170,7 @@ nodeArray.sort(nodeByXY).forEach(node => { | ||
node.y += i * padding; | ||
maxY = Math.max(maxY, node.y + Math.max(node.in, node.out)); | ||
i++; | ||
} | ||
}); | ||
return maxY; | ||
} | ||
@@ -202,9 +197,8 @@ | ||
const maxY = priority ? calculateYUsingPriority(nodeArray, maxX) : calculateY(nodeArray, maxX); | ||
const rows = maxRows(nodeArray, maxX); | ||
const padding = maxY * 0.03; // rows; | ||
addPadding(nodeArray, padding); | ||
const maxYWithPadding = addPadding(nodeArray, padding); | ||
sortFlows(nodeArray); | ||
return {maxX, maxY: maxY + rows * padding}; | ||
return {maxX, maxY: maxYWithPadding}; | ||
} | ||
@@ -287,4 +281,4 @@ | ||
xScale.options.max = maxX; | ||
yScale.options.max = maxY; | ||
me._maxX = maxX; | ||
me._maxY = maxY; | ||
@@ -312,2 +306,10 @@ for (let i = 0, ilen = data.length; i < ilen; ++i) { | ||
getMinMax(scale) { | ||
const me = this; | ||
return { | ||
min: 0, | ||
max: scale === this._cachedMeta.xScale ? this._maxX : me._maxY | ||
}; | ||
} | ||
update(mode) { | ||
@@ -503,12 +505,14 @@ const me = this; | ||
type: 'linear', | ||
bounds: 'data', | ||
display: false, | ||
min: 0, | ||
offset: true | ||
offset: false | ||
}, | ||
y: { | ||
type: 'linear', | ||
bounds: 'data', | ||
display: false, | ||
min: 0, | ||
reverse: true, | ||
offset: true | ||
offset: false | ||
} | ||
@@ -518,3 +522,6 @@ }, | ||
padding: { | ||
right: 10 | ||
top: 3, | ||
left: 3, | ||
right: 13, | ||
bottom: 3 | ||
} | ||
@@ -568,5 +575,14 @@ } | ||
const fill = ctx.createLinearGradient(x, 0, x2, 0); | ||
fill.addColorStop(0, helpers.color(options.colorFrom).alpha(0.5).rgbString()); | ||
fill.addColorStop(1, helpers.color(options.colorTo).alpha(0.5).rgbString()); | ||
let fill; | ||
if (options.colorMode === 'from') { | ||
fill = helpers.color(options.colorFrom).alpha(0.5).rgbString(); | ||
} else if (options.colorMode === 'to') { | ||
fill = helpers.color(options.colorTo).alpha(0.5).rgbString(); | ||
} else { | ||
fill = ctx.createLinearGradient(x, 0, x2, 0); | ||
fill.addColorStop(0, helpers.color(options.colorFrom).alpha(0.5).rgbString()); | ||
fill.addColorStop(1, helpers.color(options.colorTo).alpha(0.5).rgbString()); | ||
} | ||
ctx.fillStyle = fill; | ||
@@ -640,3 +656,4 @@ ctx.strokeStyle = fill; | ||
colorFrom: 'red', | ||
colorTo: 'green' | ||
colorTo: 'green', | ||
colorMode: 'gradient' | ||
}; | ||
@@ -643,0 +660,0 @@ |
/*! | ||
* chartjs-chart-sankey v0.5.0 | ||
* chartjs-chart-sankey v0.6.0 | ||
* https://github.com/kurkle/chartjs-chart-sankey#readme | ||
@@ -7,2 +7,2 @@ * (c) 2021 Jukka Kurkela | ||
*/ | ||
!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 o(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var r=o(t);function a(t,e){const o=t.filter((t=>!e.has(t)));return o.length?o:t.slice(0,1)}const n=t=>void 0!==t,s=(t,e)=>t.x!==e.x?t.x-e.x:t.y-e.y,i=(t,e)=>t.reduce(((t,o)=>t+o.node[e].length+i(o.node[e],e)),0),l=t=>(e,o)=>i(e.node[t],t)-i(o.node[t],t);function c(t,e){return t.from.sort(l("from")).forEach((t=>{const o=t.node;n(o.y)||(o.y=e,e=Math.max(e+o.out,c(o,e)))})),e}function f(t,e){return t.to.sort(l("to")).forEach((t=>{const o=t.node;n(o.y)||(o.y=e,e=Math.max(e+o.in,f(o,e)))})),e}function h(t,e){return n(t.y)?t.y:(t.y=e,e)}function d(t,e){const o=function(t){return t.sort(((t,e)=>Math.max(e.in,e.out)-Math.max(t.in,t.out)))[0]}(t);o.y=0;const r=c(o,0),a=f(o,0),s=function(t,e){const o=t.filter((t=>0===t.x)),r=t.filter((t=>t.x===e)),a=o.filter((t=>!n(t.y))),s=r.filter((t=>!n(t.y)));let i=o.reduce(((t,e)=>Math.max(t,e.y+e.out||0)),0),l=r.reduce(((t,e)=>Math.max(t,e.y+e.in||0)),0);return i>=l?(a.forEach((t=>{i=h(t,i),i=Math.max(i+t.out,f(t,i))})),s.forEach((t=>{l=h(t,l),l=Math.max(l+t.in,f(t,l))}))):(s.forEach((t=>{l=h(t,l),l=Math.max(l+t.in,f(t,l))})),a.forEach((t=>{i=h(t,i),i=Math.max(i+t.out,f(t,i))}))),Math.max(i,l)}(t,e);return Math.max(r,a,s)}function u(t,e,o){const r=[...t.values()],n=function(t,e){const o=new Set(e.map((t=>t.to))),r=new Set(e.map((t=>t.from))),n=new Set([...t.keys()]);let s=0;for(;n.size;){const r=a([...n],o);for(let e=0;e<r.length;e++)t.get(r[e]).x=s,n.delete(r[e]);n.size&&(o.clear(),e.filter((t=>n.has(t.from))).forEach((t=>o.add(t.to))),s++)}return[...t.keys()].filter((t=>!r.has(t))).forEach((e=>{t.get(e).x=s})),s}(t,e),i=o?function(t,e){let o=0;for(let r=0;r<=e;r++){let e=0;const a=t.filter((t=>t.x===r)).sort(((t,e)=>t.priority-e.priority));for(const t of a)t.y=e,e+=Math.max(t.out,t.in);o=Math.max(e,o)}return o}(r,n):d(r,n),l=function(t,e){let o=0;for(let r=0;r<=e;r++)o=Math.max(o,t.filter((t=>t.x===r)).length);return o}(r,n),c=.03*i;return function(t,e){let o=1,r=0,a=0;const n=[];t.sort(s).forEach((t=>{if(t.y){if(0===t.x)n.push(t.y);else{for(r!==t.x&&(r=t.x,a=0),o=a+1;o<n.length&&!(n[o]>t.y);o++);a=o}t.y+=o*e,o++}}))}(r,c),function(t){t.forEach((t=>{let e=0;t.from.sort(((t,e)=>t.node.y+t.node.out/2-(e.node.y+e.node.out/2))).forEach((t=>{t.addY=e,e+=t.flow})),e=0,t.to.sort(((t,e)=>t.node.y+t.node.in/2-(e.node.y+e.node.in/2))).forEach((t=>{t.addY=e,e+=t.flow}))}))}(r),{maxX:n,maxY:i+l*c}}function y(t,e){for(let o=0;o<t.length;o++)if(t[o].key===e)return t[o].addY;return 0}class x extends t.DatasetController{parseObjectData(t,e,o,r){if(0===r)return[];const{xScale:a,yScale:n}=t,s=[],i=this._nodes=function(t){const e=new Map;for(let o=0;o<t.length;o++){const r=t[o];if(e.has(r.from)){const t=e.get(r.from);t.out+=r.flow,t.to.push({key:r.to,flow:r.flow})}else e.set(r.from,{key:r.from,in:0,out:r.flow,from:[],to:[{key:r.to,flow:r.flow}]});if(e.has(r.to)){const t=e.get(r.to);t.in+=r.flow,t.from.push({key:r.from,flow:r.flow})}else e.set(r.to,{key:r.to,in:r.flow,out:0,from:[{key:r.from,flow:r.flow}],to:[]})}const o=(t,e)=>e.flow-t.flow;return[...e.values()].forEach((t=>{let r=0;t.from=t.from.sort(o),t.from.forEach((t=>{t.node=e.get(t.key),t.addY=r,r+=t.flow})),r=0,t.to=t.to.sort(o),t.to.forEach((t=>{t.node=e.get(t.key),t.addY=r,r+=t.flow}))})),e}(e),l=this.getDataset().priority;if(l)for(const t of i.values())t.key in l&&(t.priority=l[t.key]);const{maxX:c,maxY:f}=u(i,e,!!l);a.options.max=c,n.options.max=f;for(let t=0,o=e.length;t<o;++t){const o=e[t],r=i.get(o.from),l=i.get(o.to),c=r.y+y(r.to,o.to),f=l.y+y(l.from,o.from);s.push({x:a.parse(r.x,t),y:n.parse(c,t),_custom:{from:r,to:l,x:a.parse(l.x,t),y:n.parse(f,t),height:n.parse(o.flow,t)}})}return s.slice(o,o+r)}update(t){const e=this._cachedMeta;this.updateElements(e.data,0,e.data.length,t)}updateElements(t,o,r,a){const n=this,{xScale:s,yScale:i}=n._cachedMeta,l=n.resolveDataElementOptions(o,a),c=n.getSharedOptions(a,t[o],l),f=n.getDataset(),h=e.valueOrDefault(f.borderWidth,1)/2+.5,d=e.valueOrDefault(f.nodeWidth,10);for(let e=o;e<o+r;e++){const o=n.getParsed(e),r=o._custom,l=i.getPixelForValue(o.y);n.updateElement(t[e],e,{x:s.getPixelForValue(o.x)+d+h,y:l,x2:s.getPixelForValue(r.x)-h,y2:i.getPixelForValue(r.y),from:r.from,to:r.to,progress:"reset"===a?0:1,height:Math.abs(i.getPixelForValue(o.y+r.height)-l),options:n.resolveDataElementOptions(e,a)},a)}n.updateSharedOptions(c,a)}_drawLabels(){const t=this,o=t._ctx,r=t._nodes||new Map,a=t.getDataset(),n=e.valueOrDefault(a.borderWidth,1),s=e.valueOrDefault(a.nodeWidth,10),i=a.labels,{xScale:l,yScale:c}=t._cachedMeta;o.save();const f=t.chart.chartArea;for(const t of r.values()){const e=l.getPixelForValue(t.x),r=c.getPixelForValue(t.y),h=Math.max(t.in,t.out),d=Math.abs(c.getPixelForValue(t.y+h)-r),u=i&&i[t.key]||t.key;let y=e;o.fillStyle=a.color||"black",o.textBaseline="middle",e<f.width/2?(o.textAlign="left",y+=s+n+4):(o.textAlign="right",y-=n+4),o.fillText(u,y,r+d/2)}o.restore()}_drawNodes(){const t=this,o=t._ctx,r=t._nodes||new Map,a=t.getDataset(),{xScale:n,yScale:s}=t._cachedMeta,i=e.valueOrDefault(a.borderWidth,1),l=e.valueOrDefault(a.nodeWidth,10);o.save(),o.strokeStyle=a.borderColor||"black",o.lineWidth=i;for(const t of r.values()){o.fillStyle=t.color;const e=n.getPixelForValue(t.x),r=s.getPixelForValue(t.y),a=Math.max(t.in,t.out),c=Math.abs(s.getPixelForValue(t.y+a)-r);i&&o.strokeRect(e,r,l,c),o.fillRect(e,r,l,c)}o.restore()}draw(){const t=this,e=t._ctx,o=t.getMeta().data||[];for(let t=0,e=o.length;t<e;++t){const e=o[t];e.from.color=e.options.colorFrom,e.to.color=e.options.colorTo}t._drawNodes();for(let t=0,r=o.length;t<r;++t)o[t].draw(e);t._drawLabels()}}x.id="sankey",x.defaults={dataElementType:"flow",animations:{numbers:{type:"number",properties:["x","y","x2","y2","height"]},progress:{easing:"linear",duration:t=>"data"===t.type?200*(t.parsed._custom.x-t.parsed.x):void 0,delay:t=>"data"===t.type?500*t.parsed.x+20*t.dataIndex:void 0},colors:{type:"color",properties:["colorFrom","colorTo"]}},transitions:{hide:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],to:"transparent"}}},show:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],from:"transparent"}}}}},x.overrides={interaction:{mode:"nearest",intersect:!0},datasets:{color:()=>"#efefef",clip:!1,parsing:!0},plugins:{tooltip:{callbacks:{title:()=>"",label(t){const e=t.dataset.data[t.dataIndex];return e.from+" -> "+e.to+": "+e.flow}}},legend:{display:!1}},scales:{x:{type:"linear",display:!1,min:0,offset:!0},y:{type:"linear",display:!1,min:0,reverse:!0,offset:!0}},layout:{padding:{right:10}}};const p=(t,e,o,r)=>t<o?{cp1:{x:t+(o-t)/3*2,y:e},cp2:{x:t+(o-t)/3,y:r}}:{cp1:{x:t-(t-o)/3,y:0},cp2:{x:o+(t-o)/3,y:0}},g=(t,e,o)=>({x:t.x+o*(e.x-t.x),y:t.y+o*(e.y-t.y)});class m extends t.Element{constructor(t){super(),this.options=void 0,this.x=void 0,this.y=void 0,this.x2=void 0,this.y2=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const{x:o,x2:r,y:a,y2:n,height:s,progress:i}=this,{cp1:l,cp2:c}=p(o,a,r,n),f=this.options;if(0===i)return;t.save(),i<1&&(t.beginPath(),t.rect(o,Math.min(a,n),(r-o)*i+1,Math.abs(n-a)+s+1),t.clip());const h=t.createLinearGradient(o,0,r,0);h.addColorStop(0,e.color(f.colorFrom).alpha(.5).rgbString()),h.addColorStop(1,e.color(f.colorTo).alpha(.5).rgbString()),t.fillStyle=h,t.strokeStyle=h,t.lineWidth=.5,t.beginPath(),t.moveTo(o,a),t.bezierCurveTo(l.x,l.y,c.x,c.y,r,n),t.lineTo(r,n+s),t.bezierCurveTo(c.x,c.y+s,l.x,l.y+s,o,a+s),t.lineTo(o,a),t.stroke(),t.closePath(),t.fill(),t.restore()}inRange(t,e,o){const{x:r,y:a,x2:n,y2:s,height:i}=this.getProps(["x","y","x2","y2","height"],o);if(t<r||t>n)return!1;const{cp1:l,cp2:c}=p(r,a,n,s),f=(t-r)/(n-r),h={x:n,y:s},d=g({x:r,y:a},l,f),u=g(l,c,f),y=g(c,h,f),x=g(d,u,f),m=g(u,y,f),w=g(x,m,f).y;return e>=w&&e<=w+i}inXRange(t,e){const{x:o,x2:r}=this.getProps(["x","x2"],e);return t>=o&&t<=r}inYRange(t,e){const{y:o,y2:r,height:a}=this.getProps(["y","y2","height"],e),n=Math.min(o,r),s=Math.max(o,r)+a;return t>=n&&t<=s}getCenterPoint(t){const{x:e,y:o,x2:r,y2:a,height:n}=this.getProps(["x","y","x2","y2","height"],t);return{x:(e+r)/2,y:(o+a+n)/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}m.id="flow",m.defaults={colorFrom:"red",colorTo:"green"},r.default.register(x,m)})); | ||
!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 o(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var r=o(t);function a(t,e){const o=t.filter((t=>!e.has(t)));return o.length?o:t.slice(0,1)}const n=t=>void 0!==t,s=(t,e)=>t.x!==e.x?t.x-e.x:t.y-e.y,l=(t,e)=>t.reduce(((t,o)=>t+o.node[e].length+l(o.node[e],e)),0),i=t=>(e,o)=>l(e.node[t],t)-l(o.node[t],t);function c(t,e){return t.from.sort(i("from")).forEach((t=>{const o=t.node;n(o.y)||(o.y=e,e=Math.max(e+o.out,c(o,e)))})),e}function f(t,e){return t.to.sort(i("to")).forEach((t=>{const o=t.node;n(o.y)||(o.y=e,e=Math.max(e+o.in,f(o,e)))})),e}function h(t,e){return n(t.y)?t.y:(t.y=e,e)}function d(t,e){const o=function(t){return t.sort(((t,e)=>Math.max(e.in,e.out)-Math.max(t.in,t.out)))[0]}(t);o.y=0;const r=c(o,0),a=f(o,0),s=function(t,e){const o=t.filter((t=>0===t.x)),r=t.filter((t=>t.x===e)),a=o.filter((t=>!n(t.y))),s=r.filter((t=>!n(t.y)));let l=o.reduce(((t,e)=>Math.max(t,e.y+e.out||0)),0),i=r.reduce(((t,e)=>Math.max(t,e.y+e.in||0)),0);return l>=i?(a.forEach((t=>{l=h(t,l),l=Math.max(l+t.out,f(t,l))})),s.forEach((t=>{i=h(t,i),i=Math.max(i+t.in,f(t,i))}))):(s.forEach((t=>{i=h(t,i),i=Math.max(i+t.in,f(t,i))})),a.forEach((t=>{l=h(t,l),l=Math.max(l+t.out,f(t,l))}))),Math.max(l,i)}(t,e);return Math.max(r,a,s)}function u(t,e,o){const r=[...t.values()],n=function(t,e){const o=new Set(e.map((t=>t.to))),r=new Set(e.map((t=>t.from))),n=new Set([...t.keys()]);let s=0;for(;n.size;){const r=a([...n],o);for(let e=0;e<r.length;e++)t.get(r[e]).x=s,n.delete(r[e]);n.size&&(o.clear(),e.filter((t=>n.has(t.from))).forEach((t=>o.add(t.to))),s++)}return[...t.keys()].filter((t=>!r.has(t))).forEach((e=>{t.get(e).x=s})),s}(t,e),l=o?function(t,e){let o=0;for(let r=0;r<=e;r++){let e=0;const a=t.filter((t=>t.x===r)).sort(((t,e)=>t.priority-e.priority));for(const t of a)t.y=e,e+=Math.max(t.out,t.in);o=Math.max(e,o)}return o}(r,n):d(r,n),i=function(t,e){let o=1,r=0,a=0,n=0;const l=[];return t.sort(s).forEach((t=>{if(t.y){if(0===t.x)l.push(t.y);else{for(r!==t.x&&(r=t.x,a=0),o=a+1;o<l.length&&!(l[o]>t.y);o++);a=o}t.y+=o*e,n=Math.max(n,t.y+Math.max(t.in,t.out)),o++}})),n}(r,.03*l);return function(t){t.forEach((t=>{let e=0;t.from.sort(((t,e)=>t.node.y+t.node.out/2-(e.node.y+e.node.out/2))).forEach((t=>{t.addY=e,e+=t.flow})),e=0,t.to.sort(((t,e)=>t.node.y+t.node.in/2-(e.node.y+e.node.in/2))).forEach((t=>{t.addY=e,e+=t.flow}))}))}(r),{maxX:n,maxY:i}}function y(t,e){for(let o=0;o<t.length;o++)if(t[o].key===e)return t[o].addY;return 0}class x extends t.DatasetController{parseObjectData(t,e,o,r){if(0===r)return[];const a=this,{xScale:n,yScale:s}=t,l=[],i=a._nodes=function(t){const e=new Map;for(let o=0;o<t.length;o++){const r=t[o];if(e.has(r.from)){const t=e.get(r.from);t.out+=r.flow,t.to.push({key:r.to,flow:r.flow})}else e.set(r.from,{key:r.from,in:0,out:r.flow,from:[],to:[{key:r.to,flow:r.flow}]});if(e.has(r.to)){const t=e.get(r.to);t.in+=r.flow,t.from.push({key:r.from,flow:r.flow})}else e.set(r.to,{key:r.to,in:r.flow,out:0,from:[{key:r.from,flow:r.flow}],to:[]})}const o=(t,e)=>e.flow-t.flow;return[...e.values()].forEach((t=>{let r=0;t.from=t.from.sort(o),t.from.forEach((t=>{t.node=e.get(t.key),t.addY=r,r+=t.flow})),r=0,t.to=t.to.sort(o),t.to.forEach((t=>{t.node=e.get(t.key),t.addY=r,r+=t.flow}))})),e}(e),c=a.getDataset().priority;if(c)for(const t of i.values())t.key in c&&(t.priority=c[t.key]);const{maxX:f,maxY:h}=u(i,e,!!c);a._maxX=f,a._maxY=h;for(let t=0,o=e.length;t<o;++t){const o=e[t],r=i.get(o.from),a=i.get(o.to),c=r.y+y(r.to,o.to),f=a.y+y(a.from,o.from);l.push({x:n.parse(r.x,t),y:s.parse(c,t),_custom:{from:r,to:a,x:n.parse(a.x,t),y:s.parse(f,t),height:s.parse(o.flow,t)}})}return l.slice(o,o+r)}getMinMax(t){return{min:0,max:t===this._cachedMeta.xScale?this._maxX:this._maxY}}update(t){const e=this._cachedMeta;this.updateElements(e.data,0,e.data.length,t)}updateElements(t,o,r,a){const n=this,{xScale:s,yScale:l}=n._cachedMeta,i=n.resolveDataElementOptions(o,a),c=n.getSharedOptions(a,t[o],i),f=n.getDataset(),h=e.valueOrDefault(f.borderWidth,1)/2+.5,d=e.valueOrDefault(f.nodeWidth,10);for(let e=o;e<o+r;e++){const o=n.getParsed(e),r=o._custom,i=l.getPixelForValue(o.y);n.updateElement(t[e],e,{x:s.getPixelForValue(o.x)+d+h,y:i,x2:s.getPixelForValue(r.x)-h,y2:l.getPixelForValue(r.y),from:r.from,to:r.to,progress:"reset"===a?0:1,height:Math.abs(l.getPixelForValue(o.y+r.height)-i),options:n.resolveDataElementOptions(e,a)},a)}n.updateSharedOptions(c,a)}_drawLabels(){const t=this,o=t._ctx,r=t._nodes||new Map,a=t.getDataset(),n=e.valueOrDefault(a.borderWidth,1),s=e.valueOrDefault(a.nodeWidth,10),l=a.labels,{xScale:i,yScale:c}=t._cachedMeta;o.save();const f=t.chart.chartArea;for(const t of r.values()){const e=i.getPixelForValue(t.x),r=c.getPixelForValue(t.y),h=Math.max(t.in,t.out),d=Math.abs(c.getPixelForValue(t.y+h)-r),u=l&&l[t.key]||t.key;let y=e;o.fillStyle=a.color||"black",o.textBaseline="middle",e<f.width/2?(o.textAlign="left",y+=s+n+4):(o.textAlign="right",y-=n+4),o.fillText(u,y,r+d/2)}o.restore()}_drawNodes(){const t=this,o=t._ctx,r=t._nodes||new Map,a=t.getDataset(),{xScale:n,yScale:s}=t._cachedMeta,l=e.valueOrDefault(a.borderWidth,1),i=e.valueOrDefault(a.nodeWidth,10);o.save(),o.strokeStyle=a.borderColor||"black",o.lineWidth=l;for(const t of r.values()){o.fillStyle=t.color;const e=n.getPixelForValue(t.x),r=s.getPixelForValue(t.y),a=Math.max(t.in,t.out),c=Math.abs(s.getPixelForValue(t.y+a)-r);l&&o.strokeRect(e,r,i,c),o.fillRect(e,r,i,c)}o.restore()}draw(){const t=this,e=t._ctx,o=t.getMeta().data||[];for(let t=0,e=o.length;t<e;++t){const e=o[t];e.from.color=e.options.colorFrom,e.to.color=e.options.colorTo}t._drawNodes();for(let t=0,r=o.length;t<r;++t)o[t].draw(e);t._drawLabels()}}x.id="sankey",x.defaults={dataElementType:"flow",animations:{numbers:{type:"number",properties:["x","y","x2","y2","height"]},progress:{easing:"linear",duration:t=>"data"===t.type?200*(t.parsed._custom.x-t.parsed.x):void 0,delay:t=>"data"===t.type?500*t.parsed.x+20*t.dataIndex:void 0},colors:{type:"color",properties:["colorFrom","colorTo"]}},transitions:{hide:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],to:"transparent"}}},show:{animations:{colors:{type:"color",properties:["colorFrom","colorTo"],from:"transparent"}}}}},x.overrides={interaction:{mode:"nearest",intersect:!0},datasets:{color:()=>"#efefef",clip:!1,parsing:!0},plugins:{tooltip:{callbacks:{title:()=>"",label(t){const e=t.dataset.data[t.dataIndex];return e.from+" -> "+e.to+": "+e.flow}}},legend:{display:!1}},scales:{x:{type:"linear",bounds:"data",display:!1,min:0,offset:!1},y:{type:"linear",bounds:"data",display:!1,min:0,reverse:!0,offset:!1}},layout:{padding:{top:3,left:3,right:13,bottom:3}}};const p=(t,e,o,r)=>t<o?{cp1:{x:t+(o-t)/3*2,y:e},cp2:{x:t+(o-t)/3,y:r}}:{cp1:{x:t-(t-o)/3,y:0},cp2:{x:o+(t-o)/3,y:0}},m=(t,e,o)=>({x:t.x+o*(e.x-t.x),y:t.y+o*(e.y-t.y)});class g extends t.Element{constructor(t){super(),this.options=void 0,this.x=void 0,this.y=void 0,this.x2=void 0,this.y2=void 0,this.height=void 0,t&&Object.assign(this,t)}draw(t){const{x:o,x2:r,y:a,y2:n,height:s,progress:l}=this,{cp1:i,cp2:c}=p(o,a,r,n),f=this.options;if(0===l)return;let h;t.save(),l<1&&(t.beginPath(),t.rect(o,Math.min(a,n),(r-o)*l+1,Math.abs(n-a)+s+1),t.clip()),"from"===f.colorMode?h=e.color(f.colorFrom).alpha(.5).rgbString():"to"===f.colorMode?h=e.color(f.colorTo).alpha(.5).rgbString():(h=t.createLinearGradient(o,0,r,0),h.addColorStop(0,e.color(f.colorFrom).alpha(.5).rgbString()),h.addColorStop(1,e.color(f.colorTo).alpha(.5).rgbString())),t.fillStyle=h,t.strokeStyle=h,t.lineWidth=.5,t.beginPath(),t.moveTo(o,a),t.bezierCurveTo(i.x,i.y,c.x,c.y,r,n),t.lineTo(r,n+s),t.bezierCurveTo(c.x,c.y+s,i.x,i.y+s,o,a+s),t.lineTo(o,a),t.stroke(),t.closePath(),t.fill(),t.restore()}inRange(t,e,o){const{x:r,y:a,x2:n,y2:s,height:l}=this.getProps(["x","y","x2","y2","height"],o);if(t<r||t>n)return!1;const{cp1:i,cp2:c}=p(r,a,n,s),f=(t-r)/(n-r),h={x:n,y:s},d=m({x:r,y:a},i,f),u=m(i,c,f),y=m(c,h,f),x=m(d,u,f),g=m(u,y,f),M=m(x,g,f).y;return e>=M&&e<=M+l}inXRange(t,e){const{x:o,x2:r}=this.getProps(["x","x2"],e);return t>=o&&t<=r}inYRange(t,e){const{y:o,y2:r,height:a}=this.getProps(["y","y2","height"],e),n=Math.min(o,r),s=Math.max(o,r)+a;return t>=n&&t<=s}getCenterPoint(t){const{x:e,y:o,x2:r,y2:a,height:n}=this.getProps(["x","y","x2","y2","height"],t);return{x:(e+r)/2,y:(o+a+n)/2}}tooltipPosition(){return this.getCenterPoint()}getRange(t){return"x"===t?this.width/2:this.height/2}}g.id="flow",g.defaults={colorFrom:"red",colorTo:"green",colorMode:"gradient"},r.default.register(x,g)})); |
{ | ||
"name": "chartjs-chart-sankey", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Chart.js module for creating sankey diagrams", | ||
@@ -38,3 +38,3 @@ "main": "dist/chartjs-chart-sankey.js", | ||
"chartjs-adapter-date-fns": "^2.0.0", | ||
"chartjs-test-utils": "^0.2.2", | ||
"chartjs-test-utils": "^0.3.0", | ||
"concurrently": "^6.0.0", | ||
@@ -41,0 +41,0 @@ "cross-env": "^7.0.3", |
@@ -50,2 +50,3 @@ # chartjs-chart-sankey | ||
colorTo: (c) => getColor(c.dataset.data[c.dataIndex].to), | ||
colorMode: 'gradient' // or 'from' or 'to' | ||
}] | ||
@@ -52,0 +53,0 @@ }, |
48806
1189
87