Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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" + '
Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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('^' + ".*" + ' Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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('^' + ".*" + ' Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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" + ' Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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('^' + ".*" + ' Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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('^' + ".*" + ' Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
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); } })(); })(); Common interface to interpret and execute API methods by rreusser · Pull Request #1016 · plotly/plotly.js · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2d268d
Start implementing command execution wrapper
rreusser Oct 4, 2016
65f618d
Write failing tests for binding computation
rreusser Oct 6, 2016
7ef0404
Add a lot of tests for binding computation
rreusser Oct 10, 2016
edb6773
Implement animate and update binding computation
rreusser Oct 10, 2016
f50fcaa
Switch return format of bindings to structured output
rreusser Oct 11, 2016
60dd634
Merge remote-tracking branch 'origin/master' into lib-commands
rreusser Oct 11, 2016
4bc8387
Command to decide when bindings are simple
rreusser Oct 17, 2016
93b8253
First working version of bindings
rreusser Oct 18, 2016
43dc3d4
Change the method name
rreusser Oct 18, 2016
65d24f3
Fix updatemenus bindings
rreusser Oct 18, 2016
d2696e7
Hook up animations to sliders via bindings
rreusser Oct 18, 2016
7db7f03
Clean up slider positioning code
rreusser Oct 20, 2016
57fcf96
Add mock for bindings
rreusser Oct 20, 2016
7db5f1f
emove custom plotmodified event
rreusser Oct 20, 2016
26ad1ff
Add binding baseline image
rreusser Oct 20, 2016
986b4dc
Fix irritating self-interaction when dragging slider
rreusser Oct 24, 2016
20ac69f
Fix lint issue
rreusser Oct 24, 2016
f993ed7
Change failure modes for command API
rreusser Oct 24, 2016
99d7c8e
Ugh linter again
rreusser Oct 24, 2016
f8c0094
Improve robustness of command bindings
rreusser Oct 24, 2016
6abec15
Test more behavior of bindings
rreusser Oct 24, 2016
5df94a7
Fix linter issue
rreusser Oct 24, 2016
d35ee35
Remove binding test file
rreusser Oct 24, 2016
a554cad
Add equivalent command API test for udpatemenus
rreusser Oct 24, 2016
e5a80ee
DRY up binding change check
rreusser Oct 24, 2016
45717b1
Add note about test failure
rreusser Oct 24, 2016
52de9e4
createCommandObserver --> manageCommandObserver
rreusser Oct 25, 2016
0c40b02
Remove hard-coded updatemenus active default
rreusser Oct 25, 2016
df2d5bb
Revert updatemenus initialization and fix sliders initialization
rreusser Oct 25, 2016
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
2 changes: 1 addition & 1 deletion src/components/sliders/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,7 +68,7 @@ module.exports = {
active: {
valType: 'number',
role: 'info',
min: -10,
min: 0,
dflt: 0,
description: [
'Determines which button (by index starting from 0) is',
Expand Down
38 changes: 23 additions & 15 deletions src/components/sliders/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand DownExpand Up@@ -52,6 +51,9 @@ module.exports = function draw(gd) {
sliderGroups.exit().each(function(sliderOpts) {
d3.select(this).remove();

sliderOpts._commandObserver.remove();
delete sliderOpts._commandObserver;

Plots.autoMargin(gd, constants.autoMarginIdRoot + sliderOpts._index);
});

Expand All@@ -65,12 +67,20 @@ module.exports = function draw(gd) {
// If it has fewer than two options, it's not really a slider:
if(sliderOpts.steps.length < 2) return;

var gSlider = d3.select(this);

computeLabelSteps(sliderOpts);

Plots.manageCommandObserver(gd, sliderOpts, sliderOpts.steps, function(data) {
if(sliderOpts.active === data.index) return;
if(sliderOpts._dragging) return;

setActive(gd, gSlider, sliderOpts, data.index, false, true);
});

drawSlider(gd, d3.select(this), sliderOpts);

// makeInputProxy(gd, d3.select(this), sliderOpts);

});
};

Expand DownExpand Up@@ -227,7 +237,9 @@ function drawSlider(gd, sliderGroup, sliderOpts) {
// Position the rectangle:
Lib.setTranslate(sliderGroup, sliderOpts.lx + sliderOpts.pad.l, sliderOpts.ly + sliderOpts.pad.t);

setActive(gd, sliderGroup, sliderOpts, sliderOpts.active, false, false);
sliderGroup.call(setGripPosition, sliderOpts, sliderOpts.active / (sliderOpts.steps.length - 1), false);
sliderGroup.call(drawCurrentValue, sliderOpts);

}

function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
Expand DownExpand Up@@ -371,19 +383,9 @@ function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition)
sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
var _step = sliderGroup._nextMethod.step;
var args = _step.args;
if(!_step.method) return;

sliderOpts._invokingCommand = true;
Plotly[_step.method](gd, args[0], args[1], args[2]).then(function() {
sliderOpts._invokingCommand = false;
}, function() {
sliderOpts._invokingCommand = false;

// This is not a disaster. Some methods like `animate` reject if interrupted
// and *should* nicely log a warning.
Lib.warn('Warning: Plotly.' + _step.method + ' was called and rejected.');
});
Plots.executeAPICommand(gd, _step.method, _step.args);

sliderGroup._nextMethod = null;
sliderGroup._nextMethodRaf = null;
Expand All@@ -405,13 +407,15 @@ function attachGripEvents(item, gd, sliderGroup, sliderOpts) {

var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
sliderOpts._dragging = true;

$gd.on('mousemove', function() {
var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
});

$gd.on('mouseup', function() {
sliderOpts._dragging = false;
grip.call(Color.fill, sliderOpts.bgcolor);
$gd.on('mouseup', null);
$gd.on('mousemove', null);
Expand DownExpand Up@@ -467,8 +471,12 @@ function setGripPosition(sliderGroup, sliderOpts, position, doTransition) {

var x = normalizedValueToPosition(sliderOpts, position);

// If this is true, then *this component* is already invoking its own command
// and has triggered its own animation.
if(sliderOpts._invokingCommand) return;

var el = grip;
if(doTransition && sliderOpts.transition.duration > 0 && !sliderOpts._invokingCommand) {
if(doTransition && sliderOpts.transition.duration > 0) {
el = el.transition()
.duration(sliderOpts.transition.duration)
.ease(sliderOpts.transition.easing);
Expand Down
39 changes: 23 additions & 16 deletions src/components/updatemenus/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,7 +11,6 @@

var d3 = require('d3');

var Plotly = require('../../plotly');
var Plots = require('../../plots/plots');
var Lib = require('../../lib');
var Color = require('../color');
Expand All@@ -21,7 +20,6 @@ var anchorUtils = require('../legend/anchor_utils');

var constants = require('./constants');


module.exports = function draw(gd) {
var fullLayout = gd._fullLayout,
menuData = makeMenuData(fullLayout);
Expand DownExpand Up@@ -115,6 +113,11 @@ module.exports = function draw(gd) {
headerGroups.each(function(menuOpts) {
var gHeader = d3.select(this);

var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, data.index, true);
});

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);

Expand DownExpand Up@@ -293,21 +296,9 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
.call(setItemPosition, menuOpts, posOpts);

button.on('click', function() {
// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');
setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex);

if(menuOpts.type === 'dropdown') {
drawHeader(gd, gHeader, gButton, menuOpts);
}

drawButtons(gd, gHeader, gButton, menuOpts);

// call button method
var args = buttonOpts.args;
Plotly[buttonOpts.method](gd, args[0], args[1], args[2]);
Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner 🎉

});

button.on('mouseover', function() {
Expand All@@ -326,6 +317,22 @@ function drawButtons(gd, gHeader, gButton, menuOpts) {
Lib.setTranslate(gButton, menuOpts.lx, menuOpts.ly);
}

function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, buttonIndex, isSilentUpdate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice abstraction

// update 'active' attribute in menuOpts
menuOpts._input.active = menuOpts.active = buttonIndex;

if(menuOpts.type === 'dropdown') {
// fold up buttons and redraw header
gButton.attr(constants.menuIndexAttrName, '-1');

drawHeader(gd, gHeader, gButton, menuOpts);
}

if(!isSilentUpdate || menuOpts.type === 'buttons') {
drawButtons(gd, gHeader, gButton, menuOpts);
}
}

function drawItem(item, menuOpts, itemOpts) {
item.call(drawItemRect, menuOpts)
.call(drawItemText, menuOpts, itemOpts);
Expand Down
18 changes: 10 additions & 8 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -2302,14 +2302,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
var newFrame = trans._currentFrame = trans._frameQueue.shift();

if(newFrame) {
gd.emit('plotly_animatingframe', {
name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
gd._fullLayout._currentFrame = newFrame.name;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice. I'll use that for layout.breakpoints


trans._lastFrameAt = Date.now();
trans._timeToNext = newFrame.frameOpts.duration;
Expand All@@ -2324,6 +2317,15 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) {
newFrame.frameOpts,
newFrame.transitionOpts
);

gd.emit('plotly_animatingframe', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rreusser can you share some info about why the emit('plotly_animatingframe') changed location?

@rreusserrreusserOct 21, 2016

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things taking place synchronously and asynchronously so that I wanted to move the event trigger as late as possible to ensure the changes have actually taken place in order to be picked up by bound components. I think maybe a better long-term solution is an event afterPlots.transition has supplied defaults (coming from that method itself to ensure correctness) in order to ensure that the changes have absolutely 100% certainly been applied.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the info 👍

name: newFrame.name,
frame: newFrame.frame,
animation: {
frame: newFrame.frameOpts,
transition: newFrame.transitionOpts,
}
});
} else {
// If there are no more frames, then stop the RAF loop:
stopAnimationLoop();
Expand Down
Loading