diff --git a/common/utils.ts b/common/utils.ts
index 014e29e7..2e5bf2d9 100644
--- a/common/utils.ts
+++ b/common/utils.ts
@@ -15,6 +15,8 @@ import {
TemplateFlows,
WorkflowTemplate,
DATE_FORMAT_PATTERN,
+ COMPONENT_CATEGORY,
+ NODE_CATEGORY,
} from './';
// TODO: implement this and remove hardcoded return values
@@ -42,32 +44,92 @@ export function toTemplateFlows(
export function toWorkspaceFlow(
templateFlows: TemplateFlows
): WorkspaceFlowState {
- const id1 = generateId('text_embedding_processor');
- const id2 = generateId('text_embedding_processor');
- const id3 = generateId('knn_index');
- const dummyNodes = [
+ const ingestId1 = generateId('text_embedding_processor');
+ const ingestId2 = generateId('knn_index');
+ const ingestGroupId = generateId(COMPONENT_CATEGORY.INGEST);
+
+ const searchId1 = generateId('text_embedding_processor');
+ const searchId2 = generateId('knn_index');
+ const searchGroupId = generateId(COMPONENT_CATEGORY.SEARCH);
+
+ const ingestNodes = [
+ {
+ id: ingestGroupId,
+ position: { x: 400, y: 400 },
+ type: NODE_CATEGORY.INGEST_GROUP,
+ data: { label: COMPONENT_CATEGORY.INGEST },
+ style: {
+ width: 900,
+ height: 400,
+ overflowX: 'auto',
+ overflowY: 'auto',
+ },
+ className: 'reactflow__group-node__ingest',
+ selectable: true,
+ },
+ {
+ id: ingestId1,
+ position: { x: 100, y: 70 },
+ data: initComponentData(
+ new TextEmbeddingTransformer().toObj(),
+ ingestId1
+ ),
+ type: NODE_CATEGORY.CUSTOM,
+ parentNode: ingestGroupId,
+ extent: 'parent',
+ draggable: true,
+ },
+ {
+ id: ingestId2,
+ position: { x: 500, y: 70 },
+ data: initComponentData(new KnnIndexer().toObj(), ingestId2),
+ type: NODE_CATEGORY.CUSTOM,
+ parentNode: ingestGroupId,
+ extent: 'parent',
+ draggable: true,
+ },
+ ] as ReactFlowComponent[];
+
+ const searchNodes = [
{
- id: id1,
- position: { x: 0, y: 500 },
- data: initComponentData(new TextEmbeddingTransformer().toObj(), id1),
- type: 'customComponent',
+ id: searchGroupId,
+ position: { x: 400, y: 1000 },
+ type: NODE_CATEGORY.SEARCH_GROUP,
+ data: { label: COMPONENT_CATEGORY.SEARCH },
+ style: {
+ width: 900,
+ height: 400,
+ overflowX: 'auto',
+ overflowY: 'auto',
+ },
+ className: 'reactflow__group-node__search',
+ selectable: true,
},
{
- id: id2,
- position: { x: 0, y: 200 },
- data: initComponentData(new TextEmbeddingTransformer().toObj(), id2),
- type: 'customComponent',
+ id: searchId1,
+ position: { x: 100, y: 70 },
+ data: initComponentData(
+ new TextEmbeddingTransformer().toObj(),
+ searchId1
+ ),
+ type: NODE_CATEGORY.CUSTOM,
+ parentNode: searchGroupId,
+ extent: 'parent',
+ draggable: true,
},
{
- id: id3,
- position: { x: 500, y: 500 },
- data: initComponentData(new KnnIndexer().toObj(), id3),
- type: 'customComponent',
+ id: searchId2,
+ position: { x: 500, y: 70 },
+ data: initComponentData(new KnnIndexer().toObj(), searchId2),
+ type: NODE_CATEGORY.CUSTOM,
+ parentNode: searchGroupId,
+ extent: 'parent',
+ draggable: true,
},
] as ReactFlowComponent[];
return {
- nodes: dummyNodes,
+ nodes: [...ingestNodes, ...searchNodes],
edges: [] as ReactFlowEdge[],
};
}
diff --git a/public/pages/workflow_detail/component_details/component_inputs.tsx b/public/pages/workflow_detail/component_details/component_inputs.tsx
index cae794df..3d9f076e 100644
--- a/public/pages/workflow_detail/component_details/component_inputs.tsx
+++ b/public/pages/workflow_detail/component_details/component_inputs.tsx
@@ -4,9 +4,9 @@
*/
import React from 'react';
-import { EuiSpacer, EuiTitle } from '@elastic/eui';
+import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { InputFieldList } from './input_field_list';
-import { ReactFlowComponent } from '../../../../common';
+import { NODE_CATEGORY, ReactFlowComponent } from '../../../../common';
interface ComponentInputsProps {
selectedComponent: ReactFlowComponent;
@@ -14,16 +14,45 @@ interface ComponentInputsProps {
}
export function ComponentInputs(props: ComponentInputsProps) {
- return (
- <>
-
- {props.selectedComponent.data.label || ''}
-
-
-
- >
- );
+ // Have custom layouts for parent/group flows
+ if (props.selectedComponent.type === NODE_CATEGORY.INGEST_GROUP) {
+ return (
+ <>
+
+ Ingest flow
+
+
+
+ Configure a flow to transform your data as it is ingested into
+ OpenSearch.
+
+ >
+ );
+ } else if (props.selectedComponent.type === NODE_CATEGORY.SEARCH_GROUP) {
+ return (
+ <>
+
+ Search flow
+
+
+
+ Configure a flow to transform input when searching against your
+ OpenSearch cluster.
+
+ >
+ );
+ } else {
+ return (
+ <>
+
+ {props.selectedComponent.data.label || ''}
+
+
+
+ >
+ );
+ }
}
diff --git a/public/pages/workflow_detail/components/header.tsx b/public/pages/workflow_detail/components/header.tsx
index 94b63195..c3c2e967 100644
--- a/public/pages/workflow_detail/components/header.tsx
+++ b/public/pages/workflow_detail/components/header.tsx
@@ -3,12 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useContext } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import React from 'react';
import { EuiPageHeader, EuiButton, EuiLoadingSpinner } from '@elastic/eui';
import { DEFAULT_NEW_WORKFLOW_NAME, Workflow } from '../../../../common';
-import { saveWorkflow } from '../utils';
-import { rfContext, AppState, removeDirty } from '../../../store';
interface WorkflowDetailHeaderProps {
tabs: any[];
@@ -17,10 +14,6 @@ interface WorkflowDetailHeaderProps {
}
export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
- const dispatch = useDispatch();
- const { reactFlowInstance } = useContext(rfContext);
- const isDirty = useSelector((state: AppState) => state.workspace.isDirty);
-
return (
state.workflows
);
- const { isDirty } = useSelector((state: AppState) => state.workspace);
// selected workflow state
const workflowId = props.match?.params?.workflowId;
diff --git a/public/pages/workflow_detail/workspace/reactflow-styles.scss b/public/pages/workflow_detail/workspace/reactflow-styles.scss
index 1bd9b1aa..900bdc45 100644
--- a/public/pages/workflow_detail/workspace/reactflow-styles.scss
+++ b/public/pages/workflow_detail/workspace/reactflow-styles.scss
@@ -19,12 +19,28 @@ $handle-color-invalid: $euiColorDanger;
.reactflow-workspace .react-flow__node {
width: 300px;
+ height: 250px;
+}
+
+.reactflow__group-node {
+ width: 1200px;
+ height: 700px;
+ overflow-x: auto;
+ overflow-y: auto;
+ border: 'none';
+
+ &__ingest {
+ background: rgba($euiColorVis0, 0.3);
+ }
+ &__search {
+ background: rgba($euiColorVis1, 0.3);
+ }
}
// Overriding the styling for the reactflow node when it is selected.
// We need to use important tag to override ReactFlow's wrapNode that sets the box-shadow.
// Ref: https://github.com/wbkd/react-flow/blob/main/packages/core/src/components/Nodes/wrapNode.tsx#L187
-.reactflow-workspace .react-flow__node-customComponent.selected {
+.reactflow-workspace .react-flow__node-custom.selected {
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.5);
border-radius: 5px;
&:focus {
diff --git a/public/pages/workflow_detail/workspace/resizable_workspace.tsx b/public/pages/workflow_detail/workspace/resizable_workspace.tsx
index b827fdc9..11f93956 100644
--- a/public/pages/workflow_detail/workspace/resizable_workspace.tsx
+++ b/public/pages/workflow_detail/workspace/resizable_workspace.tsx
@@ -3,15 +3,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useRef, useState, useEffect, useContext } from 'react';
+import React, { useRef, useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { useOnSelectionChange } from 'reactflow';
+import { ReactFlowProvider, useReactFlow } from 'reactflow';
import { Form, Formik } from 'formik';
import * as yup from 'yup';
import { cloneDeep } from 'lodash';
import {
EuiButton,
EuiCallOut,
+ EuiFlexGroup,
+ EuiFlexItem,
EuiPageHeader,
EuiResizableContainer,
} from '@elastic/eui';
@@ -28,11 +30,14 @@ import {
WorkspaceFlowState,
toTemplateFlows,
} from '../../../../common';
-import { AppState, removeDirty, setDirty, rfContext } from '../../../store';
+import { AppState, removeDirty, setDirty } from '../../../store';
import { Workspace } from './workspace';
import { ComponentDetails } from '../component_details';
import { processNodes, saveWorkflow } from '../utils';
+// styling
+import './workspace-styles.scss';
+
interface ResizableWorkspaceProps {
isNewWorkflow: boolean;
workflow?: Workflow;
@@ -77,29 +82,30 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
};
// Selected component state
- const { reactFlowInstance } = useContext(rfContext);
+ const reactFlowInstance = useReactFlow();
const [selectedComponent, setSelectedComponent] = useState<
ReactFlowComponent
>();
/**
- * Hook provided by reactflow to listen on when nodes are selected / de-selected.
+ * Custom listener on when nodes are selected / de-selected. Passed to
+ * downstream ReactFlow components you can listen using
+ * the out-of-the-box useOnSelectionChange hook.
* - populate panel content appropriately
* - open the panel if a node is selected and the panel is closed
* - it is assumed that only one node can be selected at once
*/
- useOnSelectionChange({
- onChange: ({ nodes, edges }) => {
- if (nodes && nodes.length > 0) {
- setSelectedComponent(nodes[0]);
- if (!isDetailsPanelOpen) {
- onToggleChange();
- }
- } else {
- setSelectedComponent(undefined);
+ // TODO: make more typesafe
+ function onSelectionChange({ nodes, edges }) {
+ if (nodes && nodes.length > 0) {
+ setSelectedComponent(nodes[0]);
+ if (!isDetailsPanelOpen) {
+ onToggleChange();
}
- },
- });
+ } else {
+ setSelectedComponent(undefined);
+ }
+ }
// Hook to update the workflow's flow state, if applicable. It may not exist if
// it is a backend-only-created workflow, or a new, unsaved workflow. If so,
@@ -290,13 +296,26 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
minSize="50%"
paddingSize="s"
>
-
+
+
+
+
+
+
+
void;
+ id: string;
+ // TODO: make more typesafe
+ onSelectionChange: ({ nodes, edges }) => void;
}
-const nodeTypes = { customComponent: WorkspaceComponent };
+const nodeTypes = {
+ custom: WorkspaceComponent,
+ ingestGroup: IngestGroupComponent,
+ searchGroup: SearchGroupComponent,
+};
const edgeTypes = { customEdge: DeletableEdge };
export function Workspace(props: WorkspaceProps) {
const dispatch = useDispatch();
- const reactFlowWrapper = useRef(null);
- const { reactFlowInstance, setReactFlowInstance } = useContext(rfContext);
+ // ReactFlow state
+ const reactFlowWrapper = useRef(null);
+ const reactFlowInstance = useReactFlow();
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
@@ -56,6 +68,14 @@ export function Workspace(props: WorkspaceProps) {
props.onNodesChange(nodes);
}, [nodesLength]);
+ /**
+ * Hook provided by reactflow to listen on when nodes are selected / de-selected.
+ * Trigger the callback fn to propagate changes to parent components.
+ */
+ useOnSelectionChange({
+ onChange: props.onSelectionChange,
+ });
+
const onConnect = useCallback(
(params) => {
const edge = {
@@ -68,53 +88,6 @@ export function Workspace(props: WorkspaceProps) {
[setEdges]
);
- const onDragOver = useCallback((event) => {
- event.preventDefault();
- event.dataTransfer.dropEffect = 'move';
- }, []);
-
- const onDrop = useCallback(
- (event) => {
- event.preventDefault();
- // Get the node info from the event metadata
- const nodeData = event.dataTransfer.getData(
- 'application/reactflow'
- ) as IComponent;
-
- // check if the dropped element is valid
- if (typeof nodeData === 'undefined' || !nodeData) {
- return;
- }
-
- // Fetch bounds based on the ref'd div component, adjust as needed.
- // TODO: remove hardcoded bounds and fetch from a constant somewhere
- // @ts-ignore
- const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
- // @ts-ignore
- const position = reactFlowInstance.project({
- x: event.clientX - reactFlowBounds.left - 80,
- y: event.clientY - reactFlowBounds.top - 90,
- });
-
- // 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,
- type: nodeData.type,
- position,
- data: initComponentData(nodeData, id),
- style: {
- background: 'white',
- },
- };
-
- setNodes((nds) => nds.concat(newNode));
- dispatch(setDirty());
- },
- [reactFlowInstance]
- );
-
// Initialization. Set the nodes and edges to an existing workflow state,
useEffect(() => {
const workflow = { ...props.workflow };
@@ -129,7 +102,6 @@ export function Workspace(props: WorkspaceProps) {
direction="column"
gutterSize="none"
justifyContent="spaceBetween"
- className="workspace-panel"
>
{/**
@@ -139,6 +111,7 @@ export function Workspace(props: WorkspaceProps) {
-
+
+
+
+ {props.data.label}
+
+
+
+
+ );
+}
diff --git a/public/pages/workflow_detail/workspace_component/index.ts b/public/pages/workflow_detail/workspace/workspace_components/index.ts
similarity index 52%
rename from public/pages/workflow_detail/workspace_component/index.ts
rename to public/pages/workflow_detail/workspace/workspace_components/index.ts
index 6bd992e7..938fb16a 100644
--- a/public/pages/workflow_detail/workspace_component/index.ts
+++ b/public/pages/workflow_detail/workspace/workspace_components/index.ts
@@ -4,3 +4,5 @@
*/
export { WorkspaceComponent } from './workspace_component';
+export { IngestGroupComponent } from './ingest_group_component';
+export { SearchGroupComponent } from './search_group_component';
diff --git a/public/pages/workflow_detail/workspace/workspace_components/ingest_group_component.tsx b/public/pages/workflow_detail/workspace/workspace_components/ingest_group_component.tsx
new file mode 100644
index 00000000..a7ddea12
--- /dev/null
+++ b/public/pages/workflow_detail/workspace/workspace_components/ingest_group_component.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { GroupComponent } from './group_component';
+
+interface IngestGroupComponentProps {
+ data: { label: string };
+}
+
+/**
+ * A lightweight wrapper on the group component.
+ * Any specific additions to ingest can be specified here.
+ */
+export function IngestGroupComponent(props: IngestGroupComponentProps) {
+ return ;
+}
diff --git a/public/pages/workflow_detail/workspace_component/input_handle.tsx b/public/pages/workflow_detail/workspace/workspace_components/input_handle.tsx
similarity index 76%
rename from public/pages/workflow_detail/workspace_component/input_handle.tsx
rename to public/pages/workflow_detail/workspace/workspace_components/input_handle.tsx
index 33407d9e..aac57ffe 100644
--- a/public/pages/workflow_detail/workspace_component/input_handle.tsx
+++ b/public/pages/workflow_detail/workspace/workspace_components/input_handle.tsx
@@ -3,12 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState, useRef, useEffect, useContext } from 'react';
-import { Connection, Handle, Position } from 'reactflow';
+import React, { useState, useRef, useEffect } from 'react';
+import { Connection, Handle, Position, useReactFlow } from 'reactflow';
import { EuiText } from '@elastic/eui';
-import { IComponent, IComponentInput } from '../../../component_types';
+import { IComponent, IComponentInput } from '../../../../component_types';
import { calculateHandlePosition, isValidConnection } from './utils';
-import { rfContext } from '../../../store';
interface InputHandleProps {
data: IComponent;
@@ -17,7 +16,7 @@ interface InputHandleProps {
export function InputHandle(props: InputHandleProps) {
const ref = useRef(null);
- const { reactFlowInstance } = useContext(rfContext);
+ const reactFlowInstance = useReactFlow();
const [position, setPosition] = useState(0);
useEffect(() => {
diff --git a/public/pages/workflow_detail/workspace_component/new_or_existing_tabs.tsx b/public/pages/workflow_detail/workspace/workspace_components/new_or_existing_tabs.tsx
similarity index 100%
rename from public/pages/workflow_detail/workspace_component/new_or_existing_tabs.tsx
rename to public/pages/workflow_detail/workspace/workspace_components/new_or_existing_tabs.tsx
diff --git a/public/pages/workflow_detail/workspace_component/output_handle.tsx b/public/pages/workflow_detail/workspace/workspace_components/output_handle.tsx
similarity index 77%
rename from public/pages/workflow_detail/workspace_component/output_handle.tsx
rename to public/pages/workflow_detail/workspace/workspace_components/output_handle.tsx
index b19d2d61..c8276b60 100644
--- a/public/pages/workflow_detail/workspace_component/output_handle.tsx
+++ b/public/pages/workflow_detail/workspace/workspace_components/output_handle.tsx
@@ -3,12 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState, useRef, useEffect, useContext } from 'react';
-import { Connection, Handle, Position } from 'reactflow';
+import React, { useState, useRef, useEffect } from 'react';
+import { Connection, Handle, Position, useReactFlow } from 'reactflow';
import { EuiText } from '@elastic/eui';
-import { IComponent, IComponentOutput } from '../../../component_types';
+import { IComponent, IComponentOutput } from '../../../../component_types';
import { calculateHandlePosition, isValidConnection } from './utils';
-import { rfContext } from '../../../store';
interface OutputHandleProps {
data: IComponent;
@@ -17,7 +16,7 @@ interface OutputHandleProps {
export function OutputHandle(props: OutputHandleProps) {
const ref = useRef(null);
- const { reactFlowInstance } = useContext(rfContext);
+ const reactFlowInstance = useReactFlow();
const [position, setPosition] = useState(0);
const outputClasses = props.output.baseClasses.join('|');
diff --git a/public/pages/workflow_detail/workspace/workspace_components/search_group_component.tsx b/public/pages/workflow_detail/workspace/workspace_components/search_group_component.tsx
new file mode 100644
index 00000000..925a845e
--- /dev/null
+++ b/public/pages/workflow_detail/workspace/workspace_components/search_group_component.tsx
@@ -0,0 +1,19 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { GroupComponent } from './group_component';
+
+interface SearchGroupComponentProps {
+ data: { label: string };
+}
+
+/**
+ * A lightweight wrapper on the group component.
+ * Any specific additions to search can be specified here.
+ */
+export function SearchGroupComponent(props: SearchGroupComponentProps) {
+ return ;
+}
diff --git a/public/pages/workflow_detail/workspace_component/utils.ts b/public/pages/workflow_detail/workspace/workspace_components/utils.ts
similarity index 97%
rename from public/pages/workflow_detail/workspace_component/utils.ts
rename to public/pages/workflow_detail/workspace/workspace_components/utils.ts
index fc424e8b..9f8d1715 100644
--- a/public/pages/workflow_detail/workspace_component/utils.ts
+++ b/public/pages/workflow_detail/workspace/workspace_components/utils.ts
@@ -4,7 +4,7 @@
*/
import { Connection, ReactFlowInstance } from 'reactflow';
-import { IComponentInput } from '../../../../common';
+import { IComponentInput } from '../../../../../common';
/**
* Collection of utility functions for the workspace component
diff --git a/public/pages/workflow_detail/workspace_component/workspace_component.tsx b/public/pages/workflow_detail/workspace/workspace_components/workspace_component.tsx
similarity index 66%
rename from public/pages/workflow_detail/workspace_component/workspace_component.tsx
rename to public/pages/workflow_detail/workspace/workspace_components/workspace_component.tsx
index 20d4953c..6822592c 100644
--- a/public/pages/workflow_detail/workspace_component/workspace_component.tsx
+++ b/public/pages/workflow_detail/workspace/workspace_components/workspace_component.tsx
@@ -3,19 +3,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useContext } from 'react';
+import React from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiCard,
EuiText,
EuiTitle,
- EuiButtonIcon,
} from '@elastic/eui';
-import { rfContext } from '../../../store';
-import { IComponentData } from '../../../component_types';
+import { setDirty } from '../../../../store';
+import { IComponentData } from '../../../../component_types';
import { InputHandle } from './input_handle';
import { OutputHandle } from './output_handle';
+import { Edge, useReactFlow } from 'reactflow';
+import { useDispatch } from 'react-redux';
+
+// styling
+import '../../workspace/reactflow-styles.scss';
interface WorkspaceComponentProps {
data: IComponentData;
@@ -27,11 +31,29 @@ interface WorkspaceComponentProps {
* As users interact with it (input data, add connections), the stored IComponent data will update.
*/
export function WorkspaceComponent(props: WorkspaceComponentProps) {
+ const dispatch = useDispatch();
const component = props.data;
- const { deleteNode } = useContext(rfContext);
+ const reactFlowInstance = useReactFlow();
+
+ // TODO: re-enable deletion
+ const deleteNode = (nodeId: string) => {
+ 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());
+ };
return (
@@ -41,13 +63,16 @@ export function WorkspaceComponent(props: WorkspaceComponentProps) {
- {
deleteNode(component.id);
}}
aria-label="Delete"
- />
+ /> */}
}
diff --git a/public/pages/workflow_detail/workspace_edge/deletable-edge-styles.scss b/public/pages/workflow_detail/workspace/workspace_edge/deletable-edge-styles.scss
similarity index 100%
rename from public/pages/workflow_detail/workspace_edge/deletable-edge-styles.scss
rename to public/pages/workflow_detail/workspace/workspace_edge/deletable-edge-styles.scss
diff --git a/public/pages/workflow_detail/workspace_edge/deletable_edge.tsx b/public/pages/workflow_detail/workspace/workspace_edge/deletable_edge.tsx
similarity index 82%
rename from public/pages/workflow_detail/workspace_edge/deletable_edge.tsx
rename to public/pages/workflow_detail/workspace/workspace_edge/deletable_edge.tsx
index 692afddd..81ca6063 100644
--- a/public/pages/workflow_detail/workspace_edge/deletable_edge.tsx
+++ b/public/pages/workflow_detail/workspace/workspace_edge/deletable_edge.tsx
@@ -3,17 +3,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useContext } from 'react';
+import React from 'react';
import {
BaseEdge,
+ Edge,
EdgeLabelRenderer,
EdgeProps,
getBezierPath,
+ useReactFlow,
} from 'reactflow';
-import { rfContext } from '../../../store';
+import { setDirty } from '../../../../store';
// styling
import './deletable-edge-styles.scss';
+import { useDispatch } from 'react-redux';
type DeletableEdgeProps = EdgeProps;
@@ -23,6 +26,8 @@ type DeletableEdgeProps = EdgeProps;
* see https://reactflow.dev/docs/examples/edges/edge-types/
*/
export function DeletableEdge(props: DeletableEdgeProps) {
+ const dispatch = useDispatch();
+
const [edgePath, labelX, labelY] = getBezierPath({
sourceX: props.sourceX,
sourceY: props.sourceY,
@@ -32,7 +37,14 @@ export function DeletableEdge(props: DeletableEdgeProps) {
targetPosition: props.targetPosition,
});
- const { deleteEdge } = useContext(rfContext);
+ const reactFlowInstance = useReactFlow();
+
+ const deleteEdge = (edgeId: string) => {
+ reactFlowInstance.setEdges(
+ reactFlowInstance.getEdges().filter((edge: Edge) => edge.id !== edgeId)
+ );
+ dispatch(setDirty());
+ };
const onEdgeClick = (event: any, edgeId: string) => {
// Prevent this event from bubbling up and putting reactflow into an unexpected state.
@@ -52,6 +64,7 @@ export function DeletableEdge(props: DeletableEdgeProps) {
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
fontSize: 12,
pointerEvents: 'all',
+ zIndex: 1,
}}
className="nodrag nopan"
>
diff --git a/public/pages/workflow_detail/workspace_edge/index.ts b/public/pages/workflow_detail/workspace/workspace_edge/index.ts
similarity index 100%
rename from public/pages/workflow_detail/workspace_edge/index.ts
rename to public/pages/workflow_detail/workspace/workspace_edge/index.ts
diff --git a/public/render_app.tsx b/public/render_app.tsx
index e1618fb8..279d4325 100644
--- a/public/render_app.tsx
+++ b/public/render_app.tsx
@@ -9,7 +9,7 @@ import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import { AppMountParameters, CoreStart } from '../../../src/core/public';
import { FlowFrameworkDashboardsApp } from './app';
-import { store, ReactFlowContextProvider } from './store';
+import { store } from './store';
export const renderApp = (
coreStart: CoreStart,
@@ -17,13 +17,9 @@ export const renderApp = (
) => {
ReactDOM.render(
-
-
- }
- />
-
-
+
+ } />
+
,
element
);
diff --git a/public/store/context/index.ts b/public/store/context/index.ts
deleted file mode 100644
index 1584425e..00000000
--- a/public/store/context/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-export * from './react_flow_context_provider';
diff --git a/public/store/context/react_flow_context_provider.tsx b/public/store/context/react_flow_context_provider.tsx
deleted file mode 100644
index 1b10eade..00000000
--- a/public/store/context/react_flow_context_provider.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React, { createContext, useState } from 'react';
-import { useDispatch } from 'react-redux';
-import { Edge, Node } from 'reactflow';
-import { setDirty } from '../reducers';
-
-const initialValues = {
- reactFlowInstance: null,
- setReactFlowInstance: () => {},
- deleteNode: (nodeId: string) => {},
- deleteEdge: (edgeId: string) => {},
-};
-
-export const rfContext = createContext(initialValues);
-
-/**
- * This returns a provider from the rfContext context created above. The initial
- * values are set so any nested components can use useContext to access these
- * values.
- *
- * This is how we can manage ReactFlow context consistently across the various
- * nested child components.
- */
-export function ReactFlowContextProvider({ children }: any) {
- const dispatch = useDispatch();
- const [reactFlowInstance, setReactFlowInstance] = useState(null);
-
- const deleteNode = (nodeId: string) => {
- 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) => {
- reactFlowInstance.setEdges(
- reactFlowInstance.getEdges().filter((edge: Edge) => edge.id !== edgeId)
- );
- dispatch(setDirty());
- };
-
- return (
-
- {children}
-
- );
-}
diff --git a/public/store/index.ts b/public/store/index.ts
index 45aab826..ccc2465d 100644
--- a/public/store/index.ts
+++ b/public/store/index.ts
@@ -5,4 +5,3 @@
export * from './store';
export * from './reducers';
-export * from './context';
diff --git a/public/utils/constants.ts b/public/utils/constants.ts
index eb078018..e7f9f800 100644
--- a/public/utils/constants.ts
+++ b/public/utils/constants.ts
@@ -32,6 +32,12 @@ export enum COMPONENT_CATEGORY {
SEARCH = 'Search',
}
+export enum NODE_CATEGORY {
+ CUSTOM = 'custom',
+ INGEST_GROUP = 'ingestGroup',
+ SEARCH_GROUP = 'searchGroup',
+}
+
// TODO: subject to change
/**
* A base set of component classes / types.