Skip to content

Commit

Permalink
Merge pull request #3708 from neos/bugfix/3040-paste-in-order
Browse files Browse the repository at this point in the history
BUGFIX: Insert copied or moved nodes in order of selection
  • Loading branch information
Sebobo authored Apr 10, 2024
2 parents 42ff732 + ceeccbc commit 994f4e1
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 8 deletions.
12 changes: 10 additions & 2 deletions Classes/Domain/Model/Changes/CopyInto.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,16 @@ public function getMode()
public function apply()
{
if ($this->canApply()) {
$nodeName = $this->generateUniqueNodeName($this->getParentNode());
$node = $this->getSubject()->copyInto($this->getParentNode(), $nodeName);
$parentNode = $this->getParentNode();
$nodeName = $this->generateUniqueNodeName($parentNode);
// If the parent node has children, we copy the node after the last child node to prevent the copied nodes
// from being mixed with the existing ones due the duplication of their relative indices.
if ($parentNode->hasChildNodes()) {
$lastChildNode = array_slice($parentNode->getChildNodes(), -1, 1)[0];
$node = $this->getSubject()->copyAfter($lastChildNode, $nodeName);
} else {
$node = $this->getSubject()->copyInto($parentNode, $nodeName);
}
$this->finish($node);
}
}
Expand Down
15 changes: 13 additions & 2 deletions packages/neos-ui-redux-store/src/CR/Nodes/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,18 @@ export const calculateNewFocusedNodes = (selectionMode: SelectionModeTypes, cont
const lastSelectedNode = nodesByContextPath[lastSelectedNodeContextPath];
if (lastSelectedNode && lastSelectedNode.parent) {
const parentNode = getNodeOrThrow(nodesByContextPath, lastSelectedNode.parent);
const tempSelection: string[] = [];
let tempSelection: string[] = [];
let startSelectionFlag = false;
let startIndex = -1;
let endIndex = -1;
// if both start and end nodes are within children, then we can do range select
const startAndEndOfSelectionAreOnOneLevel = parentNode.children.some(child => {
const startAndEndOfSelectionAreOnOneLevel = parentNode.children.some((child, index) => {
if (child.contextPath === lastSelectedNodeContextPath || child.contextPath === contextPath) {
if (child.contextPath === lastSelectedNodeContextPath) {
startIndex = index;
} else if (child.contextPath === contextPath) {
endIndex = index;
}
if (startSelectionFlag) { // if matches for the second time it means that both start and end of selection were found
tempSelection.push(child.contextPath); // also push the last node
return true;
Expand All @@ -84,6 +91,10 @@ export const calculateNewFocusedNodes = (selectionMode: SelectionModeTypes, cont
return false;
});
if (startAndEndOfSelectionAreOnOneLevel) {
// Reverse the selection if the nodes were selected from "bottom" to "top" in the tree, or they would be out of order on insertion
if (startIndex > endIndex) {
tempSelection = tempSelection.reverse();
}
const focusedNodesContextPathsSet = new Set(focusedNodesContextPaths);
tempSelection.forEach(contextPath => focusedNodesContextPathsSet.add(contextPath));
return [...focusedNodesContextPathsSet] as string[];
Expand Down
15 changes: 12 additions & 3 deletions packages/neos-ui-sagas/src/CR/NodeOperations/pasteNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,25 @@ export default function * pasteNode({globalRegistry}) {
const referenceNodeSelector = selectors.CR.Nodes.makeGetNodeByContextPathSelector(reference);
const referenceNode = yield select(referenceNodeSelector);
const baseNodeType = yield select($get('ui.pageTree.filterNodeType'));
const changeType = calculateChangeTypeFromMode(mode, clipboardMode);
const domAddresses = calculateDomAddressesFromMode(mode, referenceNode, fusionPath);

yield put(actions.CR.Nodes.commitPaste(clipboardMode));
const changes = subject.map(contextPath => ({
type: calculateChangeTypeFromMode(mode, clipboardMode),
let changes = subject.map(contextPath => ({
type: changeType,
subject: contextPath,
payload: {
...calculateDomAddressesFromMode(mode, referenceNode, fusionPath),
...domAddresses,
baseNodeType
}
}));

// Reverse the order of nodes if we are pasting after the reference node as the insertions are all
// applied to the same reference node and would otherwise be applied in reverse order.
if (mode === 'after') {
changes = changes.reverse();
}

yield put(actions.Changes.persistChanges(changes));
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,14 @@ export default class NodeTree extends PureComponent {
}

handleDrop = (targetNode, position) => {
const {currentlyDraggedNodes} = this.state;
let {currentlyDraggedNodes} = this.state;
const {moveNodes, focus} = this.props;

if (position === 'after') {
// Reverse the order of nodes to keep the correct order after each node is inserted after the target node individually
currentlyDraggedNodes = Array.from(currentlyDraggedNodes).reverse();
}

moveNodes(currentlyDraggedNodes, $get('contextPath', targetNode), position);
// We need to refocus the tree, so all focus would be reset, because its context paths have changed while moving
// Could be removed with the new CR
Expand Down

0 comments on commit 994f4e1

Please sign in to comment.