Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f48e296
Start SF-05
JoshuaCWebDeveloper Jun 5, 2024
fe59e2f
Add Edit Subflow Template button to title of subflow editor
JoshuaCWebDeveloper Jun 11, 2024
6d593b2
Correctly specify type to buttons in forms
JoshuaCWebDeveloper Jun 11, 2024
112d739
Add new nodes to redux state instead of diagram engine in flow-canvas…
JoshuaCWebDeveloper Jun 11, 2024
912dafe
Correctly render changes to draggable nodes in draggable-node-wrapper…
JoshuaCWebDeveloper Jun 11, 2024
8d93454
Write three new updateSubflow(), updateSubflowInputs() and updateSubf…
JoshuaCWebDeveloper Jun 11, 2024
f0b1851
Write NodeLogic.createFlowNode()
JoshuaCWebDeveloper Jun 11, 2024
50df610
Write new editFlow(), editSubflow(), and editFlowEntityById() methods…
JoshuaCWebDeveloper Jun 11, 2024
1d00030
Render custom port labels in flow/node.logic
JoshuaCWebDeveloper Jun 11, 2024
274be16
Create new in ports when inputs increase in flow/node.logic (handle n…
JoshuaCWebDeveloper Jun 11, 2024
02d1bb8
Add input and output controls to subflow <Workspace /> that create an…
JoshuaCWebDeveloper Jun 11, 2024
e365147
Standardize palette node icons
JoshuaCWebDeveloper Jun 11, 2024
c451e39
Write new builder.logic tests
JoshuaCWebDeveloper Jun 11, 2024
3306f83
Write new tests for flow.slice
JoshuaCWebDeveloper Jun 11, 2024
343c702
Add palette node entities to in/out node instances
JoshuaCWebDeveloper Jun 11, 2024
be22edb
Update node.logic tests
JoshuaCWebDeveloper Jun 11, 2024
9069880
Update graph.logic tests
JoshuaCWebDeveloper Jun 11, 2024
7d9d565
Finish SF-05
JoshuaCWebDeveloper Jun 11, 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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,11 @@ The backlog is organized by epic, with each task having a unique ID, description

| To Do | In Progress | In Review | Done |
| ----- | ----------- | --------- | ----- |
| SF-05 | | | SF-04 |
| | | | SF-04 |
| | | | SF-01 |
| | | | SF-02 |
| | | | SF-03 |
| | | | SF-05 |

### Progress Tracking

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ const StyledSecondarySidebar = styled(TabbedSidebar)`
overflow: hidden; // Prevents overflow of child components

.workspace-section {
min-height: 50px;
min-height: 130px;

&:has(.flow) {
min-height: 50px;
}

&.collapsed {
min-height: 0;
Expand Down
230 changes: 181 additions & 49 deletions packages/flow-client/src/app/components/builder/workspace.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import {
EDITING_TYPE,
builderActions,
selectActiveFlow,
} from '../../redux/modules/builder/builder.slice';

import { useAppDispatch, useAppLogic, useAppSelector } from '../../redux/hooks';
import { selectActiveFlow } from '../../redux/modules/builder/builder.slice';
import {
SubflowEntity,
selectFlowEntityById,
} from '../../redux/modules/flow/flow.slice';
import { useCallback } from 'react';

const StyledWorkspace = styled.div`
display: flex;
Expand All @@ -18,25 +15,28 @@ const StyledWorkspace = styled.div`
color: var(--color-text-sharp);
font-size: 0.8em;

p {
.row {
display: flex;
align-items: center;
gap: 5px;

margin: 0;

.type {
label {
font-weight: bold;
text-transform: capitalize;
flex: 0 0 55px;
}
}

.title {
.name {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

i {
margin-left: 5px;
color: var(--color-text-medium);
cursor: pointer;

Expand All @@ -45,61 +45,193 @@ const StyledWorkspace = styled.div`
}
}
}

