dash-renderer
Version:
render dash components in react
162 lines (151 loc) • 5.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.updateCallback = updateCallback;
exports.updateChangedProps = updateChangedProps;
exports.updateSelectedNode = updateSelectedNode;
var _constants = require("../../../constants/constants");
/**
* getEdgeTypes
*
* Finds all edges connected to a node and splits them by type.
*
* @param {Object} node - Cytoscape node.
* @returns {Object} - Object containing the edges, sorted by type.
*/
function getEdgeTypes(node) {
var elements = node.connectedEdges();
return {
input: elements.filter('[type = "input"]'),
state: elements.filter('[type = "state"]'),
output: elements.filter('[type = "output"]')
};
}
/**
* updateSelected
*
* Updates the classes of the selected node and recenters the viewport.
*
* @param {Object} cy - Reference to the cytoscape instance.
* @param {String} id - The id of the selected node.
* @returns {function} - cleanup function, for useEffect hook
*/
function updateSelectedNode(cy, id) {
function ascend(node, collection) {
var visited = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Set();
if (visited.has(node.id())) {
return;
}
visited.add(node.id());
var type = node.data().type === 'callback' ? 'input' : 'output';
var edges = getEdgeTypes(node)[type];
var parents = edges.sources();
collection.merge(edges);
collection.merge(parents);
if (node.data().type === 'property') {
collection.merge(node.ancestors());
}
parents.forEach(node => ascend(node, collection, visited));
}
function descend(node, collection) {
var visited = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Set();
if (visited.has(node.id())) {
return;
}
visited.add(node.id());
var type = node.data().type === 'callback' ? 'output' : 'input';
var edges = getEdgeTypes(node)[type];
var children = edges.targets();
collection.merge(edges);
collection.merge(children);
if (node.data().type === 'property') {
collection.merge(node.ancestors());
}
children.forEach(node => descend(node, collection, visited));
}
if (id) {
var node = cy.getElementById(id);
// Highlight the selected node.
node.addClass('selected-node');
// Find the subtree that the node belongs to. A subtree contains
// all all ancestors and descendants that are connected via Inputs
// or Outputs (but not State).
var subtree = cy.collection();
subtree.merge(node);
ascend(node, subtree);
descend(node, subtree);
var other = subtree.absoluteComplement();
other.addClass('inactive');
return () => {
node.removeClass('selected-node');
other.removeClass('inactive');
};
}
return undefined;
}
/**
* updateChangedProp
*
* Flashes property nodes that updated and any inputs they are connected to.
*
* @param {Object} cy - Reference to the cytoscape instance.
* @param {String} id - The component id which updated.
* @param {Object} props - The props that updated.
* @param {Number} flashTime - The time to flash classes for in ms.
* @returns {undefined}
*/
function updateChangedProps(cy, id, props) {
var flashTime = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 500;
Object.keys(props).forEach(prop => {
var node = cy.getElementById("".concat(id, ".").concat(prop));
node.flashClass('prop-changed', flashTime);
node.edgesTo('*').filter('[type = "input"]').flashClass('triggered', flashTime);
});
}
/**
* updateCallback
*
* Updates a callback node with profiling information (call count, avg time)
* and adds or removes classes as necessary. Classes are always assert for
* at least flashTime ms so that they can be visually observed. When callbacks
* resolve, all output edges are flashed.
*
* @param {Object} cy - Reference to the cytoscape instance.
* @param {String} id - The id of the callback (i.e., it's output identifier)
* @param {Object} profile - The callback profiling information.
* @param {Number} flashTime - The time to flash classes for in ms.
* @returns {undefined}
*/
function updateCallback(cy, id, profile) {
var flashTime = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 500;
var node = cy.getElementById("__dash_callback__.".concat(id));
var count = profile.count,
total = profile.total,
status = profile.status;
var latest = status.latest;
// Update data.
var avgTime = count > 0 ? total / count : 0;
node.data('count', count);
node.data('time', Math.round(avgTime));
// Either flash the classes OR maintain it for long callbacks.
if (latest === 'loading') {
node.data('loadingSet', Date.now());
node.addClass('callback-loading');
} else if (node.hasClass('callback-loading')) {
var timeLeft = node.data('loadingSet') + flashTime - Date.now();
setTimeout(() => node.removeClass('callback-loading'), Math.max(timeLeft, 0));
}
if (latest !== 'loading' && latest !== _constants.STATUSMAP[_constants.STATUS.OK] && latest !== _constants.STATUSMAP[_constants.STATUS.PREVENT_UPDATE]) {
node.data('errorSet', Date.now());
node.addClass('callback-error');
} else if (node.hasClass('callback-error')) {
var _timeLeft = node.data('errorSet') + flashTime - Date.now();
setTimeout(() => node.removeClass('callback-error'), Math.max(_timeLeft, 0));
}
// FIXME: This will flash branches that return no_update!!
// If the callback resolved properly, flash the outputs.
if (latest === _constants.STATUSMAP[_constants.STATUS.OK]) {
node.edgesTo('*').flashClass('triggered', flashTime);
}
}