From c5fe4ed0abec5262261749e4586f47c8263c3b5e Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Thu, 2 Feb 2023 08:42:26 +0100
Subject: [PATCH] [BUGFIX] Avoid re-paints and re-layouts in page tree on
 drag&drop

When using drag&drop in the page tree, many style changes happen:

* a shadow node is rendered that is moved around
* the content within the shadow node changes depending on whether
  dropping is allowed
* CSS classes are changed

The previous implementation was not optimal in several ways:

* the nodes wrapper (aka the "tree") was updated twice which each
  `dragover` event, being expensive on huge trees
* adding and removing CSS classes may have become redundant in some
  cases, triggering a re-paint every time

All these cases are handled in this patch by executing tasks only when
absolutely necessary.

Resolves: #99786
Releases: main, 11.5
Change-Id: Ibc8cbce2785e2de646e254590b4eddbdc42839c1
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77669
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
---
 .../TypeScript/PageTree/PageTreeElement.ts    |  3 ++-
 .../Public/TypeScript/Tree/DragDrop.ts        | 24 +++++++++----------
 .../JavaScript/PageTree/PageTreeElement.js    |  2 +-
 .../Public/JavaScript/Tree/DragDrop.js        |  4 ++--
 4 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/PageTree/PageTreeElement.ts b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/PageTree/PageTreeElement.ts
index 495b011e0cfe..5a585bfe68fc 100644
--- a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/PageTree/PageTreeElement.ts
+++ b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/PageTree/PageTreeElement.ts
@@ -909,8 +909,9 @@ class PageTreeNodeDragHandler implements DragDropHandler {
       this.tree.nodesBgContainer.selectAll('.node-bg__border').style('display', 'none');
     } else if (this.dropZoneDelete && this.dropZoneDelete.node().dataset.open !== 'false') {
       this.animateDropZone('hide', this.dropZoneDelete.node(), node);
+    } else {
+      this.dragDrop.changeNodeClasses(event);
     }
-    this.dragDrop.changeNodeClasses(event);
     return true;
   }
 
diff --git a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Tree/DragDrop.ts b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Tree/DragDrop.ts
index 5b13dd18e442..7bea5871a7a9 100644
--- a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Tree/DragDrop.ts
+++ b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Tree/DragDrop.ts
@@ -216,15 +216,7 @@ export class DragDrop {
   // Clean up after a finished drag+drop move
   public removeNodeDdClass(): void {
     const nodesWrap = this.tree.svg.node().querySelector('.nodes-wrapper');
-    // remove any classes from wrapper
-    [
-      'nodes-wrapper--nodrop',
-      'nodes-wrapper--ok-append',
-      'nodes-wrapper--ok-below',
-      'nodes-wrapper--ok-between',
-      'nodes-wrapper--ok-above',
-      'nodes-wrapper--dragging'
-    ].forEach((className: string) => nodesWrap.classList.remove(className) );
+    nodesWrap.classList.remove('nodes-wrapper--nodrop', 'nodes-wrapper--ok-append', 'nodes-wrapper--ok-below', 'nodes-wrapper--ok-between', 'nodes-wrapper--ok-above', 'nodes-wrapper--dragging');
 
     this.tree.nodesBgContainer.node().querySelector('.node-bg.node-bg--dragging')?.classList.remove('node-bg--dragging');
     this.tree.nodesBgContainer.selectAll('.node-bg__border').style('display', 'none');
@@ -243,10 +235,16 @@ export class DragDrop {
   }
 
   private applyNodeClassNames(target: HTMLElement|SVGElement, prefix: string, className: string): void {
-    const classNames = ['nodrop', 'ok-append', 'ok-below', 'ok-between', 'ok-above', 'dragging'];
+    const classNames = ['nodrop', 'ok-append', 'ok-below', 'ok-between', 'ok-above']
+      .filter((classNameToRemove: string) => classNameToRemove !== className)
+      .map((classNameToRemove: string) => prefix + classNameToRemove);
+
     // remove any existing classes
-    classNames.forEach((className: string) => target.classList.remove(prefix + className));
-    // apply new class
-    target.classList.add(prefix + className);
+    target.classList.remove(...classNames);
+
+    if (!target.classList.contains(prefix + className)) {
+      // apply new class
+      target.classList.add(prefix + className);
+    }
   }
 }
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeElement.js b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeElement.js
index 94e4dd6f5738..74c7e163733b 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeElement.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTreeElement.js
@@ -66,4 +66,4 @@ var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,o,i)
           </ul>
         </div>
       </div>
