Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Implement legendrank attribute in traces by archmoj · Pull Request #5591 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/components/legend/get_legend_data.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,9 @@ var Registry = require('../../registry');
var helpers = require('./helpers');

module.exports = function getLegendData(calcdata, opts) {
var grouped = helpers.isGrouped(opts);
var reversed = helpers.isReversed(opts);

var lgroupToTraces = {};
var lgroups = [];
var hasOneNonBlankGroup = false;
Expand All@@ -18,14 +21,14 @@ module.exports = function getLegendData(calcdata, opts) {
// TODO: check this against fullData legendgroups?
var uniqueGroup = '~~i' + lgroupi;
lgroups.push(uniqueGroup);
lgroupToTraces[uniqueGroup] = [[legendItem]];
lgroupToTraces[uniqueGroup] = [legendItem];
lgroupi++;
} else if(lgroups.indexOf(legendGroup) === -1) {
lgroups.push(legendGroup);
hasOneNonBlankGroup = true;
lgroupToTraces[legendGroup] = [[legendItem]];
lgroupToTraces[legendGroup] = [legendItem];
} else {
lgroupToTraces[legendGroup].push([legendItem]);
lgroupToTraces[legendGroup].push(legendItem);
}
}

Expand DownExpand Up@@ -66,31 +69,66 @@ module.exports = function getLegendData(calcdata, opts) {
// won't draw a legend in this case
if(!lgroups.length) return [];

// rearrange lgroupToTraces into a d3-friendly array of arrays
var lgroupsLength = lgroups.length;
var ltraces;
var legendData;

if(hasOneNonBlankGroup && helpers.isGrouped(opts)) {
legendData = new Array(lgroupsLength);
// collapse all groups into one if all groups are blank
var shouldCollapse = !hasOneNonBlankGroup || !grouped;

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]];
legendData[i] = helpers.isReversed(opts) ? ltraces.reverse() : ltraces;
var legendData = [];
for(i = 0; i < lgroups.length; i++) {
var t = lgroupToTraces[lgroups[i]];
if(shouldCollapse) {
legendData.push(t[0]);
} else {
legendData.push(t);
}
} else {
// collapse all groups into one if all groups are blank
legendData = [new Array(lgroupsLength)];
}
if(shouldCollapse) legendData = [legendData];

for(i = 0; i < legendData.length; i++) {
// find minimum rank within group
var groupMinRank = Infinity;
for(j = 0; j < legendData[i].length; j++) {
var rank = legendData[i][j].trace.legendrank;
if(groupMinRank > rank) groupMinRank = rank;
}

// record on first group element
legendData[i][0]._groupMinRank = groupMinRank;
legendData[i][0]._preGroupSort = i;
}

for(i = 0; i < lgroupsLength; i++) {
ltraces = lgroupToTraces[lgroups[i]][0];
legendData[0][helpers.isReversed(opts) ? lgroupsLength - i - 1 : i] = ltraces;
var orderFn1 = function(a, b) {
return (
(a[0]._groupMinRank - b[0]._groupMinRank) ||
(a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

var orderFn2 = function(a, b) {
return (
(a.trace.legendrank - b.trace.legendrank) ||
(a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
);
};

// sort considering minimum group legendrank
legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
legendData.sort(orderFn1);
for(i = 0; i < legendData.length; i++) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);
if(reversed) legendData[i].reverse();

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
legendData[i][j]
];
}
lgroupsLength = 1;
}

// number of legend groups - needed in legend/draw.js
opts._lgroupsLength = lgroupsLength;
opts._lgroupsLength = legendData.length;
// maximum name/label length - needed in legend/draw.js
opts._maxNameLength = maxNameLength;

Expand Down
13 changes: 13 additions & 0 deletions src/plots/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,19 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendrank: {
valType: 'number',
dflt: 1000,
editType: 'style',
description: [
'Sets the legend rank for this trace.',
'Items and groups with smaller ranks are presented on top/left side while',
'with `*reversed* `legend.traceorder` they are on bottom/right side.',
'The default legendrank is 1000,',
'so that you can use ranks less than 1000 to place certain items before all unranked items,',
'and ranks greater than 1000 to go after all unranked items.'
].join(' ')
},
opacity: {
valType: 'number',
min: 0,
Expand Down
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1309,6 +1309,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
coerce('legendrank');

traceOut._dfltShowLegend = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions src/traces/parcats/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -198,6 +198,7 @@ module.exports = {
hoverlabel: undefined,
ids: undefined,
legendgroup: undefined,
legendrank: undefined,
opacity: undefined,
selectedpoints: undefined,
showlegend: undefined
Expand Down
Binary file addedtest/image/baselines/legendrank.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 addedtest/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions test/image/mocks/legendrank.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
{
"data": [
{"type": "bar", "name": "1", "y": [1], "yaxis": "y2", "legendgroup": "two", "legendrank": 3},
{
"legendrank": 2,
"legendgroup": "pie",
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
"domain": {
"x": [0, 0.45],
"y": [0.35, 0.65]
}
},
{
"legendrank": 1,
"legendgroup": "pie",
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
"textinfo": "none",
"domain": {
"x": [0.55, 1],
"y": [0.35, 0.65]
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
"title": {
"text": "legendrank"
},
"hovermode": "x unified",
"margin": {
"t": 50
},
"width": 300,
"height": 400,
"yaxis2": {
"domain": [0.7, 1]
},
"yaxis": {
"autorange": "reversed",
"domain": [0, 0.3]
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/legendrank2.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"name": "A",
"legendrank": 2,
"y": [-2]
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
}
],
"layout": {
"title": {
"text": "rank groups using<br>minimum of the group"
},
"width": 300,
"height": 300,
"margin": {
"b": 25
},
"hovermode": "x unified"
}
}
Loading