Skip to content

Commit cd336ef

Browse files
author
Nico Sammito
authored
Merge pull request #602 from code0-tech/feat/flow-loading-state
Flow loading state
2 parents 10c7ce2 + fc59643 commit cd336ef

4 files changed

Lines changed: 130 additions & 16 deletions

File tree

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@radix-ui/react-one-time-password-field": "^0.1.8",
3232
"@radix-ui/react-radio-group": "^1.3.8",
3333
"@radix-ui/react-scroll-area": "^1.2.10",
34+
"ldrs": "^1.1.9",
3435
"@radix-ui/react-tabs": "^1.1.13",
3536
"@radix-ui/react-toggle-group": "^1.1.11",
3637
"@radix-ui/react-tooltip": "^1.2.8",

src/components/d-flow/DFlow.style.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
.flow {
22
overflow: hidden;
3+
4+
&[data-tree-visibility=false] {
5+
.react-flow__node, .react-flow__edgelabel-renderer, .react-flow__edge {
6+
opacity: 0;
7+
}
8+
}
9+
10+
&[data-tree-visibility=true] {
11+
.react-flow__node, .react-flow__edgelabel-renderer, .react-flow__edge {
12+
opacity: 100%;
13+
}
14+
}
15+
316
.react-flow__node {
417
background: transparent;
518
padding: unset;

src/components/d-flow/DFlow.tsx

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import {
33
Background,
44
BackgroundVariant,
55
Edge,
6-
Node, Panel,
6+
Node,
77
ReactFlow,
88
ReactFlowProvider,
99
useEdgesState,
1010
useNodesState,
11-
useUpdateNodeInternals
11+
useReactFlow,
12+
useUpdateNodeInternals,
13+
ViewportPortal
1214
} from "@xyflow/react";
1315
import React from "react";
1416
import '@xyflow/react/dist/style.css';
@@ -17,16 +19,16 @@ import {DFlowNodeDefaultCard} from "../d-flow-node/DFlowNodeDefaultCard";
1719
import {DFlowNodeGroupCard} from "../d-flow-node/DFlowNodeGroupCard";
1820
import {DFlowNodeTriggerCard} from "../d-flow-node/DFlowNodeTriggerCard";
1921
import {DFlowEdge} from "./DFlowEdge";
20-
import {DFlowPanelSize} from "../d-flow-panel";
22+
import {DFlowPanelControl, DFlowPanelLayout, DFlowPanelSize} from "../d-flow-panel";
2123
import {DFlowValidation} from "../d-flow-validation";
2224
import {Flow, type Namespace, type NamespaceProject} from "@code0-tech/sagittarius-graphql-types";
2325
import {useFlowNodes} from "./DFlow.nodes.hook";
2426
import {useFlowEdges} from "./DFlow.edges.hook";
25-
import {DFlowPanelControl} from "../d-flow-panel";
26-
import {DFlowPanelLayout} from "../d-flow-panel";
2727
import {DFlowPanelUpdate} from "../d-flow-panel/DFlowPanelUpdate";
28-
import {Button} from "../button/Button";
29-
import {ButtonGroup} from "../button-group/ButtonGroup";
28+
import {Text} from "../text/Text";
29+
import {LineWobble} from 'ldrs/react'
30+
import 'ldrs/react/LineWobble.css'
31+
import {Spacing} from "../spacing/Spacing";
3032

3133
/**
3234
* Dynamically layouts a tree of nodes and their parameter nodes for a flow-based editor.
@@ -612,8 +614,8 @@ export const DFlow: React.FC<DFlowProps> = (props) => {
612614
}
613615

614616
const InternalDFlow: React.FC<DFlowProps> = (props) => {
617+
const {flowId, namespaceId, projectId, ...rest} = props
615618

616-
const {flowId, namespaceId, projectId} = props
617619
const nodeTypes = React.useMemo(() => ({
618620
default: DFlowNodeDefaultCard,
619621
group: DFlowNodeGroupCard,
@@ -626,10 +628,16 @@ const InternalDFlow: React.FC<DFlowProps> = (props) => {
626628

627629
const initialNodes = useFlowNodes(flowId, namespaceId, projectId)
628630
const initialEdges = useFlowEdges(flowId, namespaceId, projectId)
631+
629632
const [nodes, setNodes] = useNodesState<Node>([])
630633
const [edges, setEdges, edgeChangeEvent] = useEdgesState<Edge>([])
634+
const [showTree, setShowTree] = React.useState<boolean>(false)
635+
631636
const updateNodeInternals = useUpdateNodeInternals()
632637

638+
const {fitView} = useReactFlow()
639+
const didFitViewRef = React.useRef(false)
640+
633641
const revalidateHandles = React.useCallback((ids: string[]) => {
634642
requestAnimationFrame(() => {
635643
ids.forEach(id => updateNodeInternals(id))
@@ -688,26 +696,110 @@ const InternalDFlow: React.FC<DFlowProps> = (props) => {
688696

689697
}, [initialNodes, initialEdges, revalidateHandles])
690698

699+
React.useEffect(() => {
700+
if (didFitViewRef.current) return
701+
if (nodes.length <= 0) return
702+
703+
didFitViewRef.current = true
704+
705+
requestAnimationFrame(() => {
706+
requestAnimationFrame(() => {
707+
setTimeout(async () => {
708+
await fitView({
709+
padding: "64px",
710+
maxZoom: 1
711+
})
712+
setShowTree(true)
713+
}, 1000)
714+
})
715+
})
716+
}, [nodes, didFitViewRef])
717+
691718
return (
692719
<ReactFlow
693720
onlyRenderVisibleElements
694721
panOnScroll={false}
695722
nodeTypes={nodeTypes}
696723
edgeTypes={edgeTypes}
697-
onInit={(rf) => rf.fitView()}
698724
onNodesChange={nodeChangeEvent}
699725
onEdgesChange={edgeChangeEvent}
700-
{...mergeCode0Props("flow", props)}
726+
{...mergeCode0Props("flow", rest)}
727+
data-tree-visibility={showTree}
701728
proOptions={{hideAttribution: true}}
702729
nodes={nodes}
703730
edges={edges}
731+
panOnDrag={showTree}
732+
zoomOnScroll={showTree}
733+
zoomOnPinch={showTree}
734+
zoomOnDoubleClick={showTree}
704735
>
705-
<Background variant={BackgroundVariant.Dots} color="rgba(255,255,255, .05)" gap={8} size={2}/>
706-
<DFlowPanelSize/>
707-
<DFlowPanelLayout/>
708-
<DFlowValidation flowId={"gid://sagittarius/Flow/1"}/>
709-
<DFlowPanelControl flowId={flowId}/>
710-
<DFlowPanelUpdate flowId={flowId}/>
736+
737+
{!showTree ? (
738+
<ViewportPortal>
739+
<div style={{
740+
left: "50%",
741+
top: "50%",
742+
transform: "translate(-50%, -50%)",
743+
position: 'absolute',
744+
display: "flex",
745+
alignItems: "center",
746+
justifyContent: "center",
747+
flexDirection: "column"
748+
}}>
749+
<LineWobble
750+
size="200"
751+
stroke="5"
752+
bgOpacity="0.5"
753+
speed="2"
754+
color="rgba(255, 255, 255, 0.15)"
755+
/>
756+
<Spacing spacing={"xl"}/>
757+
<LoadingFlowText/>
758+
<Spacing spacing={"xs"}/>
759+
<Text hierarchy={"tertiary"} style={{
760+
margin: "0 20%",
761+
textAlign: "center"
762+
}}>
763+
We are running requests to prepare your flow. This may take a few moments.
764+
</Text>
765+
</div>
766+
</ViewportPortal>
767+
) : null}
768+
{showTree ? (
769+
<>
770+
<Background variant={BackgroundVariant.Dots} color="rgba(255,255,255, .05)" gap={8} size={2}/>
771+
<DFlowPanelSize/>
772+
<DFlowPanelLayout/>
773+
<DFlowValidation flowId={"gid://sagittarius/Flow/1"}/>
774+
<DFlowPanelControl flowId={flowId}/>
775+
<DFlowPanelUpdate flowId={flowId}/>
776+
</>
777+
) : null}
711778
</ReactFlow>
712779
)
780+
}
781+
782+
const LoadingFlowText: React.FC = () => {
783+
784+
const [index, setIndex] = React.useState(0)
785+
786+
const loadingTexts = [
787+
"Preparing flow data",
788+
"Loading node and edge definitions",
789+
"Calculating perfect layout"
790+
]
791+
792+
React.useEffect(() => {
793+
const id = setInterval(() => {
794+
setIndex(i => (i + 1) % loadingTexts.length)
795+
}, 2000)
796+
797+
return () => clearInterval(id)
798+
}, [])
799+
800+
return (
801+
<Text size="md">
802+
{loadingTexts[index]}
803+
</Text>
804+
)
713805
}

0 commit comments

Comments
 (0)