Skip to content

Commit 5e60062

Browse files
author
Jon M. Mease
committed
Full support for categoryorder, categoryarray, and categorylabels
Mocks updated, but not tests yet.
1 parent 7f90fc1 commit 5e60062

File tree

4 files changed

+94
-38
lines changed

4 files changed

+94
-38
lines changed

src/traces/parcats/calc.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,9 @@ module.exports = function calc(gd, trace) {
177177
var cats = dimensionModels[d].categories;
178178

179179
if(cats[catInd] === undefined) {
180+
var catValue = trace.dimensions[containerInd].categoryarray[catInd];
180181
var catLabel = trace.dimensions[containerInd].categorylabels[catInd];
181-
cats[catInd] = createCategoryModel(d, catInd, catLabel);
182+
cats[catInd] = createCategoryModel(d, catInd, catValue, catLabel);
182183
}
183184

184185
updateCategoryModel(cats[catInd], valueInd, count);
@@ -278,8 +279,11 @@ function createDimensionModel(dimensionInd, containerInd, displayInd, dimensionL
278279
* The index of this categories dimension
279280
* @property {Number} categoryInd
280281
* The index of this category
282+
* @property {Number} displayInd
283+
* The display index of this category (where 0 is the topmost category)
281284
* @property {String} categoryLabel
282285
* The name of this category
286+
* @property categoryValue: Raw value of the category
283287
* @property {Array} valueInds
284288
* Array of indices (into the original value array) of all samples in this category
285289
* @property {Number} count
@@ -292,15 +296,17 @@ function createDimensionModel(dimensionInd, containerInd, displayInd, dimensionL
292296
* Create and return a new CategoryModel object
293297
* @param {Number} dimensionInd
294298
* @param {Number} categoryInd
295-
* @param {Number} displayInd
296299
* The display index of this category (where 0 is the topmost category)
300+
* @param {String} categoryValue
297301
* @param {String} categoryLabel
298302
* @return {CategoryModel}
299303
*/
300-
function createCategoryModel(dimensionInd, categoryInd, displayInd, categoryLabel) {
304+
function createCategoryModel(dimensionInd, categoryInd, categoryValue, categoryLabel) {
301305
return {
302306
dimensionInd: dimensionInd,
303307
categoryInd: categoryInd,
308+
categoryValue: categoryValue,
309+
displayInd: categoryInd,
304310
categoryLabel: categoryLabel,
305311
valueInds: [],
306312
count: 0,

src/traces/parcats/defaults.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,26 @@ function dimensionDefaults(dimensionIn, dimensionOut) {
5555
coerce('displayindex', dimensionOut._index);
5656

5757
// Category level
58-
// TODO: Make categoryorder and categoryarray consistent
59-
// If valid array, set order to 'array'
60-
// If order is 'array' but array is invalid set order to 'trace'
61-
coerce('categoryorder');
62-
coerce('categoryarray');
63-
coerce('categorylabels');
58+
var arrayIn = dimensionIn.categoryarray;
59+
var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0);
60+
61+
var orderDefault;
62+
if(isValidArray) orderDefault = 'array';
63+
var order = coerce('categoryorder', orderDefault);
64+
65+
// coerce 'categoryarray' only in array order case
66+
if(order === 'array') {
67+
coerce('categoryarray');
68+
coerce('categorylabels');
69+
} else {
70+
delete dimensionIn.categoryarray;
71+
delete dimensionIn.categorylabels;
72+
}
73+
74+
// cannot set 'categoryorder' to 'array' with an invalid 'categoryarray'
75+
if(!isValidArray && order === 'array') {
76+
dimensionOut.categoryorder = 'trace';
77+
}
6478
}
6579
}
6680

src/traces/parcats/parcats.js

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,11 +1014,11 @@ function dragDimension(d) {
10141014
var categoryY = dragCategory.model.dragY;
10151015

10161016
// Check for category drag swaps
1017-
var categoryInd = dragCategory.model.categoryInd;
1017+
var catDisplayInd = dragCategory.model.displayInd;
10181018
var dimCategoryViews = dragDimension.categories;
10191019

1020-
var catAbove = dimCategoryViews[categoryInd - 1];
1021-
var catBelow = dimCategoryViews[categoryInd + 1];
1020+
var catAbove = dimCategoryViews[catDisplayInd - 1];
1021+
var catBelow = dimCategoryViews[catDisplayInd + 1];
10221022

10231023
// Check for overlap above
10241024
if(catAbove !== undefined) {
@@ -1027,7 +1027,7 @@ function dragDimension(d) {
10271027

10281028
// Swap display inds
10291029
dragCategory.model.displayInd = catAbove.model.displayInd;
1030-
catAbove.model.displayInd = categoryInd;
1030+
catAbove.model.displayInd = catDisplayInd;
10311031
}
10321032
}
10331033

@@ -1037,7 +1037,7 @@ function dragDimension(d) {
10371037

10381038
// Swap display inds
10391039
dragCategory.model.displayInd = catBelow.model.displayInd;
1040-
catBelow.model.displayInd = categoryInd;
1040+
catBelow.model.displayInd = catDisplayInd;
10411041
}
10421042
}
10431043

@@ -1121,20 +1121,31 @@ function dragDimensionEnd(d) {
11211121
}
11221122

11231123
// ### Handle category reordering ###
1124-
// var anyCatsReordered = false;
1125-
// if(d.dragCategoryDisplayInd !== null) {
1126-
// var finalDragCategoryDisplayInds = d.model.categories.map(function(c) {
1127-
// return c.displayInd;
1128-
// });
1129-
//
1130-
// anyCatsReordered = d.initialDragCategoryDisplayInds.some(function(initCatDisplay, catInd) {
1131-
// return initCatDisplay !== finalDragCategoryDisplayInds[catInd];
1132-
// });
1133-
//
1134-
// if(anyCatsReordered) {
1135-
// restyleData['dimensions[' + d.model.containerInd + '].catDisplayInds'] = [finalDragCategoryDisplayInds];
1136-
// }
1137-
// }
1124+
var anyCatsReordered = false;
1125+
if(d.dragCategoryDisplayInd !== null) {
1126+
var finalDragCategoryDisplayInds = d.model.categories.map(function(c) {
1127+
return c.displayInd;
1128+
});
1129+
1130+
anyCatsReordered = d.initialDragCategoryDisplayInds.some(function(initCatDisplay, catInd) {
1131+
return initCatDisplay !== finalDragCategoryDisplayInds[catInd];
1132+
});
1133+
1134+
if(anyCatsReordered) {
1135+
1136+
// Sort a shallow copy of the category models by display index
1137+
var sortedCategoryModels = d.model.categories.slice().sort(
1138+
function (a, b) { return a.displayInd - b.displayInd });
1139+
1140+
// Get new categoryarray and categorylabels values
1141+
var newCategoryArray = sortedCategoryModels.map(function (v) { return v.categoryValue });
1142+
var newCategoryLabels = sortedCategoryModels.map(function (v) { return v.categoryLabel });
1143+
1144+
restyleData['dimensions[' + d.model.containerInd + '].categoryarray'] = [newCategoryArray];
1145+
restyleData['dimensions[' + d.model.containerInd + '].categorylabels'] = [newCategoryLabels];
1146+
restyleData['dimensions[' + d.model.containerInd + '].categoryorder'] = ['array'];
1147+
}
1148+
}
11381149

11391150
// Handle potential click event
11401151
// ----------------------------
@@ -1520,6 +1531,12 @@ function updatePathViewModels(parcatsViewModel) {
15201531
});
15211532
});
15221533

1534+
// Array from category index to category display index for each true dimension index
1535+
var catToDisplayIndPerDim = parcatsViewModel.model.dimensions.map(
1536+
function(d) {
1537+
return d.categories.map(function(c) {return c.displayInd;});
1538+
});
1539+
15231540
// Array from true dimension index to dimension display index
15241541
var dimToDisplayInd = parcatsViewModel.model.dimensions.map(function(d) {return d.displayInd;});
15251542
var displayToDimInd = parcatsViewModel.dimensions.map(function(d) {return d.model.dimensionInd;});
@@ -1541,12 +1558,21 @@ function updatePathViewModels(parcatsViewModel) {
15411558
}
15421559
}
15431560

