Skip to content

Commit

Permalink
Merge pull request #2511 from plotly/ragged-table
Browse files Browse the repository at this point in the history
Support ragged tables and show full table borders
  • Loading branch information
alexcjohnson committed Mar 30, 2018
2 parents d3ff96b + 4936e20 commit da37b4f
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 163 deletions.
73 changes: 63 additions & 10 deletions src/traces/table/data_preparation_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,28 @@ var isNumeric = require('fast-isnumeric');

// pure functions, don't alter but passes on `gd` and parts of `trace` without deep copying
module.exports = function calc(gd, trace) {
var cellsValues = trace.cells.values;
var cellsValues = squareStringMatrix(trace.cells.values);
var slicer = function(a) {
return a.slice(trace.header.values.length, a.length);
};
var headerValues = trace.header.values.map(function(c) {
return Array.isArray(c) ? c : [c];
}).concat(slicer(cellsValues).map(function() {return [''];}));
var headerValuesIn = squareStringMatrix(trace.header.values);
if(headerValuesIn.length && !headerValuesIn[0].length) {
headerValuesIn[0] = [''];
headerValuesIn = squareStringMatrix(headerValuesIn);
}
var headerValues = headerValuesIn
.concat(slicer(cellsValues).map(function() {
return emptyStrings((headerValuesIn[0] || ['']).length);
}));

var domain = trace.domain;
var groupWidth = Math.floor(gd._fullLayout._size.w * (domain.x[1] - domain.x[0]));
var groupHeight = Math.floor(gd._fullLayout._size.h * (domain.y[1] - domain.y[0]));
var headerRowHeights = trace.header.values.length ? headerValues[0].map(function() {return trace.header.height;}) : [c.emptyHeaderHeight];
var rowHeights = cellsValues.length ? cellsValues[0].map(function() {return trace.cells.height;}) : [];
var headerHeight = headerRowHeights.reduce(function(a, b) {return a + b;}, 0);
var headerRowHeights = trace.header.values.length ?
headerValues[0].map(function() { return trace.header.height; }) :
[c.emptyHeaderHeight];
var rowHeights = cellsValues.length ? cellsValues[0].map(function() { return trace.cells.height; }) : [];
var headerHeight = headerRowHeights.reduce(sum, 0);
var scrollHeight = groupHeight - headerHeight;
var minimumFillHeight = scrollHeight + c.uplift;
var anchorToRowBlock = makeAnchorToRowBlock(rowHeights, minimumFillHeight);
Expand All @@ -41,24 +50,27 @@ module.exports = function calc(gd, trace) {
trace.columnwidth;
return isNumeric(value) ? Number(value) : 1;
});
var totalColumnWidths = columnWidths.reduce(function(p, n) {return p + n;}, 0);
var totalColumnWidths = columnWidths.reduce(sum, 0);

// fit columns in the available vertical space as there's no vertical scrolling now
columnWidths = columnWidths.map(function(d) {return d / totalColumnWidths * groupWidth;});
columnWidths = columnWidths.map(function(d) { return d / totalColumnWidths * groupWidth; });

var maxLineWidth = Math.max(arrayMax(trace.header.line.width), arrayMax(trace.cells.line.width));

