Canvas: Update layout
Combine separate Overview and Canvas tabs into one Build tab
Add Overview <-> Canvas switcher to Actions
Change-Id: I40f7742be587b475ae6e88af2bcf9cae34f93168
diff --git a/apps/canvas/front/src/components/actions.tsx b/apps/canvas/front/src/components/actions.tsx
index f283ec8..3047fdf 100644
--- a/apps/canvas/front/src/components/actions.tsx
+++ b/apps/canvas/front/src/components/actions.tsx
@@ -26,11 +26,7 @@
}
}
-interface ActionsProps {
- isOverview?: boolean;
-}
-
-export function Actions({ isOverview = false }: ActionsProps) {
+export function Actions() {
const { toast } = useToast();
const store = useStateStore();
const projectId = useProjectId();
@@ -275,6 +271,12 @@
if (store.mode === "deploy") {
return (
<div className="flex flex-row gap-1 items-center">
+ <Button
+ onClick={() => store.setBuildMode(store.buildMode === "overview" ? "canvas" : "overview")}
+ {...reloadProps}
+ >
+ {store.buildMode === "overview" ? "Canvas" : "Overview"}
+ </Button>
<Button onClick={edit} {...reloadProps}>
Edit
</Button>
@@ -323,12 +325,13 @@
return (
<>
<div className="flex flex-row gap-1 items-center">
- {isOverview && (
- <Button onClick={() => setShowResourcesModal(true)}>
- <Plus className="w-4 h-4 mr-1" />
- Add
- </Button>
- )}
+ <Button onClick={() => store.setBuildMode(store.buildMode === "overview" ? "canvas" : "overview")}>
+ {store.buildMode === "overview" ? "Canvas" : "Overview"}
+ </Button>
+ <Button onClick={() => setShowResourcesModal(true)}>
+ <Plus className="w-4 h-4 mr-1" />
+ Add
+ </Button>
<Button onClick={deploy} {...deployProps}>
{deployProps.loading ? (
<>
diff --git a/apps/canvas/front/src/components/canvas.tsx b/apps/canvas/front/src/components/canvas.tsx
deleted file mode 100644
index 2d78abd..0000000
--- a/apps/canvas/front/src/components/canvas.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import "@xyflow/react/dist/style.css";
-import {
- ReactFlow,
- Background,
- Controls,
- Connection,
- BackgroundVariant,
- Edge,
- useReactFlow,
- Panel,
- useStoreApi,
-} from "@xyflow/react";
-import { useStateStore, AppState, useZoom } from "@/lib/state";
-import { useShallow } from "zustand/react/shallow";
-import { useCallback, useEffect, useMemo } from "react";
-import { NodeGatewayHttps } from "@/components/node-gateway-https";
-import { NodeApp } from "@/components/node-app";
-import { NodeVolume } from "./node-volume";
-import { NodePostgreSQL } from "./node-postgresql";
-import { NodeMongoDB } from "./node-mongodb";
-import { NodeGithub } from "./node-github";
-import { Actions } from "./actions";
-import { NodeGatewayTCP } from "./node-gateway-tcp";
-import { NodeNetwork } from "./node-network";
-import { AppNode } from "config";
-
-const selector = (state: AppState) => ({
- nodes: state.nodes,
- edges: state.edges,
- onNodesChange: state.onNodesChange,
- onEdgesChange: state.onEdgesChange,
- onConnect: state.onConnect,
-});
-
-export function Canvas({ className }: { className?: string }) {
- const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useStateStore(useShallow(selector));
- const store = useStateStore();
- const instance = useReactFlow();
- const flow = useStoreApi();
- const viewport = useZoom();
- useEffect(() => {
- store.setViewport({
- width: flow.getState().width,
- height: flow.getState().height,
- transformX: flow.getState().transform[0],
- transformY: flow.getState().transform[1],
- transformZoom: flow.getState().transform[2],
- });
- }, [store, flow]);
- useEffect(() => {
- instance.setViewport(viewport);
- }, [viewport, instance]);
- const nodeTypes = useMemo(
- () => ({
- network: NodeNetwork,
- app: NodeApp,
- "gateway-https": NodeGatewayHttps,
- "gateway-tcp": NodeGatewayTCP,
- volume: NodeVolume,
- postgresql: NodePostgreSQL,
- mongodb: NodeMongoDB,
- github: NodeGithub,
- }),
- [],
- );
- const isValidConnection = useCallback(
- (c: Edge | Connection) => {
- if (c.source === c.target) {
- return false;
- }
- const sn = instance.getNode(c.source)! as AppNode;
- const tn = instance.getNode(c.target)! as AppNode;
-
- if (sn.type === "github") {
- return c.targetHandle === "repository";
- }
- if (sn.type === "app") {
- if (c.sourceHandle === "ports" && (!sn.data.ports || sn.data.ports.length === 0)) {
- return false;
- }
- }
- if (tn.type === "gateway-https") {
- if (c.targetHandle === "https" && tn.data.https !== undefined) {
- return false;
- }
- }
- if (sn.type === "volume") {
- if (c.targetHandle !== "volume") {
- return false;
- }
- return true;
- }
- if (tn.type === "network") {
- if (c.sourceHandle !== "subdomain") {
- return false;
- }
- if (sn.type !== "gateway-https" && sn.type !== "gateway-tcp") {
- return false;
- }
- }
- return true;
- },
- [instance],
- );
- return (
- <div style={{ width: "100%", height: "100%" }} className={className}>
- <ReactFlow
- nodeTypes={nodeTypes}
- nodes={nodes}
- edges={edges}
- onNodesChange={onNodesChange}
- onEdgesChange={onEdgesChange}
- onConnect={onConnect}
- isValidConnection={isValidConnection}
- fitView
- proOptions={{ hideAttribution: true }}
- >
- <Controls showInteractive={false} />
- <Background
- variant={store.mode === "deploy" ? BackgroundVariant.Dots : BackgroundVariant.Lines}
- gap={12}
- size={1}
- />
- <Panel position="top-right">
- <Actions />
- </Panel>
- </ReactFlow>
- </div>
- );
-}
-
-export default Canvas;