feat: allows better drag and drop interaction for connector anchors

This commit is contained in:
Mark Mankarious
2023-10-07 20:29:38 +01:00
parent f80815976d
commit 7661bcb069
6 changed files with 75 additions and 52 deletions

View File

@@ -3,7 +3,7 @@ import { useTheme, Box } from '@mui/material';
import { Connector as ConnectorI } from 'src/types';
import { UNPROJECTED_TILE_SIZE } from 'src/config';
import {
getAnchorPosition,
getAnchorTile,
CoordsUtils,
getColorVariant,
getAllAnchors
@@ -53,11 +53,7 @@ export const Connector = ({ connector }: Props) => {
const anchorPositions = useMemo(() => {
return connector.anchors.map((anchor) => {
const position = getAnchorPosition({
anchor,
nodes,
allAnchors: getAllAnchors(connectors)
});
const position = getAnchorTile(anchor, nodes, getAllAnchors(connectors));
return {
x: (connector.path.rectangle.from.x - position.x) * unprojectedTileSize,

View File

@@ -7,7 +7,7 @@ import {
getBoundingBoxSize,
sortByPosition,
clamp,
getAnchorPosition,
getAnchorTile,
getAllAnchors
} from 'src/utils';
import { useGetTilePosition } from 'src/hooks/useGetTilePosition';
@@ -44,11 +44,11 @@ export const useDiagramUtils = () => {
return [
...acc,
...item.anchors.map((anchor) => {
return getAnchorPosition({
return getAnchorTile(
anchor,
nodes: scene.nodes,
allAnchors: getAllAnchors(scene.connectors)
});
scene.nodes,
getAllAnchors(scene.connectors)
);
})
];
case 'RECTANGLE':

View File

@@ -1,11 +1,13 @@
import { produce } from 'immer';
import {
ConnectorAnchor,
Connector,
ModeActions,
ModeActionsAction,
SceneItemTypeEnum,
SceneStore,
Coords
Coords,
Node
} from 'src/types';
import {
getItemAtTile,
@@ -13,9 +15,36 @@ import {
getAnchorAtTile,
getItemById,
generateId,
CoordsUtils
CoordsUtils,
getAnchorTile,
getAllAnchors,
connectorPathTileToGlobal
} from 'src/utils';
const getAnchorOrdering = (
anchor: ConnectorAnchor,
connector: Connector,
nodes: Node[],
allAnchors: ConnectorAnchor[]
) => {
const anchorTile = getAnchorTile(anchor, nodes, allAnchors);
const index = connector.path.tiles.findIndex((pathTile) => {
const globalTile = connectorPathTileToGlobal(
pathTile,
connector.path.rectangle.from
);
return CoordsUtils.isEqual(globalTile, anchorTile);
});
if (index === -1) {
throw new Error(
`Could not calculate ordering index of anchor [anchorId: ${anchor.id}]`
);
}
return index;
};
const getAnchor = (connectorId: string, tile: Coords, scene: SceneStore) => {
const connector = getItemById(scene.connectors, connectorId).item;
const anchor = getAnchorAtTile(tile, connector.anchors);
@@ -27,11 +56,19 @@ const getAnchor = (connectorId: string, tile: Coords, scene: SceneStore) => {
ref: { type: 'TILE', coords: tile }
};
const newConnector = produce(connector, (draft) => {
draft.anchors.push(newAnchor);
});
const allAnchors = getAllAnchors(scene.connectors);
const orderedAnchors = [...connector.anchors, newAnchor]
.map((anch) => {
return {
...anch,
ordering: getAnchorOrdering(anch, connector, scene.nodes, allAnchors)
};
})
.sort((a, b) => {
return a.ordering - b.ordering;
});
scene.actions.updateConnector(connector.id, newConnector);
scene.actions.updateConnector(connector.id, { anchors: orderedAnchors });
return newAnchor;
}
@@ -84,14 +121,8 @@ export const Cursor: ModeActions = {
let item = uiState.mode.mousedownItem;
if (item?.type === 'CONNECTOR') {
const prevTile = uiState.mouse.delta
? CoordsUtils.subtract(
uiState.mouse.position.tile,
uiState.mouse.delta.tile
)
: CoordsUtils.zero();
const anchor = getAnchor(item.id, prevTile, scene);
if (item?.type === 'CONNECTOR' && uiState.mouse.mousedown) {
const anchor = getAnchor(item.id, uiState.mouse.mousedown.tile, scene);
item = anchor;
}

View File

@@ -88,13 +88,11 @@ const initialState = () => {
draft.nodes.splice(index, 1);
draft.connectors = draft.connectors.filter(
(connector) => {
return !connector.anchors.find((anchor) => {
return anchor.ref.type === 'NODE' && anchor.ref.id === id;
});
}
);
draft.connectors = draft.connectors.filter((connector) => {
return !connector.anchors.find((anchor) => {
return anchor.ref.type === 'NODE' && anchor.ref.id === id;
});
});
});
set({ nodes: newScene.nodes, connectors: newScene.connectors });
@@ -134,7 +132,6 @@ const initialState = () => {
});
set({ connectors: newScene.connectors });
console.log(newScene.connectors)
},
deleteConnector: (id: string) => {
@@ -165,10 +162,7 @@ const initialState = () => {
updateTextBox: (id, updates) => {
const newScene = produce(get(), (draft) => {
const { item: textBox, index } = getItemById(
draft.textBoxes,
id
);
const { item: textBox, index } = getItemById(draft.textBoxes, id);
if (updates.text !== undefined || updates.fontSize !== undefined) {
draft.textBoxes[index].size = {

View File

@@ -86,7 +86,12 @@ export interface Rectangle {
to: Coords;
}
export type SceneItem = Node | Connector | TextBox | Rectangle | ConnectorAnchor;
export type SceneItem =
| Node
| Connector
| TextBox
| Rectangle
| ConnectorAnchor;
export type SceneItemReference = {
type: SceneItemTypeEnum;
id: string;

View File

@@ -334,17 +334,11 @@ export const getAllAnchors = (connectors: Connector[]) => {
}, [] as ConnectorAnchor[]);
};
interface GetAnchorPositions {
anchor: ConnectorAnchor;
nodes: Node[];
allAnchors: ConnectorAnchor[];
}
export const getAnchorPosition = ({
anchor,
nodes,
allAnchors
}: GetAnchorPositions): Coords => {
export const getAnchorTile = (
anchor: ConnectorAnchor,
nodes: Node[],
allAnchors: ConnectorAnchor[]
): Coords => {
if (anchor.ref.type === 'NODE') {
const { item: node } = getItemById(nodes, anchor.ref.id);
return node.tile;
@@ -357,7 +351,7 @@ export const getAnchorPosition = ({
const nextAnchor = getItemById(anchorsWithIds, anchor.ref.id);
return getAnchorPosition({ anchor: nextAnchor.item, nodes, allAnchors });
return getAnchorTile(nextAnchor.item, nodes, allAnchors);
}
return anchor.ref.coords;
@@ -395,7 +389,7 @@ export const getConnectorPath = ({
);
const anchorPositions = anchors.map((anchor) => {
return getAnchorPosition({ anchor, nodes, allAnchors });
return getAnchorTile(anchor, nodes, allAnchors);
});
const searchArea = getBoundingBox(
@@ -411,7 +405,10 @@ export const getConnectorPath = ({
};
const positionsNormalisedFromSearchArea = anchorPositions.map((position) => {
return normalisePositionFromOrigin({ position, origin: rectangle.from });
return normalisePositionFromOrigin({
position,
origin: rectangle.from
});
});
const tiles = positionsNormalisedFromSearchArea.reduce<Coords[]>(