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

FB-05 Node Editor UI - Working w/ function node #14

Merged
merged 19 commits into from
Apr 19, 2024
Merged
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b9ea0a3
Begin FB-05
JoshuaCWebDeveloper Apr 12, 2024
b0ce91f
Merge branch 'master' into fb-05-node-editor-ui
JoshuaCWebDeveloper Apr 12, 2024
91c5156
Move overriden methods first in engine.ts
JoshuaCWebDeveloper Apr 12, 2024
ac3c669
Move node drop calculations from flow-canvas to engine
JoshuaCWebDeveloper Apr 12, 2024
95d0cb1
Make connection between react-diagram and flow.slice state two-way
JoshuaCWebDeveloper Apr 17, 2024
6233e75
Finish implementing two-way state with flow-canvas and apply fixes
JoshuaCWebDeveloper Apr 18, 2024
f9f4447
Install and configure redux-persist for flow slice
JoshuaCWebDeveloper Apr 18, 2024
d66792f
Fix current errors in PR
JoshuaCWebDeveloper Apr 18, 2024
d6df467
Correct redux-persist import
JoshuaCWebDeveloper Apr 18, 2024
2eef906
Mock and copy various modules from the Node-RED client for node editing
JoshuaCWebDeveloper Apr 18, 2024
8581d64
Add dom-iterable ts lib
JoshuaCWebDeveloper Apr 18, 2024
52a3451
Import font-awesome
JoshuaCWebDeveloper Apr 18, 2024
40773c3
Store flow node instance in CustomNodeModel config
JoshuaCWebDeveloper Apr 18, 2024
3d414e4
Move Node-RED logic from modules/node to red/
JoshuaCWebDeveloper Apr 18, 2024
b8ea876
New <NodeEditor /> and builder slice for editing nodes
JoshuaCWebDeveloper Apr 18, 2024
1102b92
Write tests for builder
JoshuaCWebDeveloper Apr 18, 2024
e945b11
Fix existing tests
JoshuaCWebDeveloper Apr 19, 2024
148fa36
Write additional tests for flow.logic
JoshuaCWebDeveloper Apr 19, 2024
78186aa
Write additional tests for node redux module
JoshuaCWebDeveloper Apr 19, 2024
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
Prev Previous commit
Next Next commit
New <NodeEditor /> and builder slice for editing nodes
JoshuaCWebDeveloper committed Apr 18, 2024

Verified

This commit was signed with the committer’s verified signature.
JoshuaCWebDeveloper Joshua Carter
commit b8ea876ff2e4eabdf48c8ced3e374c0e631d905c
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
"react-dom": "18.2.0",
"react-is": "18.2.0",
"react-redux": "^9.1.0",
"react-shadow": "^20.4.0",
"redux-persist": "^6.0.0",
"styled-components": "5.3.6"
},
5 changes: 4 additions & 1 deletion packages/flow-client/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import styled from 'styled-components';

import { FlowCanvas } from './components/flow-canvas/flow-canvas'; // Ensure the path is correct
import NodePalette from './components/node-palette/node-palette'; // Import NodePalette
import { NodeEditor } from './components/node-editor';
import { NodePalette } from './components/node-palette/node-palette'; // Import NodePalette

