@d3fc/d3fc-axis
Advanced tools
Comparing version 2.0.10 to 2.1.0
@@ -105,2 +105,112 @@ (function (global, factory) { | ||
var createReboundMethod = (function (target, source, name) { | ||
var method = source[name]; | ||
if (typeof method !== 'function') { | ||
throw new Error('Attempt to rebind ' + name + ' which isn\'t a function on the source object'); | ||
} | ||
return function () { | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
var value = method.apply(source, args); | ||
return value === source ? target : value; | ||
}; | ||
}); | ||
var createTransform = function createTransform(transforms) { | ||
return function (name) { | ||
return transforms.reduce(function (name, fn) { | ||
return name && fn(name); | ||
}, name); | ||
}; | ||
}; | ||
var rebindAll = (function (target, source) { | ||
for (var _len = arguments.length, transforms = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { | ||
transforms[_key - 2] = arguments[_key]; | ||
} | ||
var transform = createTransform(transforms); | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = Object.keys(source)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var name = _step.value; | ||
var result = transform(name); | ||
if (result) { | ||
target[result] = createReboundMethod(target, source, name); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
return target; | ||
}); | ||
var slicedToArray = function () { | ||
function sliceIterator(arr, i) { | ||
var _arr = []; | ||
var _n = true; | ||
var _d = false; | ||
var _e = undefined; | ||
try { | ||
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
} | ||
} catch (err) { | ||
_d = true; | ||
_e = err; | ||
} finally { | ||
try { | ||
if (!_n && _i["return"]) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
return function (arr, i) { | ||
if (Array.isArray(arr)) { | ||
return arr; | ||
} else if (Symbol.iterator in Object(arr)) { | ||
return sliceIterator(arr, i); | ||
} else { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance"); | ||
} | ||
}; | ||
}(); | ||
var toConsumableArray = function (arr) { | ||
@@ -120,4 +230,6 @@ if (Array.isArray(arr)) { | ||
var axis = function axis(orient, scale) { | ||
var axisBase = function axisBase(orient, scale) { | ||
var custom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var tickArguments = [10]; | ||
@@ -137,2 +249,12 @@ var tickValues = null; | ||
var defaultLabelOffset = function defaultLabelOffset() { | ||
return { offset: [0, tickSizeInner + tickPadding] }; | ||
}; | ||
var defaultTickPath = function defaultTickPath() { | ||
return { path: [[0, 0], [0, tickSizeInner]] }; | ||
}; | ||
var labelOffset = custom.labelOffset || defaultLabelOffset; | ||
var tickPath = custom.tickPath || defaultTickPath; | ||
// returns a function that creates a translation based on | ||
@@ -194,3 +316,10 @@ // the bound data | ||
var sign = orient === 'bottom' || orient === 'right' ? 1 : -1; | ||
var withSign = function withSign(_ref) { | ||
var _ref2 = slicedToArray(_ref, 2), | ||
x = _ref2[0], | ||
y = _ref2[1]; | ||
return [x, sign * y]; | ||
}; | ||
// add the domain line | ||
@@ -205,7 +334,15 @@ var range = scale.range(); | ||
var labelOffsets = ticksArray.map(function (d, i) { | ||
return labelOffset(d, i, ticksArray); | ||
}); | ||
var tickPaths = ticksArray.map(function (d, i) { | ||
return tickPath(d, i, ticksArray); | ||
}); | ||
// enter | ||
g.enter().attr('transform', containerTranslate(scaleOld, translate)).append('path').attr('stroke', '#000'); | ||
var labelOffset = sign * (tickSizeInner + tickPadding); | ||
g.enter().append('text').attr('transform', translate(0, labelOffset)).attr('fill', '#000'); | ||
g.enter().append('text').attr('transform', function (d, i) { | ||
return translate.apply(undefined, toConsumableArray(withSign(labelOffsets[i].offset))); | ||
}).attr('fill', '#000'); | ||
@@ -216,7 +353,13 @@ // exit | ||
// update | ||
g.select('path').attr('d', function (d) { | ||
return svgDomainLine(pathTranspose([[0, 0], [0, sign * tickSizeInner]])); | ||
g.select('path').attr('visibility', function (d, i) { | ||
return tickPaths[i].hidden && 'hidden'; | ||
}).attr('d', function (d, i) { | ||
return svgDomainLine(pathTranspose(tickPaths[i].path.map(withSign))); | ||
}); | ||
g.select('text').attr('transform', translate(0, labelOffset)).attr('dy', function () { | ||
g.select('text').attr('visibility', function (d, i) { | ||
return labelOffsets[i].hidden && 'hidden'; | ||
}).attr('transform', function (d, i) { | ||
return translate.apply(undefined, toConsumableArray(withSign(labelOffsets[i].offset))); | ||
}).attr('dy', function () { | ||
var offset = '0em'; | ||
@@ -321,2 +464,38 @@ if (isVertical()) { | ||
var axis = function axis(orient, scale) { | ||
var tickCenterLabel = false; | ||
var labelOffset = function labelOffset(tick, index, ticksArray) { | ||
var x = 0; | ||
var y = base.tickSizeInner() + base.tickPadding(); | ||
var hidden = false; | ||
if (tickCenterLabel) { | ||
var thisPosition = scale(tick); | ||
var nextPosition = index < ticksArray.length - 1 ? scale(ticksArray[index + 1]) : scale.range()[1]; | ||
x = (nextPosition - thisPosition) / 2; | ||
y = base.tickPadding(); | ||
hidden = index === ticksArray.length - 1 && thisPosition === nextPosition; | ||
} | ||
return { offset: [x, y], hidden: hidden }; | ||
}; | ||
var base = axisBase(orient, scale, { labelOffset: labelOffset }); | ||
var axis = function axis(selection) { | ||
return base(selection); | ||
}; | ||
axis.tickCenterLabel = function () { | ||
if (!arguments.length) { | ||
return tickCenterLabel; | ||
} | ||
tickCenterLabel = arguments.length <= 0 ? undefined : arguments[0]; | ||
return axis; | ||
}; | ||
rebindAll(axis, base); | ||
return axis; | ||
}; | ||
var axisTop = function axisTop(scale) { | ||
@@ -338,2 +517,71 @@ return axis('top', scale); | ||
var axisOrdinal = function axisOrdinal(orient, scale) { | ||
var tickOffset = null; | ||
var step = function step(tick, index, ticksArray) { | ||
if (scale.step) { | ||
// Use the scale step size | ||
return scale.step(); | ||
} | ||
var thisPosition = scale(tick); | ||
if (index < ticksArray.length - 1) { | ||
// Distance between ticks | ||
return scale(ticksArray[index + 1]) / thisPosition; | ||
} else { | ||
// 2* distance to end | ||
return (scale.range()[1] - thisPosition) * 2; | ||
} | ||
}; | ||
var tickPath = function tickPath(tick, index, ticksArray) { | ||
var x = 0; | ||
if (tickOffset) { | ||
x = tickOffset(tick, index); | ||
} else { | ||
x = step(tick, index, ticksArray) / 2; | ||
} | ||
return { | ||
path: [[x, 0], [x, base.tickSizeInner()]], | ||
hidden: index === ticksArray.length - 1 | ||
}; | ||
}; | ||
var labelOffset = function labelOffset() { | ||
// Don't include the tickSizeInner in the label positioning | ||
return { offset: [0, base.tickPadding()] }; | ||
}; | ||
var base = axisBase(orient, scale, { labelOffset: labelOffset, tickPath: tickPath }); | ||
var axis = function axis(selection) { | ||
return base(selection); | ||
}; | ||
axis.tickOffset = function () { | ||
if (!arguments.length) { | ||
return tickOffset; | ||
} | ||
tickOffset = arguments.length <= 0 ? undefined : arguments[0]; | ||
return axis; | ||
}; | ||
rebindAll(axis, base); | ||
return axis; | ||
}; | ||
var axisOrdinalTop = function axisOrdinalTop(scale) { | ||
return axisOrdinal('top', scale); | ||
}; | ||
var axisOrdinalBottom = function axisOrdinalBottom(scale) { | ||
return axisOrdinal('bottom', scale); | ||
}; | ||
var axisOrdinalLeft = function axisOrdinalLeft(scale) { | ||
return axisOrdinal('left', scale); | ||
}; | ||
var axisOrdinalRight = function axisOrdinalRight(scale) { | ||
return axisOrdinal('right', scale); | ||
}; | ||
exports.axisTop = axisTop; | ||
@@ -343,2 +591,6 @@ exports.axisBottom = axisBottom; | ||
exports.axisRight = axisRight; | ||
exports.axisOrdinalTop = axisOrdinalTop; | ||
exports.axisOrdinalBottom = axisOrdinalBottom; | ||
exports.axisOrdinalLeft = axisOrdinalLeft; | ||
exports.axisOrdinalRight = axisOrdinalRight; | ||
@@ -345,0 +597,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
@@ -1,1 +0,1 @@ | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("d3-selection"),require("d3-shape")):"function"==typeof define&&define.amd?define(["exports","d3-selection","d3-shape"],n):n(t.fc=t.fc||{},t.d3,t.d3)}(this,function(t,n,e){"use strict";var r=function(t,n){t=t||"g";var e=function(t,n){return n},r=null,i=function(i,o){o=o||function(t){return t};var u=i.selection?i:null;u&&(i=i.selection());var a=i.selectAll(function(t,n,e){return Array.from(e[n].childNodes).filter(function(t){return 1===t.nodeType})}).filter(null==n?t:t+"."+n),l=a.data(o,e),c=l.enter().append(t).attr("class",n),f=l.exit();l=l.merge(c);var s=u||r;return s&&(l=l.transition(s).style("opacity",1),c.style("opacity",1e-6),f=f.transition(s).style("opacity",1e-6)),f.remove(),l.enter=function(){return c},l.exit=function(){return f},l};return i.element=function(){return arguments.length?(t=arguments.length<=0?void 0:arguments[0],i):t},i.className=function(){return arguments.length?(n=arguments.length<=0?void 0:arguments[0],i):n},i.key=function(){return arguments.length?(e=arguments.length<=0?void 0:arguments[0],i):e},i.transition=function(){return arguments.length?(r=arguments.length<=0?void 0:arguments[0],i):r},i},i=function(t){if(Array.isArray(t)){for(var n=0,e=Array(t.length);n<t.length;n++)e[n]=t[n];return e}return Array.from(t)},o=function(t){return t},u=function(t,u){var a=[10],l=null,c=function(){},f=null,s=6,d=6,h=3,g=e.line(),v=r("g","tick").key(o),m=r("path","domain"),p=function(t,n){var e=0;return t.bandwidth&&(e=t.bandwidth()/2,t.round()&&(e=Math.round(e))),function(r){return n(t(r)+e,0)}},y=function(t,n){return x()?"translate("+n+", "+t+")":"translate("+t+", "+n+")"},k=function(t){return x()?t.map(function(t){return[t[1],t[0]]}):t},x=function(){return"left"===t||"right"===t},_=function(t,n,e){return u[t]?u[t].apply(u,n):e},b=function(e){e.selection&&(v.transition(e),m.transition(e)),e.each(function(e,r,i){var b=i[r],A=n.select(b);b.__scale__||A.attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor","right"===t?"start":"left"===t?"end":"middle");var N=b.__scale__||u;b.__scale__=u.copy();var z=null==l?_("ticks",a,u.domain()):l,S=null==f?_("tickFormat",a,o):f,j="bottom"===t||"right"===t?1:-1,q=u.range(),w=k([[q[0],j*s],[q[0],0],[q[1],0],[q[1],j*s]]);m(A,[e]).attr("d",g(w)).attr("stroke","#000");var F=v(A,z);F.enter().attr("transform",p(N,y)).append("path").attr("stroke","#000");var M=j*(d+h);F.enter().append("text").attr("transform",y(0,M)).attr("fill","#000"),F.exit().attr("transform",p(u,y)),F.select("path").attr("d",function(t){return g(k([[0,0],[0,j*d]]))}),F.select("text").attr("transform",y(0,M)).attr("dy",function(){var n="0em";return x()?n="0.32em":"bottom"===t&&(n="0.71em"),n}).text(S),F.attr("transform",p(u,y)),c(F,e,r)})};return b.tickFormat=function(){return arguments.length?(f=arguments.length<=0?void 0:arguments[0],b):f},b.tickSize=function(){return arguments.length?(d=s=Number(arguments.length<=0?void 0:arguments[0]),b):d},b.tickSizeInner=function(){return arguments.length?(d=Number(arguments.length<=0?void 0:arguments[0]),b):d},b.tickSizeOuter=function(){return arguments.length?(s=Number(arguments.length<=0?void 0:arguments[0]),b):s},b.tickPadding=function(){return arguments.length?(h=arguments.length<=0?void 0:arguments[0],b):h},b.decorate=function(){return arguments.length?(c=arguments.length<=0?void 0:arguments[0],b):c},b.scale=function(){return arguments.length?(u=arguments.length<=0?void 0:arguments[0],b):u},b.ticks=function(){for(var t=arguments.length,n=Array(t),e=0;e<t;e++)n[e]=arguments[e];return a=[].concat(n),b},b.tickArguments=function(){return arguments.length?(a=null==(arguments.length<=0?void 0:arguments[0])?[]:[].concat(i(arguments.length<=0?void 0:arguments[0])),b):a.slice()},b.tickValues=function(){return arguments.length?(l=null==(arguments.length<=0?void 0:arguments[0])?[]:[].concat(i(arguments.length<=0?void 0:arguments[0])),b):l.slice()},b},a=function(t){return u("top",t)},l=function(t){return u("bottom",t)},c=function(t){return u("left",t)},f=function(t){return u("right",t)};t.axisTop=a,t.axisBottom=l,t.axisLeft=c,t.axisRight=f,Object.defineProperty(t,"__esModule",{value:!0})}); | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("d3-selection"),require("d3-shape")):"function"==typeof define&&define.amd?define(["exports","d3-selection","d3-shape"],n):n(t.fc=t.fc||{},t.d3,t.d3)}(this,function(t,n,r){"use strict";var e=function(t,n){t=t||"g";var r=function(t,n){return n},e=null,i=function(i,o){o=o||function(t){return t};var u=i.selection?i:null;u&&(i=i.selection());var a=i.selectAll(function(t,n,r){return Array.from(r[n].childNodes).filter(function(t){return 1===t.nodeType})}).filter(null==n?t:t+"."+n),c=a.data(o,r),f=c.enter().append(t).attr("class",n),l=c.exit();c=c.merge(f);var d=u||e;return d&&(c=c.transition(d).style("opacity",1),f.style("opacity",1e-6),l=l.transition(d).style("opacity",1e-6)),l.remove(),c.enter=function(){return f},c.exit=function(){return l},c};return i.element=function(){return arguments.length?(t=arguments.length<=0?void 0:arguments[0],i):t},i.className=function(){return arguments.length?(n=arguments.length<=0?void 0:arguments[0],i):n},i.key=function(){return arguments.length?(r=arguments.length<=0?void 0:arguments[0],i):r},i.transition=function(){return arguments.length?(e=arguments.length<=0?void 0:arguments[0],i):e},i},i=function(t,n,r){var e=n[r];if("function"!=typeof e)throw new Error("Attempt to rebind "+r+" which isn't a function on the source object");return function(){for(var r=arguments.length,i=Array(r),o=0;o<r;o++)i[o]=arguments[o];var u=e.apply(n,i);return u===n?t:u}},o=function(t){return function(n){return t.reduce(function(t,n){return t&&n(t)},n)}},u=function(t,n){for(var r=arguments.length,e=Array(r>2?r-2:0),u=2;u<r;u++)e[u-2]=arguments[u];var a=o(e),c=!0,f=!1,l=void 0;try{for(var d,s=Object.keys(n)[Symbol.iterator]();!(c=(d=s.next()).done);c=!0){var h=d.value,g=a(h);g&&(t[g]=i(t,n,h))}}catch(t){f=!0,l=t}finally{try{!c&&s.return&&s.return()}finally{if(f)throw l}}return t},a=function(){function t(t,n){var r=[],e=!0,i=!1,o=void 0;try{for(var u,a=t[Symbol.iterator]();!(e=(u=a.next()).done)&&(r.push(u.value),!n||r.length!==n);e=!0);}catch(t){i=!0,o=t}finally{try{!e&&a.return&&a.return()}finally{if(i)throw o}}return r}return function(n,r){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return t(n,r);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),c=function(t){if(Array.isArray(t)){for(var n=0,r=Array(t.length);n<t.length;n++)r[n]=t[n];return r}return Array.from(t)},f=function(t){return t},l=function(t,i){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},u=[10],l=null,d=function(){},s=null,h=6,g=6,v=3,p=r.line(),y=e("g","tick").key(f),m=e("path","domain"),b=function(){return{offset:[0,g+v]}},k=function(){return{path:[[0,0],[0,g]]}},x=o.labelOffset||b,_=o.tickPath||k,A=function(t,n){var r=0;return t.bandwidth&&(r=t.bandwidth()/2,t.round()&&(r=Math.round(r))),function(e){return n(t(e)+r,0)}},O=function(t,n){return S()?"translate("+n+", "+t+")":"translate("+t+", "+n+")"},w=function(t){return S()?t.map(function(t){return[t[1],t[0]]}):t},S=function(){return"left"===t||"right"===t},P=function(t,n,r){return i[t]?i[t].apply(i,n):r},z=function(r){r.selection&&(y.transition(r),m.transition(r)),r.each(function(r,e,o){var g=o[e],v=n.select(g);g.__scale__||v.attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor","right"===t?"start":"left"===t?"end":"middle");var b=g.__scale__||i;g.__scale__=i.copy();var k=null==l?P("ticks",u,i.domain()):l,z=null==s?P("tickFormat",u,f):s,j="bottom"===t||"right"===t?1:-1,N=function(t){var n=a(t,2),r=n[0],e=n[1];return[r,j*e]},I=i.range(),T=w([[I[0],j*h],[I[0],0],[I[1],0],[I[1],j*h]]);m(v,[r]).attr("d",p(T)).attr("stroke","#000");var L=y(v,k),q=k.map(function(t,n){return x(t,n,k)}),B=k.map(function(t,n){return _(t,n,k)});L.enter().attr("transform",A(b,O)).append("path").attr("stroke","#000"),L.enter().append("text").attr("transform",function(t,n){return O.apply(void 0,c(N(q[n].offset)))}).attr("fill","#000"),L.exit().attr("transform",A(i,O)),L.select("path").attr("visibility",function(t,n){return B[n].hidden&&"hidden"}).attr("d",function(t,n){return p(w(B[n].path.map(N)))}),L.select("text").attr("visibility",function(t,n){return q[n].hidden&&"hidden"}).attr("transform",function(t,n){return O.apply(void 0,c(N(q[n].offset)))}).attr("dy",function(){var n="0em";return S()?n="0.32em":"bottom"===t&&(n="0.71em"),n}).text(z),L.attr("transform",A(i,O)),d(L,r,e)})};return z.tickFormat=function(){return arguments.length?(s=arguments.length<=0?void 0:arguments[0],z):s},z.tickSize=function(){return arguments.length?(g=h=Number(arguments.length<=0?void 0:arguments[0]),z):g},z.tickSizeInner=function(){return arguments.length?(g=Number(arguments.length<=0?void 0:arguments[0]),z):g},z.tickSizeOuter=function(){return arguments.length?(h=Number(arguments.length<=0?void 0:arguments[0]),z):h},z.tickPadding=function(){return arguments.length?(v=arguments.length<=0?void 0:arguments[0],z):v},z.decorate=function(){return arguments.length?(d=arguments.length<=0?void 0:arguments[0],z):d},z.scale=function(){return arguments.length?(i=arguments.length<=0?void 0:arguments[0],z):i},z.ticks=function(){for(var t=arguments.length,n=Array(t),r=0;r<t;r++)n[r]=arguments[r];return u=[].concat(n),z},z.tickArguments=function(){return arguments.length?(u=null==(arguments.length<=0?void 0:arguments[0])?[]:[].concat(c(arguments.length<=0?void 0:arguments[0])),z):u.slice()},z.tickValues=function(){return arguments.length?(l=null==(arguments.length<=0?void 0:arguments[0])?[]:[].concat(c(arguments.length<=0?void 0:arguments[0])),z):l.slice()},z},d=function(t,n){var r=!1,e=function(t,e,o){var u=0,a=i.tickSizeInner()+i.tickPadding(),c=!1;if(r){var f=n(t),l=e<o.length-1?n(o[e+1]):n.range()[1];u=(l-f)/2,a=i.tickPadding(),c=e===o.length-1&&f===l}return{offset:[u,a],hidden:c}},i=l(t,n,{labelOffset:e}),o=function(t){return i(t)};return o.tickCenterLabel=function(){return arguments.length?(r=arguments.length<=0?void 0:arguments[0],o):r},u(o,i),o},s=function(t){return d("top",t)},h=function(t){return d("bottom",t)},g=function(t){return d("left",t)},v=function(t){return d("right",t)},p=function(t,n){var r=null,e=function(t,r,e){if(n.step)return n.step();var i=n(t);return r<e.length-1?n(e[r+1])/i:2*(n.range()[1]-i)},i=function(t,n,i){var o=0;return o=r?r(t,n):e(t,n,i)/2,{path:[[o,0],[o,a.tickSizeInner()]],hidden:n===i.length-1}},o=function(){return{offset:[0,a.tickPadding()]}},a=l(t,n,{labelOffset:o,tickPath:i}),c=function(t){return a(t)};return c.tickOffset=function(){return arguments.length?(r=arguments.length<=0?void 0:arguments[0],c):r},u(c,a),c},y=function(t){return p("top",t)},m=function(t){return p("bottom",t)},b=function(t){return p("left",t)},k=function(t){return p("right",t)};t.axisTop=s,t.axisBottom=h,t.axisLeft=g,t.axisRight=v,t.axisOrdinalTop=y,t.axisOrdinalBottom=m,t.axisOrdinalLeft=b,t.axisOrdinalRight=k,Object.defineProperty(t,"__esModule",{value:!0})}); |
@@ -6,2 +6,13 @@ # Change Log | ||
<a name="2.1.0"></a> | ||
# [2.1.0](https://github.com/d3fc/d3fc/compare/@d3fc/d3fc-axis@2.0.10...@d3fc/d3fc-axis@2.1.0) (2019-03-14) | ||
### Features | ||
* Two types of axis components, with specialisations ([5269d28](https://github.com/d3fc/d3fc/commit/5269d28)) | ||
<a name="2.0.10"></a> | ||
@@ -8,0 +19,0 @@ ## [2.0.10](https://github.com/d3fc/d3fc/compare/@d3fc/d3fc-axis@2.0.9...@d3fc/d3fc-axis@2.0.10) (2019-01-15) |
@@ -8,2 +8,10 @@ var width = 400; | ||
var linear = d3.scaleTime() | ||
.domain([new Date('2019-03-02'), new Date('2019-03-07')]) | ||
.range([margin, width - margin]); | ||
var bands = d3.scaleBand() | ||
.domain(['Carrots', 'Bananas', 'Sausages', 'Pickles']) | ||
.range([margin, width - margin]); | ||
var axis = fc.axisBottom(scale) | ||
@@ -17,7 +25,23 @@ .decorate(function(s) { | ||
var axisLinear = fc.axisBottom(linear) | ||
.tickArguments([5]) | ||
.tickCenterLabel(true) | ||
.tickPadding(5) | ||
.tickSizeInner(10); | ||
var axisBetweenTicks = fc.axisOrdinalBottom(bands) | ||
.tickPadding(5) | ||
.tickSizeInner(10); | ||
var svg = d3.select('body').append('svg') | ||
.attr('width', width) | ||
.attr('height', 80); | ||
.attr('height', 200); | ||
svg.append('g') | ||
.attr('transform', 'translate(0, 10)') | ||
.call(axis); | ||
svg.append('g') | ||
.attr('transform', 'translate(0, 80)') | ||
.call(axisLinear); | ||
svg.append('g') | ||
.attr('transform', 'translate(0, 140)') | ||
.call(axisBetweenTicks); |
export * from './src/axis'; | ||
export * from './src/axisOrdinal'; |
{ | ||
"name": "@d3fc/d3fc-axis", | ||
"version": "2.0.10", | ||
"version": "2.1.0", | ||
"description": "A drop-in replacement for d3 axis, with support for the d3fc decorate pattern", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
# d3fc-axis | ||
A drop-in replacement for d3 axis, with support for the d3fc decorate pattern. | ||
A drop-in replacement for d3 axis, with support for the d3fc decorate pattern, and improved layout options for ordinal scales. | ||
@@ -15,4 +15,57 @@ [Main d3fc package](https://github.com/d3fc/d3fc) | ||
This is a drop-in replacement for [d3-axis](https://github.com/d3/d3-axis), so please refer to that project for detailed documentation. | ||
This is a drop-in replacement for [d3-axis](https://github.com/d3/d3-axis), so please refer to that project for detailed documentation - all of the d3-axis properties and features are supported by this component. A few additional properties have been added, which are documented below. | ||
## Tick and label layout options | ||
### Centred labels | ||
When rendering an axis that is associated with a linear scale, the default behaviour for the axis is to render labels underneath each tick. This often makes the most sense, with the label indicating a specific instance on the scale. | ||
However, in some cases the label may refer to a range of data. For example, on a time scale, 'Monday' refers to a time range rather than an instance in time. As a result, it makes more sense to render this label between the tick marks. | ||
With the d3fc axis, setting `tickCenterLabel` to `true` will render the labels in between the tick marks, offsetting them to the right: | ||
``` | ||
const linear = d3.scaleTime() | ||
.domain([new Date('2019-03-02'), new Date('2019-03-07')]) | ||
.range([0, 400]); | ||
const axis = fc.axisBottom(linear) | ||
.tickArguments([5]) | ||
.tickCenterLabel(true); | ||
``` | ||
<img src="screenshots/center.png"/> | ||
### Band scales | ||
Band scales are often used for rendering charts with a categorical dimension. With the d3 axis, the ticks and labels are rendered at the central point of each bar / column. However, it can make more sense to render the ticks at the boundary between each bar / category. | ||
The d3fc ordinal axis is a drop-in replacement that renders the labels at the central point of the bar / column, and the ticks at the boundaries: | ||
``` | ||
const vegetableScale = d3.scaleBand() | ||
.domain(['Carrots', 'Bananas', 'Sausages', 'Pickles']) | ||
.paddingInner(0.1) | ||
.paddingOuter(0.1) | ||
.range([0, width]); | ||
const barSeries = fc.autoBandwidth(fc.seriesSvgBar()) | ||
.align('left') | ||
.crossValue(x => x.veg) | ||
.mainValue(x => x.qty) | ||
.xScale(vegetableScale) | ||
.yScale(qtyScale); | ||
// this is the d3fc ordinal axis | ||
const axis = fc.axisOrdinalBottom(scale); | ||
// render the above components to SVG ... | ||
var svg = d3.select('body').append('svg') | ||
... | ||
``` | ||
<img src="screenshots/ordinal.png"/> | ||
## Decorate pattern | ||
@@ -78,2 +131,2 @@ | ||
<img src="screenshots/color.png"/> | ||
<img src="screenshots/color.png"/> |
219
src/axis.js
@@ -1,217 +0,38 @@ | ||
import { select } from 'd3-selection'; | ||
import { line } from 'd3-shape'; | ||
import { dataJoin as _dataJoin } from '@d3fc/d3fc-data-join'; | ||
import { rebindAll } from '@d3fc/d3fc-rebind'; | ||
import { axisBase } from './axisBase'; | ||
const identity = (d) => d; | ||
const axis = (orient, scale) => { | ||
let tickCenterLabel = false; | ||
let tickArguments = [10]; | ||
let tickValues = null; | ||
let decorate = () => {}; | ||
let tickFormat = null; | ||
let tickSizeOuter = 6; | ||
let tickSizeInner = 6; | ||
let tickPadding = 3; | ||
const labelOffset = (tick, index, ticksArray) => { | ||
let x = 0; | ||
let y = base.tickSizeInner() + base.tickPadding(); | ||
let hidden = false; | ||
if (tickCenterLabel) { | ||
const thisPosition = scale(tick); | ||
const nextPosition = index < ticksArray.length - 1 ? scale(ticksArray[index + 1]) : scale.range()[1]; | ||
x = (nextPosition - thisPosition) / 2; | ||
const svgDomainLine = line(); | ||
const dataJoin = _dataJoin('g', 'tick') | ||
.key(identity); | ||
const domainPathDataJoin = _dataJoin('path', 'domain'); | ||
// returns a function that creates a translation based on | ||
// the bound data | ||
const containerTranslate = (scale, trans) => { | ||
let offset = 0; | ||
if (scale.bandwidth) { | ||
offset = scale.bandwidth() / 2; | ||
if (scale.round()) { | ||
offset = Math.round(offset); | ||
} | ||
y = base.tickPadding(); | ||
hidden = (index === ticksArray.length - 1) && (thisPosition === nextPosition); | ||
} | ||
return d => trans(scale(d) + offset, 0); | ||
return { offset: [x, y], hidden }; | ||
}; | ||
const translate = (x, y) => | ||
isVertical() | ||
? `translate(${y}, ${x})` | ||
: `translate(${x}, ${y})`; | ||
const base = axisBase(orient, scale, {labelOffset}); | ||
const pathTranspose = (arr) => | ||
isVertical() | ||
? arr.map(d => [d[1], d[0]]) | ||
: arr; | ||
const isVertical = () => | ||
orient === 'left' || orient === 'right'; | ||
const tryApply = (fn, args, defaultVal) => | ||
scale[fn] ? scale[fn].apply(scale, args) : defaultVal; | ||
const axis = (selection) => { | ||
if (selection.selection) { | ||
dataJoin.transition(selection); | ||
domainPathDataJoin.transition(selection); | ||
} | ||
selection.each((data, index, group) => { | ||
const element = group[index]; | ||
const container = select(element); | ||
if (!element.__scale__) { | ||
container | ||
.attr('fill', 'none') | ||
.attr('font-size', 10) | ||
.attr('font-family', 'sans-serif') | ||
.attr('text-anchor', orient === 'right' ? 'start' : orient === 'left' ? 'end' : 'middle'); | ||
} | ||
// Stash a snapshot of the new scale, and retrieve the old snapshot. | ||
const scaleOld = element.__scale__ || scale; | ||
element.__scale__ = scale.copy(); | ||
const ticksArray = tickValues == null ? tryApply('ticks', tickArguments, scale.domain()) : tickValues; | ||
const tickFormatter = tickFormat == null ? tryApply('tickFormat', tickArguments, identity) : tickFormat; | ||
const sign = orient === 'bottom' || orient === 'right' ? 1 : -1; | ||
// add the domain line | ||
const range = scale.range(); | ||
const domainPathData = pathTranspose([ | ||
[range[0], sign * tickSizeOuter], | ||
[range[0], 0], | ||
[range[1], 0], | ||
[range[1], sign * tickSizeOuter] | ||
]); | ||
const domainLine = domainPathDataJoin(container, [data]); | ||
domainLine | ||
.attr('d', svgDomainLine(domainPathData)) | ||
.attr('stroke', '#000'); | ||
const g = dataJoin(container, ticksArray); | ||
// enter | ||
g.enter() | ||
.attr('transform', containerTranslate(scaleOld, translate)) | ||
.append('path') | ||
.attr('stroke', '#000'); | ||
const labelOffset = sign * (tickSizeInner + tickPadding); | ||
g.enter() | ||
.append('text') | ||
.attr('transform', translate(0, labelOffset)) | ||
.attr('fill', '#000'); | ||
// exit | ||
g.exit() | ||
.attr('transform', containerTranslate(scale, translate)); | ||
// update | ||
g.select('path') | ||
.attr('d', | ||
(d) => svgDomainLine(pathTranspose([ | ||
[0, 0], [0, sign * tickSizeInner] | ||
])) | ||
); | ||
g.select('text') | ||
.attr('transform', translate(0, labelOffset)) | ||
.attr('dy', () => { | ||
let offset = '0em'; | ||
if (isVertical()) { | ||
offset = '0.32em'; | ||
} else if (orient === 'bottom') { | ||
offset = '0.71em'; | ||
} | ||
return offset; | ||
}) | ||
.text(tickFormatter); | ||
g.attr('transform', containerTranslate(scale, translate)); | ||
decorate(g, data, index); | ||
}); | ||
return base(selection); | ||
}; | ||
axis.tickFormat = (...args) => { | ||
axis.tickCenterLabel = (...args) => { | ||
if (!args.length) { | ||
return tickFormat; | ||
return tickCenterLabel; | ||
} | ||
tickFormat = args[0]; | ||
tickCenterLabel = args[0]; | ||
return axis; | ||
}; | ||
axis.tickSize = (...args) => { | ||
if (!args.length) { | ||
return tickSizeInner; | ||
} | ||
tickSizeInner = tickSizeOuter = Number(args[0]); | ||
return axis; | ||
}; | ||
axis.tickSizeInner = (...args) => { | ||
if (!args.length) { | ||
return tickSizeInner; | ||
} | ||
tickSizeInner = Number(args[0]); | ||
return axis; | ||
}; | ||
axis.tickSizeOuter = (...args) => { | ||
if (!args.length) { | ||
return tickSizeOuter; | ||
} | ||
tickSizeOuter = Number(args[0]); | ||
return axis; | ||
}; | ||
axis.tickPadding = (...args) => { | ||
if (!args.length) { | ||
return tickPadding; | ||
} | ||
tickPadding = args[0]; | ||
return axis; | ||
}; | ||
axis.decorate = (...args) => { | ||
if (!args.length) { | ||
return decorate; | ||
} | ||
decorate = args[0]; | ||
return axis; | ||
}; | ||
axis.scale = (...args) => { | ||
if (!args.length) { | ||
return scale; | ||
} | ||
scale = args[0]; | ||
return axis; | ||
}; | ||
axis.ticks = (...args) => { | ||
tickArguments = [...args]; | ||
return axis; | ||
}; | ||
axis.tickArguments = (...args) => { | ||
if (!args.length) { | ||
return tickArguments.slice(); | ||
} | ||
tickArguments = args[0] == null ? [] : [...args[0]]; | ||
return axis; | ||
}; | ||
axis.tickValues = (...args) => { | ||
if (!args.length) { | ||
return tickValues.slice(); | ||
} | ||
tickValues = args[0] == null ? [] : [...args[0]]; | ||
return axis; | ||
}; | ||
rebindAll(axis, base); | ||
return axis; | ||
@@ -218,0 +39,0 @@ }; |
93905
36
1019
130