.control-group {
display: flex;
margin-top: 10px;

button,
input {
background-color: var(--color-background-element-light);
border: 1px solid var(--color-border-sharp);
border-right-style: none;
color: var(--color-text-sharp);
cursor: pointer;
padding: 5px 10px;
width: 30px;
height: 30px;

&:first-child {
border-radius: 2px 0 0 2px;
}

&:last-child {
border-right-style: solid;
border-radius: 0 2px 2px 0;
}

&.active,
&:active {
background-color: var(--color-background-element-medium);
color: var(--color-active-text);
}
}

input:focus {
background-color: var(--color-background-element-focus);
outline: 0;
}
}
`;

export const Workspace = () => {
const dispatch = useAppDispatch();
const builderLogic = useAppLogic().builder;
const flowLogic = useAppLogic().flow;
const activeFlowId = useAppSelector(selectActiveFlow);
const activeFlow = useAppSelector(state =>
activeFlowId ? selectFlowEntityById(state, activeFlowId) : null
);

const inputs = (activeFlow as SubflowEntity)?.in?.length ?? 0;
const outputs = (activeFlow as SubflowEntity)?.out?.length ?? 0;
const [currentOutputs, setCurrentOutputs] = useState(outputs);

const handleEditClick = useCallback(() => {
if (!activeFlowId || !activeFlow) {
if (!activeFlowId) {
return;
}

dispatch(
builderActions.setEditing({
id: activeFlowId,
type: {
flow: EDITING_TYPE.FLOW,
subflow: EDITING_TYPE.SUBFLOW,
}[activeFlow.type],
data: {
info: activeFlow.info,
name: activeFlow.name,
env: activeFlow.env,
...{
flow: {},
subflow: {
color: (activeFlow as SubflowEntity).color,
icon: (activeFlow as SubflowEntity).icon,
category: (activeFlow as SubflowEntity).category,
inputLabels: (activeFlow as SubflowEntity)
.inputLabels,
outputLabels: (activeFlow as SubflowEntity)
.outputLabels,
},
}[activeFlow.type],
},
})
);
}, [dispatch, activeFlowId, activeFlow]);
dispatch(builderLogic.editFlowEntityById(activeFlowId));
}, [dispatch, activeFlowId, builderLogic]);

const handleInputsChange = useCallback(
(inputs: number) => {
if (!activeFlowId || !activeFlow || activeFlow.type !== 'subflow') {
return;
}

dispatch(flowLogic.updateSubflowInputs(activeFlow, inputs));
},
[activeFlow, activeFlowId, dispatch, flowLogic]
);

const handleOutputsChange = useCallback(
(outputs: number) => {
if (!activeFlowId || !activeFlow || activeFlow.type !== 'subflow') {
return;
}

dispatch(flowLogic.updateSubflowOutputs(activeFlow, outputs));
},
[activeFlow, activeFlowId, dispatch, flowLogic]
);

const handleIncrementOutputs = useCallback(() => {
const newOutputs = currentOutputs + 1;
setCurrentOutputs(newOutputs);
handleOutputsChange(newOutputs);
}, [currentOutputs, handleOutputsChange]);

const handleDecrementOutputs = useCallback(() => {
const newOutputs = Math.max(currentOutputs - 1, 0);
setCurrentOutputs(newOutputs);
handleOutputsChange(newOutputs);
}, [currentOutputs, handleOutputsChange]);

const handleCurrentOutputsChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const newOutputs = Number(e.target.value);
setCurrentOutputs(
Math.max(isNaN(newOutputs) ? currentOutputs : newOutputs, 0)
);
},
[currentOutputs]
);

const handleOutputsSubmit = useCallback(
(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
e.currentTarget.querySelector('input')?.blur();

handleOutputsChange(currentOutputs);
},
[currentOutputs, handleOutputsChange]
);

useEffect(() => {
setCurrentOutputs(outputs);
}, [outputs]);

return (
<StyledWorkspace className="workspace">
{activeFlow ? (
<p>
<span className="type">{activeFlow.type}:</span>{' '}
<span className="name">{activeFlow.name} </span>
<i
className="fa-solid fa-pencil"
onClick={handleEditClick}
/>
</p>
<StyledWorkspace className={`workspace ${activeFlow?.type ?? ''}`}>
{!activeFlow ? (
<p className="empty">No active workspace</p>
) : (
<p>No active workspace</p>
<>
<div className="row title">
<label className="type">{activeFlow.type}:</label>{' '}
<span className="name">{activeFlow.name} </span>
<i
className="fa-solid fa-pencil"
onClick={handleEditClick}
/>
</div>

{activeFlow.type === 'subflow' && (
<>
<div className="row inputs">
<label>Inputs: </label>
<span className="control-group">
<button
type="button"
className={inputs === 0 ? 'active' : ''}
onClick={() => handleInputsChange(0)}
>
0
</button>

<button
type="button"
className={inputs === 1 ? 'active' : ''}
onClick={() => handleInputsChange(1)}
>
1
</button>
</span>
</div>

<div className="row outputs">
<label>Outputs: </label>
<form
className="control-group"
onSubmit={handleOutputsSubmit}
onBlur={handleOutputsSubmit}
>
<button
type="button"
onClick={handleDecrementOutputs}
>
-
</button>

<input
type="text"
value={currentOutputs}
onChange={handleCurrentOutputsChange}
/>

<button
type="button"
onClick={handleIncrementOutputs}
>
+
</button>
</form>
</div>
</>
)}
</>
)}
</StyledWorkspace>
);
Expand Down
Loading