2024-06-04 22:05:44 +02:00
|
|
|
// Copyright 2024, Command Line Inc.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2024-07-18 03:42:49 +02:00
|
|
|
import { lazy } from "@/util/util";
|
2024-06-04 22:05:44 +02:00
|
|
|
import {
|
|
|
|
addChildAt,
|
|
|
|
addIntermediateNode,
|
2024-08-01 06:27:46 +02:00
|
|
|
findInsertLocationFromIndexArr,
|
2024-06-04 22:05:44 +02:00
|
|
|
findNextInsertLocation,
|
|
|
|
findNode,
|
|
|
|
findParent,
|
|
|
|
removeChild,
|
2024-06-12 02:42:10 +02:00
|
|
|
} from "./layoutNode";
|
2024-06-04 22:05:44 +02:00
|
|
|
import {
|
2024-07-03 23:31:02 +02:00
|
|
|
DefaultNodeSize,
|
2024-08-22 02:43:11 +02:00
|
|
|
DropDirection,
|
|
|
|
FlexDirection,
|
2024-06-04 22:05:44 +02:00
|
|
|
LayoutTreeActionType,
|
|
|
|
LayoutTreeComputeMoveNodeAction,
|
|
|
|
LayoutTreeDeleteNodeAction,
|
2024-08-26 20:56:00 +02:00
|
|
|
LayoutTreeFocusNodeAction,
|
2024-06-04 22:05:44 +02:00
|
|
|
LayoutTreeInsertNodeAction,
|
2024-08-01 06:27:46 +02:00
|
|
|
LayoutTreeInsertNodeAtIndexAction,
|
2024-07-30 19:59:53 +02:00
|
|
|
LayoutTreeMagnifyNodeToggleAction,
|
2024-06-04 22:05:44 +02:00
|
|
|
LayoutTreeMoveNodeAction,
|
2024-07-03 23:31:02 +02:00
|
|
|
LayoutTreeResizeNodeAction,
|
2024-06-04 22:05:44 +02:00
|
|
|
LayoutTreeState,
|
2024-06-17 23:14:09 +02:00
|
|
|
LayoutTreeSwapNodeAction,
|
2024-06-04 22:05:44 +02:00
|
|
|
MoveOperation,
|
2024-08-15 03:40:41 +02:00
|
|
|
} from "./types";
|
2024-06-04 22:05:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Computes an operation for inserting a new node into the tree in the given direction relative to the specified node.
|
|
|
|
*
|
2024-08-15 03:40:41 +02:00
|
|
|
* @param layoutState The state of the tree.
|
2024-06-04 22:05:44 +02:00
|
|
|
* @param computeInsertAction The operation to compute.
|
|
|
|
*/
|
2024-08-15 03:40:41 +02:00
|
|
|
export function computeMoveNode(layoutState: LayoutTreeState, computeInsertAction: LayoutTreeComputeMoveNodeAction) {
|
|
|
|
const rootNode = layoutState.rootNode;
|
2024-06-04 22:05:44 +02:00
|
|
|
const { node, nodeToMove, direction } = computeInsertAction;
|
|
|
|
if (direction === undefined) {
|
|
|
|
console.warn("No direction provided for insertItemInDirection");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-17 23:14:09 +02:00
|
|
|
if (node.id === nodeToMove.id) {
|
|
|
|
console.warn("Cannot compute move node action since both nodes are equal");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
let newMoveOperation: MoveOperation;
|
2024-06-04 22:05:44 +02:00
|
|
|
const parent = lazy(() => findParent(rootNode, node.id));
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
const grandparent = lazy(() => findParent(rootNode, parent().id));
|
2024-06-04 22:05:44 +02:00
|
|
|
const indexInParent = lazy(() => parent()?.children.findIndex((child) => node.id === child.id));
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
const indexInGrandparent = lazy(() => grandparent()?.children.findIndex((child) => parent().id === child.id));
|
2024-06-19 01:03:00 +02:00
|
|
|
const nodeToMoveParent = lazy(() => findParent(rootNode, nodeToMove.id));
|
|
|
|
const nodeToMoveIndexInParent = lazy(() =>
|
|
|
|
nodeToMoveParent()?.children.findIndex((child) => nodeToMove.id === child.id)
|
|
|
|
);
|
2024-06-04 22:05:44 +02:00
|
|
|
const isRoot = rootNode.id === node.id;
|
|
|
|
|
|
|
|
switch (direction) {
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
case DropDirection.OuterTop:
|
|
|
|
if (node.flexDirection === FlexDirection.Column) {
|
|
|
|
const grandparentNode = grandparent();
|
|
|
|
if (grandparentNode) {
|
|
|
|
const index = indexInGrandparent();
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
parentId: grandparentNode.id,
|
|
|
|
node: nodeToMove,
|
|
|
|
index,
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
case DropDirection.Top:
|
|
|
|
if (node.flexDirection === FlexDirection.Column) {
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = { parentId: node.id, index: 0, node: nodeToMove };
|
2024-06-04 22:05:44 +02:00
|
|
|
} else {
|
|
|
|
if (isRoot)
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
2024-06-04 22:05:44 +02:00
|
|
|
node: nodeToMove,
|
|
|
|
index: 0,
|
|
|
|
insertAtRoot: true,
|
|
|
|
};
|
|
|
|
|
|
|
|
const parentNode = parent();
|
|
|
|
if (parentNode)
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
2024-06-04 22:05:44 +02:00
|
|
|
parentId: parentNode.id,
|
|
|
|
index: indexInParent() ?? 0,
|
|
|
|
node: nodeToMove,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
break;
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
case DropDirection.OuterBottom:
|
|
|
|
if (node.flexDirection === FlexDirection.Column) {
|
|
|
|
const grandparentNode = grandparent();
|
|
|
|
if (grandparentNode) {
|
|
|
|
const index = indexInGrandparent() + 1;
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
parentId: grandparentNode.id,
|
|
|
|
node: nodeToMove,
|
|
|
|
index,
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
case DropDirection.Bottom:
|
|
|
|
if (node.flexDirection === FlexDirection.Column) {
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = { parentId: node.id, index: 1, node: nodeToMove };
|
2024-06-04 22:05:44 +02:00
|
|
|
} else {
|
|
|
|
if (isRoot)
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
2024-06-04 22:05:44 +02:00
|
|
|
node: nodeToMove,
|
|
|
|
index: 1,
|
|
|
|
insertAtRoot: true,
|
|
|
|
};
|
|
|
|
|
|
|
|
const parentNode = parent();
|
|
|
|
if (parentNode)
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
2024-06-04 22:05:44 +02:00
|
|
|
parentId: parentNode.id,
|
|
|
|
index: indexInParent() + 1,
|
|
|
|
node: nodeToMove,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
break;
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
case DropDirection.OuterLeft:
|
|
|
|
if (node.flexDirection === FlexDirection.Row) {
|
|
|
|
const grandparentNode = grandparent();
|
|
|
|
if (grandparentNode) {
|
|
|
|
const index = indexInGrandparent();
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
parentId: grandparentNode.id,
|
|
|
|
node: nodeToMove,
|
|
|
|
index,
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
case DropDirection.Left:
|
|
|
|
if (node.flexDirection === FlexDirection.Row) {
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = { parentId: node.id, index: 0, node: nodeToMove };
|
2024-06-04 22:05:44 +02:00
|
|
|
} else {
|
|
|
|
const parentNode = parent();
|
|
|
|
if (parentNode)
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
2024-06-04 22:05:44 +02:00
|
|
|
parentId: parentNode.id,
|
|
|
|
index: indexInParent(),
|
|
|
|
node: nodeToMove,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
break;
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
case DropDirection.OuterRight:
|
|
|
|
if (node.flexDirection === FlexDirection.Row) {
|
|
|
|
const grandparentNode = grandparent();
|
|
|
|
if (grandparentNode) {
|
|
|
|
const index = indexInGrandparent() + 1;
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
parentId: grandparentNode.id,
|
|
|
|
node: nodeToMove,
|
|
|
|
index,
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
case DropDirection.Right:
|
|
|
|
if (node.flexDirection === FlexDirection.Row) {
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = { parentId: node.id, index: 1, node: nodeToMove };
|
2024-06-04 22:05:44 +02:00
|
|
|
} else {
|
|
|
|
const parentNode = parent();
|
|
|
|
if (parentNode)
|
2024-06-17 23:14:09 +02:00
|
|
|
newMoveOperation = {
|
2024-06-04 22:05:44 +02:00
|
|
|
parentId: parentNode.id,
|
|
|
|
index: indexInParent() + 1,
|
|
|
|
node: nodeToMove,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
break;
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
case DropDirection.Center:
|
2024-06-17 23:14:09 +02:00
|
|
|
if (node.id !== rootNode.id && nodeToMove.id !== rootNode.id) {
|
2024-07-03 23:31:02 +02:00
|
|
|
const swapAction: LayoutTreeSwapNodeAction = {
|
2024-06-17 23:14:09 +02:00
|
|
|
type: LayoutTreeActionType.Swap,
|
2024-07-03 23:31:02 +02:00
|
|
|
node1Id: node.id,
|
|
|
|
node2Id: nodeToMove.id,
|
2024-06-17 23:14:09 +02:00
|
|
|
};
|
2024-08-15 03:40:41 +02:00
|
|
|
return swapAction;
|
2024-06-17 23:14:09 +02:00
|
|
|
} else {
|
|
|
|
console.warn("cannot swap");
|
|
|
|
}
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
break;
|
2024-06-04 22:05:44 +02:00
|
|
|
default:
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
throw new Error(`Invalid direction: ${direction}`);
|
2024-06-04 22:05:44 +02:00
|
|
|
}
|
|
|
|
|
2024-06-19 01:03:00 +02:00
|
|
|
if (
|
|
|
|
newMoveOperation?.parentId !== nodeToMoveParent()?.id ||
|
|
|
|
(newMoveOperation.index !== nodeToMoveIndexInParent() &&
|
|
|
|
newMoveOperation.index !== nodeToMoveIndexInParent() + 1)
|
|
|
|
)
|
2024-08-15 03:40:41 +02:00
|
|
|
return {
|
2024-06-17 23:14:09 +02:00
|
|
|
type: LayoutTreeActionType.Move,
|
|
|
|
...newMoveOperation,
|
2024-08-15 03:40:41 +02:00
|
|
|
} as LayoutTreeMoveNodeAction;
|
2024-06-19 01:03:00 +02:00
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function moveNode(layoutState: LayoutTreeState, action: LayoutTreeMoveNodeAction) {
|
|
|
|
const rootNode = layoutState.rootNode;
|
2024-06-04 22:05:44 +02:00
|
|
|
if (!action) {
|
|
|
|
console.error("no move node action provided");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (action.parentId && action.insertAtRoot) {
|
|
|
|
console.error("parent and insertAtRoot cannot both be defined in a move node action");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-17 23:14:09 +02:00
|
|
|
const node = findNode(rootNode, action.node.id) ?? action.node;
|
|
|
|
const parent = findNode(rootNode, action.parentId);
|
|
|
|
const oldParent = findParent(rootNode, action.node.id);
|
2024-06-04 22:05:44 +02:00
|
|
|
|
2024-06-17 23:14:09 +02:00
|
|
|
let startingIndex = 0;
|
|
|
|
|
|
|
|
// If moving under the same parent, we need to make sure that we are removing the child from its old position, not its new one.
|
|
|
|
// If the new index is before the old index, we need to start our search for the node to delete after the new index position.
|
2024-07-03 23:31:02 +02:00
|
|
|
// If a node is being moved under the same parent, it can keep its size. Otherwise, it should get reset.
|
|
|
|
if (oldParent && parent) {
|
|
|
|
if (oldParent.id === parent.id) {
|
|
|
|
const curIndexInParent = parent.children!.indexOf(node);
|
|
|
|
if (curIndexInParent >= action.index) {
|
|
|
|
startingIndex = action.index + 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
node.size = DefaultNodeSize;
|
2024-06-17 23:14:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-04 22:05:44 +02:00
|
|
|
if (!parent && action.insertAtRoot) {
|
Implement outer drop direction, add rudimentary drag preview image rendering (#29)
This PR adds support for Outer variants of each DropDirection.
When calculating the drop direction, the cursor position is calculated
relevant to the box over which it is hovering. The following diagram
shows how drop directions are calculated. The colored in center is
currently not supported, it is assigned to the top, bottom, left, right
direction for now, though it will ultimately be its own distinct
direction.
![IMG_3505](https://github.com/wavetermdev/thenextwave/assets/16651283/a7ea7387-b95d-4831-9e29-d3225b824c97)
When an outer drop direction is provided for a move operation, if the
reference node flexes in the same axis as the drop direction, the new
node will be inserted at the same level as the parent of the reference
node. If the reference node flexes in a different direction or the
reference node does not have a grandparent, the operation will fall back
to its non-Outer variant.
This also removes some chatty debug statements, adds a blur to the
currently-dragging node to indicate that it cannot be dropped onto, and
simplifies the deriving of the layout state atom from the tab atom so
there's no longer another intermediate derived atom for the layout node.
This also adds rudimentary support for rendering custom preview images
for any tile being dragged. Right now, this is a simple block containing
the block ID, but this can be anything. This resolves an issue where
letting React-DnD generate its own previews could take up to a half
second, and would block dragging until complete. For Monaco, this was
outright failing.
It also fixes an issue where the tile layout could animate on first
paint. Now, I use React Suspense to prevent the layout from displaying
until all the children have loaded.
2024-06-11 22:03:41 +02:00
|
|
|
if (!rootNode.children) {
|
|
|
|
addIntermediateNode(rootNode);
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
addChildAt(rootNode, action.index, node);
|
|
|
|
} else if (parent) {
|
|
|
|
addChildAt(parent, action.index, node);
|
|
|
|
} else {
|
|
|
|
throw new Error("Invalid InsertOperation");
|
|
|
|
}
|
2024-06-12 00:10:20 +02:00
|
|
|
|
|
|
|
// Remove nodeToInsert from its old parent
|
|
|
|
if (oldParent) {
|
2024-06-17 23:14:09 +02:00
|
|
|
removeChild(oldParent, node, startingIndex);
|
2024-06-12 00:10:20 +02:00
|
|
|
}
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.generation++;
|
2024-06-04 22:05:44 +02:00
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function insertNode(layoutState: LayoutTreeState, action: LayoutTreeInsertNodeAction) {
|
2024-06-04 22:05:44 +02:00
|
|
|
if (!action?.node) {
|
2024-08-01 06:27:46 +02:00
|
|
|
console.error("insertNode cannot run, no insert node action provided");
|
2024-06-04 22:05:44 +02:00
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
if (!layoutState.rootNode) {
|
2024-08-21 05:14:14 +02:00
|
|
|
layoutState.rootNode = action.node;
|
2024-08-26 20:56:00 +02:00
|
|
|
} else {
|
|
|
|
const insertLoc = findNextInsertLocation(layoutState.rootNode, 5);
|
|
|
|
addChildAt(insertLoc.node, insertLoc.index, action.node);
|
|
|
|
if (action.magnified) {
|
|
|
|
layoutState.magnifiedNodeId = action.node.id;
|
|
|
|
}
|
|
|
|
layoutState.focusedNodeId = action.node.id;
|
2024-08-21 05:14:14 +02:00
|
|
|
}
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.generation++;
|
2024-06-04 22:05:44 +02:00
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function insertNodeAtIndex(layoutState: LayoutTreeState, action: LayoutTreeInsertNodeAtIndexAction) {
|
2024-08-01 06:27:46 +02:00
|
|
|
if (!action?.node || !action?.indexArr) {
|
|
|
|
console.error("insertNodeAtIndex cannot run, either node or indexArr field is missing");
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
if (!layoutState.rootNode) {
|
2024-08-21 05:14:14 +02:00
|
|
|
layoutState.rootNode = action.node;
|
2024-08-26 20:56:00 +02:00
|
|
|
} else {
|
|
|
|
const insertLoc = findInsertLocationFromIndexArr(layoutState.rootNode, action.indexArr);
|
|
|
|
if (!insertLoc) {
|
|
|
|
console.error("insertNodeAtIndex unable to find insert location");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
addChildAt(insertLoc.node, insertLoc.index + 1, action.node);
|
|
|
|
if (action.magnified) {
|
|
|
|
layoutState.magnifiedNodeId = action.node.id;
|
|
|
|
}
|
|
|
|
layoutState.focusedNodeId = action.node.id;
|
2024-08-21 05:14:14 +02:00
|
|
|
}
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.generation++;
|
2024-08-01 06:27:46 +02:00
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function swapNode(layoutState: LayoutTreeState, action: LayoutTreeSwapNodeAction) {
|
2024-07-03 23:31:02 +02:00
|
|
|
if (!action.node1Id || !action.node2Id) {
|
2024-06-17 23:14:09 +02:00
|
|
|
console.error("invalid swapNode action, both node1 and node2 must be defined");
|
|
|
|
return;
|
|
|
|
}
|
2024-07-03 23:31:02 +02:00
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
if (action.node1Id === layoutState.rootNode.id || action.node2Id === layoutState.rootNode.id) {
|
2024-06-17 23:14:09 +02:00
|
|
|
console.error("invalid swapNode action, the root node cannot be swapped");
|
|
|
|
return;
|
|
|
|
}
|
2024-07-03 23:31:02 +02:00
|
|
|
if (action.node1Id === action.node2Id) {
|
2024-06-17 23:14:09 +02:00
|
|
|
console.error("invalid swapNode action, node1 and node2 are equal");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
const parentNode1 = findParent(layoutState.rootNode, action.node1Id);
|
|
|
|
const parentNode2 = findParent(layoutState.rootNode, action.node2Id);
|
2024-07-03 23:31:02 +02:00
|
|
|
const parentNode1Index = parentNode1.children!.findIndex((child) => child.id === action.node1Id);
|
|
|
|
const parentNode2Index = parentNode2.children!.findIndex((child) => child.id === action.node2Id);
|
|
|
|
|
|
|
|
const node1 = parentNode1.children![parentNode1Index];
|
|
|
|
const node2 = parentNode2.children![parentNode2Index];
|
2024-06-17 23:14:09 +02:00
|
|
|
|
2024-07-03 23:31:02 +02:00
|
|
|
const node1Size = node1.size;
|
|
|
|
node1.size = node2.size;
|
|
|
|
node2.size = node1Size;
|
|
|
|
|
|
|
|
parentNode1.children[parentNode1Index] = node2;
|
|
|
|
parentNode2.children[parentNode2Index] = node1;
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.generation++;
|
2024-06-17 23:14:09 +02:00
|
|
|
}
|
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function deleteNode(layoutState: LayoutTreeState, action: LayoutTreeDeleteNodeAction) {
|
2024-06-04 22:05:44 +02:00
|
|
|
if (!action?.nodeId) {
|
|
|
|
console.error("no delete node action provided");
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
if (!layoutState.rootNode) {
|
2024-06-04 22:05:44 +02:00
|
|
|
console.error("no root node");
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
if (layoutState.rootNode.id === action.nodeId) {
|
|
|
|
layoutState.rootNode = undefined;
|
2024-06-04 22:05:44 +02:00
|
|
|
} else {
|
2024-08-26 20:56:00 +02:00
|
|
|
const parent = findParent(layoutState.rootNode, action.nodeId);
|
|
|
|
if (parent) {
|
|
|
|
const node = parent.children.find((child) => child.id === action.nodeId);
|
|
|
|
removeChild(parent, node);
|
|
|
|
if (layoutState.focusedNodeId === node.id) {
|
|
|
|
layoutState.focusedNodeId = undefined;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
console.error("unable to delete node, not found in tree");
|
|
|
|
}
|
2024-06-04 22:05:44 +02:00
|
|
|
}
|
2024-08-26 20:56:00 +02:00
|
|
|
|
|
|
|
layoutState.generation++;
|
2024-06-04 22:05:44 +02:00
|
|
|
}
|
2024-07-03 23:31:02 +02:00
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function resizeNode(layoutState: LayoutTreeState, action: LayoutTreeResizeNodeAction) {
|
2024-07-03 23:31:02 +02:00
|
|
|
if (!action.resizeOperations) {
|
|
|
|
console.error("invalid resizeNode operation. nodeSizes array must be defined.");
|
|
|
|
}
|
|
|
|
for (const resize of action.resizeOperations) {
|
|
|
|
if (!resize.nodeId || resize.size < 0 || resize.size > 100) {
|
|
|
|
console.error("invalid resizeNode operation. nodeId must be defined and size must be between 0 and 100");
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
const node = findNode(layoutState.rootNode, resize.nodeId);
|
2024-07-03 23:31:02 +02:00
|
|
|
node.size = resize.size;
|
|
|
|
}
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.generation++;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function focusNode(layoutState: LayoutTreeState, action: LayoutTreeFocusNodeAction) {
|
|
|
|
if (!action.nodeId) {
|
|
|
|
console.error("invalid focusNode operation, nodeId must be defined.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
layoutState.focusedNodeId = action.nodeId;
|
|
|
|
layoutState.generation++;
|
2024-07-03 23:31:02 +02:00
|
|
|
}
|
2024-07-30 19:59:53 +02:00
|
|
|
|
2024-08-15 03:40:41 +02:00
|
|
|
export function magnifyNodeToggle(layoutState: LayoutTreeState, action: LayoutTreeMagnifyNodeToggleAction) {
|
2024-07-30 19:59:53 +02:00
|
|
|
if (!action.nodeId) {
|
|
|
|
console.error("invalid magnifyNodeToggle operation. nodeId must be defined.");
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
if (layoutState.rootNode.id === action.nodeId) {
|
2024-07-30 19:59:53 +02:00
|
|
|
console.warn(`cannot toggle magnification of node ${action.nodeId} because it is the root node.`);
|
|
|
|
return;
|
|
|
|
}
|
2024-08-15 03:40:41 +02:00
|
|
|
if (layoutState.magnifiedNodeId === action.nodeId) {
|
|
|
|
layoutState.magnifiedNodeId = undefined;
|
2024-07-30 19:59:53 +02:00
|
|
|
} else {
|
2024-08-15 03:40:41 +02:00
|
|
|
layoutState.magnifiedNodeId = action.nodeId;
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.focusedNodeId = action.nodeId;
|
2024-07-30 19:59:53 +02:00
|
|
|
}
|
2024-08-26 20:56:00 +02:00
|
|
|
layoutState.generation++;
|
2024-07-30 19:59:53 +02:00
|
|
|
}
|