@webwriter/flowchart
Version:
Create programming flowcharts with interactive tasks. Use standardized Elements such as loops and Branchings.
116 lines (103 loc) • 4.11 kB
text/typescript
import { GraphNode } from '../../definitions/GraphNode';
import { Arrow } from '../../definitions/Arrow';
import { findLastGraphNode } from '../helper/utilities';
import { findArrow } from '../helper/arrowHelper';
import { findLastIndex } from '../helper/utilities';
export function handleSequenceSelection(
ctx: CanvasRenderingContext2D,
selectedSequence: {
id: string;
order: number;
type: string;
}[],
graphNodes: GraphNode[],
arrows: Arrow[],
x: number,
y: number
) {
const clickedNode = findLastGraphNode(ctx, graphNodes, x, y);
const clickedArrow = findArrow(arrows, x, y);
let clickedItem: GraphNode | Arrow;
let sequenceType: string;
if (clickedNode) {
clickedItem = clickedNode;
sequenceType = 'node';
} else if (clickedArrow) {
clickedItem = clickedArrow;
sequenceType = 'arrow';
}
if (clickedItem) {
selectedSequence.push({
id: clickedItem.id,
order: selectedSequence.length + 1,
type: sequenceType,
});
return;
const existingIndex = findLastIndex(
selectedSequence,
(item) => item.id === clickedItem.id && item.type === sequenceType
);
const lastIndex = selectedSequence.length - 1;
const lastItemType = lastIndex >= 0 ? selectedSequence[lastIndex].type : null;
const lastItemId = lastIndex >= 0 ? selectedSequence[lastIndex].id : null;
if (selectedSequence.length === 0 || (lastItemType !== sequenceType && existingIndex !== -1)) {
selectedSequence.push({
id: clickedItem.id,
order: selectedSequence.length + 1,
type: sequenceType,
});
} else if (existingIndex === lastIndex) {
selectedSequence.pop();
} else {
let lastItem: GraphNode | Arrow;
if (lastItemType === 'node') {
lastItem = graphNodes.find((node) => node.id === lastItemId);
} else {
lastItem = arrows.find((arrow) => arrow.id === lastItemId);
}
if (sequenceType === 'node' && isArrow(lastItem)) {
const clickedNode = clickedItem as GraphNode;
const connectedNode = findConnectedNode(lastItem, clickedNode, graphNodes);
if (connectedNode || existingIndex !== -1) {
selectedSequence.push({
id: clickedItem.id,
order: selectedSequence.length + 1,
type: sequenceType,
});
}
} else if (sequenceType === 'arrow' && isGraphNode(lastItem)) {
const clickedArrow = clickedItem as Arrow;
const connectedArrow = findConnectedArrow(lastItem, clickedArrow, arrows);
if (connectedArrow || existingIndex !== -1) {
selectedSequence.push({
id: clickedItem.id,
order: selectedSequence.length + 1,
type: sequenceType,
});
}
}
}
}
}
function findConnectedArrow(node: GraphNode, clickedArrow: Arrow, arrows: Arrow[]): Arrow | null {
for (const arrow of arrows) {
if (arrow.from.id === node.id && arrow.id === clickedArrow.id) {
return arrow;
}
}
return null;
}
function findConnectedNode(arrow: Arrow, clickedNode: GraphNode, graphNodes: GraphNode[]): GraphNode | null {
for (const node of graphNodes) {
if (arrow.to.id === node.id && clickedNode.id === node.id) {
return node;
}
}
return null;
}
function isGraphNode(obj: GraphNode | Arrow): obj is GraphNode {
return (obj as GraphNode).node !== undefined;
}
function isArrow(obj: GraphNode | Arrow): obj is Arrow {
return (obj as Arrow).from !== undefined;
}