Create Element
Controller Utils
Learn how to update the controller utilities to include the logic for the new element.
Enter / Leave Drop Area
When the block node of the element is dragged, the function in the following file is called:
// src/shared/kits/workflow/mvc/controller/utils/index.ts
// ...
/**
* Called whenever a node is being dragged. Determines whether the node
* has entered or left a drop area and generates the corresponding changes.
*
* @param node The node being dragged.
* @param position The current position of the dragged node.
* @param workflow The workflow object.
* @param flows The flows object.
* @returns An array of drop area changes (enter/leave) generated during the drag.
*/
export function createDropAreaChanges(
node: TypedFlowNode,
position: {
x: number;
y: number;
},
workflow: Workflow,
flows: TypedFlows,
): WorkflowChange[] {
const id = node.entity.meta.id;
const changes: WorkflowChange[] = [];
const currentDropArea = WorkflowUtils.dropArea(workflow, id);
if (currentDropArea) {
changes.push(createLeaveDropAreaChange(currentDropArea, id));
}
const closestNode = getClosestDropAreaNode(node, position, workflow, flows);
if (closestNode) {
changes.push(createEnterDropAreaChange(closestNode, id));
}
return changes;
}
// ...
This function checks whether the element needs to leave a drop area and enter a new one, and creates the corresponding changes based on that information.
Enter Drop Area
We need to update the logic that creates the "enter drop area" change so it also supports the new element, and to do that we'll add a new function to this file:
// src/shared/kits/workflow/mvc/controller/utils/enter-drop-area/element-type/index.ts
import { zet, type Refine } from "@/shared/lib/zet";
import type { WorkflowChange } from "@/shared/kits/workflow/mvc/model/workflow-change";
import type { ElementType } from "..";
import { actionEnterDropArea } from "./action";
import { conditionEnterDropArea } from "./condition";
import { switchEnterDropArea } from "./switch";
import { loopEnterDropArea } from "./loop";
import { parallelEnterDropArea } from "./parallel";
interface Zet {
object: ElementType;
nested: ["entity"];
filter: ["meta", "elementType"];
params: [string];
return: WorkflowChange;
}
const dispatch = zet<Zet>(["entity"], ["meta", "elementType"], {
action: actionEnterDropArea,
condition: conditionEnterDropArea,
switch: switchEnterDropArea,
loop: loopEnterDropArea,
parallel: parallelEnterDropArea,
});
export function elementTypeEnterDropArea(
dropAreaNode: ElementType,
id: string,
): WorkflowChange {
return dispatch(dropAreaNode, id);
}
export type Action = Refine<{
object: ElementType;
nested: ["entity"];
filter: ["meta", "elementType"];
narrow: "action";
}>;
export type Condition = Refine<{
object: ElementType;
nested: ["entity"];
filter: ["meta", "elementType"];
narrow: "condition";
}>;
export type Switch = Refine<{
object: ElementType;
nested: ["entity"];
filter: ["meta", "elementType"];
narrow: "switch";
}>;
export type Loop = Refine<{
object: ElementType;
nested: ["entity"];
filter: ["meta", "elementType"];
narrow: "loop";
}>;
export type Parallel = Refine<{
object: ElementType;
nested: ["entity"];
filter: ["meta", "elementType"];
narrow: "parallel";
}>;
We'll create this new function by adding the files shown below.
The enter-drop-area/element-type/parallel/index.ts file:
// src/shared/kits/workflow/mvc/controller/utils/enter-drop-area/element-type/parallel/index.ts
import { zet, type Refine } from "@/shared/lib/zet";
import type { WorkflowChange } from "@/shared/kits/workflow/mvc/model/workflow-change";
import type { Parallel } from "..";
import { addNext } from "./add-next";
import { addBranch } from "./add-branch";
interface Zet {
object: Parallel;
nested: ["entity"];
filter: ["meta", "item"];
params: [string];
return: WorkflowChange;
}
const dispatch = zet<Zet>(["entity"], ["meta", "item"], {
addNext: addNext,
addBranch: addBranch,
});
export function parallelEnterDropArea(
dropAreaNode: Parallel,
id: string,
): WorkflowChange {
return dispatch(dropAreaNode, id);
}
export type AddNext = Refine<{
object: Parallel;
nested: ["entity"];
filter: ["meta", "item"];
narrow: "addNext";
}>;
export type AddBranch = Refine<{
object: Parallel;
nested: ["entity"];
filter: ["meta", "item"];
narrow: "addBranch";
}>;
The enter-drop-area/element-type/parallel/add-next.ts file:
// src/shared/kits/workflow/mvc/controller/utils/enter-drop-area/element-type/parallel/add-next.ts
import type {
WorkflowChange,
ElementEnterNextDropAreaChange,
} from "@/shared/kits/workflow/mvc/model/workflow-change";
import { AddNext } from ".";
export function addNext(dropAreaNode: AddNext, id: string): WorkflowChange {
const workflowChange: ElementEnterNextDropAreaChange = {
type: "element",
change: "enterNextDropArea",
id: dropAreaNode.entity.meta.id,
enter: id,
};
return workflowChange;
}
The enter-drop-area/element-type/parallel/add-branch.ts file:
// src/shared/kits/workflow/mvc/controller/utils/enter-drop-area/element-type/parallel/add-branch.ts
import type {
WorkflowChange,
ParallelEnterBranchDropAreaChange,
} from "@/shared/kits/workflow/mvc/model/workflow-change";
import { AddBranch } from ".";
export function addBranch(dropAreaNode: AddBranch, id: string): WorkflowChange {
const workflowChange: ParallelEnterBranchDropAreaChange = {
type: "elementType",
elementType: "parallel",
change: "enterBranchDropArea",
id: dropAreaNode.entity.meta.id,
branch: dropAreaNode.entity.meta.branch,
enter: id,
};
return workflowChange;
}
Leave Drop Area
We need to update the logic that creates the "leave drop area" change so it also supports the new element, and to do that we'll add a new function to this file:
// src/shared/kits/workflow/mvc/controller/utils/leave-drop-area/element/index.ts
import { zet } from "@/shared/lib/zet";
import type { WorkflowChange } from "@/shared/kits/workflow/mvc/model/workflow-change";
import { actionLeaveDropArea } from "./action";
import { conditionLeaveDropArea } from "./condition";
import { switchLeaveDropArea } from "./switch";
import { loopLeaveDropArea } from "./loop";
import { parallelLeaveDropArea } from "./parallel";
import type { ElementDropArea } from "@/shared/kits/workflow/mvc/drop-area";
interface Zet {
object: ElementDropArea;
nested: [];
filter: ["element", "type"];
params: [string];
return: WorkflowChange;
}
const dispatch = zet<Zet>([], ["element", "type"], {
action: actionLeaveDropArea,
condition: conditionLeaveDropArea,
switch: switchLeaveDropArea,
loop: loopLeaveDropArea,
parallel: parallelLeaveDropArea,
});
export function elementLeaveDropArea(
dropArea: ElementDropArea,
id: string,
): WorkflowChange {
return dispatch(dropArea, id);
}
We'll create this new function by adding the files shown below.
The leave-drop-area/element/parallel/index.ts file:
// src/shared/kits/workflow/mvc/controller/utils/leave-drop-area/element/parallel/index.ts
import { zet } from "@/shared/lib/zet";
import type { WorkflowChange } from "@/shared/kits/workflow/mvc/model/workflow-change";
import type { ParallelDropArea } from "@/shared/kits/workflow/mvc/drop-area";
import { nextLeaveDropArea } from "./next";
import { branchLeaveDropArea } from "./branch";
interface Zet {
object: ParallelDropArea;
nested: [];
filter: ["dropArea"];
params: [string];
return: WorkflowChange;
}
const dispatch = zet<Zet>([], ["dropArea"], {
next: nextLeaveDropArea,
branch: branchLeaveDropArea,
});
export function parallelLeaveDropArea(
dropArea: ParallelDropArea,
id: string,
): WorkflowChange {
return dispatch(dropArea, id);
}
The leave-drop-area/element/parallel/next.ts file:
// src/shared/kits/workflow/mvc/controller/utils/leave-drop-area/element/parallel/next.ts
import type {
WorkflowChange,
ElementLeaveNextDropAreaChange,
} from "@/shared/kits/workflow/mvc/model/workflow-change";
import type { ParallelNextDropArea } from "@/shared/kits/workflow/mvc/drop-area";
export function nextLeaveDropArea(
dropArea: ParallelNextDropArea,
id: string,
): WorkflowChange {
const workflowChange: ElementLeaveNextDropAreaChange = {
type: "element",
change: "leaveNextDropArea",
id: dropArea.element.id,
leave: id,
};
return workflowChange;
}
The leave-drop-area/element/parallel/branch.ts file:
// src/shared/kits/workflow/mvc/controller/utils/leave-drop-area/element/parallel/branch.ts
import type {
WorkflowChange,
ParallelLeaveBranchDropAreaChange,
} from "@/shared/kits/workflow/mvc/model/workflow-change";
import type { ParallelBranchDropArea } from "@/shared/kits/workflow/mvc/drop-area";
export function branchLeaveDropArea(
dropArea: ParallelBranchDropArea,
id: string,
): WorkflowChange {
const workflowChange: ParallelLeaveBranchDropAreaChange = {
type: "elementType",
elementType: "parallel",
change: "leaveBranchDropArea",
id: dropArea.element.id,
branch: dropArea.branch,
leave: id,
};
return workflowChange;
}