revised x-axis, y-axis, handler, main.less to use flexbox to handle resizing

This commit is contained in:
Juan Thomassie 2014-09-11 11:35:58 -05:00
parent 8a48d1c79a
commit 96b5eb3067
8 changed files with 113 additions and 286 deletions

View file

@ -48,11 +48,6 @@ visualize {
.flex-direction(column);
}
.y-axis-div {
.flex(4 1);
min-width: 25px
}
.y-axis-filler-div {
.flex(1 1);
}
@ -81,11 +76,11 @@ div.y-axis-label {
/* legend */
.legend-col-wrapper {
.flex(0.3 1 auto);
.flex(0 1 auto);
z-index: 10;
overflow-x: hidden;
overflow-y: auto;
min-width: 40px
min-width: 40px;
}
.header {
@ -269,36 +264,46 @@ path, line, .axis line, .axis path {
/* YAxis logic */
.y-axis-col-wrapper {
.display(flex);
.flex(0.1 1);
.flex(0 0 auto);
.flex-direction(column);
min-width: 40px;
}
.y-axis-col {
.display(flex);
.flex(35 1);
.flex-direction(row);
.flex(1 0 50px);
}
.y-axis-spacer-block {
.flex(0 1 50px);
}
.y-axis-div-wrapper {
.display(flex);
.flex-direction(column);
.flex(0 0 33px);
}
.y-axis-div {
.flex(1 1 100%);
}
.y-axis-title {
// flex: 1 1;
width: 15px;
min-width: 15px;
.flex(0 0 15px);
}
.y-axis-chart-title {
.display(flex);
.flex-direction(column);
.flex(0 0 15px);
}
.y-axis-title text {
font-size: 11px;
}
.y-axis-chart-title {
.display(flex);
// flex: 1 1;
.flex-direction(column);
width: 15px
}
.chart-title {
.flex(1 1);
.flex(1 1 100%);
}
.chart-title text {
@ -306,35 +311,28 @@ path, line, .axis line, .axis path {
fill: #848e96;
}
.y-axis-div-wrapper {
.display(flex);
//flex: 1 1 auto;
.flex-direction(column);
min-width: 20px
}
.vis-col-wrapper {
.display(flex);
.flex(35 1);
.flex(1 0 20px);
.flex-direction(column);
}
.chart-wrapper {
.display(flex);
.flex(35 1);
.flex(1 0 20px);
overflow: visible;
}
.chart-wrapper-column {
.display(flex);
.flex(35 1);
.flex(1 0 20px);
.flex-direction(row);
}
.chart-wrapper-row {
.display(flex);
.flex(35 1);
.flex-direction(column);
.flex(1 0 50px);
}
.chart {
@ -350,36 +348,27 @@ path, line, .axis line, .axis path {
.flex(1 1);
}
.y-axis-spacer-block {
.flex(8 1);
}
.x-axis-wrapper {
.display(flex);
.flex(8 1);
.flex-direction(column);
.flex(0 1 50px);
overflow: visible;
}
.x-axis-div-wrapper {
.display(flex);
.flex(8 1);
.flex-direction(row);
min-height: 20px;
.flex(0 1 15px);
}
.x-axis-chart-title {
.display(flex);
.flex(1 1);
.flex-direction(row);
min-height: 15px;
max-height: 15px;
.flex(0 0 15px);
}
.x-axis-title {
.flex(1 1);
min-height: 15px;
max-height: 15px;
.flex(0 0 15px);
}
.x-axis-title text {
@ -387,8 +376,7 @@ path, line, .axis line, .axis path {
}
.x-axis-div {
.flex(8 1);
overflow: visible;
.flex(1 1 100%);
margin-top: -5px;
}

View file

@ -7,6 +7,7 @@ define(function (require) {
// Validate that the height and width are not 0 or NaN
ErrorHandler.prototype.validateWidthandHeight = function (width, height) {
console.log(width, height);
if (_.isNaN(height) || height <= 0 || _.isNaN(width) || width <= 0) {
throw new Error('The height and/or width of this container is too ' +
'small for this chart.');

View file

@ -95,9 +95,9 @@ define(function (require) {
.attr('transform', function () {
if (dataType === 'rows') {
// if `rows`, rotate the chart titles
return 'translate(11,' + height / 2.2 + ')rotate(270)';
return 'translate(11,' + height / 2 + ')rotate(270)';
}
return 'translate(' + width / 2 + ',11)';
return 'translate(' + width / 2 + ',8)';
})
.attr('text-anchor', 'middle')
.text(function (d) {

View file

@ -76,10 +76,10 @@ define(function (require) {
this.layout,
this.legend,
this.tooltip,
this.xAxis,
this.yAxis,
this.axisTitle,
this.chartTitle
this.chartTitle,
this.yAxis,
this.xAxis
];
}

View file

@ -71,8 +71,8 @@ define(function (require) {
var tipHeight = tooltipDiv[0][0].clientHeight;
var yOffset = 5;
// apply y offset to keep tooltip within bottom of chart
if ((chartHeight - offsetY + 10) < (tipHeight)) {
yOffset = tipHeight - (chartHeight - offsetY + 5);
if ((chartHeight - offsetY + 5) < (tipHeight)) {
yOffset = tipHeight - (chartHeight - offsetY + 0);
}
// return text and position for tooltip

View file

@ -4,6 +4,7 @@ define(function (require) {
var _ = require('lodash');
var ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
var ChartTitle = Private(require('components/vislib/lib/chart_title'));
/*
* Add an x axis to the visualization
@ -148,6 +149,7 @@ define(function (require) {
// save a reference to the xAxis
this.xAxis = d3.svg.axis()
.scale(this.xScale)
.ticks(10)
.tickFormat(this.xAxisFormatter)
.orient('bottom');
};
@ -187,7 +189,6 @@ define(function (require) {
});
selection.call(self.filterOrRotate());
};
};
@ -215,40 +216,7 @@ define(function (require) {
}
});
selection.call(self.resizeAxisLayoutForLabels());
};
};
// Filter out text labels by width and position on axis
XAxis.prototype.filterAxisLabels = function () {
var self = this;
var startX = 0;
var maxW = $('.x-axis-div').width();
var par;
var myX;
var myWidth;
var halfWidth;
return function (selection) {
selection.selectAll('.tick text')
.text(function (d, i) {
par = d3.select(this.parentNode).node();
myX = +self.xScale(d).toFixed(1);
myWidth = +par.getBBox().width.toFixed(1);
halfWidth = +((par.getBBox().width / 2).toFixed(1));
// trims labels that would overlap each other
// or extend past left or right edges
// if prev label pos (or 0) + half of label width is < label pos
// and label pos + half width is not > width of axis
if ((startX + halfWidth) < myX && maxW > (myX + halfWidth)) {
startX = myX + halfWidth;
return self.xAxisFormatter(d);
} else {
d3.select(this.parentNode).select('line').remove();
return '';
}
});
selection.call(self.fitTitles());
};
};
@ -312,95 +280,69 @@ define(function (require) {
};
};
// Returns a function that resizes layout divs and
// adds css flexbox values to fit axis labels
XAxis.prototype.resizeAxisLayoutForLabels = function () {
// Filter out text labels by width and position on axis
XAxis.prototype.filterAxisLabels = function () {
var self = this;
var visEl = $(self.el);
var div;
var svg;
var tick;
var chartwrap;
var titlespace;
var xwrapper;
var xdiv;
var xdivwrapper;
var yspacerblock;
var ratio;
var flex;
var chartToXaxis;
var dataType;
var tickHt;
var chartHt;
var startX = 0;
var maxW;
var par;
var myX;
var myWidth;
var halfWidth;
return function (selection) {
selection.each(function () {
div = d3.select(this);
svg = div.select('svg');
tick = svg.select('.tick');
dataType = this.parentNode.__data__.series ? 'series' : this.parentNode.__data__.rows ? 'rows' : 'columns';
xwrapper = visEl.find('.x-axis-wrapper');
xdiv = visEl.find('.x-axis-div');
xdivwrapper = visEl.find('.x-axis-div-wrapper');
yspacerblock = visEl.find('.y-axis-spacer-block');
// define chartwrap and titlespace, for chart title
// and axis title based on data type
if (dataType === 'series') {
chartwrap = visEl.find('.chart-wrapper');
titlespace = 15;
} else if (dataType === 'rows') {
chartwrap = visEl.find('.chart-wrapper-row');
titlespace = 15;
} else {
chartwrap = visEl.find('.chart-wrapper-column');
titlespace = 30;
}
// should have a tick node
if (!tick.node()) {
throw new Error('x-axis tick.node() is undefined');
}
tickHt = tick.node().getBBox().height;
chartHt = chartwrap.height();
flex = self.getFlexVal(self._attr.isRotated, titlespace, tickHt, chartHt);
// set height of svg, transform to fit axis labels
svg.attr('height', chartHt);
xwrapper.css('flex', flex + ' 1');
xdiv.css('flex', flex + ' 1');
yspacerblock.css('flex', flex + ' 1');
});
selection.selectAll('.tick text')
.text(function (d, i) {
par = d3.select(this.parentNode).node();
myX = self.xScale(d);
myWidth = par.getBBox().width;
halfWidth = par.getBBox().width / 2;
maxW = $('.x-axis-div').width();
// trims labels that would overlap each other
// or extend past left or right edges
// if prev label pos (or 0) + half of label width is < label pos
// and label pos + half width is not > width of axis
if ((startX + halfWidth) < myX && maxW > (myX + halfWidth)) {
startX = myX + halfWidth;
return self.xAxisFormatter(d);
} else {
d3.select(this.parentNode).remove();
}
});
};
};
// Returns flexbox css value using linear scales
XAxis.prototype.getFlexVal = function (isRotated, titleSpace, tickHt, chartHt) {
var ratio;
var rotScale = d3.scale.linear()
.domain([0.1, 0.5, 2])
.range([3.3, 22, 70]);
// Returns a function that adjusts axis title and
// all chart title transforms to fit axis labels
XAxis.prototype.fitTitles = function () {
var self = this;
var visEl = $(self.el);
var xAxisTitle = visEl.find('.x-axis-title');
var xAxisChartTitle = visEl.find('.x-axis-chart-title');
var text;
var titles;
var titleWidth;
var flatScale = d3.scale.linear()
.domain([0.2, 1, 2, 20])
.range([1.1, 1, 2, 20]);
if (!isRotated) {
// flat labels
ratio = flatScale(35 * (titleSpace + tickHt) / chartHt);
//console.log('flat', +ratio.toFixed(1), 35 * (titleSpace + tickHt) / chartHt);
} else {
// rotated labels
ratio = rotScale((titleSpace + tickHt) / chartHt);
//console.log('rotated', +ratio.toFixed(1), (titleSpace + tickHt) / chartHt);
}
return ratio.toFixed(1);
return function () {
// set transform of x-axis-title text to fit .x-axis-title div width
titleWidth = xAxisTitle.width();
text = d3.select('.x-axis-title')
.select('svg')
.select('text')
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
// set transform of x-axis-chart-titles text to fit .chart-title div width
titleWidth = xAxisChartTitle.find('.chart-title').width();
titles = d3.select('.x-axis-chart-title')
.selectAll('.chart-title');
titles.each(function () {
text = d3.select(this)
.select('svg')
.select('text')
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
});
};
};
return XAxis;

View file

@ -30,7 +30,6 @@ define(function (require) {
// Render the y axis
YAxis.prototype.render = function () {
d3.select(this.el).selectAll('.y-axis-div').call(this.draw());
d3.select(this.el).selectAll('.y-axis-div').call(this.resizeAxisLayoutForLabels());
};
// Determine if data should be stacked
@ -99,6 +98,7 @@ define(function (require) {
// Return the d3 y axis
YAxis.prototype.getYAxis = function (height) {
var self = this;
var yScale = this.getYScale(height);
// y scale should never be `NaN`
@ -112,6 +112,11 @@ define(function (require) {
.tickFormat(d3.format('s'))
.ticks(this.tickScale(height))
.orient('left');
if (self.yScale.domain()[1] <= 10) {
this.yAxis.tickFormat(d3.format('n'));
}
return this.yAxis;
};
@ -138,12 +143,14 @@ define(function (require) {
var svg;
return function (selection) {
selection.each(function () {
div = d3.select(this);
width = $(this).width();
height = $(this).height() - margin.top - margin.bottom;
// Validate whether width and height are not 0 or `NaN`
console.log('y-axis', width, height);
self.validateWidthandHeight(width, height);
var yAxis = self.getYAxis(height);
@ -161,118 +168,6 @@ define(function (require) {
};
};
// Returns max tick label length
YAxis.prototype.getMaxLabelLength = function (labels) {
var arr = [];
// get max tick label length
_.forEach(labels[0], function (n) {
arr.push(n.getBBox().width);
});
return _.max(arr);
};
// Set width of svg and trnasform axis to fit labels
YAxis.prototype.updateLayoutForRotatedLabels = function (svg, length) {
var margin = this._attr.margin;
var tickspace = 14;
length += tickspace;
// set width of svg, x-axis-div and x-axis-div-wrapper to fit ticklabels
svg.attr('width', length + 6);
d3.selectAll('.y.axis').attr('transform', 'translate(' + (length + 2) + ',' + margin.top + ')');
};
// Return a function that resizes layout divs and
// adds css flexbox values to fit axis labels
YAxis.prototype.resizeAxisLayoutForLabels = function () {
var self = this;
var visEl = $(self.el);
var div;
var svg;
var tick;
var titlespace;
var flex;
var dataType;
var visWrap = visEl.find('.vis-col-wrapper');
var yAxisColWrap = visEl.find('.y-axis-col-wrapper');
var yAxisDivWrap = visEl.find('.y-axis-div-wrapper');
var yAxisDiv = visEl.find('.y-axis-div');
var yAxisTitle = visEl.find('.y-axis-title');
var yAxisChartTitle = visEl.find('.y-axis-chart-title');
var legendColWrap = visEl.find('.legend-col-wrapper');
var labelWidths = [];
var maxWidth;
var labels;
return function (selection) {
selection.each(function () {
div = d3.select(this);
svg = div.select('svg');
dataType = this.parentNode.__data__.series ? 'series' : this.parentNode.__data__.rows ? 'rows' : 'columns';
labels = selection.selectAll('.tick text');
if (dataType === 'series') {
titlespace = 15;
} else if (dataType === 'rows') {
titlespace = 15;
} else {
titlespace = 30;
}
// get max width tick
_.forEach(labels[0], function (n) {
labelWidths.push(n.getBBox().width);
});
maxWidth = d3.max(labelWidths) + 18;
flex = self.getFlexVal(dataType, titlespace, maxWidth, visWrap.width());
// set flex values
yAxisColWrap.css('flex', flex + ' 1');
yAxisDiv.css('width', maxWidth + 'px');
yAxisDivWrap.css('width', (maxWidth + 12) + 'px');
// set width of svg, transform to fit axis labels
svg.attr('width', maxWidth);
svg.attr('transform', 'translate(0,0)');
svg.select('g').attr('transform', 'translate(' + (maxWidth - 1) + ',10)');
});
};
};
// Return flexbox css value using linear scales
YAxis.prototype.getFlexVal = function (dataType, titleSpace, tickWidth, visWidth) {
var ratio;
var seriesScale = d3.scale.linear()
.domain([0.2, 2])
.range([0.24, 2]);
var rowsScale = d3.scale.linear()
.domain([0.2, 2])
.range([0.26, 2.6]);
var colsScale = d3.scale.linear()
.domain([0.2, 2])
.range([0.16, 1.6]);
// define ratio based on datatype
if (dataType === 'rows') {
ratio = rowsScale(35 * (titleSpace + tickWidth) / visWidth);
} else if (dataType === 'columns') {
ratio = colsScale(35 * (titleSpace + tickWidth) / visWidth);
} else {
ratio = seriesScale(35 * (titleSpace + tickWidth) / visWidth);
}
//console.log(dataType, ratio.toFixed(1), 35 * (titleSpace + tickWidth) / visWidth);
return ratio.toFixed(1);
};
return YAxis;
};
});

View file

@ -126,11 +126,12 @@ define(function (require) {
el = d3.select('body').append('div')
.attr('class', 'y-axis-wrapper')
.style('width', '40px')
.style('height', '40px')
.datum(data);
yAxisDiv = el.append('div')
.attr('class', 'y-axis-div')
.style('height', '40px');
.attr('class', 'y-axis-div');
dataObj = new Data(data);
yAxis = new YAxis({
@ -145,7 +146,7 @@ define(function (require) {
});
afterEach(function () {
el.remove();
//el.remove();
});
describe('render Method', function () {