mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-12 15:22:54 +01:00
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
160 lines
4.7 KiB
JavaScript
160 lines
4.7 KiB
JavaScript
import ReactDOM from 'react-dom';
|
|
|
|
export class Highlighter {
|
|
constructor(noodlRuntime) {
|
|
this.highlightedNodes = new Map();
|
|
this.selectedNodes = new Map();
|
|
this.noodlRuntime = noodlRuntime;
|
|
|
|
this.isUpdatingHighlights = false;
|
|
|
|
//create the div that holds the highlight and selection UI
|
|
const div = document.createElement('div');
|
|
div.style.width = '100%';
|
|
div.style.height = '100%';
|
|
div.style.top = '0';
|
|
div.style.left = '0';
|
|
div.style.overflow = 'hidden';
|
|
div.style.position = 'fixed';
|
|
div.style.zIndex = '1000000000';
|
|
div.style.pointerEvents = 'none';
|
|
document.body.appendChild(div);
|
|
this.highlightRootDiv = div;
|
|
|
|
this.windowBorderDiv = this.createHighlightDiv();
|
|
this.windowBorderDiv.style.position = 'absolute';
|
|
this.windowBorderDiv.style.top = '0';
|
|
this.windowBorderDiv.style.left = '0';
|
|
this.windowBorderDiv.style.boxShadow = 'inset 0 0 0 3px #2CA7BA';
|
|
this.windowBorderDiv.style.opacity = '1.0';
|
|
this.windowBorderDiv.style.width = '100vw';
|
|
this.windowBorderDiv.style.height = '100vh';
|
|
}
|
|
|
|
createHighlightDiv() {
|
|
const div = document.createElement('div');
|
|
div.style.position = 'absolute';
|
|
div.style.top = '0';
|
|
div.style.left = '0';
|
|
div.style.outline = '2px solid #2CA7BA';
|
|
div.style.opacity = '1.0';
|
|
return div;
|
|
}
|
|
|
|
setWindowSelected(enabled) {
|
|
return; //disable this feature for now, needs some iteration
|
|
|
|
/*if (enabled) {
|
|
this.highlightRootDiv.appendChild(this.windowBorderDiv);
|
|
} else {
|
|
this.windowBorderDiv.parentNode && this.windowBorderDiv.parentNode.removeChild(this.windowBorderDiv);
|
|
}*/
|
|
}
|
|
|
|
updateHighlights() {
|
|
const items = Array.from(this.highlightedNodes.entries()).concat(Array.from(this.selectedNodes.entries()));
|
|
|
|
for (const item of items) {
|
|
const domNode = item[0].getRef() && ReactDOM.findDOMNode(item[0].getRef());
|
|
|
|
if (!domNode) {
|
|
//user has deleted this node, just remove it
|
|
this.highlightedNodes.delete(item[0]);
|
|
item[1].remove();
|
|
continue;
|
|
}
|
|
|
|
const rect = domNode.getBoundingClientRect();
|
|
const highlight = item[1];
|
|
|
|
highlight.style.transform = `translateX(${rect.x}px) translateY(${rect.y}px)`;
|
|
highlight.style.width = rect.width + 'px';
|
|
highlight.style.height = rect.height + 'px';
|
|
}
|
|
|
|
this.isUpdatingHighlights = this.highlightedNodes.size > 0 || this.selectedNodes.size > 0;
|
|
|
|
if (this.isUpdatingHighlights) {
|
|
requestAnimationFrame(this.updateHighlights.bind(this));
|
|
}
|
|
}
|
|
|
|
highlightNodesWithId(nodeId) {
|
|
//gather all nodes with a DOM node we can highlight, that aren't already highlighted
|
|
const nodes = getNodes(this.noodlRuntime, nodeId)
|
|
.filter((node) => node.getRef)
|
|
.filter((node) => !this.highlightedNodes.has(node));
|
|
|
|
for (const node of nodes) {
|
|
const highlight = this.createHighlightDiv();
|
|
|
|
this.highlightRootDiv.appendChild(highlight);
|
|
this.highlightedNodes.set(node, highlight);
|
|
}
|
|
|
|
if ((this.selectedNodes.size > 0 || this.highlightedNodes.size > 0) && !this.isUpdatingHighlights) {
|
|
this.updateHighlights();
|
|
}
|
|
}
|
|
|
|
disableHighlight() {
|
|
for (const item of this.highlightedNodes) {
|
|
const highlight = item[1];
|
|
if (highlight) {
|
|
highlight.remove();
|
|
}
|
|
}
|
|
this.highlightedNodes.clear();
|
|
}
|
|
|
|
selectNodesWithId(nodeId) {
|
|
//we don't track when nodes are created, so if there's no root component, wait a while and then highlight so we can get all the instances
|
|
//TODO: track nodes as they're created so newly created nodes can be selected if their IDs match
|
|
if (!this.noodlRuntime.rootComponent) {
|
|
this.noodlRuntime.eventEmitter.once('rootComponentUpdated', () => {
|
|
setTimeout(() => {
|
|
this.selectNodesWithId(nodeId);
|
|
}, 300);
|
|
});
|
|
}
|
|
|
|
const nodes = getNodes(this.noodlRuntime, nodeId)
|
|
.filter((node) => node.getRef)
|
|
.filter((node) => !this.selectedNodes.has(node));
|
|
|
|
for (const node of nodes) {
|
|
const selection = this.createHighlightDiv();
|
|
|
|
this.highlightRootDiv.appendChild(selection);
|
|
this.selectedNodes.set(node, selection);
|
|
}
|
|
|
|
if (this.selectedNodes.size > 0) {
|
|
this.setWindowSelected(false);
|
|
}
|
|
|
|
if ((this.selectedNodes.size > 0 || this.highlightedNodes.size > 0) && !this.isUpdatingHighlights) {
|
|
this.updateHighlights();
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
deselectNodes() {
|
|
for (const item of this.selectedNodes) {
|
|
const selection = item[1];
|
|
if (selection) {
|
|
selection.remove();
|
|
}
|
|
}
|
|
this.selectedNodes.clear();
|
|
}
|
|
}
|
|
|
|
function getNodes(noodlRuntime, nodeId) {
|
|
if (!noodlRuntime.rootComponent) {
|
|
return [];
|
|
}
|
|
return noodlRuntime.rootComponent.nodeScope.getNodesWithIdRecursive(nodeId);
|
|
}
|