Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Add node deletion functionality in Workspace #60

Merged
merged 1 commit into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions public/component_types/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,11 @@ export interface IComponent {
createFields?: IComponentField[];
outputs?: IComponentOutput[];
}

/**
* We need to include some extra instance-specific data to the ReactFlow component
* to perform extra functionality, such as deleting the node from the ReactFlowInstance.
*/
export interface IComponentData extends IComponent {
id: string;
}
7 changes: 4 additions & 3 deletions public/pages/workflow_detail/workspace/workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import ReactFlow, {
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { rfContext, setDirty } from '../../../store';
import { IComponent, Workflow } from '../../../../common';
import { generateId } from '../../../utils';
import { generateId, initComponentData } from '../../../utils';
import { getCore } from '../../../services';
import { WorkspaceComponent } from '../workspace_component';
import { DeletableEdge } from '../workspace_edge';
Expand Down Expand Up @@ -84,11 +84,12 @@ export function Workspace(props: WorkspaceProps) {

// TODO: remove hardcoded values when more component info is passed in the event.
// Only keep the calculated 'position' field.
const id = generateId(nodeData.type);
const newNode = {
id: generateId(nodeData.type),
id,
type: nodeData.type,
position,
data: nodeData,
data: initComponentData(nodeData, id),
style: {
background: 'white',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiCard } from '@elastic/eui';
import { IComponent } from '../../../component_types';
import React, { useContext } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiCard,
EuiText,
EuiTitle,
EuiButtonIcon,
} from '@elastic/eui';
import { rfContext } from '../../../store';
import { IComponentData } from '../../../component_types';
import { InputHandle } from './input_handle';
import { OutputHandle } from './output_handle';

interface WorkspaceComponentProps {
data: IComponent;
data: IComponentData;
}

/**
Expand All @@ -20,18 +28,43 @@ interface WorkspaceComponentProps {
*/
export function WorkspaceComponent(props: WorkspaceComponentProps) {
const component = props.data;
const { deleteNode } = useContext(rfContext);

return (
<EuiCard title={component.label}>
<EuiCard
textAlign="left"
title={
<EuiFlexGroup direction="row" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h3>{component.label}</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="trash"
onClick={() => {
deleteNode(component.id);
}}
aria-label="Delete"
/>
</EuiFlexItem>
</EuiFlexGroup>
}
>
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiText size="s" color="subdued">
{component.description}
</EuiText>
</EuiFlexItem>
{component.inputs?.map((input, index) => {
return (
<EuiFlexItem key={index}>
<InputHandle input={input} data={component} />
</EuiFlexItem>
);
})}
{/* TODO: finalize from UX what we show in the component itself. Readonly fields? Configure in the component JSON definition? */}
{component.outputs?.map((output, index) => {
return (
<EuiFlexItem key={index}>
Expand Down
17 changes: 14 additions & 3 deletions public/store/context/react_flow_context_provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import React, { createContext, useState } from 'react';
import { useDispatch } from 'react-redux';
import { Edge } from 'reactflow';
import { Edge, Node } from 'reactflow';
import { setDirty } from '../reducers';

const initialValues = {
Expand All @@ -30,8 +30,19 @@ export function ReactFlowContextProvider({ children }: any) {
const [reactFlowInstance, setReactFlowInstance] = useState(null);

const deleteNode = (nodeId: string) => {
// TODO: implement node deletion
// reactFlowInstance.setNodes(...)
reactFlowInstance.setNodes(
reactFlowInstance.getNodes().filter((node: Node) => node.id !== nodeId)
);
// Also delete any dangling edges attached to the component
reactFlowInstance.setEdges(
reactFlowInstance
.getEdges()
.filter(
(edge: Edge) => edge.source !== nodeId && edge.target !== nodeId
)
);

dispatch(setDirty());
};

const deleteEdge = (edgeId: string) => {
Expand Down
16 changes: 10 additions & 6 deletions public/store/reducers/workflows_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,30 @@ import {
KnnIndex,
TextEmbeddingProcessor,
generateId,
initComponentData,
} from '../../../common';

// TODO: remove after fetching from server-side
const id1 = generateId('text_embedding_processor');
const id2 = generateId('text_embedding_processor');
const id3 = generateId('knn_index');
const dummyNodes = [
{
id: generateId('text_embedding_processor'),
id: id1,
position: { x: 0, y: 500 },
data: new TextEmbeddingProcessor().toObj(),
data: initComponentData(new TextEmbeddingProcessor().toObj(), id1),
type: 'customComponent',
},
{
id: generateId('text_embedding_processor'),
id: id2,
position: { x: 0, y: 200 },
data: new TextEmbeddingProcessor().toObj(),
data: initComponentData(new TextEmbeddingProcessor().toObj(), id2),
type: 'customComponent',
},
{
id: generateId('knn_index'),
id: id3,
position: { x: 500, y: 500 },
data: new KnnIndex().toObj(),
data: initComponentData(new KnnIndex().toObj(), id3),
type: 'customComponent',
},
] as ReactFlowComponent[];
Expand Down
14 changes: 14 additions & 0 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { IComponent, IComponentData } from '../../common';

// Append 16 random characters
export function generateId(prefix: string) {
const uniqueChar = () => {
Expand All @@ -11,3 +13,15 @@ export function generateId(prefix: string) {
};
return `${prefix}_${uniqueChar()}${uniqueChar()}${uniqueChar()}${uniqueChar()}`;
}

// Adding any instance metadata. Converting the base IComponent obj into
// an instance-specific IComponentData obj.
export function initComponentData(
data: IComponent,
componentId: string
): IComponentData {
return {
...data,
id: componentId,
} as IComponentData;
}
Loading