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

SF-01 Create Subflows #27

Merged
merged 11 commits into from
May 23, 2024
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,8 @@ The backlog is organized by epic, with each task having a unique ID, description

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

### Progress Tracking

Expand Down
132 changes: 126 additions & 6 deletions packages/flow-client/src/app/components/builder/tab-manager.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { v4 as uuidv4 } from 'uuid';
Expand All @@ -9,17 +9,20 @@ import {
selectActiveFlow,
selectNewFlowCounter,
selectOpenFlows,
selectTheme,
} from '../../redux/modules/builder/builder.slice';
import {
flowActions,
selectFlowEntities,
} from '../../redux/modules/flow/flow.slice';
import { FlowCanvas } from '../flow-canvas/flow-canvas';
import { Theme } from '../../themes';

const StyledTabManager = styled.div`
const StyledTabManager = styled.div<{ dropdownX: number; customTheme: Theme }>`
flex-grow: 1;
display: flex;
flex-direction: column;
position: relative;
text-wrap: nowrap;

.tab-container {
Expand Down Expand Up @@ -60,6 +63,12 @@ const StyledTabManager = styled.div`
text-wrap: nowrap;
transition: background-color 0.3s;

p i {
font-size: 0.7em;
margin-right: 0.5em;
vertical-align: middle;
}

.close-btn {
margin-left: 10px;
color: var(--color-danger);
Expand Down Expand Up @@ -119,6 +128,56 @@ const StyledTabManager = styled.div`
background-color: var(--color-background-element-medium);
}
}

.new-flow-dropdown {
position: absolute;
background-color: var(--color-background-element-light);
border: 1px solid var(--color-border-light);
border-radius: 0px;
box-shadow: 0 2px 5px
rgba(
0,
0,
0,
${props =>
({
dark: 1,
light: 0.2,
}[props.customTheme])}
);
z-index: 100;
top: calc(
var(--builder-tab-container-height) - 2px
); // Position below the button
left: ${props => props.dropdownX}px;
width: auto;
min-width: 150px;

p {
border-bottom: 1px solid var(--color-border-light);
margin: 0;
padding: 4px 10px;
font-weight: bold;
color: var(--color-text-sharp);
}

ul {
list-style: none;
padding: 0;
margin: 0;

li {
padding: 4px 10px;
cursor: pointer;
color: var(--color-text-sharp);
transition: background-color 0.3s;

&:hover {
background-color: var(--color-background-element-medium);
}
}
}
}
`;

export const TabManager = () => {
Expand All @@ -127,8 +186,12 @@ export const TabManager = () => {
const openFlows = useAppSelector(selectOpenFlows);
const activeFlow = useAppSelector(selectActiveFlow);
const flowCounter = useAppSelector(selectNewFlowCounter);
const theme = useAppSelector(selectTheme);

const tabContentRef = useRef<HTMLDivElement>(null);
// State to toggle dropdown visibility
const [showDropdown, setShowDropdown] = useState(false);
const [dropdownX, setDropdownX] = useState(0);

const switchTab = useCallback(
(tabId: string) => {
Expand All @@ -144,7 +207,21 @@ export const TabManager = () => {
[dispatch]
);

const createNewTab = useCallback(() => {
const handleNewTabClick = useCallback(
(e: React.MouseEvent) => {
// Adjust dropdown position to align with the button's horizontal position
setDropdownX(
e.currentTarget.getBoundingClientRect().left -
(document
.querySelector('.tab-manager')
?.getBoundingClientRect().left ?? 0)
);
setShowDropdown(!showDropdown);
},
[showDropdown]
);

const createNewFlow = useCallback(() => {
const flowId = uuidv4();
dispatch(
flowActions.addFlowEntity({
Expand All @@ -158,6 +235,25 @@ export const TabManager = () => {
);
dispatch(builderActions.addNewFlow(flowId));
dispatch(builderActions.setActiveFlow(flowId));
setShowDropdown(false); // Hide dropdown after selection
}, [dispatch, flowCounter]);

const createNewSubflow = useCallback(() => {
const subflowId = uuidv4();
dispatch(
flowActions.addFlowEntity({
id: subflowId,
type: 'subflow',
name: `New Subflow${flowCounter ? ` ${flowCounter}` : ''}`,
category: 'subflows',
color: '#ddaa99',
info: '',
env: [],
})
);
dispatch(builderActions.addNewFlow(subflowId));
dispatch(builderActions.setActiveFlow(subflowId));
setShowDropdown(false); // Hide dropdown after selection
}, [dispatch, flowCounter]);

useEffect(() => {
Expand All @@ -174,7 +270,11 @@ export const TabManager = () => {
}, []);

return (
<StyledTabManager>
<StyledTabManager
dropdownX={dropdownX}
className="tab-manager"
customTheme={theme}
>
<div className="tab-container">
<div
className="tab-content"
Expand All @@ -196,7 +296,16 @@ export const TabManager = () => {
}`}
onClick={() => switchTab(flowEntity.id)}
>
<p>{flowEntity.name}</p>
<p>
{flowEntity.type === 'flow' ? (
<i className="fas fa-map"></i>
) : (
<i className="fas fa-sitemap"></i>
)}