var calcdata = {
key: trace.index,
translateX: domain.x[0] * gd._fullLayout._size.w,
translateY: gd._fullLayout._size.h * (1 - domain.y[1]),
size: gd._fullLayout._size,
width: groupWidth,
maxLineWidth: maxLineWidth,
height: groupHeight,
columnOrder: columnOrder, // will be mutated on column move, todo use in callback
groupHeight: groupHeight,
rowBlocks: rowBlocks,
headerRowBlocks: headerRowBlocks,
scrollY: 0, // will be mutated on scroll
cells: trace.cells,
cells: extendFlat({}, trace.cells, {values: cellsValues}),
headerCells: extendFlat({}, trace.header, {values: headerValues}),
gdColumns: headerValues.map(function(d) {return d[0];}),
gdColumnsOriginalOrder: headerValues.map(function(d) {return d[0];}),
Expand Down Expand Up @@ -89,6 +101,47 @@ module.exports = function calc(gd, trace) {
return calcdata;
};

function arrayMax(maybeArray) {
if(Array.isArray(maybeArray)) {
var max = 0;
for(var i = 0; i < maybeArray.length; i++) {
max = Math.max(max, arrayMax(maybeArray[i]));
}
return max;
}
return maybeArray;
}

function sum(a, b) { return a + b; }

// fill matrix in place to equal lengths
// and ensure it's uniformly 2D
function squareStringMatrix(matrixIn) {
var matrix = matrixIn.slice();
var minLen = Infinity;
var maxLen = 0;
var i;
for(i = 0; i < matrix.length; i++) {
if(!Array.isArray(matrix[i])) matrix[i] = [matrix[i]];
minLen = Math.min(minLen, matrix[i].length);
maxLen = Math.max(maxLen, matrix[i].length);
}

if(minLen !== maxLen) {
for(i = 0; i < matrix.length; i++) {
var padLen = maxLen - matrix[i].length;
if(padLen) matrix[i] = matrix[i].concat(emptyStrings(padLen));
}
}
return matrix;
}

function emptyStrings(len) {
var padArray = new Array(len);
for(var j = 0; j < len; j++) padArray[j] = '';
return padArray;
}

function xScale(d) {
return d.calcdata.columns.reduce(function(prev, next) {
return next.xIndex < d.xIndex ? prev + next.columnWidth : prev;
Expand Down
10 changes: 8 additions & 2 deletions src/traces/table/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,18 @@ module.exports = function plot(gd, wrappedTraceHolders) {
.attr('fill', 'none');

columnBoundaryRect
.attr('width', function(d) {return d.columnWidth;})
.attr('height', function(d) {return d.calcdata.height + c.uplift;});
.attr('width', function(d) { return d.columnWidth + 2 * roundHalfWidth(d); })
.attr('height', function(d) {return d.calcdata.height + 2 * roundHalfWidth(d) + c.uplift;})
.attr('x', function(d) { return -roundHalfWidth(d); })
.attr('y', function(d) { return -roundHalfWidth(d); });

updateBlockYPosition(null, cellsColumnBlock, tableControlView);
};

function roundHalfWidth(d) {
return Math.ceil(d.calcdata.maxLineWidth / 2);
}

function scrollAreaBottomClipKey(gd, d) {
return 'clip' + gd._fullLayout._uid + '_scrollAreaBottomClip_' + d.key;
}
Expand Down
Binary file modified test/image/baselines/grid_subplot_types.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed test/image/baselines/table_latex_multitrace.png
Binary file not shown.
Binary file modified test/image/baselines/table_latex_multitrace_scatter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/table_plain_birds.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/table_ragged.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/table_wrapped_birds.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
137 changes: 0 additions & 137 deletions test/image/mocks/table_latex_multitrace.json

This file was deleted.

8 changes: 4 additions & 4 deletions test/image/mocks/table_latex_multitrace_scatter.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"header": {
"values": [["<b>#</b>"], ["Half-angle form"], ["Equivalent"]],
"align": ["right", "center", "center"],
"line": {"width": 0.0},
"line": {"width": 1, "color": ["dimgray", "grey"]},
"fill": {"color": ["dimgray", "grey"]},
"font": {"family": "Arial", "size": 14, "color": "white"}
},
Expand Down Expand Up @@ -55,7 +55,7 @@
"header": {
"values": [["<b>#</b>"], ["$$\\theta$$"], ["Half-angle sine"], ["Half-angle cosine"]],
"align": "right",
"line": {"width": 0.0},
"line": {"width": 1, "color": ["dimgray", "grey"]},
"fill": {"color": ["dimgray", "grey"]},
"font": {"family": "Arial", "size": 14, "color": "white"}
},
Expand Down Expand Up @@ -88,7 +88,7 @@
"header": {
"values": [["Trig function"], ["Double-angle form"]],
"align": ["right", "left"],
"line": {"width": 0.0},
"line": {"width": 1, "color": ["dimgray", "grey"]},
"fill": {"color": ["dimgray", "grey"]},
"font": {"family": "Arial", "size": 14, "color": "white"}
},
Expand Down Expand Up @@ -117,7 +117,7 @@
"header": {
"values": [["<b>#</b>"], ["$$\\theta$$"], ["Double-angle sine"], ["Double-angle cosine"]],
"align": "right",
"line": {"width": 0.0},
"line": {"width": 1, "color": ["dimgray", "grey"]},
"fill": {"color": ["dimgray", "grey"]},
"font": {"family": "Arial", "size": 14, "color": "white"}
},
Expand Down
5 changes: 1 addition & 4 deletions test/image/mocks/table_plain_birds.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@

"align": ["right", "left", "right", "right", "left", "left", "left"],

"line": {
"color": "lightgray",
"width": 0.0
},
"line": {"width": 1, "color": ["dimgray", "grey"]},

"fill": {
"color": ["dimgray", "grey"]
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/table_ragged.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"data": [{
"type": "table",
"header": {"values": ["z", ["a","b"], ["c"], []],
"line": {"width": 4}},
"cells": {
"values": [[1],[2,3],4,[],[5]],
"fill": {"color": "#eee"},
"line": {"width": [[1,9],[3,7],[5,5],[7,3],[9,1]]}
}
}],
"layout": {"width": 400, "height": 400}
}
5 changes: 1 addition & 4 deletions test/image/mocks/table_wrapped_birds.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@

"align": ["right", "left", "right", "right", "left", "left", "left"],

"line": {
"color": "lightgray",
"width": 0.0
},
"line": {"width": 1, "color": ["dimgray", "grey"]},

"fill": {
"color": ["dimgray", "grey"]
Expand Down
4 changes: 2 additions & 2 deletions test/jasmine/tests/table_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var supplyAllDefaults = require('../assets/supply_defaults');

var mockMulti = require('@mocks/table_latex_multitrace.json');
var mockMulti = require('@mocks/table_latex_multitrace_scatter.json');

// mock with two columns; lowest column count of general case
var mock2 = Lib.extendDeep({}, mockMulti);
Expand Down Expand Up @@ -305,7 +305,7 @@ describe('table', function() {
expect(gd.data[0].header.fill.color).toEqual('magenta');
expect(gd.data[0].header.values.length).toEqual(7);
expect(gd.data[0].cells.values.length).toEqual(7);
expect(gd.data[0].header.line.color).toEqual('lightgray'); // no change relative to original mock value
expect(gd.data[0].header.line.color).toEqual(['dimgray', 'grey']); // no change relative to original mock value
expect(gd.data[0].cells.line.color).toEqual(['grey']); // no change relative to original mock value

done();
Expand Down

0 comments on commit da37b4f

Please sign in to comment.