diff options
Diffstat (limited to 'app/assets/javascripts/pipelines/components/dag/interactions.js')
-rw-r--r-- | app/assets/javascripts/pipelines/components/dag/interactions.js | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/app/assets/javascripts/pipelines/components/dag/interactions.js b/app/assets/javascripts/pipelines/components/dag/interactions.js new file mode 100644 index 00000000000..c9008730c90 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/dag/interactions.js @@ -0,0 +1,134 @@ +import * as d3 from 'd3'; +import { LINK_SELECTOR, NODE_SELECTOR, IS_HIGHLIGHTED } from './constants'; + +export const highlightIn = 1; +export const highlightOut = 0.2; + +const getCurrent = (idx, collection) => d3.select(collection[idx]); +const currentIsLive = (idx, collection) => getCurrent(idx, collection).classed(IS_HIGHLIGHTED); +const getOtherLinks = () => d3.selectAll(`.${LINK_SELECTOR}:not(.${IS_HIGHLIGHTED})`); +const getNodesNotLive = () => d3.selectAll(`.${NODE_SELECTOR}:not(.${IS_HIGHLIGHTED})`); + +const backgroundLinks = selection => selection.style('stroke-opacity', highlightOut); +const backgroundNodes = selection => selection.attr('stroke', '#f2f2f2'); +const foregroundLinks = selection => selection.style('stroke-opacity', highlightIn); +const foregroundNodes = selection => selection.attr('stroke', d => d.color); +const renewLinks = (selection, baseOpacity) => selection.style('stroke-opacity', baseOpacity); +const renewNodes = selection => selection.attr('stroke', d => d.color); + +const getAllLinkAncestors = node => { + if (node.targetLinks) { + return node.targetLinks.flatMap(n => { + return [n.uid, ...getAllLinkAncestors(n.source)]; + }); + } + + return []; +}; + +const getAllNodeAncestors = node => { + let allNodes = []; + + if (node.targetLinks) { + allNodes = node.targetLinks.flatMap(n => { + return getAllNodeAncestors(n.source); + }); + } + + return [...allNodes, node.uid]; +}; + +export const highlightLinks = (d, idx, collection) => { + const currentLink = getCurrent(idx, collection); + const currentSourceNode = d3.select(`#${d.source.uid}`); + const currentTargetNode = d3.select(`#${d.target.uid}`); + + /* Higlight selected link, de-emphasize others */ + backgroundLinks(getOtherLinks()); + foregroundLinks(currentLink); + + /* Do the same to related nodes */ + backgroundNodes(getNodesNotLive()); + foregroundNodes(currentSourceNode); + foregroundNodes(currentTargetNode); +}; + +const highlightPath = (parentLinks, parentNodes) => { + /* de-emphasize everything else */ + backgroundLinks(getOtherLinks()); + backgroundNodes(getNodesNotLive()); + + /* highlight correct links */ + parentLinks.forEach(id => { + foregroundLinks(d3.select(`#${id}`)).classed(IS_HIGHLIGHTED, true); + }); + + /* highlight correct nodes */ + parentNodes.forEach(id => { + foregroundNodes(d3.select(`#${id}`)).classed(IS_HIGHLIGHTED, true); + }); +}; + +const restorePath = (parentLinks, parentNodes, baseOpacity) => { + parentLinks.forEach(id => { + renewLinks(d3.select(`#${id}`), baseOpacity).classed(IS_HIGHLIGHTED, false); + }); + + parentNodes.forEach(id => { + d3.select(`#${id}`).classed(IS_HIGHLIGHTED, false); + }); + + if (d3.selectAll(`.${IS_HIGHLIGHTED}`).empty()) { + renewLinks(getOtherLinks(), baseOpacity); + renewNodes(getNodesNotLive()); + return; + } + + backgroundLinks(getOtherLinks()); + backgroundNodes(getNodesNotLive()); +}; + +export const restoreLinks = (baseOpacity, d, idx, collection) => { + /* in this case, it has just been clicked */ + if (currentIsLive(idx, collection)) { + return; + } + + /* + if there exist live links, reset to highlight out / pale + otherwise, reset to base + */ + + if (d3.selectAll(`.${IS_HIGHLIGHTED}`).empty()) { + renewLinks(d3.selectAll(`.${LINK_SELECTOR}`), baseOpacity); + renewNodes(d3.selectAll(`.${NODE_SELECTOR}`)); + return; + } + + backgroundLinks(getOtherLinks()); + backgroundNodes(getNodesNotLive()); +}; + +export const toggleLinkHighlight = (baseOpacity, d, idx, collection) => { + if (currentIsLive(idx, collection)) { + restorePath([d.uid], [d.source.uid, d.target.uid], baseOpacity); + return; + } + + highlightPath([d.uid], [d.source.uid, d.target.uid]); +}; + +export const togglePathHighlights = (baseOpacity, d, idx, collection) => { + const parentLinks = getAllLinkAncestors(d); + const parentNodes = getAllNodeAncestors(d); + const currentNode = getCurrent(idx, collection); + + /* if this node is already live, make it unlive and reset its path */ + if (currentIsLive(idx, collection)) { + currentNode.classed(IS_HIGHLIGHTED, false); + restorePath(parentLinks, parentNodes, baseOpacity); + return; + } + + highlightPath(parentLinks, parentNodes); +}; |