{flowEntity.name}
</p>

<span
className="close-btn"
onClick={e => {
Expand All @@ -211,11 +320,22 @@ export const TabManager = () => {
</div>
</div>

<button className="new-tab" onClick={createNewTab}>
<button className="new-tab" onClick={handleNewTabClick}>
<i className="fa fa-plus"></i>
</button>
</div>

{showDropdown && (
<div className="new-flow-dropdown">
<p>New...</p>

<ul>
<li onClick={createNewFlow}>New Flow</li>
<li onClick={createNewSubflow}>New Subflow</li>
</ul>
</div>
)}

<FlowCanvas key={activeFlow} flowId={activeFlow ?? undefined} />
</StyledTabManager>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export const FlowCanvas: React.FC<FlowCanvasProps> = ({ flowId }) => {
);

const node = new CustomNodeModel({
name: entity.type,
name: entity.name,
color: entity.color,
extras: {
entity,
Expand Down
47 changes: 45 additions & 2 deletions packages/flow-client/src/app/components/flow-tree/flow-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '../../redux/modules/builder/builder.slice';
import { flowActions } from '../../redux/modules/flow/flow.slice';
import { TreeItemData } from '../../redux/modules/flow/tree.logic';
import { Tooltip } from '../shared/tooltip';
import { TreeItem } from './tree-item';

const StyledFlowTree = styled.div`
Expand All @@ -31,6 +32,7 @@ const StyledFlowTree = styled.div`
button {
background-color: inherit;
color: inherit;
cursor: pointer;
border: 0;
outline: 0;
}
Expand Down Expand Up @@ -103,6 +105,25 @@ export const FlowTree = () => {
dispatch(builderActions.setActiveFlow(flowId));
}, [dispatch, flowCounter, getSelectedDirectory]);

const handleNewSubflow = useCallback(() => {
const subflowId = uuidv4();
dispatch(
flowActions.addFlowEntity({
id: subflowId,
type: 'subflow',
name: `New Subflow${flowCounter ? ` ${flowCounter}` : ''}`,
category: 'subflows',
color: '#ddaa99',
info: '',
env: [],
directory: getSelectedDirectory(),
})
);
dispatch(builderActions.addNewFlow(subflowId));
dispatch(builderActions.setActiveFlow(subflowId));
setSelectedItemId(subflowId);
}, [dispatch, flowCounter, getSelectedDirectory]);

const handleItemSelect = useCallback((item: TreeItemData) => {
setSelectedItemId(item.id);
}, []);
Expand Down Expand Up @@ -136,12 +157,32 @@ export const FlowTree = () => {
return (
<StyledFlowTree className="flow-tree">
<div className="actions">
<button className="new-folder" onClick={handleNewFolder}>
<button
className="new-folder"
onClick={handleNewFolder}
data-tooltip-content="New Folder"
data-tooltip-id="action-tooltip"
>
<i className="fas fa-folder-plus"></i>
</button>
<button className="new-flow" onClick={handleNewFlow}>

<button
className="new-flow"
onClick={handleNewFlow}
data-tooltip-content="New Flow"
data-tooltip-id="action-tooltip"
>
<i className="fas fa-file-circle-plus"></i>
</button>

<button
className="new-subflow"
onClick={handleNewSubflow}
data-tooltip-content="New Subflow"
data-tooltip-id="action-tooltip"
>
<i className="fas fa-calendar-plus"></i>
</button>
</div>

<div
Expand All @@ -160,6 +201,8 @@ export const FlowTree = () => {
/>
))}
</div>

<Tooltip id="action-tooltip" />
</StyledFlowTree>
);
};
Expand Down
Loading