chartjs-chart-sankey 0.14.4 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * chartjs-chart-sankey v0.14.4
2
+ * chartjs-chart-sankey v0.15.0
3
3
  * https://chartjs-chart-sankey.pages.dev/
4
4
  * (c) 2026 Jukka Kurkela
5
5
  * Released under the MIT license
6
6
  */
7
- !function(t,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],o):o((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-sankey"]={},t.Chart,t.Chart.helpers)}(this,function(t,o,e){"use strict";const r=t=>void 0!==t;function n(t){return t&&-1!==["min","max"].indexOf(t)?t:"max"}const s=(t,o)=>o.flow===t.flow?t.index-o.index:o.flow-t.flow;function i(t,{size:o,priority:e,column:r}){const i=new Map;for(let o=0;o<t.length;o++){const{from:e,to:r,flow:n}=t[o],s=i.get(e)??{from:[],in:0,key:e,out:0,size:0,to:[]},a=(e===r?s:i.get(r))??{from:[],in:0,key:r,out:0,size:0,to:[]};s.out+=n,s.to.push({addY:0,flow:n,index:o,key:r,node:a}),1===s.to.length&&i.set(e,s),a.in+=n,a.from.push({addY:0,flow:n,index:o,key:e,node:s}),1===a.from.length&&i.set(r,a)}return((t,o)=>{const e=n(o);for(const o of t.values())o.from.sort(s),o.to.sort(s),o.size=Math[e](o.in||o.out,o.out||o.in)})(i,o),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.priority=o[e.key])})(i,e),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.column=!0,e.x=o[e.key])})(i,r),i}const a=1e-6;function l(t){return t.x??0}function h(t){return t.y??0}const c=(t,o=new Set)=>{const e=[];for(const r of t)o.has(r.key)||(o.add(r.key),e.push(r.key,...c(r.to.map(t=>t.node),o)));return e},f=(t,o)=>{const e=o.filter(t=>0===t.from.length),r=e.map(t=>t.key),n=c(e),s=new Set(n);for(const o of t)s.has(o.from)||s.has(o.to)||(r.push(o.from),s.add(o.from)),s.add(o.to);return r},d=(t,o)=>{const e=new Set(t.filter(t=>o.has(t.from)).map(t=>t.to)),r=[...o],n=r.filter(t=>!e.has(t));return n.length?n:r.slice(0,1)};let u=-1;function p(t,o,e=function(){return u=u<100?u+1:0,u}()){let r=0;for(const n of t)n.node._visited!==e&&(n.node._visited=e,r+=n.node[o].length+p(n.node[o],o,e));return r}const x=t=>(o,e)=>p(o.node[t],t)-p(e.node[t],t)||o.node[t].length-e.node[t].length;function m(t,o){if(!t.from.length)return o;t.from.sort(x("from"));for(const e of t.from){const t=e.node;r(t.y)||(t.y=o,m(t,o?o+a:0)),o=Math.max(t.y+t.out,o)}return h(t)+t.size}function y(t,o){if(!t.to.length)return o;t.to.sort(x("to"));for(const e of t.to){const t=e.node;r(t.y)||(t.y=o,y(t,o?o+a:0)),o=Math.max(t.y+Math.max(t.in,t.out),o)}return h(t)+t.size}function g(t,o){return r(t.y)?t.y:(t.y=o,o)}function w(t,o){if(!t.length)return 0;const e=((t,o)=>{const e=[...t].sort((t,o)=>t.size-o.size),r=e[e.length-1].size,n=t.filter(t=>t.size===r),s=n[0];if(1===n.length)return s;if(n.sort((t,o)=>l(t)-l(o)),0===l(s))return s;const i=n[n.length-1];return l(i)===o?i:n[Math.floor(n.length/2)]})(t,o);return e.y=0,m(e,0),y(e,0),function(t,o){const e=t.filter(t=>0===t.x),n=t.filter(t=>t.x===o),s=e.filter(t=>!r(t.y)),i=n.filter(t=>!r(t.y)),c=t.filter(t=>l(t)>0&&l(t)<o&&!r(t.y));let f=e.reduce((t,o)=>Math.max(t,h(o)+o.out||0),0)+a,d=n.reduce((t,o)=>Math.max(t,h(o)+o.in||0),0)+a,u=0;f>=d?(s.forEach(t=>{f=g(t,f),f=Math.max(f+t.out,y(t,f))}),i.forEach(t=>{d=g(t,d),d=Math.max(d+t.in,m(t,d))})):(s.forEach(t=>{f=g(t,f)}),i.forEach(t=>{d=g(t,d),d=Math.max(d+t.in,m(t,d))})),c.forEach(o=>{let e=t.filter(t=>l(t)===l(o)&&r(t.y)).reduce((t,o)=>Math.max(t,h(o)+Math.max(o.in,o.out)),0);e=g(o,e),e=Math.max(e+o.in,m(o,e)),e=Math.max(e+o.out,y(o,e)),u=Math.max(u,e)}),Math.max(f,d,u)}(t,o),((t,o)=>{let e=0;for(let r=0;r<=o;r++){const o=t.filter(t=>l(t)===r).sort((t,o)=>h(t)-h(o));let n=0;for(const t of o)h(t)<n&&(t.y=n),n=h(t)+t.size;e=Math.max(e,n)}return e})(t,o)}const M=(t,o)=>l(t)!==l(o)?l(t)-l(o):h(t)===h(o)?t.size-o.size:h(t)-h(o);function b(t,o,{priority:e,height:n,nodePadding:s,modeX:i}){const a=[...t.values()],c=function(t,o,e){const n=o.filter(t=>t.from!==t.to),s=[...t.keys()],i=[...t.values()],a=new Set(s);let h=0;for(;a.size;){const e=0===h?f(o,i):d(n,a);if(!e.length)throw new Error("Fatal error: Unable to place nodes to columns. Please report this issue.");for(const o of e){const e=t.get(o);e&&!r(e.x)&&(e.x=h),a.delete(o)}a.size&&h++}const c=i.reduce((t,o)=>Math.max(t,l(o)),0);if("edge"===e){const e=new Set(o.map(t=>t.from));s.filter(t=>!e.has(t)).forEach(o=>{const e=t.get(o);e&&!e.column&&(e.x=c)})}return c}(t,o,i??"edge"),u=e?function(t,o){let e=0,r=0;for(let n=0;n<=o;n++){let o=r;const s=t.filter(t=>l(t)===n).sort((t,o)=>(t.priority??0)-(o.priority??0));r=s.length&&s[0].to.filter(t=>l(t.node)>n+1).reduce((t,o)=>t+o.flow,0)||0;for(const t of s)t.y=o,o+=Math.max(t.out,t.in);e=Math.max(o,e)}return e}(a,c):w(a,c),p=function(t,o){let e=0;const r=new Map,n=[],s=t=>{if(!r.has(t)){const o=n.length;return r.set(t,o),n.push([]),o}return r.get(t)??0};t.sort(M);for(const r of t){const t=s(l(r)),i=n[t]??[];if(h(r)){i.push(h(r));let e=i.length;if(r.in){for(let o=0;o<t;o++){const t=n[o]??[];for(let o=0;o<t.length&&!(t[o]>h(r));o++)e=Math.max(o+1,e)}for(;i.length<e;)i.push(h(r))}r.y=h(r)+e*o}e=Math.max(e,h(r)+Math.max(r.in,r.out))}return e}(a,u/n*s);return function(t){t.forEach(t=>{const o=t.size,e=o<t.in,r=o<t.out;let n=0,s=t.from.length;t.from.sort((t,o)=>h(t.node)+t.node.out/2-(h(o.node)+o.node.out/2)).forEach((t,r)=>{e?t.addY=r*(o-t.flow)/(s-1):(t.addY=n,n+=t.flow)}),n=0,s=t.to.length,t.to.sort((t,o)=>h(t.node)+t.node.in/2-(h(o.node)+o.node.in/2)).forEach((t,e)=>{r?t.addY=e*(o-t.flow)/(s-1):(t.addY=n,n+=t.flow)})})}(a),{maxX:c,maxY:p}}function k(t){return t.x??0}function v(t){return t.y??0}function _(t,o,e){for(const r of t)if(r.key===o&&r.index===e)return r.addY;return 0}class P extends o.DatasetController{parseObjectData(t,o,e,r){const n=((t,o)=>{const{from:e="from",to:r="to",flow:n="flow"}=o;return t.map(({[e]:t,[r]:o,[n]:s})=>({flow:s,from:t,to:o}))})(o,this.options.parsing),{xScale:s,yScale:a}=t,l=[],h=i(n,this.options);this._nodes=h;const{maxX:c,maxY:f}=b(h,n,{height:this.chart.canvas.height,modeX:this.options.modeX,nodePadding:this.options.nodePadding??10,priority:!!this.options.priority});if(this._maxX=c,this._maxY=f,!s||!a)return[];for(let t=0,o=n.length;t<o;++t){const o=n[t],e=h.get(o.from),r=h.get(o.to);if(!e||!r)continue;const i=v(e)+_(e.to,o.to,t),c=v(r)+_(r.from,o.from,t);l.push({_custom:{flow:o.flow,from:e,height:a.parse(o.flow,t),to:r,x:s.parse(k(r),t),y:a.parse(c,t)},x:s.parse(k(e),t),y:a.parse(i,t)})}return l.slice(e,e+r)}getMinMax(t){return{max:t===this._cachedMeta.xScale?this._maxX:this._maxY,min:0}}update(t){const{data:o}=this._cachedMeta;this.updateElements(o,0,o.length,t)}updateElements(t,o,e,r){const{xScale:n,yScale:s}=this._cachedMeta;if(!n||!s)return;const i=this.resolveDataElementOptions(o,r),a=this.getSharedOptions(i),{borderWidth:l,nodeWidth:h=10}=this.options,c=l?l/2+.5:0;for(let i=o;i<o+e;i++){const o=this.getParsed(i),e=o._custom,a=s.getPixelForValue(o.y);this.updateElement(t[i],i,{from:e.from,height:Math.abs(s.getPixelForValue(o.y+e.height)-a),options:this.resolveDataElementOptions(i,r),progress:"reset"===r?0:1,to:e.to,x:n.getPixelForValue(o.x)+h+c,x2:n.getPixelForValue(e.x)-c,y:a,y2:s.getPixelForValue(e.y)},r)}a&&this.updateSharedOptions(a,r,i)}_drawLabels(){const t=this.chart.ctx,o=this.options,e=this._nodes||new Map,r=n(o.size),s=o.borderWidth??1,i=o.nodeWidth??10,a=o.labels,{xScale:l,yScale:h}=this._cachedMeta;if(!l||!h)return;t.save();const c=this.chart.chartArea;for(const n of e.values()){const e=l.getPixelForValue(k(n)),f=h.getPixelForValue(v(n)),d=Math[r](n.in||n.out,n.out||n.in),u=Math.abs(h.getPixelForValue(v(n)+d)-f),p=a?.[n.key]??n.key;let x=e;t.fillStyle=o.color??"black",t.textBaseline="middle",e<c.width/2?(t.textAlign="left",x+=i+s+4):(t.textAlign="right",x-=s+4),this._drawLabel(p,f,u,t,x)}t.restore()}_drawLabel(t,r,n,s,i){const a=e.toFont(this.options.font??this.chart.options.font??o.Chart.defaults.font),l=function(t){if(!t)return[];const o=[],e=Array.isArray(t)?t:[t];for(;e.length;){const t=e.pop();"string"==typeof t?o.unshift(...t.split("\n")):Array.isArray(t)?e.push(...t):t&&o.unshift(`${t}`)}return o}(t),h=l.length,c=r+n/2,f=a.lineHeight,d=e.valueOrDefault(this.options.padding,f/2);if(s.font=a.string,h>1){const t=c-f*h/2+d;for(let o=0;o<h;o++)s.fillText(l[o],i,t+o*f)}else s.fillText(t,i,c)}_drawNodes(){const t=this.chart.ctx,o=this._nodes||new Map,{borderColor:e,borderWidth:r=0,nodeWidth:s=10,size:i}=this.options,a=n(i),{xScale:l,yScale:h}=this._cachedMeta;t.save(),e&&r&&(t.strokeStyle=e,t.lineWidth=r);for(const e of o.values()){if(t.fillStyle=e.color??"black",!l||!h)return;const o=l.getPixelForValue(k(e)),n=h.getPixelForValue(v(e)),i=Math[a](e.in||e.out,e.out||e.in),c=Math.abs(h.getPixelForValue(v(e)+i)-n);r&&t.strokeRect(o,n,s,c),t.fillRect(o,n,s,c)}t.restore()}draw(){const t=this.chart.ctx,o=this.getMeta().data??[],e=[];for(let t=0,r=o.length;t<r;++t){const r=o[t];r.from&&r.to&&(r.from.color=r.options.colorFrom,r.to.color=r.options.colorTo,r.active&&e.push(r))}for(const t of e)t.from&&t.to&&(t.from.color=t.options.colorFrom,t.to.color=t.options.colorTo);this._drawNodes();for(let e=0,r=o.length;e<r;++e)o[e].draw(t);this._drawLabels()}constructor(...t){super(...t),this._nodes=new Map,this._maxX=0,this._maxY=0}}P.id="sankey",P.defaults={animations:{colors:{properties:["colorFrom","colorTo"],type:"color"},numbers:{properties:["x","y","x2","y2","height"],type:"number"},progress:{delay:t=>"data"===t.type?500*t.parsed.x+20*t.dataIndex:void 0,duration:t=>"data"===t.type?200*(t.parsed._custom.x-t.parsed.x):void 0,easing:"linear"}},borderColor:"black",borderWidth:1,color:"black",dataElementType:"flow",modeX:"edge",nodePadding:10,nodeWidth:10,transitions:{hide:{animations:{colors:{properties:["colorFrom","colorTo"],to:"transparent",type:"color"}}},show:{animations:{colors:{from:"transparent",properties:["colorFrom","colorTo"],type:"color"}}}}},P.overrides={datasets:{clip:!1,parsing:{flow:"flow",from:"from",to:"to"}},interaction:{intersect:!0,mode:"nearest"},layout:{padding:{bottom:3,left:3,right:13,top:3}},plugins:{legend:{display:!1},tooltip:{callbacks:{label(t){const o=t.parsed._custom;return`${o.from.key} -> ${o.to.key}: ${o.flow}`},title:()=>""}}},scales:{x:{bounds:"data",display:!1,min:0,offset:!1,type:"linear"},y:{bounds:"data",display:!1,min:0,offset:!1,reverse:!0,type:"linear"}}};const F=(t,o,e,r)=>t<e?{cp1:{x:t+(e-t)/3*2,y:o},cp2:{x:t+(e-t)/3,y:r}}:{cp1:{x:t-(t-e)/3,y:0},cp2:{x:e+(t-e)/3,y:0}},S=(t,o,e)=>({x:t.x+e*(o.x-t.x),y:t.y+e*(o.y-t.y)}),T=(t,o)=>e.color(t).alpha(o).rgbString(),z=(t,o)=>"string"==typeof t?T(t,o):t,C=t=>"string"==typeof t?e.getHoverColor(t):t;class E extends o.Element{draw(t){const{x:o,x2:e,y:r,y2:n,height:s,progress:i}=this,{cp1:a,cp2:l}=F(o,r,e,n);0!==i&&(t.save(),i<1&&(t.beginPath(),t.rect(o,Math.min(r,n),(e-o)*i+1,Math.abs(n-r)+s+1),t.clip()),function(t,{x:o,x2:e,options:r}){let n="black";"from"===r.colorMode?n=z(r.colorFrom,r.alpha):"to"===r.colorMode?n=z(r.colorTo,r.alpha):"string"==typeof r.colorFrom&&"string"==typeof r.colorTo&&(n=t.createLinearGradient(o,0,e,0),n.addColorStop(0,T(r.colorFrom,r.alpha)),n.addColorStop(1,T(r.colorTo,r.alpha))),t.fillStyle=n,t.strokeStyle=n,t.lineWidth=.5}(t,this),t.beginPath(),t.moveTo(o,r),t.bezierCurveTo(a.x,a.y,l.x,l.y,e,n),t.lineTo(e,n+s),t.bezierCurveTo(l.x,l.y+s,a.x,a.y+s,o,r+s),t.lineTo(o,r),t.stroke(),t.closePath(),t.fill(),t.restore())}inRange(t,o,e){const{x:r,y:n,x2:s,y2:i,height:a}=this.getProps(["x","y","x2","y2","height"],e);if(t<r||t>s)return!1;const{cp1:l,cp2:h}=F(r,n,s,i),c=(t-r)/(s-r),f={x:s,y:i},d=S({x:r,y:n},l,c),u=S(l,h,c),p=S(h,f,c),x=S(d,u,c),m=S(u,p,c),y=S(x,m,c).y;return o>=y&&o<=y+a}inXRange(t,o){const{x:e,x2:r}=this.getProps(["x","x2"],o);return t>=e&&t<=r}inYRange(t,o){const{y:e,y2:r,height:n}=this.getProps(["y","y2","height"],o),s=Math.min(e,r),i=Math.max(e,r)+n;return t>=s&&t<=i}getCenterPoint(t){const{x:o,y:e,x2:r,y2:n,height:s}=this.getProps(["x","y","x2","y2","height"],t);return{x:(o+r)/2,y:(e+n+s)/2}}tooltipPosition(t=!1){return this.getCenterPoint(t)}getRange(t){return"x"===t?this.width/2:this.height/2}constructor(t){super(),this.x2=0,this.y2=0,this.width=0,this.height=0,this.progress=1,t&&Object.assign(this,t)}}E.id="flow",E.defaults={alpha:.5,colorFrom:"red",colorMode:"gradient",colorTo:"green",hoverColorFrom:(t,o)=>C(o.colorFrom),hoverColorTo:(t,o)=>C(o.colorTo)},E.descriptors={_scriptable:!0},o.Chart.register(P,E),t.Flow=E,t.SankeyController=P});
7
+ !function(t,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],o):o((t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-chart-sankey"]={},t.Chart,t.Chart.helpers)}(this,function(t,o,e){"use strict";const r=t=>void 0!==t;function n(t){return t&&-1!==["min","max"].indexOf(t)?t:"max"}function i(t,o){return"function"==typeof t?t(o):t&&"object"==typeof t&&!function(t){const o=Object.prototype.toString.call(t);return"[object CanvasPattern]"===o||"[object CanvasGradient]"===o}(t)?t[o.key]:t}function a(t,o,e,r,n,i,a){t.save(),t.fillStyle=o,a>0?(!function(t,o,e,r,n,i){const a=Math.max(0,Math.min(i,r/2,n/2));t.beginPath(),t.moveTo(o+a,e),t.lineTo(o+r-a,e),t.quadraticCurveTo(o+r,e,o+r,e+a),t.lineTo(o+r,e+n-a),t.quadraticCurveTo(o+r,e+n,o+r-a,e+n),t.lineTo(o+a,e+n),t.quadraticCurveTo(o,e+n,o,e+n-a),t.lineTo(o,e+a),t.quadraticCurveTo(o,e,o+a,e),t.closePath()}(t,e,r,n,i,a),t.fill()):t.fillRect(e,r,n,i),t.restore()}function s(t,o,e){const r=function(t){if(!t)return[];const o=[],e=Array.isArray(t)?t:[t];for(;e.length;){const t=e.pop();"string"==typeof t?o.unshift(...t.split("\n")):Array.isArray(t)?e.push(...t):t&&o.unshift(`${t}`)}return o}(o);if(!r.length)return;const{backgroundColor:n,borderRadius:i,color:s,font:l,lineOffset:h,padding:c}=e,d=function(t,o){return"auto"===t?o:t}(e.position,e.autoPosition),f=Number(l.lineHeight);t.font=l.string;const u=Math.max(...r.map(o=>t.measureText(o).width)),p=f*r.length,y=function(t,o,e){const{borderWidth:r,height:n,padding:i,width:a,x:s,y:l}=o,h={align:"center",x:s+a/2,y:l+n/2};return"left"===t?(h.align="right",h.x=s-r-i):"right"===t?(h.align="left",h.x=s+a+r+i):"top"===t?h.y=l-i-e/2:"bottom"===t&&(h.y=l+n+i+e/2),h}(d,e,p);t.textAlign=y.align,t.textBaseline="middle";const x=u+2*c,g=p+2*c,m=function(t,o,e,r,n){return"left"===t?o-n:"right"===t?o-e-n:o-r/2}(y.align,y.x,u,x,c),w=1===r.length?y.y:y.y-p/2+h,b=w+(r.length-1)*f/2;void 0!==n&&a(t,n,m,b-g/2,x,g,i),t.fillStyle=s;for(let o=0;o<r.length;o++)t.fillText(r[o],y.x,w+o*f)}const l=(t,o)=>o.flow===t.flow?t.index-o.index:o.flow-t.flow;function h(t,{size:o,priority:e,column:r}){const i=new Map;for(let o=0;o<t.length;o++){const{from:e,to:r,flow:n}=t[o],a=i.get(e)??{from:[],in:0,key:e,out:0,size:0,to:[]},s=(e===r?a:i.get(r))??{from:[],in:0,key:r,out:0,size:0,to:[]};a.out+=n,a.to.push({addY:0,flow:n,index:o,key:r,node:s}),1===a.to.length&&i.set(e,a),s.in+=n,s.from.push({addY:0,flow:n,index:o,key:e,node:a}),1===s.from.length&&i.set(r,s)}return((t,o)=>{const e=n(o);for(const o of t.values())o.from.sort(l),o.to.sort(l),o.size=Math[e](o.in||o.out,o.out||o.in)})(i,o),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.priority=o[e.key])})(i,e),((t,o)=>{if(o)for(const e of t.values())e.key in o&&(e.column=!0,e.x=o[e.key])})(i,r),i}const c=1e-6;function d(t){return t.x??0}function f(t){return t.y??0}const u=(t,o=new Set)=>{const e=[];for(const r of t)o.has(r.key)||(o.add(r.key),e.push(r.key,...u(r.to.map(t=>t.node),o)));return e},p=(t,o)=>{const e=o.filter(t=>0===t.from.length),r=e.map(t=>t.key),n=u(e),i=new Set(n);for(const o of t)i.has(o.from)||i.has(o.to)||(r.push(o.from),i.add(o.from)),i.add(o.to);return r},y=(t,o)=>{const e=new Set(t.filter(t=>o.has(t.from)).map(t=>t.to)),r=[...o],n=r.filter(t=>!e.has(t));return n.length?n:r.slice(0,1)};let x=-1;function g(t,o,e=function(){return x=x<100?x+1:0,x}()){let r=0;for(const n of t)n.node._visited!==e&&(n.node._visited=e,r+=n.node[o].length+g(n.node[o],o,e));return r}const m=t=>(o,e)=>g(o.node[t],t)-g(e.node[t],t)||o.node[t].length-e.node[t].length;function w(t,o){if(!t.from.length)return o;t.from.sort(m("from"));for(const e of t.from){const t=e.node;r(t.y)||(t.y=o,w(t,o?o+c:0)),o=Math.max(t.y+t.out,o)}return f(t)+t.size}const b=(t,o)=>Boolean(o&&d(o)<d(t));function M(t,o){if(!t.to.length)return o;t.to.sort(m("to"));for(let e=0;e<t.to.length;e++){const n=t.to[e],i=n.node;r(i.y)||(i.y=o,M(i,o?o+c:0)),b(i,t.to[e+1]?.node)?o+=n.flow:o=Math.max(i.y+Math.max(i.in,i.out),o)}return f(t)+t.size}function v(t,o){return r(t.y)?t.y:(t.y=o,o)}function k(t,o){if(!t.length)return 0;const e=((t,o)=>{const e=[...t].sort((t,o)=>t.size-o.size),r=e[e.length-1].size,n=t.filter(t=>t.size===r),i=n[0];if(1===n.length)return i;if(n.sort((t,o)=>d(t)-d(o)),0===d(i))return i;const a=n[n.length-1];return d(a)===o?a:n[Math.floor(n.length/2)]})(t,o);return e.y=0,w(e,0),M(e,0),function(t,o){const e=t.filter(t=>0===t.x),n=t.filter(t=>t.x===o),i=e.filter(t=>!r(t.y)),a=n.filter(t=>!r(t.y)),s=t.filter(t=>d(t)>0&&d(t)<o&&!r(t.y));let l=e.reduce((t,o)=>Math.max(t,f(o)+o.out||0),0)+c,h=n.reduce((t,o)=>Math.max(t,f(o)+o.in||0),0)+c,u=0;l>=h?(i.forEach(t=>{l=v(t,l),l=Math.max(l+t.out,M(t,l))}),a.forEach(t=>{h=v(t,h),h=Math.max(h+t.in,w(t,h))})):(i.forEach(t=>{l=v(t,l)}),a.forEach(t=>{h=v(t,h),h=Math.max(h+t.in,w(t,h))})),s.forEach(o=>{let e=t.filter(t=>d(t)===d(o)&&r(t.y)).reduce((t,o)=>Math.max(t,f(o)+Math.max(o.in,o.out)),0);e=v(o,e),e=Math.max(e+o.in,w(o,e)),e=Math.max(e+o.out,M(o,e)),u=Math.max(u,e)}),Math.max(l,h,u)}(t,o),((t,o)=>{let e=0;for(let r=0;r<=o;r++){const o=t.filter(t=>d(t)===r).sort((t,o)=>f(t)-f(o));let n=0;for(const t of o)f(t)<n&&(t.y=n),n=f(t)+t.size;e=Math.max(e,n)}return e})(t,o)}const _=(t,o)=>d(t)!==d(o)?d(t)-d(o):f(t)===f(o)?t.size-o.size:f(t)-f(o);function C(t,o,{priority:e,height:n,nodePadding:i,modeX:a}){const s=[...t.values()],l=function(t,o,e){const n=o.filter(t=>t.from!==t.to),i=[...t.keys()],a=[...t.values()],s=new Set(i);let l=0;for(;s.size;){const e=0===l?p(o,a):y(n,s);if(!e.length)throw new Error("Fatal error: Unable to place nodes to columns. Please report this issue.");for(const o of e){const e=t.get(o);e&&!r(e.x)&&(e.x=l),s.delete(o)}s.size&&l++}const h=a.reduce((t,o)=>Math.max(t,d(o)),0);if("edge"===e){const e=new Set(o.map(t=>t.from));i.filter(t=>!e.has(t)).forEach(o=>{const e=t.get(o);e&&!e.column&&(e.x=h)})}return h}(t,o,a??"edge"),h=e?function(t,o){let e=0,r=0;for(let n=0;n<=o;n++){let o=r;const i=t.filter(t=>d(t)===n).sort((t,o)=>(t.priority??0)-(o.priority??0));if(i.length){const o=t.reduce((t,o)=>d(o)>n?Math.min(t,d(o)):t,1/0);r=i[0].to.filter(t=>d(t.node)>o).reduce((t,o)=>t+o.flow,0)||0}for(const t of i)t.y=o,o+=Math.max(t.out,t.in);e=Math.max(o,e)}return e}(s,l):k(s,l),c=function(t,o){let e=0;const r=new Map,n=[],i=t=>{if(!r.has(t)){const o=n.length;return r.set(t,o),n.push([]),o}return r.get(t)??0};t.sort(_);for(const r of t){const t=i(d(r)),a=n[t]??[];if(f(r)){a.push(f(r));let e=a.length;if(r.in){for(let o=0;o<t;o++){const t=n[o]??[];for(let o=0;o<t.length&&!(t[o]>f(r));o++)e=Math.max(o+1,e)}for(;a.length<e;)a.push(f(r))}r.y=f(r)+e*o}e=Math.max(e,f(r)+Math.max(r.in,r.out))}return e}(s,h/n*i);return function(t){t.forEach(t=>{const o=t.size,e=o<t.in,r=o<t.out;let n=0,i=t.from.length;t.from.sort((t,o)=>f(t.node)+t.node.out/2-(f(o.node)+o.node.out/2)).forEach((t,r)=>{e?t.addY=r*(o-t.flow)/(i-1):(t.addY=n,n+=t.flow)}),n=0,i=t.to.length,t.to.sort((t,o)=>f(t.node)+t.node.in/2-(f(o.node)+o.node.in/2)).forEach((t,e)=>{r?t.addY=e*(o-t.flow)/(i-1):(t.addY=n,n+=t.flow)})})}(s),{maxX:l,maxY:c}}function P(t){return t.x??0}function T(t){return t.y??0}function S(t,o){return Math[o](t.in||t.out,t.out||t.in)}function z(t,o,e,r){return"vertical"===r?o<(e.top+e.bottom)/2?"bottom":"top":t<(e.left+e.right)/2?"right":"left"}function F(t,o,e){for(const r of t)if(r.key===o&&r.index===e)return r.addY;return 0}function E(t,o,e,r,n,i,a,s,l){return"vertical"===l?{_custom:{flow:n,from:t,height:a.parse(n,i),to:o,x:a.parse(r,i),y:s.parse(P(o),i)},x:a.parse(e,i),y:s.parse(P(t),i)}:{_custom:{flow:n,from:t,height:s.parse(n,i),to:o,x:a.parse(P(o),i),y:s.parse(r,i)},x:a.parse(P(t),i),y:s.parse(e,i)}}function R(t,o,e,r,n,i,a,s){const l=t._custom,h=o.getPixelForValue(t.x),c=e.getPixelForValue(t.y);return"vertical"===s?{flow:l.flow,from:l.from,height:0,to:l.to,width:Math.abs(o.getPixelForValue(t.x+l.height)-h),x:h,x2:o.getPixelForValue(l.x),y:W(e,t.y,r,i)+n+a,y2:W(e,l.y,r,i)-a}:{flow:l.flow,from:l.from,height:Math.abs(e.getPixelForValue(t.y+l.height)-c),to:l.to,width:0,x:W(o,t.x,r,i)+n+a,x2:W(o,l.x,r,i)-a,y:c,y2:e.getPixelForValue(l.y)}}function W(t,o,e,r){const n=t.getPixelForValue(o);return e?n-o/e*r:n}function X(t,o,e){const r="vertical"===o?e.height-e.chartArea.bottom:e.width-e.chartArea.right;return Math.max(0,t+3-r)}function Y(t,o,e,r,n,i,a,s){if("vertical"===s){const s=e.getPixelForValue(T(t));return{height:i,width:Math.abs(e.getPixelForValue(T(t)+o)-s),x:s,y:W(r,P(t),n,a)}}const l=r.getPixelForValue(T(t));return{height:Math.abs(r.getPixelForValue(T(t)+o)-l),width:i,x:W(e,P(t),n,a),y:l}}function j(t,o){const{backgroundColor:e,borderRadius:r=0,color:n,display:a,font:s,padding:l=4,position:h}=t.nodeLabels??{};return{backgroundColor:i(e,o),borderRadius:r,color:i(n,o)??t.color??"black",display:i(a,o)??!0,font:s,padding:l,position:i(h,o)??"auto"}}class O extends o.DatasetController{parseObjectData(t,o,e,r){const n=((t,o)=>{const{from:e="from",to:r="to",flow:n="flow"}=o;return t.map(({[e]:t,[r]:o,[n]:i})=>({flow:i,from:t,to:o}))})(o,this.options.parsing),{xScale:i,yScale:a}=t,s=[],l=h(n,this.options),c=this.options.orientation??"horizontal";this._nodes=l;const{maxX:d,maxY:f}=C(l,n,{height:"vertical"===c?this.chart.canvas.width:this.chart.canvas.height,modeX:this.options.modeX,nodePadding:this.options.nodePadding??10,priority:!!this.options.priority});if(this._maxX=d,this._maxY=f,!i||!a)return[];for(let t=0,o=n.length;t<o;++t){const o=n[t],e=l.get(o.from),r=l.get(o.to);if(!e||!r)continue;const h=T(e)+F(e.to,o.to,t),d=T(r)+F(r.from,o.from,t);s.push(E(e,r,h,d,o.flow,t,i,a,c))}return s.slice(e,e+r)}getMinMax(t){return{max:t===("vertical"===this.options.orientation?this._cachedMeta.yScale:this._cachedMeta.xScale)?this._maxX:this._maxY,min:0}}update(t){const{data:o}=this._cachedMeta;this.updateElements(o,0,o.length,t)}updateElements(t,o,e,r){const{xScale:n,yScale:i}=this._cachedMeta;if(!n||!i)return;const a=this.resolveDataElementOptions(o,r),s=this.getSharedOptions(a),{borderWidth:l,nodeWidth:h=10,orientation:c="horizontal"}=this.options,d=X(h,c,this.chart),f=l?l/2+.5:0;for(let a=o;a<o+e;a++){const o=this.getParsed(a);this.updateElement(t[a],a,{options:this.resolveDataElementOptions(a,r),progress:"reset"===r?0:1,...R(o,n,i,this._maxX,h,d,f,c)},r)}s&&this.updateSharedOptions(s,r,a)}_drawLabels(){const t=this.chart.ctx,r=this.options,i=this._nodes||new Map,a=n(r.size),l=r.labels,{borderWidth:h=1,nodeWidth:c=10,orientation:d="horizontal"}=r,f=X(c,d,this.chart),u=r.font??this.chart.options.font??o.Chart.defaults.font,{xScale:p,yScale:y}=this._cachedMeta;if(!p||!y)return;t.save();const x=this.chart.chartArea;for(const o of i.values()){const n=S(o,a),{height:i,width:g,x:m,y:w}=Y(o,n,p,y,this._maxX,c,f,d),b=l?.[o.key]??o.key,M=j(r,o);if(M.display){const o=e.toFont(M.font??u);s(t,b,{autoPosition:z(m,w,x,d),backgroundColor:M.backgroundColor,borderRadius:M.borderRadius,borderWidth:h,color:M.color,font:o,height:i,lineOffset:e.valueOrDefault(r.padding,o.lineHeight/2),padding:M.padding,position:M.position,width:g,x:m,y:w})}}t.restore()}_drawNodes(){const t=this.chart.ctx,o=this._nodes||new Map,{borderColor:e,borderWidth:r=0,nodeWidth:i=10,orientation:a="horizontal",size:s}=this.options,l=X(i,a,this.chart),h=n(s),{xScale:c,yScale:d}=this._cachedMeta;t.save(),e&&r&&(t.strokeStyle=e,t.lineWidth=r);for(const e of o.values()){if(t.fillStyle=e.color??"black",!c||!d)return;const o=Math[h](e.in||e.out,e.out||e.in),{height:n,width:s,x:f,y:u}=Y(e,o,c,d,this._maxX,i,l,a);r&&t.strokeRect(f,u,s,n),t.fillRect(f,u,s,n)}t.restore()}draw(){const t=this.chart.ctx,o=this.getMeta().data??[],e=[];for(let t=0,r=o.length;t<r;++t){const r=o[t];r.from&&r.to&&(r.from.color=r.options.colorFrom,r.to.color=r.options.colorTo,r.active&&e.push(r))}for(const t of e)t.from&&t.to&&(t.from.color=t.options.colorFrom,t.to.color=t.options.colorTo);this._drawNodes();for(let e=0,r=o.length;e<r;++e)o[e].draw(t);this._drawLabels()}constructor(...t){super(...t),this._nodes=new Map,this._maxX=0,this._maxY=0}}O.id="sankey",O.descriptors={_indexable:!1,_scriptable:!0,nodeLabels:{_indexable:!1,_scriptable:!1}},O.defaults={animations:{colors:{properties:["colorFrom","colorTo"],type:"color"},numbers:{properties:["x","y","x2","y2","height","width"],type:"number"},progress:{delay:t=>"data"===t.type?500*t.parsed["vertical"===t.dataset.orientation?"y":"x"]+20*t.dataIndex:void 0,duration:t=>"data"===t.type?200*(t.parsed._custom["vertical"===t.dataset.orientation?"y":"x"]-t.parsed["vertical"===t.dataset.orientation?"y":"x"]):void 0,easing:"linear"}},borderColor:"black",borderWidth:1,color:"black",dataElementType:"flow",modeX:"edge",nodePadding:10,nodeWidth:10,orientation:"horizontal",transitions:{hide:{animations:{colors:{properties:["colorFrom","colorTo"],to:"transparent",type:"color"}}},show:{animations:{colors:{from:"transparent",properties:["colorFrom","colorTo"],type:"color"}}}}},O.overrides={datasets:{clip:!1,parsing:{flow:"flow",from:"from",to:"to"}},interaction:{intersect:!0,mode:"nearest"},layout:{padding:{bottom:3,left:3,right:13,top:3}},plugins:{legend:{display:!1},tooltip:{callbacks:{label(t){const o=t.parsed._custom;return`${o.from.key} -> ${o.to.key}: ${o.flow}`},title:()=>""}}},scales:{x:{bounds:"data",display:!1,min:0,offset:!1,type:"linear"},y:{bounds:"data",display:!1,min:0,offset:!1,reverse:!0,type:"linear"}}};const V=(t,o,e,r,n)=>"vertical"===n?o<r?{cp1:{x:t,y:o+(r-o)/3*2},cp2:{x:e,y:o+(r-o)/3}}:{cp1:{x:0,y:o-(o-r)/3},cp2:{x:0,y:r+(o-r)/3}}:t<e?{cp1:{x:t+(e-t)/3*2,y:o},cp2:{x:t+(e-t)/3,y:r}}:{cp1:{x:t-(t-e)/3,y:0},cp2:{x:e+(t-e)/3,y:0}},L=(t,o,e)=>({x:t.x+e*(o.x-t.x),y:t.y+e*(o.y-t.y)}),A=(t,o)=>e.color(t).alpha(o).rgbString(),q=(t,o)=>"string"==typeof t?A(t,o):t,D=t=>"string"==typeof t?e.getHoverColor(t):t;class $ extends o.Element{draw(t){const{x:r,x2:n,y:i,y2:a,height:l,progress:h,width:c}=this,d=this.options.orientation,f=V(r,i,n,a,d),u={height:l,width:c,x:r,x2:n,y:i,y2:a};if(0===h)return;t.save(),h<1&&function(t,{height:o,width:e,x:r,x2:n,y:i,y2:a},s,l){t.beginPath(),"vertical"===l?t.rect(Math.min(r,n),i,Math.abs(n-r)+e+1,(a-i)*s+1):t.rect(r,Math.min(i,a),(n-r)*s+1,Math.abs(a-i)+o+1),t.clip()}(t,u,h,d),function(t,{x:o,x2:e,y:r,y2:n,options:i}){let a="black";null!==i.flowColor?a=i.flowColor:"from"===i.colorMode?a=q(i.colorFrom,i.alpha):"to"===i.colorMode?a=q(i.colorTo,i.alpha):"string"==typeof i.colorFrom&&"string"==typeof i.colorTo&&(a="vertical"===i.orientation?t.createLinearGradient(0,r,0,n):t.createLinearGradient(o,0,e,0),a.addColorStop(0,A(i.colorFrom,i.alpha)),a.addColorStop(1,A(i.colorTo,i.alpha))),t.fillStyle=a,t.strokeStyle=a,t.lineWidth=.5}(t,this),function(t,{height:o,width:e,x:r,x2:n,y:i,y2:a},{cp1:s,cp2:l},h){t.beginPath(),t.moveTo(r,i),t.bezierCurveTo(s.x,s.y,l.x,l.y,n,a),"vertical"===h?(t.lineTo(n+e,a),t.bezierCurveTo(l.x+e,l.y,s.x+e,s.y,r+e,i)):(t.lineTo(n,a+o),t.bezierCurveTo(l.x,l.y+o,s.x,s.y+o,r,i+o)),t.lineTo(r,i),t.stroke(),t.closePath(),t.fill()}(t,u,f,d);const p=this.options.flowLabels;if(p.display){const r=e.toFont(p.font??o.Chart.defaults.font),n=function({height:t,width:o,x:e,x2:r,y:n,y2:i},a){return"vertical"===a?{height:i-n,width:Math.abs(r-e)+o,x:Math.min(e,r),y:n}:{height:Math.abs(i-n)+t,width:r-e,x:e,y:Math.min(n,i)}}(u,d);s(t,`${this.flow}`,{autoPosition:"center",backgroundColor:p.backgroundColor,borderRadius:p.borderRadius,borderWidth:0,color:p.color,font:r,height:n.height,lineOffset:r.lineHeight/2,padding:p.padding,position:p.position,width:n.width,x:n.x,y:n.y})}t.restore()}inRange(t,o,e){const{x:r,y:n,x2:i,y2:a,height:s,width:l}=this.getProps(["x","y","x2","y2","height","width"],e),h="vertical"===this.options.orientation;if(h?o<n||o>a:t<r||t>i)return!1;const{cp1:c,cp2:d}=V(r,n,i,a,this.options.orientation),f=h?(o-n)/(a-n):(t-r)/(i-r),u={x:i,y:a},p=L({x:r,y:n},c,f),y=L(c,d,f),x=L(d,u,f),g=L(p,y,f),m=L(y,x,f),w=L(g,m,f);return h?t>=w.x&&t<=w.x+l:o>=w.y&&o<=w.y+s}inXRange(t,o){const{x:e,x2:r,width:n}=this.getProps(["x","x2","width"],o),i=Math.min(e,r),a=Math.max(e,r)+("vertical"===this.options.orientation?n:0);return t>=i&&t<=a}inYRange(t,o){const{y:e,y2:r,height:n}=this.getProps(["y","y2","height"],o),i=Math.min(e,r),a=Math.max(e,r)+("vertical"===this.options.orientation?0:n);return t>=i&&t<=a}getCenterPoint(t){const{x:o,y:e,x2:r,y2:n,height:i,width:a}=this.getProps(["x","y","x2","y2","height","width"],t),s="vertical"===this.options.orientation;return{x:(o+r+(s?a:0))/2,y:(e+n+(s?0:i))/2}}tooltipPosition(t=!1){return this.getCenterPoint(t)}getRange(t){const o="vertical"===this.options.orientation;return"x"===t?o?this.width/2:0:o?0:this.height/2}constructor(t){super(),this.flow=0,this.x2=0,this.y2=0,this.width=0,this.height=0,this.progress=1,t&&Object.assign(this,t)}}$.id="flow",$.defaults={alpha:.5,colorFrom:"red",colorMode:"gradient",colorTo:"green",flowColor:null,flowLabels:{borderRadius:0,color:"black",display:!1,padding:4,position:"center"},hoverColorFrom:(t,o)=>D(o.colorFrom),hoverColorTo:(t,o)=>D(o.colorTo),orientation:"horizontal"},$.descriptors={_scriptable:!0,flowLabels:{_scriptable:!0}},o.Chart.register(O,$),t.Flow=$,t.SankeyController=O});
@@ -1,9 +1,17 @@
1
- import type { ChartMeta, SankeyControllerDatasetOptions, SankeyParsedData } from 'chart.js';
1
+ import type { ChartMeta } from 'chart.js';
2
2
  import type Flow from './flow.js';
3
- import type { AnyObject } from './types.js';
3
+ import type { AnyObject, SankeyControllerDatasetOptions, SankeyParsedData } from './types.js';
4
4
  import { DatasetController } from 'chart.js';
5
5
  export default class SankeyController extends DatasetController {
6
6
  static readonly id = "sankey";
7
+ static readonly descriptors: {
8
+ _indexable: boolean;
9
+ _scriptable: boolean;
10
+ nodeLabels: {
11
+ _indexable: boolean;
12
+ _scriptable: boolean;
13
+ };
14
+ };
7
15
  static readonly defaults: {
8
16
  animations: {
9
17
  colors: {
@@ -27,6 +35,7 @@ export default class SankeyController extends DatasetController {
27
35
  modeX: string;
28
36
  nodePadding: number;
29
37
  nodeWidth: number;
38
+ orientation: string;
30
39
  transitions: {
31
40
  hide: {
32
41
  animations: {
@@ -110,7 +119,6 @@ export default class SankeyController extends DatasetController {
110
119
  update(mode: any): void;
111
120
  updateElements(elems: Flow[], start: number, count: number, mode: 'default' | 'resize' | 'reset' | 'none' | 'hide' | 'show' | 'active'): void;
112
121
  private _drawLabels;
113
- private _drawLabel;
114
122
  private _drawNodes;
115
123
  /**
116
124
  * That's where the drawing process happens
package/dist/flow.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { Color, SankeyNode } from 'chart.js';
2
- import type { FlowConfig, FlowOptions, FlowProps } from './types.js';
1
+ import type { Color } from 'chart.js';
2
+ import type { FlowConfig, FlowOptions, FlowProps, SankeyNode } from './types.js';
3
3
  import { Element } from 'chart.js';
4
4
  export default class Flow extends Element<FlowProps, FlowOptions> {
5
5
  static readonly id = "flow";
@@ -8,12 +8,25 @@ export default class Flow extends Element<FlowProps, FlowOptions> {
8
8
  colorFrom: string;
9
9
  colorMode: string;
10
10
  colorTo: string;
11
+ flowColor: null;
12
+ flowLabels: {
13
+ borderRadius: number;
14
+ color: string;
15
+ display: boolean;
16
+ padding: number;
17
+ position: string;
18
+ };
11
19
  hoverColorFrom: (_ctx: unknown, options: FlowOptions) => Color;
12
20
  hoverColorTo: (_ctx: unknown, options: FlowOptions) => Color;
21
+ orientation: string;
13
22
  };
14
23
  static readonly descriptors: {
15
24
  _scriptable: boolean;
25
+ flowLabels: {
26
+ _scriptable: boolean;
27
+ };
16
28
  };
29
+ flow: number;
17
30
  x2: number;
18
31
  y2: number;
19
32
  width: number;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import Sankey from './controller.js';
2
2
  import Flow from './flow.js';
3
- export type { FlowConfig, FlowOptions, FlowProps, } from './types.js';
3
+ export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.js';
4
4
  export { Flow, Sankey as SankeyController };
@@ -1,3 +1,3 @@
1
- export type { FlowConfig, FlowOptions, FlowProps, } from './types.js';
1
+ export type { FlowConfig, FlowOptions, FlowProps, SankeyControllerDatasetFlowLabelsOptions, SankeyControllerDatasetNodeLabelsOptions, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyLabelPosition, SankeyNodeLabelOption, SankeyNodeLabelPosition, SankeyOrientation, SankeyParsedData, SankeyParsingOptions, SankeyScriptableContext, } from './types.js';
2
2
  export { default as SankeyController } from './controller.js';
3
3
  export { default as Flow } from './flow.js';
@@ -0,0 +1,22 @@
1
+ import type { CanvasFontSpec, Color } from 'chart.js';
2
+ import type { SankeyLabelPosition, SankeyNode, SankeyNodeLabelOption } from './types.js';
3
+ type ResolvableNodeLabelValue = boolean | Color | SankeyLabelPosition;
4
+ type ResolvedLabelPosition = Exclude<SankeyLabelPosition, 'auto'>;
5
+ export interface DrawLabelOptions {
6
+ autoPosition: ResolvedLabelPosition;
7
+ backgroundColor?: Color;
8
+ borderRadius: number;
9
+ borderWidth: number;
10
+ color: Color;
11
+ font: CanvasFontSpec;
12
+ height: number;
13
+ lineOffset: number;
14
+ padding: number;
15
+ position: SankeyLabelPosition;
16
+ width: number;
17
+ x: number;
18
+ y: number;
19
+ }
20
+ export declare function resolveNodeLabelOption<T extends ResolvableNodeLabelValue>(option: SankeyNodeLabelOption<T> | undefined, node: SankeyNode): T | undefined;
21
+ export declare function drawLabel(ctx: CanvasRenderingContext2D, label: string, options: DrawLabelOptions): void;
22
+ export {};
@@ -1,4 +1,3 @@
1
- import type { SankeyControllerDatasetOptions, SankeyDataPoint, SankeyNode } from 'chart.js';
2
- import type { AnyObject } from '../types.js';
1
+ import type { AnyObject, SankeyControllerDatasetOptions, SankeyDataPoint, SankeyNode } from '../types.js';
3
2
  export declare const getParsedData: (data: AnyObject[], parsing: SankeyControllerDatasetOptions["parsing"]) => SankeyDataPoint[];
4
3
  export declare function buildNodesFromData(data: SankeyDataPoint[], { size, priority, column }: Pick<SankeyControllerDatasetOptions, 'size' | 'priority' | 'column'>): Map<string, SankeyNode>;
@@ -1,4 +1,4 @@
1
- import type { SankeyControllerDatasetOptions, SankeyDataPoint, SankeyNode } from 'chart.js';
1
+ import type { SankeyControllerDatasetOptions, SankeyDataPoint, SankeyNode } from '../types.js';
2
2
  export type SankeyMode = 'edge' | 'even';
3
3
  /**
4
4
  * Get all keys the input nodes flow to, including keys of the input nodes
@@ -10,6 +10,7 @@ export declare const getAllKeysForward: (nodes: SankeyNode[], visited?: Set<stri
10
10
  */
11
11
  export declare const startColumn: (data: SankeyDataPoint[], nodes: SankeyNode[]) => string[];
12
12
  export declare function calculateX(nodeMap: Map<string, SankeyNode>, data: SankeyDataPoint[], mode: SankeyMode): number;
13
+ export declare const returnsToNearerColumn: (current: SankeyNode, next?: SankeyNode) => boolean;
13
14
  export declare function calculateY(nodeArray: SankeyNode[], maxX: number): number;
14
15
  export declare function calculateYUsingPriority(nodeArray: SankeyNode[], maxX: number): number;
15
16
  /**
package/dist/types.d.ts CHANGED
@@ -1,82 +1,114 @@
1
- import type { CartesianScaleTypeRegistry, Color, FontSpec, SankeyNode, Scriptable, ScriptableContext } from 'chart.js';
1
+ import type { CartesianScaleTypeRegistry, Color, ControllerDatasetOptions, CoreChartOptions, FontSpec, Scriptable, ScriptableAndArray, ScriptableContext } from 'chart.js';
2
2
  export type AnyObject = Record<string, unknown>;
3
- declare module 'chart.js' {
4
- interface SankeyDataPoint {
5
- from: string;
6
- to: string;
7
- flow: number;
8
- }
9
- interface SankeyControllerDatasetOptions {
10
- label: string;
11
- data: Array<SankeyDataPoint>;
12
- colorFrom: (data: ScriptableContext<'sankey'>) => string;
13
- colorTo: (data: ScriptableContext<'sankey'>) => string;
14
- colorMode: 'gradient' | 'from' | 'to';
15
- hoverColorFrom?: Scriptable<string, ScriptableContext<'sankey'>>;
16
- hoverColorTo?: Scriptable<string, ScriptableContext<'sankey'>>;
17
- priority?: Record<string, number>;
18
- column?: Record<string, number>;
19
- labels?: Record<string, string>;
20
- modeX?: 'edge' | 'even';
21
- size?: 'min' | 'max';
22
- borderWidth?: number;
23
- nodeWidth?: number;
24
- nodePadding?: number;
25
- color?: string;
26
- borderColor?: string;
27
- font?: FontSpec;
28
- padding?: number;
29
- parsing: {
30
- from: string;
31
- to: string;
32
- flow: string;
33
- };
34
- }
35
- type FromToElement = {
36
- addY: number;
37
- flow: number;
38
- key: string;
39
- node: SankeyNode;
40
- index: number;
41
- };
42
- type SankeyNode = {
43
- key: string;
44
- in: number;
45
- out: number;
46
- size: number;
47
- from: Array<FromToElement>;
48
- to: Array<FromToElement>;
49
- column?: boolean;
50
- priority?: number;
51
- y?: number;
52
- x?: number;
53
- color?: Color;
54
- _visited?: number;
55
- };
56
- interface SankeyParsedData {
3
+ export interface SankeyDataPoint {
4
+ from: string;
5
+ to: string;
6
+ flow: number;
7
+ }
8
+ export type SankeyScriptableContext = ScriptableContext<'sankey'> & {
9
+ raw: SankeyDataPoint;
10
+ };
11
+ export type SankeyLabelPosition = 'auto' | 'bottom' | 'center' | 'left' | 'right' | 'top';
12
+ export type SankeyNodeLabelPosition = SankeyLabelPosition;
13
+ export type SankeyOrientation = 'horizontal' | 'vertical';
14
+ export type SankeyNodeLabelOption<T> = T | Record<string, T> | ((node: SankeyNode) => T | undefined);
15
+ export interface SankeyControllerDatasetNodeLabelsOptions {
16
+ backgroundColor?: SankeyNodeLabelOption<Color>;
17
+ borderRadius?: number;
18
+ color?: SankeyNodeLabelOption<Color>;
19
+ display?: SankeyNodeLabelOption<boolean>;
20
+ font?: Partial<FontSpec>;
21
+ padding?: number;
22
+ position?: SankeyNodeLabelOption<SankeyLabelPosition>;
23
+ }
24
+ export interface SankeyControllerDatasetFlowLabelsOptions {
25
+ backgroundColor?: Scriptable<Color, SankeyScriptableContext>;
26
+ borderRadius?: Scriptable<number, SankeyScriptableContext>;
27
+ color?: Scriptable<Color, SankeyScriptableContext>;
28
+ display?: Scriptable<boolean, SankeyScriptableContext>;
29
+ font?: Scriptable<Partial<FontSpec>, SankeyScriptableContext>;
30
+ padding?: Scriptable<number, SankeyScriptableContext>;
31
+ position?: Scriptable<SankeyLabelPosition, SankeyScriptableContext>;
32
+ }
33
+ export interface SankeyParsingOptions {
34
+ from: string;
35
+ to: string;
36
+ flow: string;
37
+ }
38
+ export interface SankeyControllerDatasetOptions extends Omit<ControllerDatasetOptions, 'parsing'> {
39
+ alpha?: number;
40
+ backgroundColor?: never;
41
+ borderColor?: Color;
42
+ borderWidth?: number;
43
+ color?: Color;
44
+ colorFrom?: ScriptableAndArray<Color, SankeyScriptableContext>;
45
+ colorMode?: 'gradient' | 'from' | 'to';
46
+ colorTo?: ScriptableAndArray<Color, SankeyScriptableContext>;
47
+ column?: Record<string, number>;
48
+ font?: Partial<FontSpec>;
49
+ flowLabels?: SankeyControllerDatasetFlowLabelsOptions;
50
+ hoverColorFrom?: ScriptableAndArray<Color, SankeyScriptableContext>;
51
+ hoverColorTo?: ScriptableAndArray<Color, SankeyScriptableContext>;
52
+ hoverFlowColor?: ScriptableAndArray<Color, SankeyScriptableContext>;
53
+ labels?: Record<string, string>;
54
+ flowColor?: ScriptableAndArray<Color, SankeyScriptableContext>;
55
+ modeX?: 'edge' | 'even';
56
+ nodeLabels?: SankeyControllerDatasetNodeLabelsOptions;
57
+ nodePadding?: number;
58
+ nodeWidth?: number;
59
+ orientation?: SankeyOrientation;
60
+ padding?: number;
61
+ parsing: SankeyParsingOptions;
62
+ priority?: Record<string, number>;
63
+ size?: 'min' | 'max';
64
+ }
65
+ export interface FromToElement {
66
+ addY: number;
67
+ flow: number;
68
+ key: string;
69
+ node: SankeyNode;
70
+ index: number;
71
+ }
72
+ export interface SankeyNode {
73
+ key: string;
74
+ in: number;
75
+ out: number;
76
+ size: number;
77
+ from: FromToElement[];
78
+ to: FromToElement[];
79
+ column?: boolean;
80
+ priority?: number;
81
+ y?: number;
82
+ x?: number;
83
+ color?: Color;
84
+ _visited?: number;
85
+ }
86
+ export interface SankeyParsedData {
87
+ x: number;
88
+ y: number;
89
+ _custom: {
90
+ from: SankeyNode;
91
+ to: SankeyNode;
57
92
  x: number;
58
93
  y: number;
59
- _custom: {
60
- from: SankeyNode;
61
- to: SankeyNode;
62
- x: number;
63
- y: number;
64
- height: number;
65
- flow: number;
66
- };
67
- }
94
+ height: number;
95
+ flow: number;
96
+ };
97
+ }
98
+ declare module 'chart.js' {
68
99
  interface ChartTypeRegistry {
69
100
  sankey: {
70
101
  datasetOptions: SankeyControllerDatasetOptions;
71
102
  defaultDataPoint: SankeyDataPoint;
72
103
  parsedDataType: SankeyParsedData;
73
104
  metaExtensions: AnyObject;
74
- chartOptions: FlowOptions;
105
+ chartOptions: CoreChartOptions<'sankey'>;
75
106
  scales: keyof CartesianScaleTypeRegistry;
76
107
  };
77
108
  }
78
109
  }
79
110
  export interface FlowProps {
111
+ flow: number;
80
112
  x: number;
81
113
  y: number;
82
114
  x2: number;
@@ -87,12 +119,24 @@ export interface FlowProps {
87
119
  export interface FlowOptions {
88
120
  alpha: number;
89
121
  colorMode: 'gradient' | 'from' | 'to';
90
- colorFrom: 'string';
122
+ colorFrom: Color;
91
123
  colorTo: Color;
92
124
  hoverColorFrom: Color;
93
125
  hoverColorTo: Color;
126
+ flowColor: Color | null;
127
+ orientation: SankeyOrientation;
128
+ flowLabels: {
129
+ backgroundColor?: Color;
130
+ borderRadius: number;
131
+ color: Color;
132
+ display: boolean;
133
+ font?: Partial<FontSpec>;
134
+ padding: number;
135
+ position: SankeyLabelPosition;
136
+ };
94
137
  }
95
138
  export interface FlowConfig {
139
+ flow?: number;
96
140
  x: number;
97
141
  y: number;
98
142
  x2: number;
package/package.json CHANGED
@@ -11,6 +11,7 @@
11
11
  "@biomejs/biome": "^2.4.14",
12
12
  "@emnapi/core": "^1.11.2",
13
13
  "@emnapi/runtime": "^1.11.2",
14
+ "@kurkle/astro-chartjs-editor": "^1.0.0",
14
15
  "@kurkle/configs": "^1.0.1",
15
16
  "@napi-rs/canvas": "^1.0.0",
16
17
  "@rollup/plugin-node-resolve": "^16.0.0",
@@ -82,6 +83,7 @@
82
83
  "predocs": "npm run docs:version",
83
84
  "predocs:dev": "npm run docs:version",
84
85
  "prepack": "npm run build",
86
+ "prepare": "git config core.hooksPath .githooks || true",
85
87
  "pretest:unit": "swc --config-file .swcrc-spec src -d build",
86
88
  "test": "npm run test:unit && npm run test:fixture && npm run test:integration:browser-module && npm run test:integration:node-commonjs && npm run test:integration:node-module",
87
89
  "test:fixture": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --no-auto-watch --single-run",
@@ -95,5 +97,5 @@
95
97
  "type": "module",
96
98
  "types": "dist/index.esm.d.ts",
97
99
  "unpkg": "dist/chartjs-chart-sankey.min.js",
98
- "version": "0.14.4"
100
+ "version": "0.15.0"
99
101
  }