Skip to content

Commit

Permalink
feat: Deletable Edges (#763)
Browse files Browse the repository at this point in the history
  • Loading branch information
nesadrian committed Jun 19, 2024
1 parent a7c9bb3 commit a516a13
Show file tree
Hide file tree
Showing 34 changed files with 626 additions and 282 deletions.
12 changes: 5 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

## [2.3.0](https://github.com/equinor/flyt/compare/v2.2.0...v2.3.0) (2024-06-18)


### Features

* Click to rename project title ([#489](https://github.com/equinor/flyt/issues/489)) ([d74062f](https://github.com/equinor/flyt/commit/d74062fe1256dbdba4863e9041f0397df908ade1))
* Edge labels ([#743](https://github.com/equinor/flyt/issues/743)) ([cae915c](https://github.com/equinor/flyt/commit/cae915ce4a9a7e69c1b894854d3997eb58c9c3ed))

- Click to rename project title ([#489](https://github.com/equinor/flyt/issues/489)) ([d74062f](https://github.com/equinor/flyt/commit/d74062fe1256dbdba4863e9041f0397df908ade1))
- Edge labels ([#743](https://github.com/equinor/flyt/issues/743)) ([cae915c](https://github.com/equinor/flyt/commit/cae915ce4a9a7e69c1b894854d3997eb58c9c3ed))

### Bug Fixes

* close scrim, fix ts errors ([#764](https://github.com/equinor/flyt/issues/764)) ([00ed2b6](https://github.com/equinor/flyt/commit/00ed2b62f2ca723335df7bf29a7c4180b228b55c))
* edges no longer disappearing when creating hidden nodes ([#761](https://github.com/equinor/flyt/issues/761)) ([0f8ec0e](https://github.com/equinor/flyt/commit/0f8ec0ebad2f558b89c36f31a65750916d0562c4))
* Remove duration bug ([#753](https://github.com/equinor/flyt/issues/753)) ([4edc93d](https://github.com/equinor/flyt/commit/4edc93db75872adbca7d98089487c35ac308f615))
- close scrim, fix ts errors ([#764](https://github.com/equinor/flyt/issues/764)) ([00ed2b6](https://github.com/equinor/flyt/commit/00ed2b62f2ca723335df7bf29a7c4180b228b55c))
- edges no longer disappearing when creating hidden nodes ([#761](https://github.com/equinor/flyt/issues/761)) ([0f8ec0e](https://github.com/equinor/flyt/commit/0f8ec0ebad2f558b89c36f31a65750916d0562c4))
- Remove duration bug ([#753](https://github.com/equinor/flyt/issues/753)) ([4edc93d](https://github.com/equinor/flyt/commit/4edc93db75872adbca7d98089487c35ac308f615))

## [2.2.0](https://github.com/equinor/flyt/compare/v2.1.0...v2.2.0) (2024-06-03)

Expand Down
53 changes: 14 additions & 39 deletions components/DeleteNodeDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Button, Icon, Scrim, Typography } from "@equinor/eds-core-react";
import { close as closeIcon, delete_forever } from "@equinor/eds-icons";
import { useAccount, useMsal } from "@azure/msal-react";
import { useMutation, useQueryClient } from "react-query";
import { getNodeTypeName } from "@/utils/getNodeTypeName";
import { notifyOthers } from "@/services/notifyOthers";
import styles from "../layouts/default.layout.module.scss";
import { unknownErrorToString } from "@/utils/isError";
import { useStoreDispatch } from "hooks/storeHooks";
import { NodeDataApi } from "@/types/NodeDataApi";
import { NodeTypes } from "@/types/NodeTypes";
import { deleteVertice } from "services/graphApi";
import { useProjectId } from "@/hooks/useProjectId";
import { ScrimDelete } from "./ScrimDelete";

export function DeleteNodeDialog(props: {
objectToDelete: NodeDataApi;
Expand Down Expand Up @@ -59,8 +57,8 @@ export function DeleteNodeDialog(props: {

const { type } = props.objectToDelete;

const header = `Delete "${getNodeTypeName(type).toLowerCase()}"`;
let warningMessage = "This will delete the selected object.";
const header = `Delete ${getNodeTypeName(type).toLowerCase()}`;
let warningMessage = "This will delete the selected card.";
if (type === mainActivity) {
warningMessage =
"This will delete everything under it.\nAre you sure you want to proceed?";
Expand All @@ -70,39 +68,16 @@ export function DeleteNodeDialog(props: {
}
const confirmMessage = "Delete";
return (
<Scrim open onClose={handleClose} isDismissable>
<div className={styles.scrimWrapper}>
{deleteMutation.isLoading ? (
<Typography>Deleting...</Typography>
) : (
<>
<div className={styles.scrimHeaderWrapper}>
<div className={styles.scrimTitle}>{header}</div>
<Button autoFocus variant={"ghost_icon"} onClick={handleClose}>
<Icon data={closeIcon} title="Close" />
</Button>
</div>
<div className={styles.scrimContent}>
{deleteMutation.error && (
<Typography color={"warning"} variant={"h4"}>
{unknownErrorToString(deleteMutation.error)}
</Typography>
)}
<Typography variant={"h4"}>{warningMessage}</Typography>
</div>
<div className={styles.deleteButton}>
<Button
variant={"contained"}
color={"danger"}
onClick={handleDelete}
>
<Icon data={delete_forever} title="Delete process" size={16} />
{confirmMessage}
</Button>
</div>
</>
)}
</div>
</Scrim>
<ScrimDelete
id={props.objectToDelete.id}
open
header={header}
onClose={handleClose}
onConfirm={handleDelete}
error={deleteMutation.error}
warningMessage={warningMessage}
confirmMessage={confirmMessage}
isLoading={deleteMutation.isLoading}
/>
);
}
40 changes: 40 additions & 0 deletions components/ScrimDelete.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@import "../styles/variables";

.scrimHeaderWrapper {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: solid 1px #dcdcdc;
}

.scrimTitle {
padding: 12px 18px;
}

.scrimWrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
max-width: 80vw;
max-height: 80vh;
background-color: white;
border-radius: 2px;
}

.scrimContent {
padding: 18px;
}

.buttonsGroup {
display: flex;
justify-content: space-between;
padding: 18px;
}

.loaderContainer {
display: flex;
align-items: center;
flex-direction: column;
gap: 15px;
padding: 50px;
}
74 changes: 74 additions & 0 deletions components/ScrimDelete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Button, Icon, Scrim, Typography } from "@equinor/eds-core-react";
import styles from "./ScrimDelete.module.scss";
import { close as closeIcon, delete_forever } from "@equinor/eds-icons";
import { unknownErrorToString } from "@/utils/isError";
import { CircularProgress } from "@equinor/eds-core-react";

type ScrimDelete = {
id: string;
open: boolean;
onConfirm: (id: string) => void;
onClose: () => void;
header?: string;
warningMessage?: string;
confirmMessage?: string;
error?: unknown;
isLoading?: boolean;
};

export const ScrimDelete = ({
id,
open,
onConfirm,
onClose,
header,
warningMessage,
confirmMessage,
error,
isLoading,
}: ScrimDelete) => {
return (
<Scrim isDismissable open={open} onClose={onClose}>
<div className={styles.scrimWrapper}>
{isLoading ? (
<div className={styles.loaderContainer}>
<CircularProgress />
<Typography>Deleting...</Typography>
</div>
) : (
<>
<div className={styles.scrimHeaderWrapper}>
<div className={styles.scrimTitle}>
<Typography variant={"h6"}>{header}</Typography>
</div>
<Button autoFocus variant={"ghost_icon"} onClick={onClose}>
<Icon data={closeIcon} title="Close" />
</Button>
</div>
<div className={styles.scrimContent}>
{!!error && (
<Typography color={"warning"} variant={"h4"}>
{unknownErrorToString(error)}
</Typography>
)}
<Typography variant={"h4"}>{warningMessage}</Typography>
</div>
<div className={styles.buttonsGroup}>
<Button variant={"outlined"} onClick={() => onClose()}>
{"Cancel"}
</Button>
<Button
variant={"contained"}
color={"danger"}
onClick={() => onConfirm(id)}
>
<Icon data={delete_forever} title="Delete" size={16} />
{confirmMessage}
</Button>
</div>
</>
)}
</div>
</Scrim>
);
};
90 changes: 69 additions & 21 deletions components/canvas/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ import { useNodeMerge } from "./hooks/useNodeMerge";
import { useWebSocket } from "./hooks/useWebSocket";
import { getQIPRContainerWidth } from "./utils/getQIPRContainerWidth";
import { useProjectId } from "@/hooks/useProjectId";
import { useEdgeDelete } from "./hooks/useEdgeDelete";
import { ScrimDelete } from "../ScrimDelete";
import { MiniMapCustom } from "@/components/canvas/MiniMapCustom";
import { EdgeDataApi } from "@/types/EdgeDataApi";
import { ZoomLevel } from "@/components/canvas/ZoomLevel";
import { edgeElementTypes } from "@/components/canvas/EdgeElementTypes";
import { createHiddenNodes } from "@/components/canvas/utils/createHiddenNodes";
import { createEdges } from "./utils/createEdges";

type CanvasProps = {
graph: Graph;
Expand All @@ -60,29 +62,23 @@ const Canvas = ({
new Date("2024-04-24T00:08:00.000000Z").getTime();

let tempNodes: Node<NodeDataFull>[] = [];
let tempEdges: Edge[] = [];
apiEdges.map((edge: EdgeDataApi) => {
const nodeSource = apiNodes.filter((node) => node.id === edge.source);
if (nodeSource[0] && nodeSource[0].type === NodeTypes.choice) {
tempEdges.push({
...edge,
type: "choice",
label: edge.edgeValue,
});
} else {
tempEdges.push({ ...edge });
}
});
const tempEdges: Edge[] = apiEdges.map((e) => ({ ...e, label: e.edgeValue }));

const [isEditingEdgeText, setIsEditingEdgeText] = useState(false);
const [nodes, setNodes, onNodesChange] = useNodesState<NodeDataFull>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);

const [visibleDeleteScrim, setVisibleDeleteScrim] = useState(false);
const [visibleDeleteNodeScrim, setVisibleDeleteNodeScrim] = useState(false);
const [edgeToBeDeletedId, setEdgeToBeDeletedId] = useState<
string | undefined
>(undefined);
const [visibleLabelScrim, setVisibleLabelScrim] = useState(false);
const isEditingEdge = isEditingEdgeText || edgeToBeDeletedId;

const { onNodeDragStart, onNodeDrag, onNodeDragStop } = useNodeDrag();
const { mutate: mergeNode, merging } = useNodeMerge();
const { mutate: addNode } = useNodeAdd();
const { deleteEdgeMutation } = useEdgeDelete();

const { socketConnected, socketReason } = useWebSocket();

Expand Down Expand Up @@ -145,6 +141,7 @@ const Canvas = ({
type: node.type,
height: shapeSize.height,
width: shapeSize.width + getQIPRContainerWidth(node.tasks),
deletable: false,
});
} else {
tempNodes.push({
Expand All @@ -153,11 +150,14 @@ const Canvas = ({
...node,
parents: [],
columnId: node.id,
shapeHeight: shapeSize.height,
shapeWidth: shapeSize.width,
},
position: { x: 0, y: 0 },
type: node.type,
height: shapeSize.height,
width: shapeSize.width + getQIPRContainerWidth(node.tasks),
deletable: false,
});
}

Expand All @@ -167,6 +167,16 @@ const Canvas = ({
});
};

const handleSetSelectedEdge = (selectedEdge: Edge | undefined) => {
if (userCanEdit && !isEditingEdge) {
const updatedEdges = edges.map((e) => {
e.selected = e.id === selectedEdge?.id;
return e;
});
setEdges(updatedEdges);
}
};

const mergedNodesLooping = new Map<string, [Node<NodeDataFull>, number]>();
let mergedNodesReady: Node<NodeDataFull>[] = [];

Expand Down Expand Up @@ -237,11 +247,23 @@ const Canvas = ({
}
createNodes(root);
setNodesDepth();
const { tempNodes: tempWithHiddenNodes, tempEdges: tempWithHiddenEdges } =
createHiddenNodes(tempNodes, tempEdges, shapeSize);
const {
tempNodes: tempWithHiddenNodes,
tempEdges: tempWithHiddenEdges,
longEdges,
} = createHiddenNodes(tempNodes, tempEdges, shapeSize);
const finalNodes = setLayout(tempWithHiddenNodes, tempWithHiddenEdges);
const finalEdges = createEdges(
finalNodes,
tempWithHiddenEdges,
longEdges,
shapeSize,
userCanEdit,
setIsEditingEdgeText,
setEdgeToBeDeletedId
);
setNodes(finalNodes);
setEdges(tempWithHiddenEdges);
setEdges(finalEdges);
}, [apiNodes, apiEdges, userCanEdit]);

useCenterCanvas();
Expand Down Expand Up @@ -272,16 +294,38 @@ const Canvas = ({
{selectedNode && (
<DeleteNodeDialog
objectToDelete={selectedNode}
visible={visibleDeleteScrim}
visible={visibleDeleteNodeScrim}
onClose={() => {
setVisibleDeleteScrim(false);
setVisibleDeleteNodeScrim(false);
setSelectedNode(undefined);
}}
/>
)}
{edgeToBeDeletedId && (
<ScrimDelete
id={edgeToBeDeletedId}
open={!!edgeToBeDeletedId}
onConfirm={(id) => {
deleteEdgeMutation.mutate(
{ edgeId: id },
{
onSuccess() {
setEdgeToBeDeletedId(undefined);
},
}
);
}}
onClose={() => setEdgeToBeDeletedId(undefined)}
header={"Delete line"}
warningMessage={"Are you sure you want to delete this line?"}
confirmMessage={"Delete"}
isLoading={deleteEdgeMutation.isLoading}
error={deleteEdgeMutation.error}
/>
)}
<SideBar
onClose={() => setSelectedNode(undefined)}
onDelete={() => setVisibleDeleteScrim(true)}
onDelete={() => setVisibleDeleteNodeScrim(true)}
canEdit={userCanEdit}
selectedNode={selectedNode}
/>
Expand All @@ -300,6 +344,10 @@ const Canvas = ({
onNodeDragStart={onNodeDragStart}
onNodeDrag={onNodeDrag}
onNodeDragStop={onNodeDragStop}
elevateEdgesOnSelect={true}
edgesFocusable={userCanEdit}
onEdgeMouseEnter={(event, edge) => handleSetSelectedEdge(edge)}
onEdgeMouseLeave={() => handleSetSelectedEdge(undefined)}
attributionPosition="bottom-right"
connectionRadius={100}
>
Expand Down
Loading

0 comments on commit a516a13

Please sign in to comment.