From c021c4020ebd4aa8d086a31e52d1aca7a0db5b50 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Sun, 7 Aug 2022 22:27:29 +0100 Subject: [PATCH] Add RED.utils.domSelection module --- Gruntfile.js | 1 + .../src/js/ui/utils-domselection.js | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/utils-domselection.js diff --git a/Gruntfile.js b/Gruntfile.js index 2f81da923..31422588e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -152,6 +152,7 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/history.js", "packages/node_modules/@node-red/editor-client/src/js/validators.js", "packages/node_modules/@node-red/editor-client/src/js/ui/utils.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/utils-domselection.js", "packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js", "packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js", "packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils-domselection.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils-domselection.js new file mode 100644 index 000000000..9d82e5a84 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils-domselection.js @@ -0,0 +1,61 @@ +/** + * Modelled after `d3.selection` this provides a way to keep a mapping of + * DOM Nodes with a data collection. + * + * The goal here is not to reproduce d3 functionality as-is, but to provide an + * api that is specific to what we need to do + * + * const domSelection = RED.utils.domSelection(container, selector, createNode, eachNode) + * + * - container - a DOM Node that is the container of the DOM Nodes to track + * - selector - CSS selector to get the DOM nodes to track + * - createNode - function called when a DOM node must be created for a piece of data. + * `this` is the data item. Should return the DOM Node. It will + * get added to the container. + * - eachNode - function called for each DOM node/data item in the selection + * + * DomSelection.refresh(data) - called whenever the selection should be refreshed. + * Data is expected to be an array of objects that contain an 'id' property + * + */ +RED.utils.domSelection = (function() { + + class DomSelection { + constructor(container, selector, createNode, eachNode) { + this.container = container + this.selector = selector + this.createNode = createNode + this.eachNode = eachNode + this.data = [] + } + + refresh(data) { + const domNodes = this.container.querySelectorAll(this.selector) + const domItems = new Map() + const dataLength = data.length + const domLength = domNodes.length + for (let i = 0; i < domLength; i++) { + const domNode = domNodes[i] + if (domNode.__data__) { + domItems.set(domNode.__data__.id, domNode) + } + } + for (let i = 0; i < dataLength; i++) { + const datum = data[i] + let domNode = domItems.get(datum.id) + if (!domNode) { + domNode = this.createNode.call(datum) + this.container.appendChild(domNode) + domNode.__data__ = datum + } else { + domItems.delete(datum.id) + } + this.eachNode.call(datum, domNode) + } + for (const remainingDomNodes of domItems) { + remainingDomNodes[1].remove() + } + } + } + return (container, selector, createNode, eachNode) => new DomSelection(container, selector, createNode, eachNode) +})()