1561+
// Compute category display inds to use for sorting paths
1562+
function pathDisplayCategoryInds(pathModel) {
1563+
var dimensionInds = pathModel.categoryInds.map(function(catInd, dimInd) {return catToDisplayIndPerDim[dimInd][catInd];});
1564+
var displayInds = displayToDimInd.map(function(dimInd) {
1565+
return dimensionInds[dimInd];
1566+
});
1567+
return displayInds;
1568+
}
1569+
15441570
// Sort in ascending order by display index array
15451571
pathModels.sort(function(v1, v2) {
15461572

15471573
// Build display inds for each path
1548-
var sortArray1 = v1.categoryInds;
1549-
var sortArray2 = v2.categoryInds;
1574+
var sortArray1 = pathDisplayCategoryInds(v1);
1575+
var sortArray2 = pathDisplayCategoryInds(v2);
15501576

15511577
// Handle path sort order
15521578
if(parcatsViewModel.sortpaths === 'backward') {
@@ -1599,14 +1625,15 @@ function updatePathViewModels(parcatsViewModel) {
15991625
var pathYs = new Array(nextYPositions.length);
16001626
for(var d = 0; d < pathModel.categoryInds.length; d++) {
16011627
var catInd = pathModel.categoryInds[d];
1628+
var catDisplayInd = catToDisplayIndPerDim[d][catInd];
16021629
var dimDisplayInd = dimToDisplayInd[d];
16031630

16041631
// Update next y position
1605-
pathYs[dimDisplayInd] = nextYPositions[dimDisplayInd][catInd];
1606-
nextYPositions[dimDisplayInd][catInd] += pathHeight;
1632+
pathYs[dimDisplayInd] = nextYPositions[dimDisplayInd][catDisplayInd];
1633+
nextYPositions[dimDisplayInd][catDisplayInd] += pathHeight;
16071634

16081635
// Update category color information
1609-
var catViewModle = parcatsViewModel.dimensions[dimDisplayInd].categories[catInd];
1636+
var catViewModle = parcatsViewModel.dimensions[dimDisplayInd].categories[catDisplayInd];
16101637
var numBands = catViewModle.bands.length;
16111638
var lastCatBand = catViewModle.bands[numBands - 1];
16121639

@@ -1641,7 +1668,7 @@ function updatePathViewModels(parcatsViewModel) {
16411668
}
16421669

16431670
pathViewModels[pathNumber] = {
1644-
key: pathModel.categoryInds + '-' + pathModel.valueInds[0],
1671+
key: pathModel.valueInds[0],
16451672
model: pathModel,
16461673
height: pathHeight,
16471674
leftXs: leftXPositions,
@@ -1730,14 +1757,23 @@ function createDimensionViewModel(parcatsViewModel, dimensionModel) {
17301757
nextCatHeight,
17311758
nextCatModel,
17321759
nextCat,
1733-
catInd;
1760+
catInd,
1761+
catDisplayInd;
17341762

17351763
// Compute starting Y offset
17361764
var nextCatY = (maxCats - numCats) * catSpacing / 2.0;
17371765

17381766
// Compute category ordering
1739-
for(catInd = 0; catInd < numCats; catInd++) {
1767+
var categoryIndInfo = dimensionModel.categories.map(function(c) {
1768+
return {displayInd: c.displayInd, categoryInd: c.categoryInd};
1769+
});
1770+
1771+
categoryIndInfo.sort(function(a, b) {
1772+
return a.displayInd - b.displayInd;
1773+
});
17401774

1775+
for(catDisplayInd = 0; catDisplayInd < numCats; catDisplayInd++) {
1776+
catInd = categoryIndInfo[catDisplayInd].categoryInd;
17411777
nextCatModel = dimensionModel.categories[catInd];
17421778

17431779
if(totalCount > 0) {
@@ -1747,7 +1783,7 @@ function createDimensionViewModel(parcatsViewModel, dimensionModel) {
17471783
}
17481784

17491785
nextCat = {
1750-
key: catInd,
1786+
key: nextCatModel.valueInds[0],
17511787
model: nextCatModel,
17521788
width: dimWidth,
17531789
height: nextCatHeight,

test/image/mocks/parcats_reordered.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"dimensions": [
66
{"label": "One", "values": [1, 1, 2, 1, 2, 1, 1, 2, 1], "displayindex": 0},
77
{"label": "Two", "values": ["A", "B", "A", "B", "C", "C", "A", "B", "C"], "displayindex": 2,
8-
"catDisplayInds": [1, 2, 0], "CatValues": ["A", "B", "C"]},
8+
"categoryarray": ["B", "A", "C"]},
99
{"label": "Three", "values": [11, 11, 11, 11, 11, 11, 11, 11, 11], "displayindex": 1}]}
1010
],
1111
"layout": {

0 commit comments

Comments
 (0)