-    `}dragToolbar(e,t){return t.connectDragHandler(new N(e,this.tree,t))}};__decorate([(0,i.property)({type:D})],b.prototype,"tree",void 0),b=__decorate([(0,i.customElement)("typo3-backend-navigation-component-pagetree-toolbar")],b);class P extends u.DragDrop{changeNodePosition(e,t=""){const o=this.tree.nodes,i=this.tree.settings.nodeDrag.identifier;let n=this.tree.settings.nodeDragPosition,s=e||this.tree.settings.nodeDrag;if(i===s.identifier&&"delete"!==t)return null;if(n===u.DraggablePositionEnum.BEFORE){const t=o.indexOf(e),i=this.setNodePositionAndTarget(t);if(null===i)return null;n=i.position,s=i.target}return{node:this.tree.settings.nodeDrag,uid:i,target:s,position:n,command:t}}setNodePositionAndTarget(e){const t=this.tree.nodes,o=t[e].depth;e>0&&e--;const i=t[e].depth,n=this.tree.nodes[e];if(i===o)return{position:u.DraggablePositionEnum.AFTER,target:n};if(i<o)return{position:u.DraggablePositionEnum.INSIDE,target:n};for(let i=e;i>=0;i--){if(t[i].depth===o)return{position:u.DraggablePositionEnum.AFTER,target:this.tree.nodes[i]};if(t[i].depth<o)return{position:u.DraggablePositionEnum.AFTER,target:t[i]}}return null}}class N{constructor(e,t,o){this.startDrag=!1,this.startPageX=0,this.startPageY=0,this.id="",this.name="",this.tooltip="",this.icon="",this.isDragged=!1,this.id=e.nodeType,this.name=e.title,this.tooltip=e.tooltip,this.icon=e.icon,this.tree=t,this.dragDrop=o}dragStart(e){return this.isDragged=!1,this.startDrag=!1,this.startPageX=e.sourceEvent.pageX,this.startPageY=e.sourceEvent.pageY,!0}dragDragged(e){return!!this.dragDrop.isDragNodeDistanceMore(e,this)&&(this.startDrag=!0,!1===this.isDragged&&(this.isDragged=!0,this.dragDrop.createDraggable("#icon-"+this.icon,this.name)),this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),this.dragDrop.changeNodeClasses(e),!0)}dragEnd(e){return!!this.startDrag&&(this.isDragged=!1,this.dragDrop.removeNodeDdClass(),!(!0!==this.tree.settings.allowDragMove||!this.tree.hoveredNode||!this.tree.isOverSvg)&&(this.tree.settings.canNodeDrag&&this.addNewNode({type:this.id,name:this.name,tooltip:this.tooltip,icon:this.icon,position:this.tree.settings.nodeDragPosition,target:this.tree.hoveredNode}),!0))}addNewNode(e){const t=e.target;let o=this.tree.nodes.indexOf(t);const i={command:"new"};if(i.type=e.type,i.identifier="-1",i.target=t,i.parents=t.parents,i.parentsStateIdentifier=t.parentsStateIdentifier,i.depth=t.depth,i.position=e.position,i.name=void 0!==e.title?e.title:TYPO3.lang["tree.defaultPageTitle"],i.y=i.y||i.target.y,i.x=i.x||i.target.x,this.tree.nodeIsEdit=!0,e.position===u.DraggablePositionEnum.INSIDE&&(i.depth++,i.parents.unshift(o),i.parentsStateIdentifier.unshift(this.tree.nodes[o].stateIdentifier),this.tree.nodes[o].hasChildren=!0,this.tree.showChildren(this.tree.nodes[o])),e.position!==u.DraggablePositionEnum.INSIDE&&e.position!==u.DraggablePositionEnum.AFTER||o++,e.icon&&(i.icon=e.icon),i.position===u.DraggablePositionEnum.BEFORE){const e=this.dragDrop.setNodePositionAndTarget(o);null!==e&&(i.position=e.position,i.target=e.target)}this.tree.nodes.splice(o,0,i),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText(),p.select(this.tree.svg.node().parentNode).append("input").attr("class","node-edit").style("top",i.y+this.tree.settings.marginTop+"px").style("left",i.x+this.tree.textPosition+5+"px").style("width",this.tree.settings.width-(i.x+this.tree.textPosition+20)+"px").style("height",this.tree.settings.nodeHeight+"px").attr("text","text").attr("value",i.name).on("keydown",e=>{const t=e.target,o=e.keyCode;if(13===o||9===o){this.tree.nodeIsEdit=!1;const e=t.value.trim();e.length?(i.name=e,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}else 27===o&&(this.tree.nodeIsEdit=!1,this.removeNode(i))}).on("blur",e=>{if(this.tree.nodeIsEdit&&this.tree.nodes.indexOf(i)>-1){const t=e.target.value.trim();t.length?(i.name=t,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}}).node().select()}removeNode(e){let t=this.tree.nodes.indexOf(e);this.tree.nodes[t-1].depth==e.depth||this.tree.nodes[t+1]&&this.tree.nodes[t+1].depth==e.depth||(this.tree.nodes[t-1].hasChildren=!1),this.tree.nodes.splice(t,1),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText()}}class T{constructor(e,t){this.startDrag=!1,this.startPageX=0,this.startPageY=0,this.isDragged=!1,this.nodeIsOverDelete=!1,this.tree=e,this.dragDrop=t}dragStart(e){const t=e.subject;return!0===this.tree.settings.allowDragMove&&0!==t.depth&&(this.dropZoneDelete=null,t.allowDelete&&(this.dropZoneDelete=this.tree.nodesContainer.select('.node[data-state-id="'+t.stateIdentifier+'"]').append("g").attr("class","nodes-drop-zone").attr("height",this.tree.settings.nodeHeight),this.nodeIsOverDelete=!1,this.dropZoneDelete.append("rect").attr("height",this.tree.settings.nodeHeight).attr("width","50px").attr("x",0).attr("y",0).on("mouseover",()=>{this.nodeIsOverDelete=!0}).on("mouseout",()=>{this.nodeIsOverDelete=!1}),this.dropZoneDelete.append("text").text(TYPO3.lang.deleteItem).attr("dx",5).attr("dy",15),this.dropZoneDelete.node().dataset.open="false",this.dropZoneDelete.node().style.transform=this.getDropZoneCloseTransform(t)),this.startPageX=e.sourceEvent.pageX,this.startPageY=e.sourceEvent.pageY,this.startDrag=!1,!0)}dragDragged(e){const t=e.subject;if(!this.dragDrop.isDragNodeDistanceMore(e,this))return!1;if(this.startDrag=!0,!0!==this.tree.settings.allowDragMove||0===t.depth)return!1;this.tree.settings.nodeDrag=t;const o=this.tree.svg.node().querySelector('.node-bg[data-state-id="'+t.stateIdentifier+'"]'),i=this.tree.svg.node().parentNode.querySelector(".node-dd");return this.isDragged||(this.isDragged=!0,this.dragDrop.createDraggable(this.tree.getIconId(t),t.name),o.classList.add("node-bg--dragging")),this.tree.settings.nodeDragPosition=!1,this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),t.isOver||this.tree.hoveredNode&&-1!==this.tree.hoveredNode.parentsStateIdentifier.indexOf(t.stateIdentifier)||!this.tree.isOverSvg?(this.dragDrop.addNodeDdClass(i,"nodrop"),this.tree.isOverSvg||this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none"),this.dropZoneDelete&&"true"!==this.dropZoneDelete.node().dataset.open&&this.tree.isOverSvg&&this.animateDropZone("show",this.dropZoneDelete.node(),t)):this.tree.hoveredNode?this.dropZoneDelete&&"false"!==this.dropZoneDelete.node().dataset.open&&this.animateDropZone("hide",this.dropZoneDelete.node(),t):(this.dragDrop.addNodeDdClass(i,"nodrop"),this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none")),this.dragDrop.changeNodeClasses(e),!0}dragEnd(e){const t=e.subject;if(this.dropZoneDelete&&"true"===this.dropZoneDelete.node().dataset.open){const e=this.dropZoneDelete;this.animateDropZone("hide",this.dropZoneDelete.node(),t,()=>{e.remove(),this.dropZoneDelete=null})}else this.dropZoneDelete&&"false"===this.dropZoneDelete.node().dataset.open?(this.dropZoneDelete.remove(),this.dropZoneDelete=null):this.dropZoneDelete=null;if(!this.startDrag||!0!==this.tree.settings.allowDragMove||0===t.depth)return!1;const o=this.tree.hoveredNode;if(this.isDragged=!1,this.dragDrop.removeNodeDdClass(),t.isOver||o&&-1!==o.parentsStateIdentifier.indexOf(t.stateIdentifier)||!this.tree.settings.canNodeDrag||!this.tree.isOverSvg){if(this.nodeIsOverDelete){const e=this.dragDrop.changeNodePosition(o,"delete");if(null===e)return!1;if(this.tree.settings.displayDeleteConfirmation){m.confirm(TYPO3.lang["mess.delete.title"],TYPO3.lang["mess.delete"].replace("%s",e.node.name),v.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang.delete||"Delete",btnClass:"btn-warning",name:"delete"}]).on("button.clicked",t=>{"delete"===t.target.name&&this.tree.sendChangeCommand(e),m.dismiss()})}else this.tree.sendChangeCommand(e)}}else{const e=this.dragDrop.changeNodePosition(o,"");if(null===e)return!1;let t=e.position===u.DraggablePositionEnum.INSIDE?TYPO3.lang["mess.move_into"]:TYPO3.lang["mess.move_after"];t=t.replace("%s",e.node.name).replace("%s",e.target.name),m.confirm(TYPO3.lang.move_page,t,v.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["cm.copy"]||"Copy",btnClass:"btn-warning",name:"copy"},{text:TYPO3.lang["labels.move"]||"Move",btnClass:"btn-warning",name:"move"}]).on("button.clicked",t=>{const o=t.target;"move"===o.name?(e.command="move",this.tree.sendChangeCommand(e)):"copy"===o.name&&(e.command="copy",this.tree.sendChangeCommand(e)),m.dismiss()})}return!0}getDropZoneOpenTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-58-e.x)+"px, -10px)"}getDropZoneCloseTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-e.x)+"px, -10px)"}animateDropZone(e,t,o,i=null){t.classList.add("animating"),t.dataset.open="show"===e?"true":"false";let n=[{transform:this.getDropZoneCloseTransform(o)},{transform:this.getDropZoneOpenTransform(o)}];"show"!==e&&(n=n.reverse());const s=function(){t.style.transform=n[1].transform,t.classList.remove("animating"),i&&i()};"animate"in t?t.animate(n,{duration:300,easing:"cubic-bezier(.02, .01, .47, 1)"}).onfinish=s:s()}}}));
\ No newline at end of file
+    `}dragToolbar(e,t){return t.connectDragHandler(new N(e,this.tree,t))}};__decorate([(0,i.property)({type:D})],b.prototype,"tree",void 0),b=__decorate([(0,i.customElement)("typo3-backend-navigation-component-pagetree-toolbar")],b);class P extends u.DragDrop{changeNodePosition(e,t=""){const o=this.tree.nodes,i=this.tree.settings.nodeDrag.identifier;let n=this.tree.settings.nodeDragPosition,s=e||this.tree.settings.nodeDrag;if(i===s.identifier&&"delete"!==t)return null;if(n===u.DraggablePositionEnum.BEFORE){const t=o.indexOf(e),i=this.setNodePositionAndTarget(t);if(null===i)return null;n=i.position,s=i.target}return{node:this.tree.settings.nodeDrag,uid:i,target:s,position:n,command:t}}setNodePositionAndTarget(e){const t=this.tree.nodes,o=t[e].depth;e>0&&e--;const i=t[e].depth,n=this.tree.nodes[e];if(i===o)return{position:u.DraggablePositionEnum.AFTER,target:n};if(i<o)return{position:u.DraggablePositionEnum.INSIDE,target:n};for(let i=e;i>=0;i--){if(t[i].depth===o)return{position:u.DraggablePositionEnum.AFTER,target:this.tree.nodes[i]};if(t[i].depth<o)return{position:u.DraggablePositionEnum.AFTER,target:t[i]}}return null}}class N{constructor(e,t,o){this.startDrag=!1,this.startPageX=0,this.startPageY=0,this.id="",this.name="",this.tooltip="",this.icon="",this.isDragged=!1,this.id=e.nodeType,this.name=e.title,this.tooltip=e.tooltip,this.icon=e.icon,this.tree=t,this.dragDrop=o}dragStart(e){return this.isDragged=!1,this.startDrag=!1,this.startPageX=e.sourceEvent.pageX,this.startPageY=e.sourceEvent.pageY,!0}dragDragged(e){return!!this.dragDrop.isDragNodeDistanceMore(e,this)&&(this.startDrag=!0,!1===this.isDragged&&(this.isDragged=!0,this.dragDrop.createDraggable("#icon-"+this.icon,this.name)),this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),this.dragDrop.changeNodeClasses(e),!0)}dragEnd(e){return!!this.startDrag&&(this.isDragged=!1,this.dragDrop.removeNodeDdClass(),!(!0!==this.tree.settings.allowDragMove||!this.tree.hoveredNode||!this.tree.isOverSvg)&&(this.tree.settings.canNodeDrag&&this.addNewNode({type:this.id,name:this.name,tooltip:this.tooltip,icon:this.icon,position:this.tree.settings.nodeDragPosition,target:this.tree.hoveredNode}),!0))}addNewNode(e){const t=e.target;let o=this.tree.nodes.indexOf(t);const i={command:"new"};if(i.type=e.type,i.identifier="-1",i.target=t,i.parents=t.parents,i.parentsStateIdentifier=t.parentsStateIdentifier,i.depth=t.depth,i.position=e.position,i.name=void 0!==e.title?e.title:TYPO3.lang["tree.defaultPageTitle"],i.y=i.y||i.target.y,i.x=i.x||i.target.x,this.tree.nodeIsEdit=!0,e.position===u.DraggablePositionEnum.INSIDE&&(i.depth++,i.parents.unshift(o),i.parentsStateIdentifier.unshift(this.tree.nodes[o].stateIdentifier),this.tree.nodes[o].hasChildren=!0,this.tree.showChildren(this.tree.nodes[o])),e.position!==u.DraggablePositionEnum.INSIDE&&e.position!==u.DraggablePositionEnum.AFTER||o++,e.icon&&(i.icon=e.icon),i.position===u.DraggablePositionEnum.BEFORE){const e=this.dragDrop.setNodePositionAndTarget(o);null!==e&&(i.position=e.position,i.target=e.target)}this.tree.nodes.splice(o,0,i),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText(),p.select(this.tree.svg.node().parentNode).append("input").attr("class","node-edit").style("top",i.y+this.tree.settings.marginTop+"px").style("left",i.x+this.tree.textPosition+5+"px").style("width",this.tree.settings.width-(i.x+this.tree.textPosition+20)+"px").style("height",this.tree.settings.nodeHeight+"px").attr("text","text").attr("value",i.name).on("keydown",e=>{const t=e.target,o=e.keyCode;if(13===o||9===o){this.tree.nodeIsEdit=!1;const e=t.value.trim();e.length?(i.name=e,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}else 27===o&&(this.tree.nodeIsEdit=!1,this.removeNode(i))}).on("blur",e=>{if(this.tree.nodeIsEdit&&this.tree.nodes.indexOf(i)>-1){const t=e.target.value.trim();t.length?(i.name=t,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}}).node().select()}removeNode(e){let t=this.tree.nodes.indexOf(e);this.tree.nodes[t-1].depth==e.depth||this.tree.nodes[t+1]&&this.tree.nodes[t+1].depth==e.depth||(this.tree.nodes[t-1].hasChildren=!1),this.tree.nodes.splice(t,1),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText()}}class T{constructor(e,t){this.startDrag=!1,this.startPageX=0,this.startPageY=0,this.isDragged=!1,this.nodeIsOverDelete=!1,this.tree=e,this.dragDrop=t}dragStart(e){const t=e.subject;return!0===this.tree.settings.allowDragMove&&0!==t.depth&&(this.dropZoneDelete=null,t.allowDelete&&(this.dropZoneDelete=this.tree.nodesContainer.select('.node[data-state-id="'+t.stateIdentifier+'"]').append("g").attr("class","nodes-drop-zone").attr("height",this.tree.settings.nodeHeight),this.nodeIsOverDelete=!1,this.dropZoneDelete.append("rect").attr("height",this.tree.settings.nodeHeight).attr("width","50px").attr("x",0).attr("y",0).on("mouseover",()=>{this.nodeIsOverDelete=!0}).on("mouseout",()=>{this.nodeIsOverDelete=!1}),this.dropZoneDelete.append("text").text(TYPO3.lang.deleteItem).attr("dx",5).attr("dy",15),this.dropZoneDelete.node().dataset.open="false",this.dropZoneDelete.node().style.transform=this.getDropZoneCloseTransform(t)),this.startPageX=e.sourceEvent.pageX,this.startPageY=e.sourceEvent.pageY,this.startDrag=!1,!0)}dragDragged(e){const t=e.subject;if(!this.dragDrop.isDragNodeDistanceMore(e,this))return!1;if(this.startDrag=!0,!0!==this.tree.settings.allowDragMove||0===t.depth)return!1;this.tree.settings.nodeDrag=t;const o=this.tree.svg.node().querySelector('.node-bg[data-state-id="'+t.stateIdentifier+'"]'),i=this.tree.svg.node().parentNode.querySelector(".node-dd");return this.isDragged||(this.isDragged=!0,this.dragDrop.createDraggable(this.tree.getIconId(t),t.name),o.classList.add("node-bg--dragging")),this.tree.settings.nodeDragPosition=!1,this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),t.isOver||this.tree.hoveredNode&&-1!==this.tree.hoveredNode.parentsStateIdentifier.indexOf(t.stateIdentifier)||!this.tree.isOverSvg?(this.dragDrop.addNodeDdClass(i,"nodrop"),this.tree.isOverSvg||this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none"),this.dropZoneDelete&&"true"!==this.dropZoneDelete.node().dataset.open&&this.tree.isOverSvg&&this.animateDropZone("show",this.dropZoneDelete.node(),t)):this.tree.hoveredNode?this.dropZoneDelete&&"false"!==this.dropZoneDelete.node().dataset.open?this.animateDropZone("hide",this.dropZoneDelete.node(),t):this.dragDrop.changeNodeClasses(e):(this.dragDrop.addNodeDdClass(i,"nodrop"),this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none")),!0}dragEnd(e){const t=e.subject;if(this.dropZoneDelete&&"true"===this.dropZoneDelete.node().dataset.open){const e=this.dropZoneDelete;this.animateDropZone("hide",this.dropZoneDelete.node(),t,()=>{e.remove(),this.dropZoneDelete=null})}else this.dropZoneDelete&&"false"===this.dropZoneDelete.node().dataset.open?(this.dropZoneDelete.remove(),this.dropZoneDelete=null):this.dropZoneDelete=null;if(!this.startDrag||!0!==this.tree.settings.allowDragMove||0===t.depth)return!1;const o=this.tree.hoveredNode;if(this.isDragged=!1,this.dragDrop.removeNodeDdClass(),t.isOver||o&&-1!==o.parentsStateIdentifier.indexOf(t.stateIdentifier)||!this.tree.settings.canNodeDrag||!this.tree.isOverSvg){if(this.nodeIsOverDelete){const e=this.dragDrop.changeNodePosition(o,"delete");if(null===e)return!1;if(this.tree.settings.displayDeleteConfirmation){m.confirm(TYPO3.lang["mess.delete.title"],TYPO3.lang["mess.delete"].replace("%s",e.node.name),v.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang.delete||"Delete",btnClass:"btn-warning",name:"delete"}]).on("button.clicked",t=>{"delete"===t.target.name&&this.tree.sendChangeCommand(e),m.dismiss()})}else this.tree.sendChangeCommand(e)}}else{const e=this.dragDrop.changeNodePosition(o,"");if(null===e)return!1;let t=e.position===u.DraggablePositionEnum.INSIDE?TYPO3.lang["mess.move_into"]:TYPO3.lang["mess.move_after"];t=t.replace("%s",e.node.name).replace("%s",e.target.name),m.confirm(TYPO3.lang.move_page,t,v.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["cm.copy"]||"Copy",btnClass:"btn-warning",name:"copy"},{text:TYPO3.lang["labels.move"]||"Move",btnClass:"btn-warning",name:"move"}]).on("button.clicked",t=>{const o=t.target;"move"===o.name?(e.command="move",this.tree.sendChangeCommand(e)):"copy"===o.name&&(e.command="copy",this.tree.sendChangeCommand(e)),m.dismiss()})}return!0}getDropZoneOpenTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-58-e.x)+"px, -10px)"}getDropZoneCloseTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-e.x)+"px, -10px)"}animateDropZone(e,t,o,i=null){t.classList.add("animating"),t.dataset.open="show"===e?"true":"false";let n=[{transform:this.getDropZoneCloseTransform(o)},{transform:this.getDropZoneOpenTransform(o)}];"show"!==e&&(n=n.reverse());const s=function(){t.style.transform=n[1].transform,t.classList.remove("animating"),i&&i()};"animate"in t?t.animate(n,{duration:300,easing:"cubic-bezier(.02, .01, .47, 1)"}).onfinish=s:s()}}}));
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Tree/DragDrop.js b/typo3/sysext/backend/Resources/Public/JavaScript/Tree/DragDrop.js
index 664baf131360..f1332b9610da 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/Tree/DragDrop.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/Tree/DragDrop.js
@@ -10,7 +10,7 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,o,r){void 0===r&&(r=o);var s=Object.getOwnPropertyDescriptor(t,o);s&&!("get"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[o]}}),Object.defineProperty(e,r,s)}:function(e,t,o,r){void 0===r&&(r=o),e[r]=t[o]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var o in e)"default"!==o&&Object.prototype.hasOwnProperty.call(e,o)&&__createBinding(t,e,o);return __setModuleDefault(t,e),t};define(["require","exports","lit","TYPO3/CMS/Core/lit-helper","d3-drag","d3-selection"],(function(e,t,o,r,s,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.DragDrop=t.DraggablePositionEnum=void 0,s=__importStar(s),d=__importStar(d);var n;!function(e){e.INSIDE="inside",e.BEFORE="before",e.AFTER="after"}(n=t.DraggablePositionEnum||(t.DraggablePositionEnum={}));class a{constructor(e){this.timeout={},this.minimalDistance=10,this.tree=e}static setDragStart(){document.querySelectorAll("iframe").forEach(e=>e.style.pointerEvents="none")}static setDragEnd(){document.querySelectorAll("iframe").forEach(e=>e.style.pointerEvents="")}connectDragHandler(e){return s.drag().filter(e=>e instanceof MouseEvent).clickDistance(5).on("start",(function(t){e.dragStart(t)&&a.setDragStart()})).on("drag",(function(t){e.dragDragged(t)})).on("end",(function(t){a.setDragEnd(),e.dragEnd(t)}))}createDraggable(e,t){var s;let d=this.tree.svg.node();const n=(0,r.renderNodes)(class{static get(e,t){return o.html`<div class="node-dd node-dd--nodrop">
+var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,s,o){void 0===o&&(o=s);var r=Object.getOwnPropertyDescriptor(t,s);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[s]}}),Object.defineProperty(e,o,r)}:function(e,t,s,o){void 0===o&&(o=s),e[o]=t[s]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var s in e)"default"!==s&&Object.prototype.hasOwnProperty.call(e,s)&&__createBinding(t,e,s);return __setModuleDefault(t,e),t};define(["require","exports","lit","TYPO3/CMS/Core/lit-helper","d3-drag","d3-selection"],(function(e,t,s,o,r,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.DragDrop=t.DraggablePositionEnum=void 0,r=__importStar(r),d=__importStar(d);var n;!function(e){e.INSIDE="inside",e.BEFORE="before",e.AFTER="after"}(n=t.DraggablePositionEnum||(t.DraggablePositionEnum={}));class i{constructor(e){this.timeout={},this.minimalDistance=10,this.tree=e}static setDragStart(){document.querySelectorAll("iframe").forEach(e=>e.style.pointerEvents="none")}static setDragEnd(){document.querySelectorAll("iframe").forEach(e=>e.style.pointerEvents="")}connectDragHandler(e){return r.drag().filter(e=>e instanceof MouseEvent).clickDistance(5).on("start",(function(t){e.dragStart(t)&&i.setDragStart()})).on("drag",(function(t){e.dragDragged(t)})).on("end",(function(t){i.setDragEnd(),e.dragEnd(t)}))}createDraggable(e,t){var r;let d=this.tree.svg.node();const n=(0,o.renderNodes)(class{static get(e,t){return s.html`<div class="node-dd node-dd--nodrop">
         <div class="node-dd__ctrl-icon"></div>
         <div class="node-dd__text">
             <span class="node-dd__icon">
@@ -20,4 +20,4 @@ var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,o,r)
             </span>
             <span class="node-dd__name">${t}</span>
         </div>
-    </div>`}}.get(e,t));d.after(...n),null===(s=this.tree.svg.node().querySelector(".nodes-wrapper"))||void 0===s||s.classList.add("nodes-wrapper--dragging")}updateDraggablePosition(e){let t=18,o=15;e.sourceEvent&&e.sourceEvent.pageX&&(t+=e.sourceEvent.pageX),e.sourceEvent&&e.sourceEvent.pageY&&(o+=e.sourceEvent.pageY),document.querySelectorAll(".node-dd").forEach(e=>{e.style.top=o+"px",e.style.left=t+"px",e.style.display="block"})}openNodeTimeout(){null!==this.tree.hoveredNode&&this.tree.hoveredNode.hasChildren&&!this.tree.hoveredNode.expanded?this.timeout.node!=this.tree.hoveredNode&&(this.timeout.node=this.tree.hoveredNode,clearTimeout(this.timeout.time),this.timeout.time=setTimeout(()=>{this.tree.hoveredNode&&(this.tree.showChildren(this.tree.hoveredNode),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes())},1e3)):clearTimeout(this.timeout.time)}changeNodeClasses(e){const t=this.tree.svg.select(".node-over"),o=this.tree.svg.node().parentNode.querySelector(".node-dd");let r=this.tree.nodesBgContainer.selectAll(".node-bg__border");if(t.size()&&this.tree.isOverSvg){r.empty()&&(r=this.tree.nodesBgContainer.append("rect").attr("class","node-bg__border").attr("height","1px").attr("width","100%"));let s=d.pointer(e,t.node())[1];if(s<3){r.attr("transform","translate(-8, "+(this.tree.hoveredNode.y-10)+")").style("display","block"),0===this.tree.hoveredNode.depth?this.addNodeDdClass(o,"nodrop"):this.tree.hoveredNode.firstChild?this.addNodeDdClass(o,"ok-above"):this.addNodeDdClass(o,"ok-between"),this.tree.settings.nodeDragPosition=n.BEFORE}else if(s>17)if(r.style("display","none"),this.tree.hoveredNode.expanded&&this.tree.hoveredNode.hasChildren)this.addNodeDdClass(o,"ok-append"),this.tree.settings.nodeDragPosition=n.INSIDE;else{r.attr("transform","translate(-8, "+(this.tree.hoveredNode.y+10)+")").style("display","block"),this.tree.hoveredNode.lastChild?this.addNodeDdClass(o,"ok-below"):this.addNodeDdClass(o,"ok-between"),this.tree.settings.nodeDragPosition=n.AFTER}else r.style("display","none"),this.addNodeDdClass(o,"ok-append"),this.tree.settings.nodeDragPosition=n.INSIDE}else this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none"),this.addNodeDdClass(o,"nodrop")}addNodeDdClass(e,t){const o=this.tree.svg.node().querySelector(".nodes-wrapper");e&&this.applyNodeClassNames(e,"node-dd--",t),o&&this.applyNodeClassNames(o,"nodes-wrapper--",t),this.tree.settings.canNodeDrag="nodrop"!==t}removeNodeDdClass(){var e;const t=this.tree.svg.node().querySelector(".nodes-wrapper");["nodes-wrapper--nodrop","nodes-wrapper--ok-append","nodes-wrapper--ok-below","nodes-wrapper--ok-between","nodes-wrapper--ok-above","nodes-wrapper--dragging"].forEach(e=>t.classList.remove(e)),null===(e=this.tree.nodesBgContainer.node().querySelector(".node-bg.node-bg--dragging"))||void 0===e||e.classList.remove("node-bg--dragging"),this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none"),this.tree.svg.node().parentNode.querySelector(".node-dd").remove()}isDragNodeDistanceMore(e,t){return t.startDrag||t.startPageX-this.minimalDistance>e.sourceEvent.pageX||t.startPageX+this.minimalDistance<e.sourceEvent.pageX||t.startPageY-this.minimalDistance>e.sourceEvent.pageY||t.startPageY+this.minimalDistance<e.sourceEvent.pageY}applyNodeClassNames(e,t,o){["nodrop","ok-append","ok-below","ok-between","ok-above","dragging"].forEach(o=>e.classList.remove(t+o)),e.classList.add(t+o)}}t.DragDrop=a}));
\ No newline at end of file
+    </div>`}}.get(e,t));d.after(...n),null===(r=this.tree.svg.node().querySelector(".nodes-wrapper"))||void 0===r||r.classList.add("nodes-wrapper--dragging")}updateDraggablePosition(e){let t=18,s=15;e.sourceEvent&&e.sourceEvent.pageX&&(t+=e.sourceEvent.pageX),e.sourceEvent&&e.sourceEvent.pageY&&(s+=e.sourceEvent.pageY),document.querySelectorAll(".node-dd").forEach(e=>{e.style.top=s+"px",e.style.left=t+"px",e.style.display="block"})}openNodeTimeout(){null!==this.tree.hoveredNode&&this.tree.hoveredNode.hasChildren&&!this.tree.hoveredNode.expanded?this.timeout.node!=this.tree.hoveredNode&&(this.timeout.node=this.tree.hoveredNode,clearTimeout(this.timeout.time),this.timeout.time=setTimeout(()=>{this.tree.hoveredNode&&(this.tree.showChildren(this.tree.hoveredNode),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes())},1e3)):clearTimeout(this.timeout.time)}changeNodeClasses(e){const t=this.tree.svg.select(".node-over"),s=this.tree.svg.node().parentNode.querySelector(".node-dd");let o=this.tree.nodesBgContainer.selectAll(".node-bg__border");if(t.size()&&this.tree.isOverSvg){o.empty()&&(o=this.tree.nodesBgContainer.append("rect").attr("class","node-bg__border").attr("height","1px").attr("width","100%"));let r=d.pointer(e,t.node())[1];if(r<3){o.attr("transform","translate(-8, "+(this.tree.hoveredNode.y-10)+")").style("display","block"),0===this.tree.hoveredNode.depth?this.addNodeDdClass(s,"nodrop"):this.tree.hoveredNode.firstChild?this.addNodeDdClass(s,"ok-above"):this.addNodeDdClass(s,"ok-between"),this.tree.settings.nodeDragPosition=n.BEFORE}else if(r>17)if(o.style("display","none"),this.tree.hoveredNode.expanded&&this.tree.hoveredNode.hasChildren)this.addNodeDdClass(s,"ok-append"),this.tree.settings.nodeDragPosition=n.INSIDE;else{o.attr("transform","translate(-8, "+(this.tree.hoveredNode.y+10)+")").style("display","block"),this.tree.hoveredNode.lastChild?this.addNodeDdClass(s,"ok-below"):this.addNodeDdClass(s,"ok-between"),this.tree.settings.nodeDragPosition=n.AFTER}else o.style("display","none"),this.addNodeDdClass(s,"ok-append"),this.tree.settings.nodeDragPosition=n.INSIDE}else this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none"),this.addNodeDdClass(s,"nodrop")}addNodeDdClass(e,t){const s=this.tree.svg.node().querySelector(".nodes-wrapper");e&&this.applyNodeClassNames(e,"node-dd--",t),s&&this.applyNodeClassNames(s,"nodes-wrapper--",t),this.tree.settings.canNodeDrag="nodrop"!==t}removeNodeDdClass(){var e;this.tree.svg.node().querySelector(".nodes-wrapper").classList.remove("nodes-wrapper--nodrop","nodes-wrapper--ok-append","nodes-wrapper--ok-below","nodes-wrapper--ok-between","nodes-wrapper--ok-above","nodes-wrapper--dragging"),null===(e=this.tree.nodesBgContainer.node().querySelector(".node-bg.node-bg--dragging"))||void 0===e||e.classList.remove("node-bg--dragging"),this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none"),this.tree.svg.node().parentNode.querySelector(".node-dd").remove()}isDragNodeDistanceMore(e,t){return t.startDrag||t.startPageX-this.minimalDistance>e.sourceEvent.pageX||t.startPageX+this.minimalDistance<e.sourceEvent.pageX||t.startPageY-this.minimalDistance>e.sourceEvent.pageY||t.startPageY+this.minimalDistance<e.sourceEvent.pageY}applyNodeClassNames(e,t,s){const o=["nodrop","ok-append","ok-below","ok-between","ok-above"].filter(e=>e!==s).map(e=>t+e);e.classList.remove(...o),e.classList.contains(t+s)||e.classList.add(t+s)}}t.DragDrop=i}));
\ No newline at end of file
-- 
GitLab