// StyledApp defines the main application container styles.
// It ensures the flow canvas takes up the full viewport height for better visibility.
@@ -21,6 +22,7 @@ const StyledApp = styled.div`
.builder-container {
display: flex;
flex-direction: row;
position: relative;
height: calc(100% - 60px);

.node-palette {
@@ -44,6 +46,7 @@ export function App() {
<div className="builder-container">
<NodePalette />
<FlowCanvas />
<NodeEditor />
</div>
</StyledApp>
);
Original file line number Diff line number Diff line change
@@ -192,16 +192,39 @@ export const FlowCanvas: React.FC<FlowCanvasProps> = ({

useEffect(() => {
const canvas = document.querySelector('.flow-canvas');
const canvasContainer = document.querySelector(
'.flow-canvas-container'
);

const handleZoom = (event: Event) =>
engine.increaseZoomLevel(event as WheelEvent);
const disableContextMenu = (event: Event) => event.preventDefault();

canvas?.addEventListener('wheel', handleZoom);
canvas?.addEventListener('contextmenu', disableContextMenu);

// Disable key events from outside our canvas
const actionEventBus = engine.getActionEventBus();
const originalFireAction =
actionEventBus.fireAction.bind(actionEventBus);
actionEventBus.fireAction = actionEvent => {
// Check if the event is a key event (keydown or keyup)
if (['keydown', 'keyup'].includes(actionEvent.event.type)) {
// Check if the event target is not our canvas, then exit
if (
!canvasContainer?.contains(actionEvent.event.target as Node)
) {
return;
}
}
// Call the original fireAction method
originalFireAction(actionEvent);
};

return () => {
canvas?.removeEventListener('wheel', handleZoom);
canvas?.removeEventListener('contextmenu', disableContextMenu);
actionEventBus.fireAction = originalFireAction;
};
}, []);

@@ -276,7 +299,12 @@ export const FlowCanvas: React.FC<FlowCanvasProps> = ({
// The CanvasWidget component is used to render the flow canvas within the UI.
// The "canvas-widget" className can be targeted for custom styling.
return (
<div ref={drop} style={{ height: '100%', width: '100%' }}>
<div
className="flow-canvas-container"
ref={drop}
style={{ height: '100%', width: '100%' }}
tabIndex={0}
>
<LogFlowSlice />
<StyledCanvasWidget engine={engine} className="flow-canvas" />
</div>
5 changes: 1 addition & 4 deletions packages/flow-client/src/app/components/flow-canvas/model.ts
Original file line number Diff line number Diff line change
@@ -27,12 +27,9 @@ export class CustomDiagramModel extends DiagramModel {
},
});
});
return ret;
}

// Custom method to add a link and register an event listener
override addLink(link: LinkModel): LinkModel {
const ret = super.addLink(link);
private attachLinkListeners(link: LinkModel) {
// intercept points
const linkAddPoint = link.addPoint.bind(link);
link.addPoint = <P extends PointModel>(point: P) => {
13 changes: 11 additions & 2 deletions packages/flow-client/src/app/components/flow-canvas/node.tsx
Original file line number Diff line number Diff line change
@@ -8,9 +8,12 @@ import {
import React from 'react';
import styled from 'styled-components';

import { CustomEngine } from './engine';
import NodeRedNode from '../node/node-red-node';
import { useAppDispatch } from '../../redux/hooks';
import { builderActions } from '../../redux/modules/builder/builder.slice';
import { NodeEntity } from '../../redux/modules/node/node.slice';
import NodeRedNode from '../node/node-red-node';
import { CustomEngine } from './engine';
import { FlowNodeEntity } from '../../redux/modules/flow/flow.slice';

// Styled components for the node and its elements
const StyledNode = styled.div<{ borderColor?: string }>`
@@ -131,15 +134,21 @@ export type NodeProps = {
};

export const Node: React.FC<NodeProps> = ({ node, engine }) => {
const dispatch = useAppDispatch();

// Convert the ports model to an array for rendering
const ports = Object.values(node.getPorts());

const handleDoubleClick = () => {
dispatch(builderActions.setEditing(node.getID()));
};

const entity = node.entity ?? ({} as NodeEntity);

return (
<StyledNode
className={node.isSelected() ? 'selected' : ''}
onDoubleClick={handleDoubleClick}
>
<NodeRedNode entity={entity} instance={node.config}>
{/* Render ports */}
408 changes: 408 additions & 0 deletions packages/flow-client/src/app/components/node-editor.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

export const BUILDER_FEATURE_KEY = 'builder';

// Define the state interface
export interface BuilderState {
editing: string | null;
}

// Initial state
const initialState: BuilderState = {
editing: null,
};

// Create the slice
export const builderSlice = createSlice({
name: BUILDER_FEATURE_KEY,
initialState,
reducers: {
// Action to set the editing node
setEditing: (state, action: PayloadAction<string | null>) => {
state.editing = action.payload;
},
// Action to clear the editing node
clearEditing: state => {
state.editing = null;
},
},
});

// Export the reducer and actions
export const builderReducer = builderSlice.reducer;
export const builderActions = builderSlice.actions;

// Selectors
export const selectEditing = (state: { [BUILDER_FEATURE_KEY]: BuilderState }) =>
state[BUILDER_FEATURE_KEY].editing;
5 changes: 5 additions & 0 deletions packages/flow-client/src/app/redux/store.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,10 @@ import storage from 'redux-persist/lib/storage';

import { featureApi } from './modules/api/feature.api';
import { nodeApi } from './modules/api/node.api'; // Import the nodeApi
import {
BUILDER_FEATURE_KEY,
builderReducer,
} from './modules/builder/builder.slice';
import {
FEATURE_FEATURE_KEY,
featureReducer,
@@ -39,6 +43,7 @@ export const createStore = () => {
},
flowReducer
),
[BUILDER_FEATURE_KEY]: builderReducer,
},
// Additional middleware can be passed to this array
middleware: getDefaultMiddleware =>