chartogram
Advanced tools
Comparing version 0.1.6 to 0.1.7
@@ -1,2 +0,2 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).chartogram=e()}(this,function(){"use strict";function t(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function e(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function n(t,e){return t.map(function(t,n){return"".concat(t,",").concat(e[n])})}function i(t,e,n,i,o){for(var a=0;a<i;){var r=document.createElement("div"),s=e+a*(n-e)/(i-1);o&&(s=o(s)),r.appendChild(document.createTextNode(s)),t.appendChild(r),a++}}function o(t,e){var n,i=0,o=function(){n=void 0,i=Date.now(),t()};return function(){var a=Date.now(),r=e-(a-i);r<=0?(n&&(clearTimeout(n),n=void 0),i=a,t()):n||(n=setTimeout(o,r))}}function a(t,e,n){function i(t){n(t.changedTouches[0].clientX,t.changedTouches[0].clientY)}function o(t){n(t.clientX,t.clientY)}function a(){window.removeEventListener("pointermove",o),window.removeEventListener("touchmove",i),window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",a),window.removeEventListener("touchend",a),window.removeEventListener("touchcancel",a)}function r(t){if(t.touches.length>1)return a();e(t.changedTouches[0].clientX,t.changedTouches[0].clientY),window.addEventListener("touchmove",i),window.addEventListener("touchend",a),window.addEventListener("touchcancel",a)}function s(t){e(t.clientX,t.clientY),window.addEventListener("pointermove",o),window.addEventListener("pointerup",a),window.addEventListener("pointercancel",a)}return t.addEventListener("touchstart",r),t.addEventListener("pointerdown",s),function(){a(),t.removeEventListener(s),t.removeEventListener(r)}}function r(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=[],i=!0,o=!1,a=void 0;try{for(var r,s=t[Symbol.iterator]();!(i=(r=s.next()).done)&&(n.push(r.value),!e||n.length!==e);i=!0);}catch(t){o=!0,a=t}finally{try{i||null==s.return||s.return()}finally{if(o)throw a}}return n}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function s(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function l(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var c=function(){function i(t){var e=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,i),l(this,"onResize",function(t){e.setState({aspectRatio:e.getCanvasAspectRatio()})}),l(this,"onResizeThrottled",o(this.onResize,33)),l(this,"setState",function(t){e.state=function(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},i=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(i=i.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),i.forEach(function(e){l(t,e,n[e])})}return t}({},e.state,t),e.render()}),l(this,"fixSvgCoordinate",function(t){var n=e.props.precisionFactor;return Math.round(t*n)/n}),l(this,"mapX",function(t){var n=e.props,i=n.canvasWidth,o=n.data,a=o.minX;return(t-a)/(o.maxX-a)*i}),l(this,"mapY",function(t){var n=e.props,i=n.canvasWidth,o=n.minYGlobal;return(t-o)/(n.maxYGlobal-o)*i/e.state.aspectRatio}),l(this,"setUpTimelineWindowHandle",function(t){var n,i,o,r,s="left"===t?e.timelineWindowLeftHandle:e.timelineWindowRightHandle,l=parseFloat(getComputedStyle(e.timelineWindow).borderLeftWidth);return a(s,function(a){n=e.timeline.getBoundingClientRect();var s=e.timelineWindow.getBoundingClientRect();"left"===t?(i=n.left,o=s.left+s.width-2*l,r=a-s.left):(i=s.left+2*l,o=n.left+n.width,r=a-(s.left+s.width))},function(a){a-=r;var s=((a=Math.max(Math.min(a,o),i))-n.left)/n.width;"left"===t?e.updateBounds(s,e.props.toRatio):e.updateBounds(e.props.fromRatio,s)})}),l(this,"setUpTimelineWindow",function(){var t,n,i,o,r;return a(e.timelineWindowDrag,function(a){t=e.timeline.getBoundingClientRect(),n=e.timelineWindow.getBoundingClientRect(),r=a-n.left,i=t.left,o=t.left+(t.width-n.width)},function(a){a-=r;var s=((a=Math.max(Math.min(a,o),i))-t.left)/t.width;e.updateBounds(s,s+n.width/t.width)})}),l(this,"setTimelineWindowLeft",function(t){e.timelineOverlayLeft.style.right="".concat(100*(1-t),"%"),e.timelineWindow.style.left="".concat(100*t,"%")}),l(this,"setTimelineWindowRight",function(t){e.timelineOverlayRight.style.left="".concat(100*t,"%"),e.timelineWindow.style.right="".concat(100*(1-t),"%")}),this.props=t}var c,h,d;return c=i,(h=[{key:"componentDidUpdate",value:function(t){this.props!==t&&(this.props=t,this.state={aspectRatio:this.getCanvasAspectRatio()},this.onChangeBounds(this.props.fromRatio,this.props.toRatio),this.render())}},{key:"componentDidMount",value:function(){var t=this.props.rootNode;this.timeline=t.querySelector(".chartogram__timeline"),this.timelineOverlayLeft=t.querySelector(".chartogram__timeline-overlay-left"),this.timelineWindowLeftHandle=t.querySelector(".chartogram__timeline-window__left-handle"),this.timelineWindow=t.querySelector(".chartogram__timeline-window"),this.timelineWindowDrag=t.querySelector(".chartogram__timeline-window__drag"),this.timelineWindowRightHandle=t.querySelector(".chartogram__timeline-window__right-handle"),this.timelineOverlayRight=t.querySelector(".chartogram__timeline-overlay-right"),this.timelineCanvas=t.querySelector(".chartogram__timeline-canvas"),this.state={aspectRatio:this.getCanvasAspectRatio()},this.setUpTimelineWindowHandle("left"),this.setUpTimelineWindowHandle("right"),this.setUpTimelineWindow(),window.addEventListener("resize",this.onResizeThrottled),this.onChangeBounds(this.props.fromRatio,this.props.toRatio),this.render()}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.onResizeThrottled)}},{key:"getCanvasAspectRatio",value:function(){var t=this.timelineCanvas.getBoundingClientRect();return t.width/t.height}},{key:"render",value:function(){var n=this,i=this.props,o=i.canvasWidth,a=i.y,s=i.yScale,l=i.data,c=i.maxYGlobal,h=this.state.aspectRatio,d=l.x;e(this.timelineCanvas),this.timelineCanvas.setAttribute("viewBox","0 0 ".concat(o," ").concat(this.fixSvgCoordinate(o/h)));var u=function(){if(v){if(m>=p.length)return"break";f=p[m++]}else{if((m=p.next()).done)return"break";f=m.value}var e=f,i=e.id,o=e.color,l=e.points;if(a.find(function(t){return t.id===i}).isShown){var h=r(function e(n,i,o){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:Math.max.apply(Math,t(i)),r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:.025,s=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,l=arguments.length>6&&void 0!==arguments[6]?arguments[6]:new Array(n.length),c=arguments.length>7&&void 0!==arguments[7]?arguments[7]:new Array(n.length),h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:0;if(s+2>n.length-1){for(;s<i.length;)l[h]=n[s],c[h]=i[s],h++,s++;return l=l.slice(0,h),c=c.slice(0,h),l.length<=o?[l,c]:(n.length/l.length<1.1&&(r=Math.min(r+.025,1)),e(l,c,o,a,r))}var d=(i[s+2]+i[s])/2;return Math.abs(d-i[s+1])/a<r?(l[h]=n[s],l[h+1]=n[s+2],c[h]=i[s],c[h+1]=i[s+2],e(n,i,o,a,r,s+2,l,c,h+1)):(l[h]=n[s],l[h+1]=n[s+1],l[h+2]=n[s+2],c[h]=i[s],c[h+1]=i[s+1],c[h+2]=i[s+2],e(n,i,o,a,r,s+2,l,c,h+2))}(d.points,l,80),2),u=h[0],g=h[1],y=document.createElement("polyline");y.setAttribute("stroke",o),y.setAttribute("points",n.createPolylinePoints(u.map(n.mapX),g.map(function(t){return n.mapY(c-t*s)})).join(" ")),y.classList.add("chartogram__graph"),n.timelineCanvas.appendChild(y)}},p=l.y,v=Array.isArray(p),m=0;for(p=v?p:p[Symbol.iterator]();;){var f;if("break"===u())break}this.timelineCanvas.innerHTML+=""}},{key:"onChangeBounds",value:function(t,e){this.setTimelineWindowLeft(t),this.setTimelineWindowRight(e)}},{key:"updateBounds",value:function(t,e){this.props.onChangeBounds(t,e),this.props.fromRatio=t,this.props.toRatio=e,this.onChangeBounds(t,e)}},{key:"createPolylinePoints",value:function(t,e){return n(t.map(this.fixSvgCoordinate),e.map(this.fixSvgCoordinate))}}])&&s(c.prototype,h),d&&s(c,d),i}();function h(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function d(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}c.INITIAL_MARKUP='\n\t<div class="chartogram__timeline">\n\t\t<div class="chartogram__timeline-canvas-padding">\n\t\t\t<svg class="chartogram__timeline-canvas" preserveAspectRatio="none"></svg>\n\t\t</div>\n\t\t<div class="chartogram__timeline-overlay-left"></div>\n\t\t<div class="chartogram__timeline-overlay-right"></div>\n\t\t<div class="chartogram__timeline-window">\n\t\t\t<button type="button" class="chartogram__reset-button chartogram__timeline-window__drag"></button>\n\t\t\t<button type="button" class="chartogram__reset-button chartogram__timeline-window__left-handle"></button>\n\t\t\t<button type="button" class="chartogram__reset-button chartogram__timeline-window__right-handle"></button>\n\t\t</div>\n\t</div>\n';var u=function(){function t(e){var n=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),d(this,"createGraphToggler",function(t){var e=t.id,i=t.name,o=t.color,a=document.createElement("button");a.setAttribute("type","button"),a.classList.add("chartogram__chart-toggler"),a.classList.add("chartogram__chart-toggler--on"),a.classList.add("chartogram__reset-button");var r="http://www.w3.org/2000/svg",s=document.createElementNS(r,"svg");s.setAttribute("viewBox","0 0 19 19"),s.setAttribute("class","chartogram__chart-toggler-check");var l=document.createElementNS(r,"circle");l.setAttribute("cx","9.5"),l.setAttribute("cy","9.5"),l.setAttribute("r","9.5"),l.setAttribute("fill",o),s.appendChild(l);var c=document.createElementNS(r,"circle");c.setAttribute("cx","9.5"),c.setAttribute("cy","9.5"),c.setAttribute("r","8"),c.setAttribute("class","chartogram__chart-toggler-check-circle"),s.appendChild(c);var h=document.createElementNS(r,"path");return h.setAttribute("d","M13.64 4.94l-6.2 6.34-1.69-1.9c-.73-.63-1.89.1-1.36 1.06l2 3.38c.3.43 1.04.85 1.78 0 .32-.42 6.31-7.93 6.31-7.93.74-.84-.2-1.58-.84-.95z"),h.setAttribute("fill","white"),h.setAttribute("class","chartogram__chart-toggler-check-mark"),s.appendChild(h),a.appendChild(s),a.appendChild(document.createTextNode(i)),a.addEventListener("click",function(){return n.onToggle(e,a)}),a}),d(this,"onToggle",function(t,e){(0,n.props.onToggle)(t)&&e.classList.toggle("chartogram__chart-toggler--on")}),this.props=e}var n,i,o;return n=t,(i=[{key:"componentDidMount",value:function(){var t=this.props,n=t.rootNode,i=t.data,o=n.querySelector(".chartogram__chart-togglers");e(o);var a=i.y,r=Array.isArray(a),s=0;for(a=r?a:a[Symbol.iterator]();;){var l;if(r){if(s>=a.length)break;l=a[s++]}else{if((s=a.next()).done)break;l=s.value}var c=l;o.appendChild(this.createGraphToggler(c))}}}])&&h(n.prototype,i),o&&h(n,o),t}();function p(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function v(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},i=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(i=i.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),i.forEach(function(e){f(t,e,n[e])})}return t}function m(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function f(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}u.INITIAL_MARKUP='\n\t<div class="chartogram__chart-togglers"></div>\n';var g=function(){function t(n,a){var r=this,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Title",l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),f(this,"onResize",function(t){r.setState({aspectRatio:r.getCanvasAspectRatio()},!1)}),f(this,"onResizeThrottled",o(this.onResize,33)),f(this,"onChangeBounds",function(t,e){r.setState(r.createState(t,e),!1)}),f(this,"fixSvgCoordinate",function(t){var e=r.props.precisionFactor;return Math.round(t*e)/e}),f(this,"mapX",function(t){var e=r.props.canvasWidth,n=r.state,i=n.minX;return(t-i)/(n.maxX-i)*e}),f(this,"mapY",function(t){var e=r.props.canvasWidth,n=r.state,i=n.minY;return(t-i)/(n.maxY-i)*e/n.aspectRatio}),f(this,"createGridLine",function(t){var e=r.state,n=e.minX,i=e.maxX,o=(e.minY,e.maxY),a=e.yScale,s=document.createElement("line");return s.classList.add("chartogram__grid-line"),s.setAttribute("x1",r.fixSvgCoordinate(r.mapX(n))),s.setAttribute("x2",r.fixSvgCoordinate(r.mapX(i))),s.setAttribute("y1",r.fixSvgCoordinate(r.mapY(o-a*t))),s.setAttribute("y2",r.fixSvgCoordinate(r.mapY(o-a*t))),s}),f(this,"drawGauges",function(t,n,o,a,s){var l=r.props,c=l.gaugeTickMarksCount,h=l.months;e(r.xAxis),e(r.yAxis),i(r.xAxis,t,n,c,function(t){var e=new Date(t);return"".concat(h[e.getMonth()]," ").concat(e.getDate())}),i(r.yAxis,o,a,c),r.yAxis.style.height="".concat(100/s,"%")}),f(this,"setUpCanvasTooltipListener",function(){var t,e,n=r.props,i=n.weekdays,o=n.months,a=function(n){var a,s=r.state,l=s.minX,c=s.maxX,h=s.xPoints,d=s.y,u=l+(n-t.left)/t.width*(c-l),p=h.findIndex(function(t){return t>=u}),v=p-1;if(e(p)||(p=-1),e(v)||(v=-1),p<0){if(v<0)return r.removeTooltip();a=v}else if(v<0)a=p;else{var m=h[v],f=h[p];a=u-m>f-u?p:v}var g=h[a];if(g!==r.tooltipForX){r.tooltipForX=g,r.tooltip||r.addTooltip();var y=new Date(g);r.tooltipDate.textContent="".concat(i[y.getDay()],", ").concat(o[y.getMonth()]," ").concat(y.getDate());var w=0,b=d,_=Array.isArray(b),x=0;for(b=_?b:b[Symbol.iterator]();;){var S;if(_){if(x>=b.length)break;S=b[x++]}else{if((x=b.next()).done)break;S=x.value}var A=S,L=A.isShown,C=A.points,T=A.name;L&&(r.tooltipValues.childNodes[2*w].textContent=C[a],r.tooltipValues.childNodes[2*w+1].textContent=T,w++)}var k=(g-l)/(c-l);r.tooltip.style.left="".concat(100*k,"%"),r.updateTooltipPoints(a,k),r.updateTooltipLine(g)}},s=function(){t=r.canvas.getBoundingClientRect(),e=function(t){var e=r.state.xPoints;return t>=0&&t<e.length}},l=function(t){if(t.touches.length>1)return d();s(),r.canvas.addEventListener("touchend",d),r.canvas.addEventListener("touchmove",c),r.canvas.addEventListener("touchend",d),r.canvas.addEventListener("touchcancel",d),c(t)};function c(e){var n=e.changedTouches[0].clientX,i=e.changedTouches[0].clientY;n<t.left||n>t.left+t.width||i<t.top||i>t.top+t.height?d():a(n,i)}function h(t){a(t.clientX,t.clientY)}r.canvas.addEventListener("touchstart",l);var d=function t(){r.canvas.removeEventListener("pointermove",h),r.canvas.removeEventListener("pointerleave",t),r.canvas.removeEventListener("pointercancel",t),r.canvas.removeEventListener("touchmove",c),r.canvas.removeEventListener("touchend",t),r.canvas.removeEventListener("touchcancel",t),r.removeTooltip()},u=function(){s(),r.canvas.addEventListener("pointermove",h),r.canvas.addEventListener("pointerleave",d),r.canvas.addEventListener("pointercancel",d)};return r.canvas.addEventListener("pointerenter",u),function(){d(),r.canvas.removeEventListener(u),r.canvas.removeEventListener(l)}}),f(this,"addTooltip",function(){var t=r.state.y;r.tooltip=document.createElement("div"),r.tooltip.classList.add("chartogram__tooltip"),r.tooltipContainer.appendChild(r.tooltip),r.tooltipDate=document.createElement("h1"),r.tooltipDate.classList.add("chartogram__tooltip-header"),r.tooltip.appendChild(r.tooltipDate),r.tooltipValues=document.createElement("dl"),r.tooltipValues.classList.add("chartogram__tooltip-values"),r.tooltip.appendChild(r.tooltipValues);var e=t,n=Array.isArray(e),i=0;for(e=n?e:e[Symbol.iterator]();;){var o;if(n){if(i>=e.length)break;o=e[i++]}else{if((i=e.next()).done)break;o=i.value}var a=o,s=a.isShown,l=a.color;if(s){var c=document.createElement("dt");c.style.color=l,r.tooltipValues.appendChild(c);var h=document.createElement("dd");h.style.color=l,r.tooltipValues.appendChild(h)}}}),f(this,"addTooltipLine",function(){r.tooltipLine=document.createElementNS("http://www.w3.org/2000/svg","line"),r.tooltipLine.setAttributeNS(null,"class","chartogram__tooltip-line"),r.canvas.insertBefore(r.tooltipLine,r.canvas.querySelector("polyline"))}),f(this,"removeTooltip",function(){r.tooltip&&(r.tooltipForX=void 0,r.tooltipContainer.removeChild(r.tooltip),r.tooltip=void 0,r.removeTooltipPoints(),r.removeTooltipLine())}),f(this,"removeTooltipLine",function(){r.canvas.removeChild(r.tooltipLine),r.tooltipLine=void 0}),f(this,"addTooltipPoints",function(){r.tooltipPoints=[];var t=r.state.y,e=Array.isArray(t),n=0;for(t=e?t:t[Symbol.iterator]();;){var i;if(e){if(n>=t.length)break;i=t[n++]}else{if((n=t.next()).done)break;i=n.value}var o=i;if(o.isShown){var a=document.createElement("div");a.classList.add("chartogram__tooltip-point"),a.style.color=o.color,r.tooltipPoints.push(a),r.canvasWrapper.appendChild(a)}}}),f(this,"removeTooltipPoints",function(){var t=r.tooltipPoints,e=Array.isArray(t),n=0;for(t=e?t:t[Symbol.iterator]();;){var i;if(e){if(n>=t.length)break;i=t[n++]}else{if((n=t.next()).done)break;i=n.value}var o=i;r.canvasWrapper.removeChild(o)}r.tooltipPoints=void 0}),f(this,"updateTooltipLine",function(t){var e=r.props.canvasWidth,n=r.state.aspectRatio;r.tooltipLine||r.addTooltipLine(),r.tooltipLine.setAttributeNS(null,"x1",r.fixSvgCoordinate(r.mapX(t))),r.tooltipLine.setAttributeNS(null,"x2",r.fixSvgCoordinate(r.mapX(t))),r.tooltipLine.setAttributeNS(null,"y1",0),r.tooltipLine.setAttributeNS(null,"y2",r.fixSvgCoordinate(e/n))}),f(this,"updateTooltipPoints",function(t,e){var n=r.state,i=n.maxY,o=n.y;r.tooltipPoints||r.addTooltipPoints();for(var a=0,s=0;a<o.length;){if(o[a].isShown){var l=r.tooltipPoints[s],c=o[a].points[t]/i;l.style.left="".concat(100*e,"%"),l.style.bottom="".concat(100*c,"%"),s++}a++}}),this.props=v({title:s,gaugeTickMarksCount:6,timelineWindowSize:40,canvasWidth:512,precisionFactor:Math.pow(10,l.precision||3),months:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]},l),this.rootNode=n,this.data=v({},a,{minX:Math.min.apply(Math,p(a.x.points)),maxX:Math.max.apply(Math,p(a.x.points)),y:a.y.map(function(t){return v({},t,{min:Math.min.apply(Math,p(t.points)),max:Math.max.apply(Math,p(t.points))})})})}var a,r,s;return a=t,(r=[{key:"componentDidMount",value:function(){this.rootNode.classList.add("chartogram"),this.rootNode.innerHTML='\n\t\t\t<header class="chartogram__header">\n\t\t\t\t<h1 class="chartogram__title">'.concat(this.props.title,'</h1>\n\t\t\t</header>\n\t\t\t<div class="chartogram__plan-with-axes">\n\t\t\t\t<div class="chartogram__plan">\n\t\t\t\t\t<div class="chartogram__top-border"></div>\n\t\t\t\t\t<div class="chartogram__canvas-wrapper">\n\t\t\t\t\t\t<svg class="chartogram__canvas"></svg>\n\t\t\t\t\t\t<div class="chartogram__x"></div>\n\t\t\t\t\t\t<div class="chartogram__y-wrapper">\n\t\t\t\t\t\t\t<div class="chartogram__y"></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t').concat(c.INITIAL_MARKUP,"\n\t\t\t").concat(u.INITIAL_MARKUP,"\n\t\t"),this.tooltipContainer=this.rootNode.querySelector(".chartogram__plan"),this.canvas=this.rootNode.querySelector(".chartogram__canvas"),this.canvasWrapper=this.rootNode.querySelector(".chartogram__canvas-wrapper"),this.xAxis=this.rootNode.querySelector(".chartogram__x"),this.yAxis=this.rootNode.querySelector(".chartogram__y"),this.setUpCanvasTooltipListener(),this.state=this.getInitialState(),this.timeline=new c(this.getTimelineProps()),this.timeline.componentDidMount(),this.togglers=new u(this.getTogglersProps()),this.togglers.componentDidMount(),window.addEventListener("resize",this.onResizeThrottled),this.render()}},{key:"componentWillUnmount",value:function(){this.rootNode.classList.remove("chartogram"),e(this.rootNode),window.removeEventListener("resize",this.onResizeThrottled)}},{key:"getCanvasAspectRatio",value:function(){var t=this.canvas.getBoundingClientRect();return t.width/t.height}},{key:"setState",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.state=v({},this.state,t),this.render(),e&&this.timeline.componentDidUpdate(this.getTimelineProps())}},{key:"getTimelineProps",value:function(){return{rootNode:this.rootNode,data:this.data,canvasWidth:this.props.canvasWidth,precisionFactor:this.props.precisionFactor,fromRatio:this.state.fromRatio,toRatio:this.state.toRatio,minYGlobal:this.state.minYGlobal,maxYGlobal:this.state.maxYGlobal,y:this.state.y,yScale:this.state.yScale,onChangeBounds:this.onChangeBounds}}},{key:"getTogglersProps",value:function(){var t=this;return{rootNode:this.rootNode,data:this.data,onToggle:function(e){var n=t.state.y.find(function(t){return t.id===e});if(n.isShown&&1===t.state.y.filter(function(t){return t.isShown}).length)return;return n.isShown=!n.isShown,t.setState(v({y:t.state.y},t.calculateMinMaxY(t.state.y))),!0}}}},{key:"getInitialState",value:function(){var t,e=this.props.timelineWindowSize,n=this.data,i=n.minX,o=n.maxX;t=this.data.x.points.length>e?this.data.x.points.length-e:0;var a=(this.data.x.points[t]-i)/(o-i);return v({},this.createState(a,1),{aspectRatio:this.getCanvasAspectRatio(),yScale:1})}},{key:"createState",value:function(t,e){var n,i,o=this,a=this.data.x,r=this.data.minX+t*(this.data.maxX-this.data.minX),s=this.data.minX+e*(this.data.maxX-this.data.minX);n=r===this.data.minX?0:a.points.findIndex(function(t){return t>r})-1,i=s===this.data.maxX?a.points.length-1:a.points.findIndex(function(t){return t>s});var l=a.points.slice(n,i+1),c=l.slice();l.length>=2&&(a.points[n]!==r&&(c[0]=r),a.points[i]!==s&&(c[c.length-1]=s));var h=this.data.y.map(function(t,e){var c=o.data.y[e].points.slice(n,i+1),h=c.slice();if(l.length>=2){if(a.points[n]!==r){var d=o.data.y[e].points[n],u=d+(o.data.y[e].points[n+1]-d)*((r-o.data.x.points[n])/(o.data.x.points[n+1]-o.data.x.points[n]));h[0]=u}if(a.points[i]!==s){var m=o.data.y[e].points[i],f=o.data.y[e].points[i-1],g=f+(m-f)*((s-o.data.x.points[i-1])/(o.data.x.points[i]-o.data.x.points[i-1]));h[h.length-1]=g}}return v({},o.data.y[e],o.state?o.state.y[e]:{isShown:!0},{points:c,graphPoints:h,min:0,max:Math.max.apply(Math,p(h))})});return v({minX:r,maxX:s,fromIndex:n,toIndex:i,fromRatio:t,toRatio:e,xPoints:l,xGraphPoints:c},this.calculateMinMaxY(h),{y:h})}},{key:"calculateMinMaxY",value:function(t){var e=1/0,n=-1/0,i=t,o=Array.isArray(i),a=0;for(i=o?i:i[Symbol.iterator]();;){var r;if(o){if(a>=i.length)break;r=i[a++]}else{if((a=i.next()).done)break;r=a.value}var s=r;s.isShown&&(e=Math.min(e,s.min),n=Math.max(n,s.max))}var l=1/0,c=-1/0,h=function(){if(u){if(p>=d.length)return"break";v=d[p++]}else{if((p=d.next()).done)return"break";v=p.value}var e=v;t.find(function(t){return t.id===e.id}).isShown&&(l=Math.min(l,e.min),c=Math.max(c,e.max))},d=this.data.y,u=Array.isArray(d),p=0;for(d=u?d:d[Symbol.iterator]();;){var v;if("break"===h())break}return{minY:e,maxY:n,minYGlobal:l=0,maxYGlobal:c}}},{key:"createPolylinePoints",value:function(t,e){return n(t.map(this.fixSvgCoordinate),e.map(this.fixSvgCoordinate))}},{key:"render",value:function(){var t=this,n=this.props,i=n.canvasWidth,o=n.gaugeTickMarksCount,a=this.state,r=a.minX,s=a.maxX,l=a.minY,c=a.maxY,h=a.yScale,d=a.xGraphPoints,u=a.aspectRatio;e(this.canvas),this.canvas.setAttribute("viewBox","0 0 ".concat(i," ").concat(this.fixSvgCoordinate(i/u)));var p=l,v=function(t,e){for(t=Math.floor(t);;){if(t<e)return e;if(t%e==0)return t;t--}}(c,10),m=(c-l)/(v-p),f=function(t,e,n){for(var i=new Array(n),o=0;o<n;)i[o]=t+o*(e-t)/(n-1),o++;return i}(p,v,o),g=Array.isArray(f),y=0;for(f=g?f:f[Symbol.iterator]();;){var w;if(g){if(y>=f.length)break;w=f[y++]}else{if((y=f.next()).done)break;w=y.value}var b=w;this.canvas.appendChild(this.createGridLine(b))}var _=this.state.y,x=Array.isArray(_),S=0;for(_=x?_:_[Symbol.iterator]();;){var A;if(x){if(S>=_.length)break;A=_[S++]}else{if((S=_.next()).done)break;A=S.value}var L=A,C=L.color,T=L.graphPoints;if(L.isShown){var k=document.createElement("polyline");k.setAttribute("stroke",C),k.setAttribute("points",this.createPolylinePoints(d.map(this.mapX),T.map(function(e){return t.mapY(c-e*h)})).join(" ")),k.classList.add("chartogram__graph"),this.canvas.appendChild(k)}}this.canvas.innerHTML+="",this.drawGauges(r,s,p,v,m)}}])&&m(a.prototype,r),s&&m(a,s),t}();return function(t,e,n,i){var o=new g(t,e,n,i);return o.componentDidMount(),function(){o.componentWillUnmount()}}}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).chartogram=e()}(this,function(){"use strict";function t(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function e(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function n(t,e,n,i,a){for(var o=0;o<i;){var r=document.createElement("div"),s=e+o*(n-e)/(i-1);a&&(s=a(s)),r.appendChild(document.createTextNode(s)),t.appendChild(r),o++}}function i(t,e){var n,i=0,a=function(){n=void 0,i=Date.now(),t()};return function(){var o=Date.now(),r=e-(o-i);r<=0?(n&&(clearTimeout(n),n=void 0),i=o,t()):n||(n=setTimeout(a,r))}}function a(t,e,n){function i(t){n(t.changedTouches[0].clientX,t.changedTouches[0].clientY)}function a(t){n(t.clientX,t.clientY)}function o(){window.removeEventListener("pointermove",a),window.removeEventListener("touchmove",i),window.removeEventListener("pointerup",o),window.removeEventListener("pointercancel",o),window.removeEventListener("touchend",o),window.removeEventListener("touchcancel",o)}function r(t){if(t.touches.length>1)return o();e(t.changedTouches[0].clientX,t.changedTouches[0].clientY),window.addEventListener("touchmove",i),window.addEventListener("touchend",o),window.addEventListener("touchcancel",o)}function s(t){e(t.clientX,t.clientY),window.addEventListener("pointermove",a),window.addEventListener("pointerup",o),window.addEventListener("pointercancel",o)}return t.addEventListener("touchstart",r),t.addEventListener("pointerdown",s),function(){o(),t.removeEventListener(s),t.removeEventListener(r)}}function o(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=[],i=!0,a=!1,o=void 0;try{for(var r,s=t[Symbol.iterator]();!(i=(r=s.next()).done)&&(n.push(r.value),!e||n.length!==e);i=!0);}catch(t){a=!0,o=t}finally{try{i||null==s.return||s.return()}finally{if(a)throw o}}return n}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function r(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function s(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var l=function(){function n(t){var e=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,n),s(this,"onResize",function(t){e.setState({aspectRatio:e.getCanvasAspectRatio()})}),s(this,"onResizeThrottled",i(this.onResize,33)),s(this,"setState",function(t){e.state=function(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},i=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(i=i.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),i.forEach(function(e){s(t,e,n[e])})}return t}({},e.state,t),e.render()}),s(this,"mapX",function(t){var n=e.props,i=n.canvasWidth,a=n.data,o=a.minX;return(t-o)/(a.maxX-o)*i}),s(this,"mapY",function(t){var n=e.props,i=n.canvasWidth,a=n.minYGlobal;return(t-a)/(n.maxYGlobal-a)*i/e.state.aspectRatio}),s(this,"setUpTimelineWindowHandle",function(t){var n,i,o,r,s="left"===t?e.timelineWindowLeftHandle:e.timelineWindowRightHandle,l=parseFloat(getComputedStyle(e.timelineWindow).borderLeftWidth);return a(s,function(a){n=e.timeline.getBoundingClientRect();var s=e.timelineWindow.getBoundingClientRect();"left"===t?(i=n.left,o=s.left+s.width-2*l,r=a-s.left):(i=s.left+2*l,o=n.left+n.width,r=a-(s.left+s.width))},function(a){a-=r;var s=((a=Math.max(Math.min(a,o),i))-n.left)/n.width;"left"===t?e.updateBounds(s,e.props.toRatio):e.updateBounds(e.props.fromRatio,s)})}),s(this,"setUpTimelineWindow",function(){var t,n,i,o,r;return a(e.timelineWindowDrag,function(a){t=e.timeline.getBoundingClientRect(),n=e.timelineWindow.getBoundingClientRect(),r=a-n.left,i=t.left,o=t.left+(t.width-n.width)},function(a){a-=r;var s=((a=Math.max(Math.min(a,o),i))-t.left)/t.width;e.updateBounds(s,s+n.width/t.width)})}),s(this,"setTimelineWindowLeft",function(t){e.timelineOverlayLeft.style.right="".concat(100*(1-t),"%"),e.timelineWindow.style.left="".concat(100*t,"%")}),s(this,"setTimelineWindowRight",function(t){e.timelineOverlayRight.style.left="".concat(100*t,"%"),e.timelineWindow.style.right="".concat(100*(1-t),"%")}),this.props=t}var l,c,h;return l=n,(c=[{key:"componentDidUpdate",value:function(t){this.props!==t&&(this.props=t,this.state={aspectRatio:this.getCanvasAspectRatio()},this.onChangeBounds(this.props.fromRatio,this.props.toRatio),this.render())}},{key:"componentDidMount",value:function(){var t=this.props.rootNode;this.timeline=t.querySelector(".chartogram__timeline"),this.timelineOverlayLeft=t.querySelector(".chartogram__timeline-overlay-left"),this.timelineWindowLeftHandle=t.querySelector(".chartogram__timeline-window__left-handle"),this.timelineWindow=t.querySelector(".chartogram__timeline-window"),this.timelineWindowDrag=t.querySelector(".chartogram__timeline-window__drag"),this.timelineWindowRightHandle=t.querySelector(".chartogram__timeline-window__right-handle"),this.timelineOverlayRight=t.querySelector(".chartogram__timeline-overlay-right"),this.timelineCanvas=t.querySelector(".chartogram__timeline-canvas"),this.state={aspectRatio:this.getCanvasAspectRatio()},this.setUpTimelineWindowHandle("left"),this.setUpTimelineWindowHandle("right"),this.setUpTimelineWindow(),window.addEventListener("resize",this.onResizeThrottled),this.onChangeBounds(this.props.fromRatio,this.props.toRatio),this.render()}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.onResizeThrottled)}},{key:"getCanvasAspectRatio",value:function(){var t=this.timelineCanvas.getBoundingClientRect();return t.width/t.height}},{key:"render",value:function(){var n=this,i=this.props,a=i.canvasWidth,r=i.y,s=i.data,l=i.maxYGlobal,c=i.fixSvgCoordinate,h=i.createPolylinePoints,u=this.state.aspectRatio;e(this.timelineCanvas),this.timelineCanvas.setAttribute("viewBox","0 0 ".concat(a," ").concat(c(a/u)));var d=function(){if(m){if(v>=p.length)return"break";f=p[v++]}else{if((v=p.next()).done)return"break";f=v.value}var e=f,i=e.id,a=e.color,c=e.points;if(r.find(function(t){return t.id===i}).isShown){var u=o(function e(n,i,a){var o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:Math.max.apply(Math,t(i)),r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:.025,s=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0,l=arguments.length>6&&void 0!==arguments[6]?arguments[6]:new Array(n.length),c=arguments.length>7&&void 0!==arguments[7]?arguments[7]:new Array(n.length),h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:0;if(s+2>n.length-1){for(;s<i.length;)l[h]=n[s],c[h]=i[s],h++,s++;return l=l.slice(0,h),c=c.slice(0,h),l.length<=a?[l,c]:(n.length/l.length<1.1&&(r=Math.min(r+.025,1)),e(l,c,a,o,r))}var u=(i[s+2]+i[s])/2;return Math.abs(u-i[s+1])/o<r?(l[h]=n[s],l[h+1]=n[s+2],c[h]=i[s],c[h+1]=i[s+2],e(n,i,a,o,r,s+2,l,c,h+1)):(l[h]=n[s],l[h+1]=n[s+1],l[h+2]=n[s+2],c[h]=i[s],c[h+1]=i[s+1],c[h+2]=i[s+2],e(n,i,a,o,r,s+2,l,c,h+2))}(s.x.points,c,80),2),d=u[0],g=u[1],y=document.createElement("polyline");y.setAttribute("stroke",a),y.setAttribute("points",h(d.map(n.mapX),g.map(function(t){return n.mapY(l-t)})).join(" ")),y.classList.add("chartogram__graph"),n.timelineCanvas.appendChild(y)}},p=s.y,m=Array.isArray(p),v=0;for(p=m?p:p[Symbol.iterator]();;){var f;if("break"===d())break}this.timelineCanvas.innerHTML+=""}},{key:"onChangeBounds",value:function(t,e){this.setTimelineWindowLeft(t),this.setTimelineWindowRight(e)}},{key:"updateBounds",value:function(t,e){this.props.onChangeBounds(t,e),this.props.fromRatio=t,this.props.toRatio=e,this.onChangeBounds(t,e)}}])&&r(l.prototype,c),h&&r(l,h),n}();function c(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function h(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}l.INITIAL_MARKUP='\n\t<div class="chartogram__timeline">\n\t\t<div class="chartogram__timeline-canvas-padding">\n\t\t\t<svg class="chartogram__timeline-canvas" preserveAspectRatio="none"></svg>\n\t\t</div>\n\t\t<div class="chartogram__timeline-overlay-left"></div>\n\t\t<div class="chartogram__timeline-overlay-right"></div>\n\t\t<div class="chartogram__timeline-window">\n\t\t\t<button type="button" class="chartogram__reset-button chartogram__timeline-window__drag"></button>\n\t\t\t<button type="button" class="chartogram__reset-button chartogram__timeline-window__left-handle"></button>\n\t\t\t<button type="button" class="chartogram__reset-button chartogram__timeline-window__right-handle"></button>\n\t\t</div>\n\t</div>\n';var u=function(){function t(e){var n=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),h(this,"createGraphToggler",function(t){var e=t.id,i=t.name,a=t.color,o=document.createElement("button");o.setAttribute("type","button"),o.classList.add("chartogram__chart-toggler"),o.classList.add("chartogram__chart-toggler--on"),o.classList.add("chartogram__reset-button");var r="http://www.w3.org/2000/svg",s=document.createElementNS(r,"svg");s.setAttribute("viewBox","0 0 19 19"),s.setAttribute("class","chartogram__chart-toggler-check");var l=document.createElementNS(r,"circle");l.setAttribute("cx","9.5"),l.setAttribute("cy","9.5"),l.setAttribute("r","9.5"),l.setAttribute("fill",a),s.appendChild(l);var c=document.createElementNS(r,"circle");c.setAttribute("cx","9.5"),c.setAttribute("cy","9.5"),c.setAttribute("r","8"),c.setAttribute("class","chartogram__chart-toggler-check-circle"),s.appendChild(c);var h=document.createElementNS(r,"path");return h.setAttribute("d","M13.64 4.94l-6.2 6.34-1.69-1.9c-.73-.63-1.89.1-1.36 1.06l2 3.38c.3.43 1.04.85 1.78 0 .32-.42 6.31-7.93 6.31-7.93.74-.84-.2-1.58-.84-.95z"),h.setAttribute("fill","white"),h.setAttribute("class","chartogram__chart-toggler-check-mark"),s.appendChild(h),o.appendChild(s),o.appendChild(document.createTextNode(i)),o.addEventListener("click",function(){return n.onToggle(e,o)}),o}),h(this,"onToggle",function(t,e){(0,n.props.onToggle)(t)&&e.classList.toggle("chartogram__chart-toggler--on")}),this.props=e}var n,i,a;return n=t,(i=[{key:"componentDidMount",value:function(){var t=this.props,n=t.rootNode,i=t.data,a=n.querySelector(".chartogram__chart-togglers");e(a);var o=i.y,r=Array.isArray(o),s=0;for(o=r?o:o[Symbol.iterator]();;){var l;if(r){if(s>=o.length)break;l=o[s++]}else{if((s=o.next()).done)break;l=s.value}var c=l;a.appendChild(this.createGraphToggler(c))}}}])&&c(n.prototype,i),a&&c(n,a),t}();function d(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function p(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}u.INITIAL_MARKUP='\n\t<div class="chartogram__chart-togglers"></div>\n';var m=function(){function t(e){var n=this;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),p(this,"setUpListener",function(){var t,e,i=n.props,a=i.canvas,o=i.canvasWidthPx,r=i.weekdays,s=i.months,l=function(i){var a,l=n.props,c=l.minX,h=l.maxX,u=l.xPoints,d=l.y,p=c+(i-t.left)/t.width*(h-c),m=u.findIndex(function(t){return t>=p}),v=m-1;if(e(m)||(m=-1),e(v)||(v=-1),m<0){if(v<0)return n.unmount();a=v}else if(v<0)a=m;else{var f=u[v],g=u[m];a=p-f>g-p?m:v}var y=u[a];if(y!==n.tooltipForX){n.tooltipForX=y,n.tooltip||n.mount();var b=new Date(y);n.tooltipDate.textContent="".concat(r[b.getDay()],", ").concat(s[b.getMonth()]," ").concat(b.getDate());var w=0,x=d,_=Array.isArray(x),S=0;for(x=_?x:x[Symbol.iterator]();;){var A;if(_){if(S>=x.length)break;A=x[S++]}else{if((S=x.next()).done)break;A=S.value}var C=A,k=C.isShown,L=C.points,T=C.name;k&&(n.tooltipValues.childNodes[2*w].textContent=L[a],n.tooltipValues.childNodes[2*w+1].textContent=T,w++)}var E=(y-c)/(h-c),R=E*o;R-=40;var Y=n.tooltip.getBoundingClientRect().width;R<0?R=0:R+Y>o&&(R=o-Y),n.tooltip.style.left=R+"px",n.updatePoints(a,E),n.updateLine(y)}},c=function(){t=a.getBoundingClientRect(),e=function(t){var e=n.props.xPoints;return t>=0&&t<e.length}},h=function(t){if(t.touches.length>1)return p();c(),a.addEventListener("touchend",p),a.addEventListener("touchmove",u),a.addEventListener("touchend",p),a.addEventListener("touchcancel",p),u(t)};function u(e){var n=e.changedTouches[0].clientX,i=e.changedTouches[0].clientY;n<t.left||n>t.left+t.width||i<t.top||i>t.top+t.height?p():l(n,i)}function d(t){l(t.clientX,t.clientY)}a.addEventListener("touchstart",h);var p=function t(){a.removeEventListener("pointermove",d),a.removeEventListener("pointerleave",t),a.removeEventListener("pointercancel",t),a.removeEventListener("touchmove",u),a.removeEventListener("touchend",t),a.removeEventListener("touchcancel",t),n.unmount()},m=function(){c(),a.addEventListener("pointermove",d),a.addEventListener("pointerleave",p),a.addEventListener("pointercancel",p)};return a.addEventListener("pointerenter",m),function(){p(),a.removeEventListener(m),a.removeEventListener(h)}}),p(this,"unmountLine",function(){n.props.canvas.removeChild(n.line),n.line=void 0}),p(this,"unmountPoints",function(){var t=n.props.pointsContainer,e=n.points,i=Array.isArray(e),a=0;for(e=i?e:e[Symbol.iterator]();;){var o;if(i){if(a>=e.length)break;o=e[a++]}else{if((a=e.next()).done)break;o=a.value}var r=o;t.removeChild(r)}n.points=void 0}),p(this,"updateLine",function(t){var e=n.props,i=e.canvasWidth,a=e.aspectRatio,o=e.fixSvgCoordinate,r=e.mapX;n.line||n.renderLine(),n.line.setAttributeNS(null,"x1",o(r(t))),n.line.setAttributeNS(null,"x2",o(r(t))),n.line.setAttributeNS(null,"y1",0),n.line.setAttributeNS(null,"y2",o(i/a))}),p(this,"updatePoints",function(t,e){var i=n.props,a=i.maxY,o=i.y;n.points||n.renderPoints();for(var r=0,s=0;r<o.length;){if(o[r].isShown){var l=n.points[s],c=o[r].points[t]/a;l.style.left="".concat(100*e,"%"),l.style.bottom="".concat(100*c,"%"),s++}r++}}),this.props=e}var e,n,i;return e=t,(n=[{key:"componentDidMount",value:function(){this.unlisten=this.setUpListener()}},{key:"componentWillUnmount",value:function(){this.unlisten(),this.unmount()}},{key:"componentDidUpdate",value:function(t){this.props!==t&&(this.props=t)}},{key:"mount",value:function(){var t=this.props,e=t.y,n=t.container;this.tooltip=document.createElement("div"),this.tooltip.classList.add("chartogram__tooltip"),n.appendChild(this.tooltip),this.tooltipDate=document.createElement("h1"),this.tooltipDate.classList.add("chartogram__tooltip-header"),this.tooltip.appendChild(this.tooltipDate),this.tooltipValues=document.createElement("dl"),this.tooltipValues.classList.add("chartogram__tooltip-values"),this.tooltip.appendChild(this.tooltipValues);var i=e,a=Array.isArray(i),o=0;for(i=a?i:i[Symbol.iterator]();;){var r;if(a){if(o>=i.length)break;r=i[o++]}else{if((o=i.next()).done)break;r=o.value}var s=r,l=s.isShown,c=s.color;if(l){var h=document.createElement("dt");h.style.color=c,this.tooltipValues.appendChild(h);var u=document.createElement("dd");u.style.color=c,this.tooltipValues.appendChild(u)}}}},{key:"renderLine",value:function(){var t=this.props.canvas;this.line=document.createElementNS("http://www.w3.org/2000/svg","line"),this.line.setAttributeNS(null,"class","chartogram__tooltip-line"),t.insertBefore(this.line,t.querySelector("polyline"))}},{key:"unmount",value:function(){if(this.tooltip){var t=this.props.container;this.tooltipForX=void 0,t.removeChild(this.tooltip),this.tooltip=void 0,this.unmountPoints(),this.unmountLine()}}},{key:"renderPoints",value:function(){this.points=[];var t=this.props,e=t.pointsContainer,n=t.y,i=Array.isArray(n),a=0;for(n=i?n:n[Symbol.iterator]();;){var o;if(i){if(a>=n.length)break;o=n[a++]}else{if((a=n.next()).done)break;o=a.value}var r=o;if(r.isShown){var s=document.createElement("div");s.classList.add("chartogram__tooltip-point"),s.style.color=r.color,this.points.push(s),e.appendChild(s)}}}}])&&d(e.prototype,n),i&&d(e,i),t}();function v(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function f(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},i=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(i=i.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),i.forEach(function(e){y(t,e,n[e])})}return t}function g(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function y(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var b=function(){function t(a,o){var r=this,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Title",l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),y(this,"onResize",function(t){r.setState({aspectRatio:r.getCanvasAspectRatio(),canvasWidthPx:r.getCanvasWidthPx()},!1)}),y(this,"onResizeThrottled",i(this.onResize,33)),y(this,"onToggle",function(t){var e=r.state.y.find(function(e){return e.id===t});if(e.isShown&&1===r.state.y.filter(function(t){return t.isShown}).length)return;e.isShown=!e.isShown,r.setState({y:r.state.y});var n=r.calculateMinMaxY(r.state.y),i=n.minY,a=n.maxY,o=n.minYGlobal,s=n.maxYGlobal;return r.transitionState(i,a,o,s),!0}),y(this,"onChangeBounds",function(t,e){r.setState(r.createState(t,e),!1)}),y(this,"createPolylinePoints",function(t,e){return n=t.map(r.fixSvgCoordinate),i=e.map(r.fixSvgCoordinate),n.map(function(t,e){return"".concat(t,",").concat(i[e])});var n,i}),y(this,"fixSvgCoordinate",function(t){var e=r.props.precisionFactor;return Math.round(t*e)/e}),y(this,"mapX",function(t){var e=r.props.canvasWidth,n=r.state,i=n.minX;return(t-i)/(n.maxX-i)*e}),y(this,"mapY",function(t){var e=r.props.canvasWidth,n=r.state,i=n.minY;return(t-i)/(n.maxY-i)*e/n.aspectRatio}),y(this,"transitionStateTick",function(){var t=r.props,e=t.transitionDuration,n=t.transitionEasing,i=r.state,a=i.transitionStartedAt,o=i.minYFrom,s=i.minYTo,l=i.maxYFrom,c=i.maxYTo,h=i.minYGlobalFrom,u=i.minYGlobalTo,d=i.maxYGlobalFrom,p=i.maxYGlobalTo,m=Date.now()-a,v=S[n](Math.min(m/e,1));r.setState({minY:o+(s-o)*v,maxY:l+(c-l)*v,minYGlobal:h+(u-h)*v,maxYGlobal:d+(p-d)*v}),r.transition=v<1?requestAnimationFrame(r.transitionStateTick):void 0}),y(this,"createGridLine",function(t){var e=r.state,n=e.minX,i=e.maxX,a=(e.minY,e.maxY),o=document.createElement("line");return o.classList.add("chartogram__grid-line"),o.setAttribute("x1",r.fixSvgCoordinate(r.mapX(n))),o.setAttribute("x2",r.fixSvgCoordinate(r.mapX(i))),o.setAttribute("y1",r.fixSvgCoordinate(r.mapY(a-t))),o.setAttribute("y2",r.fixSvgCoordinate(r.mapY(a-t))),o}),y(this,"drawGauges",function(t,i,a,o,s){var l=r.props,c=l.gaugeTickMarksCount,h=l.months;e(r.xAxis),e(r.yAxis),n(r.xAxis,t,i,c,function(t){var e=new Date(t);return"".concat(h[e.getMonth()]," ").concat(e.getDate())}),n(r.yAxis,a,o,c),r.yAxis.style.height="".concat(100/s,"%")}),this.props=f({title:s,transitionDuration:300,transitionEasing:"easeInOutSin",gaugeTickMarksCount:6,timelineWindowSize:40,canvasWidth:512,precisionFactor:Math.pow(10,l.precision||3),months:w,weekdays:x},l),this.rootNode=a,this.data=f({},o,{minX:Math.min.apply(Math,v(o.x.points)),maxX:Math.max.apply(Math,v(o.x.points)),y:o.y.map(function(t){return f({},t,{min:Math.min.apply(Math,v(t.points)),max:Math.max.apply(Math,v(t.points))})})})}var a,o,r;return a=t,(o=[{key:"componentDidMount",value:function(){this.rootNode.classList.add("chartogram"),this.rootNode.innerHTML="\n\t\t\t".concat(_.replace("{title}",this.props.title),"\n\t\t\t").concat(l.INITIAL_MARKUP,"\n\t\t\t").concat(u.INITIAL_MARKUP,"\n\t\t"),this.tooltipContainer=this.rootNode.querySelector(".chartogram__plan"),this.canvas=this.rootNode.querySelector(".chartogram__canvas"),this.canvasWrapper=this.rootNode.querySelector(".chartogram__canvas-wrapper"),this.xAxis=this.rootNode.querySelector(".chartogram__x"),this.yAxis=this.rootNode.querySelector(".chartogram__y"),this.state=this.getInitialState(),this.timeline=new l(this.getTimelineProps()),this.timeline.componentDidMount(),this.togglers=new u(this.getTogglersProps()),this.togglers.componentDidMount(),this.tooltip=new m(this.getTooltipProps()),this.tooltip.componentDidMount(),window.addEventListener("resize",this.onResizeThrottled),this.render()}},{key:"componentWillUnmount",value:function(){this.timeline.componentWillUnmount(),this.rootNode.classList.remove("chartogram"),e(this.rootNode),window.removeEventListener("resize",this.onResizeThrottled)}},{key:"getCanvasAspectRatio",value:function(){var t=this.canvas.getBoundingClientRect();return t.width/t.height}},{key:"getCanvasWidthPx",value:function(){return this.canvas.getBoundingClientRect().width}},{key:"setState",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.state=f({},this.state,t),this.render(),e&&this.timeline.componentDidUpdate(this.getTimelineProps()),this.tooltip.componentDidUpdate(this.getTooltipProps())}},{key:"getTimelineProps",value:function(){return{rootNode:this.rootNode,data:this.data,canvasWidth:this.props.canvasWidth,fixSvgCoordinate:this.fixSvgCoordinate,createPolylinePoints:this.createPolylinePoints,fromRatio:this.state.fromRatio,toRatio:this.state.toRatio,minYGlobal:this.state.minYGlobal,maxYGlobal:this.state.maxYGlobal,y:this.state.y,onChangeBounds:this.onChangeBounds}}},{key:"getTogglersProps",value:function(){return{rootNode:this.rootNode,data:this.data,onToggle:this.onToggle}}},{key:"getTooltipProps",value:function(){return{canvas:this.canvas,canvasWidthPx:this.state.canvasWidthPx,container:this.tooltipContainer,pointsContainer:this.canvasWrapper,weekdays:this.props.weekdays,months:this.props.months,canvasWidth:this.props.canvasWidth,aspectRatio:this.state.aspectRatio,mapX:this.mapX,fixSvgCoordinate:this.fixSvgCoordinate,minX:this.state.minX,maxX:this.state.maxX,maxY:this.state.maxY,xPoints:this.state.xPoints,y:this.state.y}}},{key:"getInitialState",value:function(){var t,e=this.props.timelineWindowSize,n=this.data,i=n.minX,a=n.maxX;t=this.data.x.points.length>e?this.data.x.points.length-e:0;var o=(this.data.x.points[t]-i)/(a-i);return f({},this.createState(o,1),{aspectRatio:this.getCanvasAspectRatio(),canvasWidthPx:this.getCanvasWidthPx()})}},{key:"createState",value:function(t,e){var n,i,a=this,o=this.data.x,r=this.data.minX+t*(this.data.maxX-this.data.minX),s=this.data.minX+e*(this.data.maxX-this.data.minX);n=r===this.data.minX?0:o.points.findIndex(function(t){return t>r})-1,i=s===this.data.maxX?o.points.length-1:o.points.findIndex(function(t){return t>s});var l=o.points.slice(n,i+1),c=l.slice();l.length>=2&&(o.points[n]!==r&&(c[0]=r),o.points[i]!==s&&(c[c.length-1]=s));var h=this.data.y.map(function(t,e){var c=a.data.y[e].points.slice(n,i+1),h=c.slice();if(l.length>=2){if(o.points[n]!==r){var u=a.data.y[e].points[n],d=u+(a.data.y[e].points[n+1]-u)*((r-a.data.x.points[n])/(a.data.x.points[n+1]-a.data.x.points[n]));h[0]=d}if(o.points[i]!==s){var p=a.data.y[e].points[i],m=a.data.y[e].points[i-1],g=m+(p-m)*((s-a.data.x.points[i-1])/(a.data.x.points[i]-a.data.x.points[i-1]));h[h.length-1]=g}}return f({},a.data.y[e],a.state?a.state.y[e]:{isShown:!0},{points:c,graphPoints:h,min:0,max:Math.max.apply(Math,v(h))})});return f({minX:r,maxX:s,fromIndex:n,toIndex:i,fromRatio:t,toRatio:e,xPoints:l,xGraphPoints:c},this.calculateMinMaxY(h),{y:h})}},{key:"calculateMinMaxY",value:function(t){var e=1/0,n=-1/0,i=t,a=Array.isArray(i),o=0;for(i=a?i:i[Symbol.iterator]();;){var r;if(a){if(o>=i.length)break;r=i[o++]}else{if((o=i.next()).done)break;r=o.value}var s=r;s.isShown&&(e=Math.min(e,s.min),n=Math.max(n,s.max))}var l=1/0,c=-1/0,h=function(){if(d){if(p>=u.length)return"break";m=u[p++]}else{if((p=u.next()).done)return"break";m=p.value}var e=m;t.find(function(t){return t.id===e.id}).isShown&&(l=Math.min(l,e.min),c=Math.max(c,e.max))},u=this.data.y,d=Array.isArray(u),p=0;for(u=d?u:u[Symbol.iterator]();;){var m;if("break"===h())break}return{minY:e,maxY:n,minYGlobal:l=0,maxYGlobal:c}}},{key:"render",value:function(){var t=this,n=this.props,i=n.canvasWidth,a=n.gaugeTickMarksCount,o=this.state,r=o.minX,s=o.maxX,l=o.minY,c=o.maxY,h=o.xGraphPoints,u=o.aspectRatio;e(this.canvas),this.canvas.setAttribute("viewBox","0 0 ".concat(i," ").concat(this.fixSvgCoordinate(i/u)));var d=l,p=function(t,e){for(t=Math.floor(t);;){if(t<e)return e;if(t%e==0)return t;t--}}(c,10),m=(c-l)/(p-d),v=function(t,e,n){for(var i=new Array(n),a=0;a<n;)i[a]=t+a*(e-t)/(n-1),a++;return i}(d,p,a),f=Array.isArray(v),g=0;for(v=f?v:v[Symbol.iterator]();;){var y;if(f){if(g>=v.length)break;y=v[g++]}else{if((g=v.next()).done)break;y=g.value}var b=y;this.canvas.appendChild(this.createGridLine(b))}var w=this.state.y,x=Array.isArray(w),_=0;for(w=x?w:w[Symbol.iterator]();;){var S;if(x){if(_>=w.length)break;S=w[_++]}else{if((_=w.next()).done)break;S=_.value}var A=S,C=A.color,k=A.graphPoints;if(A.isShown){var L=document.createElement("polyline");L.setAttribute("stroke",C),L.setAttribute("points",this.createPolylinePoints(h.map(this.mapX),k.map(function(e){return t.mapY(c-e)})).join(" ")),L.classList.add("chartogram__graph"),this.canvas.appendChild(L)}}this.canvas.innerHTML+="",this.drawGauges(r,s,d,p,m)}},{key:"transitionState",value:function(t,e,n,i){this.transition&&cancelAnimationFrame(this.transition),this.setState({minYFrom:this.state.minY,maxYFrom:this.state.maxY,minYTo:t,maxYTo:e,minYGlobalFrom:this.state.minYGlobal,maxYGlobalFrom:this.state.maxYGlobal,minYGlobalTo:n,maxYGlobalTo:i,transitionStartedAt:Date.now()}),this.transition=requestAnimationFrame(this.transitionStateTick)}}])&&g(a.prototype,o),r&&g(a,r),t}(),w=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],x=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],_='\n\t<header class="chartogram__header">\n\t\t<h1 class="chartogram__title">{title}</h1>\n\t</header>\n\t<div class="chartogram__plan-with-axes">\n\t\t<div class="chartogram__plan">\n\t\t\t<div class="chartogram__top-border"></div>\n\t\t\t<div class="chartogram__canvas-wrapper">\n\t\t\t\t<svg class="chartogram__canvas"></svg>\n\t\t\t\t<div class="chartogram__x"></div>\n\t\t\t\t<div class="chartogram__y-wrapper">\n\t\t\t\t\t<div class="chartogram__y"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n',S={easeInOutSin:function(t){return(1+Math.sin(Math.PI*t-Math.PI/2))/2}};return function(t,e,n,i){var a=new b(t,e,n,i);return a.componentDidMount(),function(){a.componentWillUnmount()}}}); | ||
//# sourceMappingURL=chartogram.js.map |
@@ -14,2 +14,4 @@ "use strict"; | ||
var _Tooltip = _interopRequireDefault(require("./Tooltip")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -48,3 +50,4 @@ | ||
_this.setState({ | ||
aspectRatio: _this.getCanvasAspectRatio() | ||
aspectRatio: _this.getCanvasAspectRatio(), | ||
canvasWidthPx: _this.getCanvasWidthPx() | ||
}, false); | ||
@@ -55,2 +58,35 @@ }); | ||
_defineProperty(this, "onToggle", function (id) { | ||
var y = _this.state.y.find(function (_) { | ||
return _.id === id; | ||
}); // Won't allow hiding all graphs. | ||
if (y.isShown) { | ||
var graphsShown = _this.state.y.filter(function (_) { | ||
return _.isShown; | ||
}); | ||
if (graphsShown.length === 1) { | ||
return; | ||
} | ||
} | ||
y.isShown = !y.isShown; | ||
_this.setState({ | ||
y: _this.state.y | ||
}); | ||
var _this$calculateMinMax = _this.calculateMinMaxY(_this.state.y), | ||
minY = _this$calculateMinMax.minY, | ||
maxY = _this$calculateMinMax.maxY, | ||
minYGlobal = _this$calculateMinMax.minYGlobal, | ||
maxYGlobal = _this$calculateMinMax.maxYGlobal; | ||
_this.transitionState(minY, maxY, minYGlobal, maxYGlobal); | ||
return true; | ||
}); | ||
_defineProperty(this, "onChangeBounds", function (from, to) { | ||
@@ -60,2 +96,7 @@ _this.setState(_this.createState(from, to), false); | ||
_defineProperty(this, "createPolylinePoints", function (x, y) { | ||
// return commaJoin(x, y) | ||
return (0, _utility.commaJoin)(x.map(_this.fixSvgCoordinate), y.map(_this.fixSvgCoordinate)); | ||
}); | ||
_defineProperty(this, "fixSvgCoordinate", function (x) { | ||
@@ -83,9 +124,39 @@ var precisionFactor = _this.props.precisionFactor; | ||
_defineProperty(this, "transitionStateTick", function () { | ||
var _this$props = _this.props, | ||
transitionDuration = _this$props.transitionDuration, | ||
transitionEasing = _this$props.transitionEasing; | ||
var _this$state3 = _this.state, | ||
transitionStartedAt = _this$state3.transitionStartedAt, | ||
minYFrom = _this$state3.minYFrom, | ||
minYTo = _this$state3.minYTo, | ||
maxYFrom = _this$state3.maxYFrom, | ||
maxYTo = _this$state3.maxYTo, | ||
minYGlobalFrom = _this$state3.minYGlobalFrom, | ||
minYGlobalTo = _this$state3.minYGlobalTo, | ||
maxYGlobalFrom = _this$state3.maxYGlobalFrom, | ||
maxYGlobalTo = _this$state3.maxYGlobalTo; | ||
var elapsed = Date.now() - transitionStartedAt; | ||
var ratio = EASING[transitionEasing](Math.min(elapsed / transitionDuration, 1)); | ||
_this.setState({ | ||
minY: minYFrom + (minYTo - minYFrom) * ratio, | ||
maxY: maxYFrom + (maxYTo - maxYFrom) * ratio, | ||
minYGlobal: minYGlobalFrom + (minYGlobalTo - minYGlobalFrom) * ratio, | ||
maxYGlobal: maxYGlobalFrom + (maxYGlobalTo - maxYGlobalFrom) * ratio | ||
}); | ||
if (ratio < 1) { | ||
_this.transition = requestAnimationFrame(_this.transitionStateTick); | ||
} else { | ||
_this.transition = undefined; | ||
} | ||
}); | ||
_defineProperty(this, "createGridLine", function (y) { | ||
var _this$state3 = _this.state, | ||
minX = _this$state3.minX, | ||
maxX = _this$state3.maxX, | ||
minY = _this$state3.minY, | ||
maxY = _this$state3.maxY, | ||
yScale = _this$state3.yScale; | ||
var _this$state4 = _this.state, | ||
minX = _this$state4.minX, | ||
maxX = _this$state4.maxX, | ||
minY = _this$state4.minY, | ||
maxY = _this$state4.maxY; | ||
var line = document.createElement('line'); | ||
@@ -95,4 +166,4 @@ line.classList.add('chartogram__grid-line'); | ||
line.setAttribute('x2', _this.fixSvgCoordinate(_this.mapX(maxX))); | ||
line.setAttribute('y1', _this.fixSvgCoordinate(_this.mapY(maxY - yScale * y))); | ||
line.setAttribute('y2', _this.fixSvgCoordinate(_this.mapY(maxY - yScale * y))); | ||
line.setAttribute('y1', _this.fixSvgCoordinate(_this.mapY(maxY - y))); | ||
line.setAttribute('y2', _this.fixSvgCoordinate(_this.mapY(maxY - y))); | ||
return line; | ||
@@ -102,5 +173,5 @@ }); | ||
_defineProperty(this, "drawGauges", function (minX, maxX, minY, maxY, yAxisScale) { | ||
var _this$props = _this.props, | ||
gaugeTickMarksCount = _this$props.gaugeTickMarksCount, | ||
months = _this$props.months; | ||
var _this$props2 = _this.props, | ||
gaugeTickMarksCount = _this$props2.gaugeTickMarksCount, | ||
months = _this$props2.months; | ||
(0, _utility.clearElement)(_this.xAxis); | ||
@@ -116,361 +187,6 @@ (0, _utility.clearElement)(_this.yAxis); | ||
_defineProperty(this, "setUpCanvasTooltipListener", function () { | ||
var _this$props2 = _this.props, | ||
weekdays = _this$props2.weekdays, | ||
months = _this$props2.months; | ||
var canvasDimensions; | ||
var isIndexInBounds; | ||
var onTrack = function onTrack(screenX) { | ||
var _this$state4 = _this.state, | ||
minX = _this$state4.minX, | ||
maxX = _this$state4.maxX, | ||
xPoints = _this$state4.xPoints, | ||
y = _this$state4.y; | ||
var xScreenRatio = (screenX - canvasDimensions.left) / canvasDimensions.width; | ||
var xPoint = minX + xScreenRatio * (maxX - minX); | ||
var xHigherIndex = xPoints.findIndex(function (_) { | ||
return _ >= xPoint; | ||
}); | ||
var xLowerIndex = xHigherIndex - 1; | ||
if (!isIndexInBounds(xHigherIndex)) { | ||
xHigherIndex = -1; | ||
} | ||
if (!isIndexInBounds(xLowerIndex)) { | ||
xLowerIndex = -1; | ||
} | ||
var xIndex; | ||
if (xHigherIndex < 0) { | ||
if (xLowerIndex < 0) { | ||
return _this.removeTooltip(); | ||
} else { | ||
xIndex = xLowerIndex; | ||
} | ||
} else { | ||
if (xLowerIndex < 0) { | ||
xIndex = xHigherIndex; | ||
} else { | ||
var xLower = xPoints[xLowerIndex]; | ||
var xHigher = xPoints[xHigherIndex]; | ||
var deltaLower = xPoint - xLower; | ||
var deltaHigher = xHigher - xPoint; | ||
xIndex = deltaLower > deltaHigher ? xHigherIndex : xLowerIndex; | ||
} | ||
} | ||
var x = xPoints[xIndex]; | ||
if (x !== _this.tooltipForX) { | ||
_this.tooltipForX = x; | ||
if (!_this.tooltip) { | ||
_this.addTooltip(); | ||
} | ||
var date = new Date(x); | ||
_this.tooltipDate.textContent = "".concat(weekdays[date.getDay()], ", ").concat(months[date.getMonth()], " ").concat(date.getDate()); | ||
var i = 0; | ||
for (var _iterator = y, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var _ref2 = _ref, | ||
isShown = _ref2.isShown, | ||
points = _ref2.points, | ||
name = _ref2.name; | ||
if (isShown) { | ||
_this.tooltipValues.childNodes[2 * i].textContent = points[xIndex]; | ||
_this.tooltipValues.childNodes[2 * i + 1].textContent = name; | ||
i++; | ||
} | ||
} | ||
var xRatio = (x - minX) / (maxX - minX); | ||
_this.tooltip.style.left = "".concat(xRatio * 100, "%"); | ||
_this.updateTooltipPoints(xIndex, xRatio); | ||
_this.updateTooltipLine(x); | ||
} | ||
}; | ||
var onTrackStart = function onTrackStart() { | ||
canvasDimensions = _this.canvas.getBoundingClientRect(); | ||
isIndexInBounds = function isIndexInBounds(index) { | ||
var xPoints = _this.state.xPoints; | ||
return index >= 0 && index < xPoints.length; | ||
}; | ||
}; | ||
var onTouchStart = function onTouchStart(event) { | ||
// Ignore multitouch. | ||
if (event.touches.length > 1) { | ||
// Reset. | ||
return onTrackStop(); | ||
} | ||
onTrackStart(); | ||
_this.canvas.addEventListener('touchend', onTrackStop); | ||
_this.canvas.addEventListener('touchmove', onTouchMove); | ||
_this.canvas.addEventListener('touchend', onTrackStop); | ||
_this.canvas.addEventListener('touchcancel', onTrackStop); | ||
onTouchMove(event); | ||
}; // Safari doesn't support pointer events. | ||
// https://caniuse.com/#feat=pointer | ||
_this.canvas.addEventListener('touchstart', onTouchStart); | ||
function onTouchMove(event) { | ||
var x = event.changedTouches[0].clientX; | ||
var y = event.changedTouches[0].clientY; // Emulate 'pointerleave' behavior. | ||
if (x < canvasDimensions.left || x > canvasDimensions.left + canvasDimensions.width || y < canvasDimensions.top || y > canvasDimensions.top + canvasDimensions.height) { | ||
onTrackStop(); | ||
} else { | ||
onTrack(x, y); | ||
} | ||
} | ||
function onPointerMove(event) { | ||
onTrack(event.clientX, event.clientY); | ||
} | ||
var onTrackStop = function onTrackStop() { | ||
_this.canvas.removeEventListener('pointermove', onPointerMove); | ||
_this.canvas.removeEventListener('pointerleave', onTrackStop); | ||
_this.canvas.removeEventListener('pointercancel', onTrackStop); | ||
_this.canvas.removeEventListener('touchmove', onTouchMove); | ||
_this.canvas.removeEventListener('touchend', onTrackStop); | ||
_this.canvas.removeEventListener('touchcancel', onTrackStop); | ||
_this.removeTooltip(); | ||
}; | ||
var onPointerEnter = function onPointerEnter() { | ||
onTrackStart(); | ||
_this.canvas.addEventListener('pointermove', onPointerMove); | ||
_this.canvas.addEventListener('pointerleave', onTrackStop); | ||
_this.canvas.addEventListener('pointercancel', onTrackStop); | ||
}; | ||
_this.canvas.addEventListener('pointerenter', onPointerEnter); | ||
return function () { | ||
onTrackStop(); | ||
_this.canvas.removeEventListener(onPointerEnter); | ||
_this.canvas.removeEventListener(onTouchStart); | ||
}; | ||
}); | ||
_defineProperty(this, "addTooltip", function () { | ||
var y = _this.state.y; // Create tooltip. | ||
_this.tooltip = document.createElement('div'); | ||
_this.tooltip.classList.add('chartogram__tooltip'); | ||
_this.tooltipContainer.appendChild(_this.tooltip); // Add tooltip title. | ||
_this.tooltipDate = document.createElement('h1'); | ||
_this.tooltipDate.classList.add('chartogram__tooltip-header'); | ||
_this.tooltip.appendChild(_this.tooltipDate); // Add graph values. | ||
_this.tooltipValues = document.createElement('dl'); | ||
_this.tooltipValues.classList.add('chartogram__tooltip-values'); | ||
_this.tooltip.appendChild(_this.tooltipValues); // Add graph values. | ||
for (var _iterator2 = y, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { | ||
var _ref3; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) break; | ||
_ref3 = _iterator2[_i2++]; | ||
} else { | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) break; | ||
_ref3 = _i2.value; | ||
} | ||
var _ref4 = _ref3, | ||
isShown = _ref4.isShown, | ||
color = _ref4.color; | ||
if (isShown) { | ||
// Add graph value. | ||
var tooltipValue = document.createElement('dt'); | ||
tooltipValue.style.color = color; | ||
_this.tooltipValues.appendChild(tooltipValue); // Add graph name. | ||
var tooltipName = document.createElement('dd'); | ||
tooltipName.style.color = color; | ||
_this.tooltipValues.appendChild(tooltipName); | ||
} | ||
} | ||
}); | ||
_defineProperty(this, "addTooltipLine", function () { | ||
var xmlns = 'http://www.w3.org/2000/svg'; | ||
_this.tooltipLine = document.createElementNS(xmlns, 'line'); | ||
_this.tooltipLine.setAttributeNS(null, 'class', 'chartogram__tooltip-line'); | ||
_this.canvas.insertBefore(_this.tooltipLine, _this.canvas.querySelector('polyline')); | ||
}); | ||
_defineProperty(this, "removeTooltip", function () { | ||
if (_this.tooltip) { | ||
_this.tooltipForX = undefined; | ||
_this.tooltipContainer.removeChild(_this.tooltip); | ||
_this.tooltip = undefined; | ||
_this.removeTooltipPoints(); | ||
_this.removeTooltipLine(); | ||
} | ||
}); | ||
_defineProperty(this, "removeTooltipLine", function () { | ||
_this.canvas.removeChild(_this.tooltipLine); | ||
_this.tooltipLine = undefined; | ||
}); | ||
_defineProperty(this, "addTooltipPoints", function () { | ||
_this.tooltipPoints = []; | ||
for (var _iterator3 = _this.state.y, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { | ||
var _ref5; | ||
if (_isArray3) { | ||
if (_i3 >= _iterator3.length) break; | ||
_ref5 = _iterator3[_i3++]; | ||
} else { | ||
_i3 = _iterator3.next(); | ||
if (_i3.done) break; | ||
_ref5 = _i3.value; | ||
} | ||
var y = _ref5; | ||
if (y.isShown) { | ||
var point = document.createElement('div'); | ||
point.classList.add('chartogram__tooltip-point'); | ||
point.style.color = y.color; | ||
_this.tooltipPoints.push(point); | ||
_this.canvasWrapper.appendChild(point); | ||
} | ||
} | ||
}); | ||
_defineProperty(this, "removeTooltipPoints", function () { | ||
for (var _iterator4 = _this.tooltipPoints, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { | ||
var _ref6; | ||
if (_isArray4) { | ||
if (_i4 >= _iterator4.length) break; | ||
_ref6 = _iterator4[_i4++]; | ||
} else { | ||
_i4 = _iterator4.next(); | ||
if (_i4.done) break; | ||
_ref6 = _i4.value; | ||
} | ||
var point = _ref6; | ||
_this.canvasWrapper.removeChild(point); | ||
} | ||
_this.tooltipPoints = undefined; | ||
}); | ||
_defineProperty(this, "updateTooltipLine", function (x) { | ||
var canvasWidth = _this.props.canvasWidth; | ||
var aspectRatio = _this.state.aspectRatio; | ||
if (!_this.tooltipLine) { | ||
_this.addTooltipLine(); | ||
} | ||
_this.tooltipLine.setAttributeNS(null, 'x1', _this.fixSvgCoordinate(_this.mapX(x))); | ||
_this.tooltipLine.setAttributeNS(null, 'x2', _this.fixSvgCoordinate(_this.mapX(x))); | ||
_this.tooltipLine.setAttributeNS(null, 'y1', 0); | ||
_this.tooltipLine.setAttributeNS(null, 'y2', _this.fixSvgCoordinate(canvasWidth / aspectRatio)); | ||
}); | ||
_defineProperty(this, "updateTooltipPoints", function (xIndex, xRatio) { | ||
var _this$state5 = _this.state, | ||
maxY = _this$state5.maxY, | ||
y = _this$state5.y; | ||
if (!_this.tooltipPoints) { | ||
_this.addTooltipPoints(); | ||
} | ||
var i = 0; | ||
var j = 0; | ||
while (i < y.length) { | ||
if (y[i].isShown) { | ||
var point = _this.tooltipPoints[j]; | ||
var _y = y[i].points[xIndex]; | ||
var yRatio = _y / maxY; | ||
point.style.left = "".concat(xRatio * 100, "%"); | ||
point.style.bottom = "".concat(yRatio * 100, "%"); | ||
j++; | ||
} | ||
i++; | ||
} | ||
}); | ||
this.props = _objectSpread({ | ||
title: title, | ||
transitionDuration: 300, | ||
transitionEasing: 'easeInOutSin', | ||
gaugeTickMarksCount: 6, | ||
@@ -480,4 +196,4 @@ timelineWindowSize: 40, | ||
precisionFactor: Math.pow(10, props.precision || 3), | ||
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], | ||
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] | ||
months: MONTHS, | ||
weekdays: WEEKDAYS | ||
}, props); | ||
@@ -501,3 +217,3 @@ this.rootNode = rootNode; | ||
this.rootNode.classList.add('chartogram'); | ||
this.rootNode.innerHTML = "\n\t\t\t<header class=\"chartogram__header\">\n\t\t\t\t<h1 class=\"chartogram__title\">".concat(this.props.title, "</h1>\n\t\t\t</header>\n\t\t\t<div class=\"chartogram__plan-with-axes\">\n\t\t\t\t<div class=\"chartogram__plan\">\n\t\t\t\t\t<div class=\"chartogram__top-border\"></div>\n\t\t\t\t\t<div class=\"chartogram__canvas-wrapper\">\n\t\t\t\t\t\t<svg class=\"chartogram__canvas\"></svg>\n\t\t\t\t\t\t<div class=\"chartogram__x\"></div>\n\t\t\t\t\t\t<div class=\"chartogram__y-wrapper\">\n\t\t\t\t\t\t\t<div class=\"chartogram__y\"></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t").concat(_Timeline.default.INITIAL_MARKUP, "\n\t\t\t").concat(_Togglers.default.INITIAL_MARKUP, "\n\t\t"); | ||
this.rootNode.innerHTML = "\n\t\t\t".concat(INITIAL_MARKUP.replace('{title}', this.props.title), "\n\t\t\t").concat(_Timeline.default.INITIAL_MARKUP, "\n\t\t\t").concat(_Togglers.default.INITIAL_MARKUP, "\n\t\t"); | ||
this.tooltipContainer = this.rootNode.querySelector('.chartogram__plan'); | ||
@@ -507,4 +223,3 @@ this.canvas = this.rootNode.querySelector('.chartogram__canvas'); | ||
this.xAxis = this.rootNode.querySelector('.chartogram__x'); | ||
this.yAxis = this.rootNode.querySelector('.chartogram__y'); | ||
this.setUpCanvasTooltipListener(); // Render will be called after `.componentDidMount()`. | ||
this.yAxis = this.rootNode.querySelector('.chartogram__y'); // Render will be called after `.componentDidMount()`. | ||
@@ -515,3 +230,5 @@ this.state = this.getInitialState(); | ||
this.togglers = new _Togglers.default(this.getTogglersProps()); | ||
this.togglers.componentDidMount(); // Add window resize event listener. | ||
this.togglers.componentDidMount(); | ||
this.tooltip = new _Tooltip.default(this.getTooltipProps()); | ||
this.tooltip.componentDidMount(); // Add window resize event listener. | ||
@@ -524,2 +241,3 @@ window.addEventListener('resize', this.onResizeThrottled); | ||
value: function componentWillUnmount() { | ||
this.timeline.componentWillUnmount(); | ||
this.rootNode.classList.remove('chartogram'); | ||
@@ -537,2 +255,8 @@ (0, _utility.clearElement)(this.rootNode); // Remove window resize event listener. | ||
}, { | ||
key: "getCanvasWidthPx", | ||
value: function getCanvasWidthPx() { | ||
var canvasDimensions = this.canvas.getBoundingClientRect(); | ||
return canvasDimensions.width; | ||
} | ||
}, { | ||
key: "setState", | ||
@@ -547,2 +271,4 @@ value: function setState(newState) { | ||
} | ||
this.tooltip.componentDidUpdate(this.getTooltipProps()); | ||
} | ||
@@ -556,3 +282,4 @@ }, { | ||
canvasWidth: this.props.canvasWidth, | ||
precisionFactor: this.props.precisionFactor, | ||
fixSvgCoordinate: this.fixSvgCoordinate, | ||
createPolylinePoints: this.createPolylinePoints, | ||
fromRatio: this.state.fromRatio, | ||
@@ -563,3 +290,2 @@ toRatio: this.state.toRatio, | ||
y: this.state.y, | ||
yScale: this.state.yScale, | ||
onChangeBounds: this.onChangeBounds | ||
@@ -571,34 +297,30 @@ }; | ||
value: function getTogglersProps() { | ||
var _this2 = this; | ||
return { | ||
rootNode: this.rootNode, | ||
data: this.data, | ||
onToggle: function onToggle(id) { | ||
var y = _this2.state.y.find(function (_) { | ||
return _.id === id; | ||
}); // Won't allow hiding all graphs. | ||
if (y.isShown) { | ||
var graphsShown = _this2.state.y.filter(function (_) { | ||
return _.isShown; | ||
}); | ||
if (graphsShown.length === 1) { | ||
return; | ||
} | ||
} | ||
y.isShown = !y.isShown; | ||
_this2.setState(_objectSpread({ | ||
y: _this2.state.y | ||
}, _this2.calculateMinMaxY(_this2.state.y))); | ||
return true; | ||
} | ||
onToggle: this.onToggle | ||
}; | ||
} | ||
}, { | ||
key: "getTooltipProps", | ||
value: function getTooltipProps() { | ||
return { | ||
canvas: this.canvas, | ||
canvasWidthPx: this.state.canvasWidthPx, | ||
container: this.tooltipContainer, | ||
pointsContainer: this.canvasWrapper, | ||
weekdays: this.props.weekdays, | ||
months: this.props.months, | ||
canvasWidth: this.props.canvasWidth, | ||
aspectRatio: this.state.aspectRatio, | ||
mapX: this.mapX, | ||
fixSvgCoordinate: this.fixSvgCoordinate, | ||
minX: this.state.minX, | ||
maxX: this.state.maxX, | ||
maxY: this.state.maxY, | ||
xPoints: this.state.xPoints, | ||
y: this.state.y | ||
}; | ||
} | ||
}, { | ||
key: "getInitialState", | ||
@@ -623,3 +345,3 @@ value: function getInitialState() { | ||
aspectRatio: this.getCanvasAspectRatio(), | ||
yScale: 1 | ||
canvasWidthPx: this.getCanvasWidthPx() | ||
}); | ||
@@ -630,3 +352,3 @@ } | ||
value: function createState(fromRatio, toRatio) { | ||
var _this3 = this; | ||
var _this2 = this; | ||
@@ -670,3 +392,3 @@ var x = this.data.x; | ||
var y = this.data.y.map(function (y, i) { | ||
var points = _this3.data.y[i].points.slice(fromIndex, toIndex + 1); | ||
var points = _this2.data.y[i].points.slice(fromIndex, toIndex + 1); | ||
@@ -677,5 +399,5 @@ var graphPoints = points.slice(); | ||
if (x.points[fromIndex] !== minX) { | ||
var beforeStartY = _this3.data.y[i].points[fromIndex]; | ||
var startY = _this3.data.y[i].points[fromIndex + 1]; | ||
var fromXExcludedTickRatio = (minX - _this3.data.x.points[fromIndex]) / (_this3.data.x.points[fromIndex + 1] - _this3.data.x.points[fromIndex]); | ||
var beforeStartY = _this2.data.y[i].points[fromIndex]; | ||
var startY = _this2.data.y[i].points[fromIndex + 1]; | ||
var fromXExcludedTickRatio = (minX - _this2.data.x.points[fromIndex]) / (_this2.data.x.points[fromIndex + 1] - _this2.data.x.points[fromIndex]); | ||
var startPoint = beforeStartY + (startY - beforeStartY) * fromXExcludedTickRatio; | ||
@@ -686,5 +408,5 @@ graphPoints[0] = startPoint; | ||
if (x.points[toIndex] !== maxX) { | ||
var afterEndY = _this3.data.y[i].points[toIndex]; | ||
var endY = _this3.data.y[i].points[toIndex - 1]; | ||
var toXIncludedTickRatio = (maxX - _this3.data.x.points[toIndex - 1]) / (_this3.data.x.points[toIndex] - _this3.data.x.points[toIndex - 1]); | ||
var afterEndY = _this2.data.y[i].points[toIndex]; | ||
var endY = _this2.data.y[i].points[toIndex - 1]; | ||
var toXIncludedTickRatio = (maxX - _this2.data.x.points[toIndex - 1]) / (_this2.data.x.points[toIndex] - _this2.data.x.points[toIndex - 1]); | ||
var endPoint = endY + (afterEndY - endY) * toXIncludedTickRatio; | ||
@@ -695,3 +417,3 @@ graphPoints[graphPoints.length - 1] = endPoint; | ||
return _objectSpread({}, _this3.data.y[i], _this3.state ? _this3.state.y[i] : { | ||
return _objectSpread({}, _this2.data.y[i], _this2.state ? _this2.state.y[i] : { | ||
isShown: true | ||
@@ -727,15 +449,15 @@ }, { | ||
for (var _iterator5 = y, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { | ||
var _ref7; | ||
for (var _iterator = y, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref; | ||
if (_isArray5) { | ||
if (_i5 >= _iterator5.length) break; | ||
_ref7 = _iterator5[_i5++]; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i5 = _iterator5.next(); | ||
if (_i5.done) break; | ||
_ref7 = _i5.value; | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var _y = _ref7; | ||
var _y = _ref; | ||
@@ -753,12 +475,12 @@ if (_y.isShown) { | ||
var _loop = function _loop() { | ||
if (_isArray6) { | ||
if (_i6 >= _iterator6.length) return "break"; | ||
_ref8 = _iterator6[_i6++]; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) return "break"; | ||
_ref2 = _iterator2[_i2++]; | ||
} else { | ||
_i6 = _iterator6.next(); | ||
if (_i6.done) return "break"; | ||
_ref8 = _i6.value; | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) return "break"; | ||
_ref2 = _i2.value; | ||
} | ||
var _y = _ref8; | ||
var _y = _ref2; | ||
var isShown = y.find(function (_) { | ||
@@ -774,4 +496,4 @@ return _.id === _y.id; | ||
for (var _iterator6 = this.data.y, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { | ||
var _ref8; | ||
for (var _iterator2 = this.data.y, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { | ||
var _ref2; | ||
@@ -793,12 +515,5 @@ var _ret = _loop(); | ||
}, { | ||
key: "createPolylinePoints", | ||
value: function createPolylinePoints(x, y) { | ||
// return commaJoin(x, y) | ||
return (0, _utility.commaJoin)(x.map(this.fixSvgCoordinate), y.map(this.fixSvgCoordinate)); | ||
} // Firefox is buggy with too high and too fractional SVG coordinates. | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this4 = this; | ||
var _this3 = this; | ||
@@ -808,10 +523,9 @@ var _this$props3 = this.props, | ||
gaugeTickMarksCount = _this$props3.gaugeTickMarksCount; | ||
var _this$state6 = this.state, | ||
minX = _this$state6.minX, | ||
maxX = _this$state6.maxX, | ||
minY = _this$state6.minY, | ||
maxY = _this$state6.maxY, | ||
yScale = _this$state6.yScale, | ||
xGraphPoints = _this$state6.xGraphPoints, | ||
aspectRatio = _this$state6.aspectRatio; // Clear canvas. | ||
var _this$state5 = this.state, | ||
minX = _this$state5.minX, | ||
maxX = _this$state5.maxX, | ||
minY = _this$state5.minY, | ||
maxY = _this$state5.maxY, | ||
xGraphPoints = _this$state5.xGraphPoints, | ||
aspectRatio = _this$state5.aspectRatio; // Clear canvas. | ||
@@ -827,15 +541,15 @@ (0, _utility.clearElement)(this.canvas); // Set canvas `viewBox`. | ||
for (var _iterator7 = yAxisTickMarks, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { | ||
var _ref9; | ||
for (var _iterator3 = yAxisTickMarks, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { | ||
var _ref3; | ||
if (_isArray7) { | ||
if (_i7 >= _iterator7.length) break; | ||
_ref9 = _iterator7[_i7++]; | ||
if (_isArray3) { | ||
if (_i3 >= _iterator3.length) break; | ||
_ref3 = _iterator3[_i3++]; | ||
} else { | ||
_i7 = _iterator7.next(); | ||
if (_i7.done) break; | ||
_ref9 = _i7.value; | ||
_i3 = _iterator3.next(); | ||
if (_i3.done) break; | ||
_ref3 = _i3.value; | ||
} | ||
var y = _ref9; | ||
var y = _ref3; | ||
this.canvas.appendChild(this.createGridLine(y)); | ||
@@ -845,18 +559,18 @@ } // Draw charts. | ||
for (var _iterator8 = this.state.y, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { | ||
var _ref10; | ||
for (var _iterator4 = this.state.y, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { | ||
var _ref4; | ||
if (_isArray8) { | ||
if (_i8 >= _iterator8.length) break; | ||
_ref10 = _iterator8[_i8++]; | ||
if (_isArray4) { | ||
if (_i4 >= _iterator4.length) break; | ||
_ref4 = _iterator4[_i4++]; | ||
} else { | ||
_i8 = _iterator8.next(); | ||
if (_i8.done) break; | ||
_ref10 = _i8.value; | ||
_i4 = _iterator4.next(); | ||
if (_i4.done) break; | ||
_ref4 = _i4.value; | ||
} | ||
var _ref11 = _ref10, | ||
color = _ref11.color, | ||
graphPoints = _ref11.graphPoints, | ||
isShown = _ref11.isShown; | ||
var _ref5 = _ref4, | ||
color = _ref5.color, | ||
graphPoints = _ref5.graphPoints, | ||
isShown = _ref5.isShown; | ||
@@ -868,3 +582,3 @@ // Draw chart. | ||
graph.setAttribute('points', this.createPolylinePoints(xGraphPoints.map(this.mapX), graphPoints.map(function (y) { | ||
return _this4.mapY(maxY - y * yScale); | ||
return _this3.mapY(maxY - y); | ||
})).join(' ')); | ||
@@ -881,18 +595,24 @@ graph.classList.add('chartogram__graph'); | ||
this.drawGauges(minX, maxX, minY_, maxY_, yAxisScale); | ||
} // function animateScale(scale) { | ||
// console.log(scale) | ||
// animateScaleTo = scale | ||
// animateScaleStartedAt = Date.now() | ||
// previousYScale = yScale | ||
// requestAnimationFrame(animateScaleTick) | ||
// } | ||
// function animateScaleTick() { | ||
// const elapsed = Date.now() - animateScaleStartedAt | ||
// yScale = previousYScale + (animateScaleTo - previousYScale) * elapsed / 300 | ||
// drawGraphs(true) | ||
// if (elapsed < 300) { | ||
// requestAnimationFrame(animateScaleTick) | ||
// } | ||
// } | ||
} | ||
}, { | ||
key: "transitionState", | ||
value: function transitionState(minY, maxY, minYGlobal, maxYGlobal) { | ||
if (this.transition) { | ||
cancelAnimationFrame(this.transition); | ||
} | ||
this.setState({ | ||
minYFrom: this.state.minY, | ||
maxYFrom: this.state.maxY, | ||
minYTo: minY, | ||
maxYTo: maxY, | ||
minYGlobalFrom: this.state.minYGlobal, | ||
maxYGlobalFrom: this.state.maxYGlobal, | ||
minYGlobalTo: minYGlobal, | ||
maxYGlobalTo: maxYGlobal, | ||
transitionStartedAt: Date.now() | ||
}); // Place in a `setState()` callback in case of React. | ||
this.transition = requestAnimationFrame(this.transitionStateTick); | ||
} | ||
}]); | ||
@@ -904,2 +624,10 @@ | ||
exports.default = Chartogram; | ||
var MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; | ||
var WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | ||
var INITIAL_MARKUP = "\n\t<header class=\"chartogram__header\">\n\t\t<h1 class=\"chartogram__title\">{title}</h1>\n\t</header>\n\t<div class=\"chartogram__plan-with-axes\">\n\t\t<div class=\"chartogram__plan\">\n\t\t\t<div class=\"chartogram__top-border\"></div>\n\t\t\t<div class=\"chartogram__canvas-wrapper\">\n\t\t\t\t<svg class=\"chartogram__canvas\"></svg>\n\t\t\t\t<div class=\"chartogram__x\"></div>\n\t\t\t\t<div class=\"chartogram__y-wrapper\">\n\t\t\t\t\t<div class=\"chartogram__y\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n"; | ||
var EASING = { | ||
easeInOutSin: function easeInOutSin(x) { | ||
return (1 + Math.sin(Math.PI * x - Math.PI / 2)) / 2; | ||
} | ||
}; | ||
//# sourceMappingURL=Chartogram.js.map |
@@ -50,7 +50,2 @@ "use strict"; | ||
_defineProperty(this, "fixSvgCoordinate", function (x) { | ||
var precisionFactor = _this.props.precisionFactor; | ||
return Math.round(x * precisionFactor) / precisionFactor; | ||
}); | ||
_defineProperty(this, "mapX", function (x) { | ||
@@ -207,11 +202,11 @@ var _this$props = _this.props, | ||
y = _this$props3.y, | ||
yScale = _this$props3.yScale, | ||
data = _this$props3.data, | ||
maxYGlobal = _this$props3.maxYGlobal; | ||
var aspectRatio = this.state.aspectRatio; | ||
var x = data.x; // Clear canvas. | ||
maxYGlobal = _this$props3.maxYGlobal, | ||
fixSvgCoordinate = _this$props3.fixSvgCoordinate, | ||
createPolylinePoints = _this$props3.createPolylinePoints; | ||
var aspectRatio = this.state.aspectRatio; // Clear canvas. | ||
(0, _utility.clearElement)(this.timelineCanvas); // Set canvas `viewBox`. | ||
this.timelineCanvas.setAttribute('viewBox', "0 0 ".concat(canvasWidth, " ").concat(this.fixSvgCoordinate(canvasWidth / aspectRatio))); | ||
this.timelineCanvas.setAttribute('viewBox', "0 0 ".concat(canvasWidth, " ").concat(fixSvgCoordinate(canvasWidth / aspectRatio))); | ||
@@ -237,3 +232,3 @@ var _loop = function _loop() { | ||
if (isShown) { | ||
var _simplifyGraph = (0, _utility.simplifyGraph)(x.points, points, 80), | ||
var _simplifyGraph = (0, _utility.simplifyGraph)(data.x.points, points, 80), | ||
_simplifyGraph2 = _slicedToArray(_simplifyGraph, 2), | ||
@@ -245,4 +240,4 @@ _x = _simplifyGraph2[0], | ||
graph.setAttribute('stroke', color); | ||
graph.setAttribute('points', _this2.createPolylinePoints(_x.map(_this2.mapX), _y.map(function (y) { | ||
return _this2.mapY(maxYGlobal - y * yScale); | ||
graph.setAttribute('points', createPolylinePoints(_x.map(_this2.mapX), _y.map(function (y) { | ||
return _this2.mapY(maxYGlobal - y); | ||
})).join(' ')); | ||
@@ -281,9 +276,2 @@ graph.classList.add('chartogram__graph'); | ||
} | ||
}, { | ||
key: "createPolylinePoints", | ||
value: function createPolylinePoints(x, y) { | ||
// return commaJoin(x, y) | ||
return (0, _utility.commaJoin)(x.map(this.fixSvgCoordinate), y.map(this.fixSvgCoordinate)); | ||
} // Firefox is buggy with too high and too fractional SVG coordinates. | ||
}]); | ||
@@ -290,0 +278,0 @@ |
@@ -22,2 +22,3 @@ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } | ||
import Togglers from './Togglers'; | ||
import Tooltip from './Tooltip'; | ||
@@ -37,3 +38,4 @@ var Chartogram = | ||
_this.setState({ | ||
aspectRatio: _this.getCanvasAspectRatio() | ||
aspectRatio: _this.getCanvasAspectRatio(), | ||
canvasWidthPx: _this.getCanvasWidthPx() | ||
}, false); | ||
@@ -44,2 +46,35 @@ }); | ||
_defineProperty(this, "onToggle", function (id) { | ||
var y = _this.state.y.find(function (_) { | ||
return _.id === id; | ||
}); // Won't allow hiding all graphs. | ||
if (y.isShown) { | ||
var graphsShown = _this.state.y.filter(function (_) { | ||
return _.isShown; | ||
}); | ||
if (graphsShown.length === 1) { | ||
return; | ||
} | ||
} | ||
y.isShown = !y.isShown; | ||
_this.setState({ | ||
y: _this.state.y | ||
}); | ||
var _this$calculateMinMax = _this.calculateMinMaxY(_this.state.y), | ||
minY = _this$calculateMinMax.minY, | ||
maxY = _this$calculateMinMax.maxY, | ||
minYGlobal = _this$calculateMinMax.minYGlobal, | ||
maxYGlobal = _this$calculateMinMax.maxYGlobal; | ||
_this.transitionState(minY, maxY, minYGlobal, maxYGlobal); | ||
return true; | ||
}); | ||
_defineProperty(this, "onChangeBounds", function (from, to) { | ||
@@ -49,2 +84,7 @@ _this.setState(_this.createState(from, to), false); | ||
_defineProperty(this, "createPolylinePoints", function (x, y) { | ||
// return commaJoin(x, y) | ||
return commaJoin(x.map(_this.fixSvgCoordinate), y.map(_this.fixSvgCoordinate)); | ||
}); | ||
_defineProperty(this, "fixSvgCoordinate", function (x) { | ||
@@ -72,9 +112,39 @@ var precisionFactor = _this.props.precisionFactor; | ||
_defineProperty(this, "transitionStateTick", function () { | ||
var _this$props = _this.props, | ||
transitionDuration = _this$props.transitionDuration, | ||
transitionEasing = _this$props.transitionEasing; | ||
var _this$state3 = _this.state, | ||
transitionStartedAt = _this$state3.transitionStartedAt, | ||
minYFrom = _this$state3.minYFrom, | ||
minYTo = _this$state3.minYTo, | ||
maxYFrom = _this$state3.maxYFrom, | ||
maxYTo = _this$state3.maxYTo, | ||
minYGlobalFrom = _this$state3.minYGlobalFrom, | ||
minYGlobalTo = _this$state3.minYGlobalTo, | ||
maxYGlobalFrom = _this$state3.maxYGlobalFrom, | ||
maxYGlobalTo = _this$state3.maxYGlobalTo; | ||
var elapsed = Date.now() - transitionStartedAt; | ||
var ratio = EASING[transitionEasing](Math.min(elapsed / transitionDuration, 1)); | ||
_this.setState({ | ||
minY: minYFrom + (minYTo - minYFrom) * ratio, | ||
maxY: maxYFrom + (maxYTo - maxYFrom) * ratio, | ||
minYGlobal: minYGlobalFrom + (minYGlobalTo - minYGlobalFrom) * ratio, | ||
maxYGlobal: maxYGlobalFrom + (maxYGlobalTo - maxYGlobalFrom) * ratio | ||
}); | ||
if (ratio < 1) { | ||
_this.transition = requestAnimationFrame(_this.transitionStateTick); | ||
} else { | ||
_this.transition = undefined; | ||
} | ||
}); | ||
_defineProperty(this, "createGridLine", function (y) { | ||
var _this$state3 = _this.state, | ||
minX = _this$state3.minX, | ||
maxX = _this$state3.maxX, | ||
minY = _this$state3.minY, | ||
maxY = _this$state3.maxY, | ||
yScale = _this$state3.yScale; | ||
var _this$state4 = _this.state, | ||
minX = _this$state4.minX, | ||
maxX = _this$state4.maxX, | ||
minY = _this$state4.minY, | ||
maxY = _this$state4.maxY; | ||
var line = document.createElement('line'); | ||
@@ -84,4 +154,4 @@ line.classList.add('chartogram__grid-line'); | ||
line.setAttribute('x2', _this.fixSvgCoordinate(_this.mapX(maxX))); | ||
line.setAttribute('y1', _this.fixSvgCoordinate(_this.mapY(maxY - yScale * y))); | ||
line.setAttribute('y2', _this.fixSvgCoordinate(_this.mapY(maxY - yScale * y))); | ||
line.setAttribute('y1', _this.fixSvgCoordinate(_this.mapY(maxY - y))); | ||
line.setAttribute('y2', _this.fixSvgCoordinate(_this.mapY(maxY - y))); | ||
return line; | ||
@@ -91,5 +161,5 @@ }); | ||
_defineProperty(this, "drawGauges", function (minX, maxX, minY, maxY, yAxisScale) { | ||
var _this$props = _this.props, | ||
gaugeTickMarksCount = _this$props.gaugeTickMarksCount, | ||
months = _this$props.months; | ||
var _this$props2 = _this.props, | ||
gaugeTickMarksCount = _this$props2.gaugeTickMarksCount, | ||
months = _this$props2.months; | ||
clearElement(_this.xAxis); | ||
@@ -105,361 +175,6 @@ clearElement(_this.yAxis); | ||
_defineProperty(this, "setUpCanvasTooltipListener", function () { | ||
var _this$props2 = _this.props, | ||
weekdays = _this$props2.weekdays, | ||
months = _this$props2.months; | ||
var canvasDimensions; | ||
var isIndexInBounds; | ||
var onTrack = function onTrack(screenX) { | ||
var _this$state4 = _this.state, | ||
minX = _this$state4.minX, | ||
maxX = _this$state4.maxX, | ||
xPoints = _this$state4.xPoints, | ||
y = _this$state4.y; | ||
var xScreenRatio = (screenX - canvasDimensions.left) / canvasDimensions.width; | ||
var xPoint = minX + xScreenRatio * (maxX - minX); | ||
var xHigherIndex = xPoints.findIndex(function (_) { | ||
return _ >= xPoint; | ||
}); | ||
var xLowerIndex = xHigherIndex - 1; | ||
if (!isIndexInBounds(xHigherIndex)) { | ||
xHigherIndex = -1; | ||
} | ||
if (!isIndexInBounds(xLowerIndex)) { | ||
xLowerIndex = -1; | ||
} | ||
var xIndex; | ||
if (xHigherIndex < 0) { | ||
if (xLowerIndex < 0) { | ||
return _this.removeTooltip(); | ||
} else { | ||
xIndex = xLowerIndex; | ||
} | ||
} else { | ||
if (xLowerIndex < 0) { | ||
xIndex = xHigherIndex; | ||
} else { | ||
var xLower = xPoints[xLowerIndex]; | ||
var xHigher = xPoints[xHigherIndex]; | ||
var deltaLower = xPoint - xLower; | ||
var deltaHigher = xHigher - xPoint; | ||
xIndex = deltaLower > deltaHigher ? xHigherIndex : xLowerIndex; | ||
} | ||
} | ||
var x = xPoints[xIndex]; | ||
if (x !== _this.tooltipForX) { | ||
_this.tooltipForX = x; | ||
if (!_this.tooltip) { | ||
_this.addTooltip(); | ||
} | ||
var date = new Date(x); | ||
_this.tooltipDate.textContent = "".concat(weekdays[date.getDay()], ", ").concat(months[date.getMonth()], " ").concat(date.getDate()); | ||
var i = 0; | ||
for (var _iterator = y, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var _ref2 = _ref, | ||
isShown = _ref2.isShown, | ||
points = _ref2.points, | ||
name = _ref2.name; | ||
if (isShown) { | ||
_this.tooltipValues.childNodes[2 * i].textContent = points[xIndex]; | ||
_this.tooltipValues.childNodes[2 * i + 1].textContent = name; | ||
i++; | ||
} | ||
} | ||
var xRatio = (x - minX) / (maxX - minX); | ||
_this.tooltip.style.left = "".concat(xRatio * 100, "%"); | ||
_this.updateTooltipPoints(xIndex, xRatio); | ||
_this.updateTooltipLine(x); | ||
} | ||
}; | ||
var onTrackStart = function onTrackStart() { | ||
canvasDimensions = _this.canvas.getBoundingClientRect(); | ||
isIndexInBounds = function isIndexInBounds(index) { | ||
var xPoints = _this.state.xPoints; | ||
return index >= 0 && index < xPoints.length; | ||
}; | ||
}; | ||
var onTouchStart = function onTouchStart(event) { | ||
// Ignore multitouch. | ||
if (event.touches.length > 1) { | ||
// Reset. | ||
return onTrackStop(); | ||
} | ||
onTrackStart(); | ||
_this.canvas.addEventListener('touchend', onTrackStop); | ||
_this.canvas.addEventListener('touchmove', onTouchMove); | ||
_this.canvas.addEventListener('touchend', onTrackStop); | ||
_this.canvas.addEventListener('touchcancel', onTrackStop); | ||
onTouchMove(event); | ||
}; // Safari doesn't support pointer events. | ||
// https://caniuse.com/#feat=pointer | ||
_this.canvas.addEventListener('touchstart', onTouchStart); | ||
function onTouchMove(event) { | ||
var x = event.changedTouches[0].clientX; | ||
var y = event.changedTouches[0].clientY; // Emulate 'pointerleave' behavior. | ||
if (x < canvasDimensions.left || x > canvasDimensions.left + canvasDimensions.width || y < canvasDimensions.top || y > canvasDimensions.top + canvasDimensions.height) { | ||
onTrackStop(); | ||
} else { | ||
onTrack(x, y); | ||
} | ||
} | ||
function onPointerMove(event) { | ||
onTrack(event.clientX, event.clientY); | ||
} | ||
var onTrackStop = function onTrackStop() { | ||
_this.canvas.removeEventListener('pointermove', onPointerMove); | ||
_this.canvas.removeEventListener('pointerleave', onTrackStop); | ||
_this.canvas.removeEventListener('pointercancel', onTrackStop); | ||
_this.canvas.removeEventListener('touchmove', onTouchMove); | ||
_this.canvas.removeEventListener('touchend', onTrackStop); | ||
_this.canvas.removeEventListener('touchcancel', onTrackStop); | ||
_this.removeTooltip(); | ||
}; | ||
var onPointerEnter = function onPointerEnter() { | ||
onTrackStart(); | ||
_this.canvas.addEventListener('pointermove', onPointerMove); | ||
_this.canvas.addEventListener('pointerleave', onTrackStop); | ||
_this.canvas.addEventListener('pointercancel', onTrackStop); | ||
}; | ||
_this.canvas.addEventListener('pointerenter', onPointerEnter); | ||
return function () { | ||
onTrackStop(); | ||
_this.canvas.removeEventListener(onPointerEnter); | ||
_this.canvas.removeEventListener(onTouchStart); | ||
}; | ||
}); | ||
_defineProperty(this, "addTooltip", function () { | ||
var y = _this.state.y; // Create tooltip. | ||
_this.tooltip = document.createElement('div'); | ||
_this.tooltip.classList.add('chartogram__tooltip'); | ||
_this.tooltipContainer.appendChild(_this.tooltip); // Add tooltip title. | ||
_this.tooltipDate = document.createElement('h1'); | ||
_this.tooltipDate.classList.add('chartogram__tooltip-header'); | ||
_this.tooltip.appendChild(_this.tooltipDate); // Add graph values. | ||
_this.tooltipValues = document.createElement('dl'); | ||
_this.tooltipValues.classList.add('chartogram__tooltip-values'); | ||
_this.tooltip.appendChild(_this.tooltipValues); // Add graph values. | ||
for (var _iterator2 = y, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { | ||
var _ref3; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) break; | ||
_ref3 = _iterator2[_i2++]; | ||
} else { | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) break; | ||
_ref3 = _i2.value; | ||
} | ||
var _ref4 = _ref3, | ||
isShown = _ref4.isShown, | ||
color = _ref4.color; | ||
if (isShown) { | ||
// Add graph value. | ||
var tooltipValue = document.createElement('dt'); | ||
tooltipValue.style.color = color; | ||
_this.tooltipValues.appendChild(tooltipValue); // Add graph name. | ||
var tooltipName = document.createElement('dd'); | ||
tooltipName.style.color = color; | ||
_this.tooltipValues.appendChild(tooltipName); | ||
} | ||
} | ||
}); | ||
_defineProperty(this, "addTooltipLine", function () { | ||
var xmlns = 'http://www.w3.org/2000/svg'; | ||
_this.tooltipLine = document.createElementNS(xmlns, 'line'); | ||
_this.tooltipLine.setAttributeNS(null, 'class', 'chartogram__tooltip-line'); | ||
_this.canvas.insertBefore(_this.tooltipLine, _this.canvas.querySelector('polyline')); | ||
}); | ||
_defineProperty(this, "removeTooltip", function () { | ||
if (_this.tooltip) { | ||
_this.tooltipForX = undefined; | ||
_this.tooltipContainer.removeChild(_this.tooltip); | ||
_this.tooltip = undefined; | ||
_this.removeTooltipPoints(); | ||
_this.removeTooltipLine(); | ||
} | ||
}); | ||
_defineProperty(this, "removeTooltipLine", function () { | ||
_this.canvas.removeChild(_this.tooltipLine); | ||
_this.tooltipLine = undefined; | ||
}); | ||
_defineProperty(this, "addTooltipPoints", function () { | ||
_this.tooltipPoints = []; | ||
for (var _iterator3 = _this.state.y, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { | ||
var _ref5; | ||
if (_isArray3) { | ||
if (_i3 >= _iterator3.length) break; | ||
_ref5 = _iterator3[_i3++]; | ||
} else { | ||
_i3 = _iterator3.next(); | ||
if (_i3.done) break; | ||
_ref5 = _i3.value; | ||
} | ||
var y = _ref5; | ||
if (y.isShown) { | ||
var point = document.createElement('div'); | ||
point.classList.add('chartogram__tooltip-point'); | ||
point.style.color = y.color; | ||
_this.tooltipPoints.push(point); | ||
_this.canvasWrapper.appendChild(point); | ||
} | ||
} | ||
}); | ||
_defineProperty(this, "removeTooltipPoints", function () { | ||
for (var _iterator4 = _this.tooltipPoints, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { | ||
var _ref6; | ||
if (_isArray4) { | ||
if (_i4 >= _iterator4.length) break; | ||
_ref6 = _iterator4[_i4++]; | ||
} else { | ||
_i4 = _iterator4.next(); | ||
if (_i4.done) break; | ||
_ref6 = _i4.value; | ||
} | ||
var point = _ref6; | ||
_this.canvasWrapper.removeChild(point); | ||
} | ||
_this.tooltipPoints = undefined; | ||
}); | ||
_defineProperty(this, "updateTooltipLine", function (x) { | ||
var canvasWidth = _this.props.canvasWidth; | ||
var aspectRatio = _this.state.aspectRatio; | ||
if (!_this.tooltipLine) { | ||
_this.addTooltipLine(); | ||
} | ||
_this.tooltipLine.setAttributeNS(null, 'x1', _this.fixSvgCoordinate(_this.mapX(x))); | ||
_this.tooltipLine.setAttributeNS(null, 'x2', _this.fixSvgCoordinate(_this.mapX(x))); | ||
_this.tooltipLine.setAttributeNS(null, 'y1', 0); | ||
_this.tooltipLine.setAttributeNS(null, 'y2', _this.fixSvgCoordinate(canvasWidth / aspectRatio)); | ||
}); | ||
_defineProperty(this, "updateTooltipPoints", function (xIndex, xRatio) { | ||
var _this$state5 = _this.state, | ||
maxY = _this$state5.maxY, | ||
y = _this$state5.y; | ||
if (!_this.tooltipPoints) { | ||
_this.addTooltipPoints(); | ||
} | ||
var i = 0; | ||
var j = 0; | ||
while (i < y.length) { | ||
if (y[i].isShown) { | ||
var point = _this.tooltipPoints[j]; | ||
var _y = y[i].points[xIndex]; | ||
var yRatio = _y / maxY; | ||
point.style.left = "".concat(xRatio * 100, "%"); | ||
point.style.bottom = "".concat(yRatio * 100, "%"); | ||
j++; | ||
} | ||
i++; | ||
} | ||
}); | ||
this.props = _objectSpread({ | ||
title: title, | ||
transitionDuration: 300, | ||
transitionEasing: 'easeInOutSin', | ||
gaugeTickMarksCount: 6, | ||
@@ -469,4 +184,4 @@ timelineWindowSize: 40, | ||
precisionFactor: Math.pow(10, props.precision || 3), | ||
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], | ||
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] | ||
months: MONTHS, | ||
weekdays: WEEKDAYS | ||
}, props); | ||
@@ -490,3 +205,3 @@ this.rootNode = rootNode; | ||
this.rootNode.classList.add('chartogram'); | ||
this.rootNode.innerHTML = "\n\t\t\t<header class=\"chartogram__header\">\n\t\t\t\t<h1 class=\"chartogram__title\">".concat(this.props.title, "</h1>\n\t\t\t</header>\n\t\t\t<div class=\"chartogram__plan-with-axes\">\n\t\t\t\t<div class=\"chartogram__plan\">\n\t\t\t\t\t<div class=\"chartogram__top-border\"></div>\n\t\t\t\t\t<div class=\"chartogram__canvas-wrapper\">\n\t\t\t\t\t\t<svg class=\"chartogram__canvas\"></svg>\n\t\t\t\t\t\t<div class=\"chartogram__x\"></div>\n\t\t\t\t\t\t<div class=\"chartogram__y-wrapper\">\n\t\t\t\t\t\t\t<div class=\"chartogram__y\"></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t").concat(Timeline.INITIAL_MARKUP, "\n\t\t\t").concat(Togglers.INITIAL_MARKUP, "\n\t\t"); | ||
this.rootNode.innerHTML = "\n\t\t\t".concat(INITIAL_MARKUP.replace('{title}', this.props.title), "\n\t\t\t").concat(Timeline.INITIAL_MARKUP, "\n\t\t\t").concat(Togglers.INITIAL_MARKUP, "\n\t\t"); | ||
this.tooltipContainer = this.rootNode.querySelector('.chartogram__plan'); | ||
@@ -496,4 +211,3 @@ this.canvas = this.rootNode.querySelector('.chartogram__canvas'); | ||
this.xAxis = this.rootNode.querySelector('.chartogram__x'); | ||
this.yAxis = this.rootNode.querySelector('.chartogram__y'); | ||
this.setUpCanvasTooltipListener(); // Render will be called after `.componentDidMount()`. | ||
this.yAxis = this.rootNode.querySelector('.chartogram__y'); // Render will be called after `.componentDidMount()`. | ||
@@ -504,3 +218,5 @@ this.state = this.getInitialState(); | ||
this.togglers = new Togglers(this.getTogglersProps()); | ||
this.togglers.componentDidMount(); // Add window resize event listener. | ||
this.togglers.componentDidMount(); | ||
this.tooltip = new Tooltip(this.getTooltipProps()); | ||
this.tooltip.componentDidMount(); // Add window resize event listener. | ||
@@ -513,2 +229,3 @@ window.addEventListener('resize', this.onResizeThrottled); | ||
value: function componentWillUnmount() { | ||
this.timeline.componentWillUnmount(); | ||
this.rootNode.classList.remove('chartogram'); | ||
@@ -526,2 +243,8 @@ clearElement(this.rootNode); // Remove window resize event listener. | ||
}, { | ||
key: "getCanvasWidthPx", | ||
value: function getCanvasWidthPx() { | ||
var canvasDimensions = this.canvas.getBoundingClientRect(); | ||
return canvasDimensions.width; | ||
} | ||
}, { | ||
key: "setState", | ||
@@ -536,2 +259,4 @@ value: function setState(newState) { | ||
} | ||
this.tooltip.componentDidUpdate(this.getTooltipProps()); | ||
} | ||
@@ -545,3 +270,4 @@ }, { | ||
canvasWidth: this.props.canvasWidth, | ||
precisionFactor: this.props.precisionFactor, | ||
fixSvgCoordinate: this.fixSvgCoordinate, | ||
createPolylinePoints: this.createPolylinePoints, | ||
fromRatio: this.state.fromRatio, | ||
@@ -552,3 +278,2 @@ toRatio: this.state.toRatio, | ||
y: this.state.y, | ||
yScale: this.state.yScale, | ||
onChangeBounds: this.onChangeBounds | ||
@@ -560,34 +285,30 @@ }; | ||
value: function getTogglersProps() { | ||
var _this2 = this; | ||
return { | ||
rootNode: this.rootNode, | ||
data: this.data, | ||
onToggle: function onToggle(id) { | ||
var y = _this2.state.y.find(function (_) { | ||
return _.id === id; | ||
}); // Won't allow hiding all graphs. | ||
if (y.isShown) { | ||
var graphsShown = _this2.state.y.filter(function (_) { | ||
return _.isShown; | ||
}); | ||
if (graphsShown.length === 1) { | ||
return; | ||
} | ||
} | ||
y.isShown = !y.isShown; | ||
_this2.setState(_objectSpread({ | ||
y: _this2.state.y | ||
}, _this2.calculateMinMaxY(_this2.state.y))); | ||
return true; | ||
} | ||
onToggle: this.onToggle | ||
}; | ||
} | ||
}, { | ||
key: "getTooltipProps", | ||
value: function getTooltipProps() { | ||
return { | ||
canvas: this.canvas, | ||
canvasWidthPx: this.state.canvasWidthPx, | ||
container: this.tooltipContainer, | ||
pointsContainer: this.canvasWrapper, | ||
weekdays: this.props.weekdays, | ||
months: this.props.months, | ||
canvasWidth: this.props.canvasWidth, | ||
aspectRatio: this.state.aspectRatio, | ||
mapX: this.mapX, | ||
fixSvgCoordinate: this.fixSvgCoordinate, | ||
minX: this.state.minX, | ||
maxX: this.state.maxX, | ||
maxY: this.state.maxY, | ||
xPoints: this.state.xPoints, | ||
y: this.state.y | ||
}; | ||
} | ||
}, { | ||
key: "getInitialState", | ||
@@ -612,3 +333,3 @@ value: function getInitialState() { | ||
aspectRatio: this.getCanvasAspectRatio(), | ||
yScale: 1 | ||
canvasWidthPx: this.getCanvasWidthPx() | ||
}); | ||
@@ -619,3 +340,3 @@ } | ||
value: function createState(fromRatio, toRatio) { | ||
var _this3 = this; | ||
var _this2 = this; | ||
@@ -659,3 +380,3 @@ var x = this.data.x; | ||
var y = this.data.y.map(function (y, i) { | ||
var points = _this3.data.y[i].points.slice(fromIndex, toIndex + 1); | ||
var points = _this2.data.y[i].points.slice(fromIndex, toIndex + 1); | ||
@@ -666,5 +387,5 @@ var graphPoints = points.slice(); | ||
if (x.points[fromIndex] !== minX) { | ||
var beforeStartY = _this3.data.y[i].points[fromIndex]; | ||
var startY = _this3.data.y[i].points[fromIndex + 1]; | ||
var fromXExcludedTickRatio = (minX - _this3.data.x.points[fromIndex]) / (_this3.data.x.points[fromIndex + 1] - _this3.data.x.points[fromIndex]); | ||
var beforeStartY = _this2.data.y[i].points[fromIndex]; | ||
var startY = _this2.data.y[i].points[fromIndex + 1]; | ||
var fromXExcludedTickRatio = (minX - _this2.data.x.points[fromIndex]) / (_this2.data.x.points[fromIndex + 1] - _this2.data.x.points[fromIndex]); | ||
var startPoint = beforeStartY + (startY - beforeStartY) * fromXExcludedTickRatio; | ||
@@ -675,5 +396,5 @@ graphPoints[0] = startPoint; | ||
if (x.points[toIndex] !== maxX) { | ||
var afterEndY = _this3.data.y[i].points[toIndex]; | ||
var endY = _this3.data.y[i].points[toIndex - 1]; | ||
var toXIncludedTickRatio = (maxX - _this3.data.x.points[toIndex - 1]) / (_this3.data.x.points[toIndex] - _this3.data.x.points[toIndex - 1]); | ||
var afterEndY = _this2.data.y[i].points[toIndex]; | ||
var endY = _this2.data.y[i].points[toIndex - 1]; | ||
var toXIncludedTickRatio = (maxX - _this2.data.x.points[toIndex - 1]) / (_this2.data.x.points[toIndex] - _this2.data.x.points[toIndex - 1]); | ||
var endPoint = endY + (afterEndY - endY) * toXIncludedTickRatio; | ||
@@ -684,3 +405,3 @@ graphPoints[graphPoints.length - 1] = endPoint; | ||
return _objectSpread({}, _this3.data.y[i], _this3.state ? _this3.state.y[i] : { | ||
return _objectSpread({}, _this2.data.y[i], _this2.state ? _this2.state.y[i] : { | ||
isShown: true | ||
@@ -716,15 +437,15 @@ }, { | ||
for (var _iterator5 = y, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { | ||
var _ref7; | ||
for (var _iterator = y, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref; | ||
if (_isArray5) { | ||
if (_i5 >= _iterator5.length) break; | ||
_ref7 = _iterator5[_i5++]; | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i5 = _iterator5.next(); | ||
if (_i5.done) break; | ||
_ref7 = _i5.value; | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
var _y = _ref7; | ||
var _y = _ref; | ||
@@ -742,12 +463,12 @@ if (_y.isShown) { | ||
var _loop = function _loop() { | ||
if (_isArray6) { | ||
if (_i6 >= _iterator6.length) return "break"; | ||
_ref8 = _iterator6[_i6++]; | ||
if (_isArray2) { | ||
if (_i2 >= _iterator2.length) return "break"; | ||
_ref2 = _iterator2[_i2++]; | ||
} else { | ||
_i6 = _iterator6.next(); | ||
if (_i6.done) return "break"; | ||
_ref8 = _i6.value; | ||
_i2 = _iterator2.next(); | ||
if (_i2.done) return "break"; | ||
_ref2 = _i2.value; | ||
} | ||
var _y = _ref8; | ||
var _y = _ref2; | ||
var isShown = y.find(function (_) { | ||
@@ -763,4 +484,4 @@ return _.id === _y.id; | ||
for (var _iterator6 = this.data.y, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { | ||
var _ref8; | ||
for (var _iterator2 = this.data.y, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { | ||
var _ref2; | ||
@@ -782,12 +503,5 @@ var _ret = _loop(); | ||
}, { | ||
key: "createPolylinePoints", | ||
value: function createPolylinePoints(x, y) { | ||
// return commaJoin(x, y) | ||
return commaJoin(x.map(this.fixSvgCoordinate), y.map(this.fixSvgCoordinate)); | ||
} // Firefox is buggy with too high and too fractional SVG coordinates. | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _this4 = this; | ||
var _this3 = this; | ||
@@ -797,10 +511,9 @@ var _this$props3 = this.props, | ||
gaugeTickMarksCount = _this$props3.gaugeTickMarksCount; | ||
var _this$state6 = this.state, | ||
minX = _this$state6.minX, | ||
maxX = _this$state6.maxX, | ||
minY = _this$state6.minY, | ||
maxY = _this$state6.maxY, | ||
yScale = _this$state6.yScale, | ||
xGraphPoints = _this$state6.xGraphPoints, | ||
aspectRatio = _this$state6.aspectRatio; // Clear canvas. | ||
var _this$state5 = this.state, | ||
minX = _this$state5.minX, | ||
maxX = _this$state5.maxX, | ||
minY = _this$state5.minY, | ||
maxY = _this$state5.maxY, | ||
xGraphPoints = _this$state5.xGraphPoints, | ||
aspectRatio = _this$state5.aspectRatio; // Clear canvas. | ||
@@ -816,15 +529,15 @@ clearElement(this.canvas); // Set canvas `viewBox`. | ||
for (var _iterator7 = yAxisTickMarks, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { | ||
var _ref9; | ||
for (var _iterator3 = yAxisTickMarks, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { | ||
var _ref3; | ||
if (_isArray7) { | ||
if (_i7 >= _iterator7.length) break; | ||
_ref9 = _iterator7[_i7++]; | ||
if (_isArray3) { | ||
if (_i3 >= _iterator3.length) break; | ||
_ref3 = _iterator3[_i3++]; | ||
} else { | ||
_i7 = _iterator7.next(); | ||
if (_i7.done) break; | ||
_ref9 = _i7.value; | ||
_i3 = _iterator3.next(); | ||
if (_i3.done) break; | ||
_ref3 = _i3.value; | ||
} | ||
var y = _ref9; | ||
var y = _ref3; | ||
this.canvas.appendChild(this.createGridLine(y)); | ||
@@ -834,18 +547,18 @@ } // Draw charts. | ||
for (var _iterator8 = this.state.y, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { | ||
var _ref10; | ||
for (var _iterator4 = this.state.y, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { | ||
var _ref4; | ||
if (_isArray8) { | ||
if (_i8 >= _iterator8.length) break; | ||
_ref10 = _iterator8[_i8++]; | ||
if (_isArray4) { | ||
if (_i4 >= _iterator4.length) break; | ||
_ref4 = _iterator4[_i4++]; | ||
} else { | ||
_i8 = _iterator8.next(); | ||
if (_i8.done) break; | ||
_ref10 = _i8.value; | ||
_i4 = _iterator4.next(); | ||
if (_i4.done) break; | ||
_ref4 = _i4.value; | ||
} | ||
var _ref11 = _ref10, | ||
color = _ref11.color, | ||
graphPoints = _ref11.graphPoints, | ||
isShown = _ref11.isShown; | ||
var _ref5 = _ref4, | ||
color = _ref5.color, | ||
graphPoints = _ref5.graphPoints, | ||
isShown = _ref5.isShown; | ||
@@ -857,3 +570,3 @@ // Draw chart. | ||
graph.setAttribute('points', this.createPolylinePoints(xGraphPoints.map(this.mapX), graphPoints.map(function (y) { | ||
return _this4.mapY(maxY - y * yScale); | ||
return _this3.mapY(maxY - y); | ||
})).join(' ')); | ||
@@ -870,18 +583,24 @@ graph.classList.add('chartogram__graph'); | ||
this.drawGauges(minX, maxX, minY_, maxY_, yAxisScale); | ||
} // function animateScale(scale) { | ||
// console.log(scale) | ||
// animateScaleTo = scale | ||
// animateScaleStartedAt = Date.now() | ||
// previousYScale = yScale | ||
// requestAnimationFrame(animateScaleTick) | ||
// } | ||
// function animateScaleTick() { | ||
// const elapsed = Date.now() - animateScaleStartedAt | ||
// yScale = previousYScale + (animateScaleTo - previousYScale) * elapsed / 300 | ||
// drawGraphs(true) | ||
// if (elapsed < 300) { | ||
// requestAnimationFrame(animateScaleTick) | ||
// } | ||
// } | ||
} | ||
}, { | ||
key: "transitionState", | ||
value: function transitionState(minY, maxY, minYGlobal, maxYGlobal) { | ||
if (this.transition) { | ||
cancelAnimationFrame(this.transition); | ||
} | ||
this.setState({ | ||
minYFrom: this.state.minY, | ||
maxYFrom: this.state.maxY, | ||
minYTo: minY, | ||
maxYTo: maxY, | ||
minYGlobalFrom: this.state.minYGlobal, | ||
maxYGlobalFrom: this.state.maxYGlobal, | ||
minYGlobalTo: minYGlobal, | ||
maxYGlobalTo: maxYGlobal, | ||
transitionStartedAt: Date.now() | ||
}); // Place in a `setState()` callback in case of React. | ||
this.transition = requestAnimationFrame(this.transitionStateTick); | ||
} | ||
}]); | ||
@@ -893,2 +612,10 @@ | ||
export { Chartogram as default }; | ||
var MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; | ||
var WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | ||
var INITIAL_MARKUP = "\n\t<header class=\"chartogram__header\">\n\t\t<h1 class=\"chartogram__title\">{title}</h1>\n\t</header>\n\t<div class=\"chartogram__plan-with-axes\">\n\t\t<div class=\"chartogram__plan\">\n\t\t\t<div class=\"chartogram__top-border\"></div>\n\t\t\t<div class=\"chartogram__canvas-wrapper\">\n\t\t\t\t<svg class=\"chartogram__canvas\"></svg>\n\t\t\t\t<div class=\"chartogram__x\"></div>\n\t\t\t\t<div class=\"chartogram__y-wrapper\">\n\t\t\t\t\t<div class=\"chartogram__y\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n"; | ||
var EASING = { | ||
easeInOutSin: function easeInOutSin(x) { | ||
return (1 + Math.sin(Math.PI * x - Math.PI / 2)) / 2; | ||
} | ||
}; | ||
//# sourceMappingURL=Chartogram.js.map |
@@ -43,7 +43,2 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
_defineProperty(this, "fixSvgCoordinate", function (x) { | ||
var precisionFactor = _this.props.precisionFactor; | ||
return Math.round(x * precisionFactor) / precisionFactor; | ||
}); | ||
_defineProperty(this, "mapX", function (x) { | ||
@@ -200,11 +195,11 @@ var _this$props = _this.props, | ||
y = _this$props3.y, | ||
yScale = _this$props3.yScale, | ||
data = _this$props3.data, | ||
maxYGlobal = _this$props3.maxYGlobal; | ||
var aspectRatio = this.state.aspectRatio; | ||
var x = data.x; // Clear canvas. | ||
maxYGlobal = _this$props3.maxYGlobal, | ||
fixSvgCoordinate = _this$props3.fixSvgCoordinate, | ||
createPolylinePoints = _this$props3.createPolylinePoints; | ||
var aspectRatio = this.state.aspectRatio; // Clear canvas. | ||
clearElement(this.timelineCanvas); // Set canvas `viewBox`. | ||
this.timelineCanvas.setAttribute('viewBox', "0 0 ".concat(canvasWidth, " ").concat(this.fixSvgCoordinate(canvasWidth / aspectRatio))); | ||
this.timelineCanvas.setAttribute('viewBox', "0 0 ".concat(canvasWidth, " ").concat(fixSvgCoordinate(canvasWidth / aspectRatio))); | ||
@@ -230,3 +225,3 @@ var _loop = function _loop() { | ||
if (isShown) { | ||
var _simplifyGraph = simplifyGraph(x.points, points, 80), | ||
var _simplifyGraph = simplifyGraph(data.x.points, points, 80), | ||
_simplifyGraph2 = _slicedToArray(_simplifyGraph, 2), | ||
@@ -238,4 +233,4 @@ _x = _simplifyGraph2[0], | ||
graph.setAttribute('stroke', color); | ||
graph.setAttribute('points', _this2.createPolylinePoints(_x.map(_this2.mapX), _y.map(function (y) { | ||
return _this2.mapY(maxYGlobal - y * yScale); | ||
graph.setAttribute('points', createPolylinePoints(_x.map(_this2.mapX), _y.map(function (y) { | ||
return _this2.mapY(maxYGlobal - y); | ||
})).join(' ')); | ||
@@ -274,9 +269,2 @@ graph.classList.add('chartogram__graph'); | ||
} | ||
}, { | ||
key: "createPolylinePoints", | ||
value: function createPolylinePoints(x, y) { | ||
// return commaJoin(x, y) | ||
return commaJoin(x.map(this.fixSvgCoordinate), y.map(this.fixSvgCoordinate)); | ||
} // Firefox is buggy with too high and too fractional SVG coordinates. | ||
}]); | ||
@@ -283,0 +271,0 @@ |
{ | ||
"name": "chartogram", | ||
"version": "0.1.6", | ||
"version": "0.1.7", | ||
"description": "Charts in JS with no dependencies", | ||
@@ -5,0 +5,0 @@ "main": "index.commonjs.js", |
@@ -12,2 +12,3 @@ import { | ||
import Togglers from './Togglers' | ||
import Tooltip from './Tooltip' | ||
@@ -18,2 +19,4 @@ export default class Chartogram { | ||
title, | ||
transitionDuration: 300, | ||
transitionEasing: 'easeInOutSin', | ||
gaugeTickMarksCount: 6, | ||
@@ -23,25 +26,4 @@ timelineWindowSize: 40, | ||
precisionFactor: Math.pow(10, props.precision || 3), | ||
months: [ | ||
'Jan', | ||
'Feb', | ||
'Mar', | ||
'Apr', | ||
'May', | ||
'Jun', | ||
'Jul', | ||
'Aug', | ||
'Sep', | ||
'Oct', | ||
'Nov', | ||
'Dec' | ||
], | ||
weekdays: [ | ||
'Sun', | ||
'Mon', | ||
'Tue', | ||
'Wed', | ||
'Thu', | ||
'Fri', | ||
'Sat' | ||
], | ||
months: MONTHS, | ||
weekdays: WEEKDAYS, | ||
...props | ||
@@ -68,17 +50,3 @@ } | ||
this.rootNode.innerHTML = ` | ||
<header class="chartogram__header"> | ||
<h1 class="chartogram__title">${this.props.title}</h1> | ||
</header> | ||
<div class="chartogram__plan-with-axes"> | ||
<div class="chartogram__plan"> | ||
<div class="chartogram__top-border"></div> | ||
<div class="chartogram__canvas-wrapper"> | ||
<svg class="chartogram__canvas"></svg> | ||
<div class="chartogram__x"></div> | ||
<div class="chartogram__y-wrapper"> | ||
<div class="chartogram__y"></div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
${INITIAL_MARKUP.replace('{title}', this.props.title)} | ||
${Timeline.INITIAL_MARKUP} | ||
@@ -94,4 +62,2 @@ ${Togglers.INITIAL_MARKUP} | ||
this.setUpCanvasTooltipListener() | ||
// Render will be called after `.componentDidMount()`. | ||
@@ -106,2 +72,5 @@ this.state = this.getInitialState() | ||
this.tooltip = new Tooltip(this.getTooltipProps()) | ||
this.tooltip.componentDidMount() | ||
// Add window resize event listener. | ||
@@ -114,2 +83,3 @@ window.addEventListener('resize', this.onResizeThrottled) | ||
componentWillUnmount() { | ||
this.timeline.componentWillUnmount() | ||
this.rootNode.classList.remove('chartogram') | ||
@@ -123,3 +93,4 @@ clearElement(this.rootNode) | ||
this.setState({ | ||
aspectRatio: this.getCanvasAspectRatio() | ||
aspectRatio: this.getCanvasAspectRatio(), | ||
canvasWidthPx: this.getCanvasWidthPx() | ||
}, false) | ||
@@ -135,2 +106,7 @@ } | ||
getCanvasWidthPx() { | ||
const canvasDimensions = this.canvas.getBoundingClientRect() | ||
return canvasDimensions.width | ||
} | ||
setState(newState, renderTimeline = true) { | ||
@@ -145,2 +121,3 @@ this.state = { | ||
} | ||
this.tooltip.componentDidUpdate(this.getTooltipProps()) | ||
} | ||
@@ -153,3 +130,4 @@ | ||
canvasWidth: this.props.canvasWidth, | ||
precisionFactor: this.props.precisionFactor, | ||
fixSvgCoordinate: this.fixSvgCoordinate, | ||
createPolylinePoints: this.createPolylinePoints, | ||
fromRatio: this.state.fromRatio, | ||
@@ -160,3 +138,2 @@ toRatio: this.state.toRatio, | ||
y: this.state.y, | ||
yScale: this.state.yScale, | ||
onChangeBounds: this.onChangeBounds | ||
@@ -170,21 +147,26 @@ } | ||
data: this.data, | ||
onToggle: (id) => { | ||
const y = this.state.y.find(_ => _.id === id) | ||
// Won't allow hiding all graphs. | ||
if (y.isShown) { | ||
const graphsShown = this.state.y.filter(_ => _.isShown) | ||
if (graphsShown.length === 1) { | ||
return | ||
} | ||
} | ||
y.isShown = !y.isShown | ||
this.setState({ | ||
y: this.state.y, | ||
...this.calculateMinMaxY(this.state.y) | ||
}) | ||
return true | ||
} | ||
onToggle: this.onToggle | ||
} | ||
} | ||
getTooltipProps() { | ||
return { | ||
canvas: this.canvas, | ||
canvasWidthPx: this.state.canvasWidthPx, | ||
container: this.tooltipContainer, | ||
pointsContainer: this.canvasWrapper, | ||
weekdays: this.props.weekdays, | ||
months: this.props.months, | ||
canvasWidth: this.props.canvasWidth, | ||
aspectRatio: this.state.aspectRatio, | ||
mapX: this.mapX, | ||
fixSvgCoordinate: this.fixSvgCoordinate, | ||
minX: this.state.minX, | ||
maxX: this.state.maxX, | ||
maxY: this.state.maxY, | ||
xPoints: this.state.xPoints, | ||
y: this.state.y | ||
} | ||
} | ||
getInitialState() { | ||
@@ -205,3 +187,3 @@ const { timelineWindowSize } = this.props | ||
aspectRatio: this.getCanvasAspectRatio(), | ||
yScale: 1 | ||
canvasWidthPx: this.getCanvasWidthPx() | ||
} | ||
@@ -311,2 +293,20 @@ } | ||
onToggle = (id) => { | ||
const y = this.state.y.find(_ => _.id === id) | ||
// Won't allow hiding all graphs. | ||
if (y.isShown) { | ||
const graphsShown = this.state.y.filter(_ => _.isShown) | ||
if (graphsShown.length === 1) { | ||
return | ||
} | ||
} | ||
y.isShown = !y.isShown | ||
this.setState({ | ||
y: this.state.y | ||
}) | ||
const { minY, maxY, minYGlobal, maxYGlobal } = this.calculateMinMaxY(this.state.y) | ||
this.transitionState(minY, maxY, minYGlobal, maxYGlobal) | ||
return true | ||
} | ||
onChangeBounds = (from, to) => { | ||
@@ -316,3 +316,3 @@ this.setState(this.createState(from, to), false) | ||
createPolylinePoints(x, y) { | ||
createPolylinePoints = (x, y) => { | ||
// return commaJoin(x, y) | ||
@@ -342,3 +342,3 @@ return commaJoin(x.map(this.fixSvgCoordinate), y.map(this.fixSvgCoordinate)) | ||
const { canvasWidth, gaugeTickMarksCount } = this.props | ||
const { minX, maxX, minY, maxY, yScale, xGraphPoints, aspectRatio } = this.state | ||
const { minX, maxX, minY, maxY, xGraphPoints, aspectRatio } = this.state | ||
// Clear canvas. | ||
@@ -365,3 +365,3 @@ clearElement(this.canvas) | ||
xGraphPoints.map(this.mapX), | ||
graphPoints.map(y => this.mapY(maxY - y * yScale)) | ||
graphPoints.map(y => this.mapY(maxY - y)) | ||
).join(' ')) | ||
@@ -385,21 +385,54 @@ graph.classList.add('chartogram__graph') | ||
// function animateScale(scale) { | ||
// console.log(scale) | ||
// animateScaleTo = scale | ||
// animateScaleStartedAt = Date.now() | ||
// previousYScale = yScale | ||
// requestAnimationFrame(animateScaleTick) | ||
// } | ||
transitionState(minY, maxY, minYGlobal, maxYGlobal) { | ||
if (this.transition) { | ||
cancelAnimationFrame(this.transition) | ||
} | ||
this.setState({ | ||
minYFrom: this.state.minY, | ||
maxYFrom: this.state.maxY, | ||
minYTo: minY, | ||
maxYTo: maxY, | ||
minYGlobalFrom: this.state.minYGlobal, | ||
maxYGlobalFrom: this.state.maxYGlobal, | ||
minYGlobalTo: minYGlobal, | ||
maxYGlobalTo: maxYGlobal, | ||
transitionStartedAt: Date.now() | ||
}) | ||
// Place in a `setState()` callback in case of React. | ||
this.transition = requestAnimationFrame(this.transitionStateTick) | ||
} | ||
// function animateScaleTick() { | ||
// const elapsed = Date.now() - animateScaleStartedAt | ||
// yScale = previousYScale + (animateScaleTo - previousYScale) * elapsed / 300 | ||
// drawGraphs(true) | ||
// if (elapsed < 300) { | ||
// requestAnimationFrame(animateScaleTick) | ||
// } | ||
// } | ||
transitionStateTick = () => { | ||
const { | ||
transitionDuration, | ||
transitionEasing | ||
} = this.props | ||
const { | ||
transitionStartedAt, | ||
minYFrom, | ||
minYTo, | ||
maxYFrom, | ||
maxYTo, | ||
minYGlobalFrom, | ||
minYGlobalTo, | ||
maxYGlobalFrom, | ||
maxYGlobalTo | ||
} = this.state | ||
const elapsed = Date.now() - transitionStartedAt | ||
const ratio = EASING[transitionEasing](Math.min(elapsed / transitionDuration, 1)) | ||
this.setState({ | ||
minY: minYFrom + (minYTo - minYFrom) * ratio, | ||
maxY: maxYFrom + (maxYTo - maxYFrom) * ratio, | ||
minYGlobal: minYGlobalFrom + (minYGlobalTo - minYGlobalFrom) * ratio, | ||
maxYGlobal: maxYGlobalFrom + (maxYGlobalTo - maxYGlobalFrom) * ratio | ||
}) | ||
if (ratio < 1) { | ||
this.transition = requestAnimationFrame(this.transitionStateTick) | ||
} else { | ||
this.transition = undefined | ||
} | ||
} | ||
createGridLine = (y) => { | ||
const { minX, maxX, minY, maxY, yScale } = this.state | ||
const { minX, maxX, minY, maxY } = this.state | ||
const line = document.createElement('line') | ||
@@ -409,4 +442,4 @@ line.classList.add('chartogram__grid-line') | ||
line.setAttribute('x2', this.fixSvgCoordinate(this.mapX(maxX))) | ||
line.setAttribute('y1', this.fixSvgCoordinate(this.mapY(maxY - yScale * y))) | ||
line.setAttribute('y2', this.fixSvgCoordinate(this.mapY(maxY - yScale * y))) | ||
line.setAttribute('y1', this.fixSvgCoordinate(this.mapY(maxY - y))) | ||
line.setAttribute('y2', this.fixSvgCoordinate(this.mapY(maxY - y))) | ||
return line | ||
@@ -426,223 +459,51 @@ } | ||
} | ||
} | ||
setUpCanvasTooltipListener = () => { | ||
const { weekdays, months } = this.props | ||
let canvasDimensions | ||
let isIndexInBounds | ||
const onTrack = (screenX) => { | ||
const { minX, maxX, xPoints, y } = this.state | ||
const xScreenRatio = (screenX - canvasDimensions.left) / canvasDimensions.width | ||
const xPoint = minX + xScreenRatio * (maxX - minX) | ||
let xHigherIndex = xPoints.findIndex(_ => _ >= xPoint) | ||
let xLowerIndex = xHigherIndex - 1 | ||
if (!isIndexInBounds(xHigherIndex)) { | ||
xHigherIndex = -1 | ||
} | ||
if (!isIndexInBounds(xLowerIndex)) { | ||
xLowerIndex = -1 | ||
} | ||
let xIndex | ||
if (xHigherIndex < 0) { | ||
if (xLowerIndex < 0) { | ||
return this.removeTooltip() | ||
} else { | ||
xIndex = xLowerIndex | ||
} | ||
} else { | ||
if (xLowerIndex < 0) { | ||
xIndex = xHigherIndex | ||
} else { | ||
const xLower = xPoints[xLowerIndex] | ||
const xHigher = xPoints[xHigherIndex] | ||
const deltaLower = xPoint - xLower | ||
const deltaHigher = xHigher - xPoint | ||
xIndex = deltaLower > deltaHigher ? xHigherIndex : xLowerIndex | ||
} | ||
} | ||
const x = xPoints[xIndex] | ||
if (x !== this.tooltipForX) { | ||
this.tooltipForX = x | ||
if (!this.tooltip) { | ||
this.addTooltip() | ||
} | ||
const date = new Date(x) | ||
this.tooltipDate.textContent = `${weekdays[date.getDay()]}, ${months[date.getMonth()]} ${date.getDate()}` | ||
let i = 0 | ||
for (const { isShown, points, name } of y) { | ||
if (isShown) { | ||
this.tooltipValues.childNodes[2 * i].textContent = points[xIndex] | ||
this.tooltipValues.childNodes[2 * i + 1].textContent = name | ||
i++ | ||
} | ||
} | ||
const xRatio = (x - minX) / (maxX - minX) | ||
this.tooltip.style.left = `${xRatio * 100}%` | ||
this.updateTooltipPoints(xIndex, xRatio) | ||
this.updateTooltipLine(x) | ||
} | ||
} | ||
const onTrackStart = () => { | ||
canvasDimensions = this.canvas.getBoundingClientRect() | ||
isIndexInBounds = (index) => { | ||
const { xPoints } = this.state | ||
return index >= 0 && index < xPoints.length | ||
} | ||
} | ||
const onTouchStart = (event) => { | ||
// Ignore multitouch. | ||
if (event.touches.length > 1) { | ||
// Reset. | ||
return onTrackStop() | ||
} | ||
onTrackStart() | ||
this.canvas.addEventListener('touchend', onTrackStop) | ||
this.canvas.addEventListener('touchmove', onTouchMove) | ||
this.canvas.addEventListener('touchend', onTrackStop) | ||
this.canvas.addEventListener('touchcancel', onTrackStop) | ||
onTouchMove(event) | ||
} | ||
// Safari doesn't support pointer events. | ||
// https://caniuse.com/#feat=pointer | ||
this.canvas.addEventListener('touchstart', onTouchStart) | ||
function onTouchMove(event) { | ||
const x = event.changedTouches[0].clientX | ||
const y = event.changedTouches[0].clientY | ||
// Emulate 'pointerleave' behavior. | ||
if (x < canvasDimensions.left || | ||
x > canvasDimensions.left + canvasDimensions.width || | ||
y < canvasDimensions.top || | ||
y > canvasDimensions.top + canvasDimensions.height) { | ||
onTrackStop() | ||
} else { | ||
onTrack(x, y) | ||
} | ||
} | ||
function onPointerMove(event) { | ||
onTrack(event.clientX, event.clientY) | ||
} | ||
const onTrackStop = () => { | ||
this.canvas.removeEventListener('pointermove', onPointerMove) | ||
this.canvas.removeEventListener('pointerleave', onTrackStop) | ||
this.canvas.removeEventListener('pointercancel', onTrackStop) | ||
this.canvas.removeEventListener('touchmove', onTouchMove) | ||
this.canvas.removeEventListener('touchend', onTrackStop) | ||
this.canvas.removeEventListener('touchcancel', onTrackStop) | ||
this.removeTooltip() | ||
} | ||
const onPointerEnter = () => { | ||
onTrackStart() | ||
this.canvas.addEventListener('pointermove', onPointerMove) | ||
this.canvas.addEventListener('pointerleave', onTrackStop) | ||
this.canvas.addEventListener('pointercancel', onTrackStop) | ||
} | ||
this.canvas.addEventListener('pointerenter', onPointerEnter) | ||
return () => { | ||
onTrackStop() | ||
this.canvas.removeEventListener(onPointerEnter) | ||
this.canvas.removeEventListener(onTouchStart) | ||
} | ||
} | ||
const MONTHS = [ | ||
'Jan', | ||
'Feb', | ||
'Mar', | ||
'Apr', | ||
'May', | ||
'Jun', | ||
'Jul', | ||
'Aug', | ||
'Sep', | ||
'Oct', | ||
'Nov', | ||
'Dec' | ||
] | ||
addTooltip = () => { | ||
const { y } = this.state | ||
// Create tooltip. | ||
this.tooltip = document.createElement('div') | ||
this.tooltip.classList.add('chartogram__tooltip') | ||
this.tooltipContainer.appendChild(this.tooltip) | ||
// Add tooltip title. | ||
this.tooltipDate = document.createElement('h1') | ||
this.tooltipDate.classList.add('chartogram__tooltip-header') | ||
this.tooltip.appendChild(this.tooltipDate) | ||
// Add graph values. | ||
this.tooltipValues = document.createElement('dl') | ||
this.tooltipValues.classList.add('chartogram__tooltip-values') | ||
this.tooltip.appendChild(this.tooltipValues) | ||
// Add graph values. | ||
for (const { isShown, color } of y) { | ||
if (isShown) { | ||
// Add graph value. | ||
const tooltipValue = document.createElement('dt') | ||
tooltipValue.style.color = color | ||
this.tooltipValues.appendChild(tooltipValue) | ||
// Add graph name. | ||
const tooltipName = document.createElement('dd') | ||
tooltipName.style.color = color | ||
this.tooltipValues.appendChild(tooltipName) | ||
} | ||
} | ||
} | ||
const WEEKDAYS = [ | ||
'Sun', | ||
'Mon', | ||
'Tue', | ||
'Wed', | ||
'Thu', | ||
'Fri', | ||
'Sat' | ||
] | ||
addTooltipLine = () => { | ||
const xmlns = 'http://www.w3.org/2000/svg' | ||
this.tooltipLine = document.createElementNS(xmlns, 'line') | ||
this.tooltipLine.setAttributeNS(null, 'class', 'chartogram__tooltip-line') | ||
this.canvas.insertBefore(this.tooltipLine, this.canvas.querySelector('polyline')) | ||
} | ||
const INITIAL_MARKUP = ` | ||
<header class="chartogram__header"> | ||
<h1 class="chartogram__title">{title}</h1> | ||
</header> | ||
<div class="chartogram__plan-with-axes"> | ||
<div class="chartogram__plan"> | ||
<div class="chartogram__top-border"></div> | ||
<div class="chartogram__canvas-wrapper"> | ||
<svg class="chartogram__canvas"></svg> | ||
<div class="chartogram__x"></div> | ||
<div class="chartogram__y-wrapper"> | ||
<div class="chartogram__y"></div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
` | ||
removeTooltip = () => { | ||
if (this.tooltip) { | ||
this.tooltipForX = undefined | ||
this.tooltipContainer.removeChild(this.tooltip) | ||
this.tooltip = undefined | ||
this.removeTooltipPoints() | ||
this.removeTooltipLine() | ||
} | ||
const EASING = { | ||
easeInOutSin(x) { | ||
return (1 + Math.sin(Math.PI * x - Math.PI / 2)) / 2 | ||
} | ||
removeTooltipLine = () => { | ||
this.canvas.removeChild(this.tooltipLine) | ||
this.tooltipLine = undefined | ||
} | ||
addTooltipPoints = () => { | ||
this.tooltipPoints = [] | ||
for (const y of this.state.y) { | ||
if (y.isShown) { | ||
const point = document.createElement('div') | ||
point.classList.add('chartogram__tooltip-point') | ||
point.style.color = y.color | ||
this.tooltipPoints.push(point) | ||
this.canvasWrapper.appendChild(point) | ||
} | ||
} | ||
} | ||
removeTooltipPoints = () => { | ||
for (const point of this.tooltipPoints) { | ||
this.canvasWrapper.removeChild(point) | ||
} | ||
this.tooltipPoints = undefined | ||
} | ||
updateTooltipLine = (x) => { | ||
const { canvasWidth } = this.props | ||
const { aspectRatio } = this.state | ||
if (!this.tooltipLine) { | ||
this.addTooltipLine() | ||
} | ||
this.tooltipLine.setAttributeNS(null, 'x1', this.fixSvgCoordinate(this.mapX(x))) | ||
this.tooltipLine.setAttributeNS(null, 'x2', this.fixSvgCoordinate(this.mapX(x))) | ||
this.tooltipLine.setAttributeNS(null, 'y1', 0) | ||
this.tooltipLine.setAttributeNS(null, 'y2', this.fixSvgCoordinate(canvasWidth / aspectRatio)) | ||
} | ||
updateTooltipPoints = (xIndex, xRatio) => { | ||
const { maxY, y } = this.state | ||
if (!this.tooltipPoints) { | ||
this.addTooltipPoints() | ||
} | ||
let i = 0 | ||
let j = 0 | ||
while (i < y.length) { | ||
if (y[i].isShown) { | ||
const point = this.tooltipPoints[j] | ||
const _y = y[i].points[xIndex] | ||
const yRatio = _y / maxY | ||
point.style.left = `${xRatio * 100}%` | ||
point.style.bottom = `${yRatio * 100}%` | ||
j++ | ||
} | ||
i++ | ||
} | ||
} | ||
} |
@@ -79,18 +79,17 @@ import { | ||
render() { | ||
const { canvasWidth, y, yScale, data, maxYGlobal } = this.props | ||
const { canvasWidth, y, data, maxYGlobal, fixSvgCoordinate, createPolylinePoints } = this.props | ||
const { aspectRatio } = this.state | ||
const { x } = data | ||
// Clear canvas. | ||
clearElement(this.timelineCanvas) | ||
// Set canvas `viewBox`. | ||
this.timelineCanvas.setAttribute('viewBox', `0 0 ${canvasWidth} ${this.fixSvgCoordinate(canvasWidth / aspectRatio)}`) | ||
this.timelineCanvas.setAttribute('viewBox', `0 0 ${canvasWidth} ${fixSvgCoordinate(canvasWidth / aspectRatio)}`) | ||
for (const { id, color, points } of data.y) { | ||
const isShown = y.find(_ => _.id === id).isShown | ||
if (isShown) { | ||
const [_x, _y] = simplifyGraph(x.points, points, 80) | ||
const [_x, _y] = simplifyGraph(data.x.points, points, 80) | ||
const graph = document.createElement('polyline') | ||
graph.setAttribute('stroke', color) | ||
graph.setAttribute('points', this.createPolylinePoints( | ||
graph.setAttribute('points', createPolylinePoints( | ||
_x.map(this.mapX), | ||
_y.map(y => this.mapY(maxYGlobal - y * yScale)) | ||
_y.map(y => this.mapY(maxYGlobal - y)) | ||
).join(' ')) | ||
@@ -118,13 +117,2 @@ graph.classList.add('chartogram__graph') | ||
createPolylinePoints(x, y) { | ||
// return commaJoin(x, y) | ||
return commaJoin(x.map(this.fixSvgCoordinate), y.map(this.fixSvgCoordinate)) | ||
} | ||
// Firefox is buggy with too high and too fractional SVG coordinates. | ||
fixSvgCoordinate = (x) => { | ||
const { precisionFactor } = this.props | ||
return Math.round(x * precisionFactor) / precisionFactor | ||
} | ||
mapX = (x) => { | ||
@@ -131,0 +119,0 @@ const { canvasWidth, data } = this.props |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1080757
43
4199