/** * @file Responsive layout example with mobile/desktop switching * Demonstrates dynamic layout switching based on viewport size */ import * as React from "react"; import { NodeEditor, type GridLayoutConfig, type LayerDefinition } from "../../../../index"; import { InspectorPanel } from "../../../../components/inspector/InspectorPanel"; import { NodeCanvas } from "../../../../components/canvas/NodeCanvas"; import { Minimap } from "../../../../components/layers/Minimap"; import { GridToolbox } from "../../../../components/layers/GridToolbox"; import { StandardNodeDefinition } from "../../../../node-definitions/standard"; import { asNodeDefinition } from "../../../../types/NodeDefinition"; import type { NodeEditorData } from "../../../../types/core"; import { getViewportWidth } from "../../../../utils/mobileDetection"; const initialData: NodeEditorData = { nodes: { "node-1": { id: "node-1", type: "standard", position: { x: 100, y: 100 }, size: { width: 180, height: 120 }, data: { title: "Start Node", content: "" }, }, "node-2": { id: "node-2", type: "standard", position: { x: 400, y: 100 }, size: { width: 180, height: 120 }, data: { title: "Process Node", content: "" }, }, "node-3": { id: "node-3", type: "standard", position: { x: 700, y: 100 }, size: { width: 180, height: 120 }, data: { title: "End Node", content: "" }, }, "node-4": { id: "node-4", type: "standard", position: { x: 250, y: 300 }, size: { width: 180, height: 120 }, data: { title: "Branch A", content: "" }, }, "node-5": { id: "node-5", type: "standard", position: { x: 550, y: 300 }, size: { width: 180, height: 120 }, data: { title: "Branch B", content: "" }, }, }, connections: { "conn-1": { id: "conn-1", fromNodeId: "node-1", fromPortId: "output", toNodeId: "node-2", toPortId: "input", }, "conn-2": { id: "conn-2", fromNodeId: "node-2", fromPortId: "output", toNodeId: "node-3", toPortId: "input", }, }, }; /** * Layout mode indicator component */ const LayoutModeIndicator: React.FC<{ mode: "mobile" | "tablet" | "desktop"; width: number; onToggleInspector?: () => void; onToggleMinimap?: () => void; showInspector: boolean; showMinimap: boolean; }> = ({ mode, width, onToggleInspector, onToggleMinimap, showInspector, showMinimap }) => { const modeColors = { mobile: "#ff6b6b", tablet: "#ffa94d", desktop: "#51cf66", }; const modeLabels = { mobile: "📱 Mobile", tablet: "💻 Tablet", desktop: "🖥️ Desktop", }; return (
{modeLabels[mode]}
Width: {width}px
{mode !== "desktop" && onToggleInspector && ( )} {mode !== "mobile" && onToggleMinimap && ( )}
); }; /** * Get layout mode based on viewport width */ const getLayoutMode = (width: number): "mobile" | "tablet" | "desktop" => { if (width < 768) { return "mobile"; } if (width < 1024) { return "tablet"; } return "desktop"; }; /** * Responsive layout example * Dynamically switches between mobile, tablet, and desktop layouts */ export const ResponsiveLayoutExample: React.FC = () => { const [viewportWidth, setViewportWidth] = React.useState(() => getViewportWidth()); const [showInspector, setShowInspector] = React.useState(false); const [showMinimap, setShowMinimap] = React.useState(true); const [layoutKey, setLayoutKey] = React.useState(0); // Update viewport width on resize React.useEffect(() => { const handleResize = () => { setViewportWidth(getViewportWidth()); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); const layoutMode = React.useMemo(() => getLayoutMode(viewportWidth), [viewportWidth]); const handleToggleInspector = React.useCallback(() => { setShowInspector((prev) => !prev); setLayoutKey((prev) => prev + 1); }, []); const handleToggleMinimap = React.useCallback(() => { setShowMinimap((prev) => !prev); }, []); const handleInspectorStateChange = React.useCallback((open: boolean) => { setShowInspector(open); }, []); // Desktop layout: Traditional 3-column grid with resizable inspector const desktopGridConfig = React.useMemo( () => ({ areas: [["canvas", "canvas", "inspector"]], rows: [{ size: "1fr" }], columns: [{ size: "1fr" }, { size: "1fr" }, { size: "320px", resizable: true, minSize: 280, maxSize: 500 }], gap: "0", }), [], ); const desktopLayers = React.useMemo( () => [ { id: "canvas", component: , gridArea: "canvas", zIndex: 0, }, { id: "inspector", component: , gridArea: "inspector", zIndex: 10, }, { id: "minimap", component: showMinimap ? : null, positionMode: "absolute", position: { right: 340, bottom: 20 }, zIndex: 100, width: 200, height: 150, draggable: true, }, { id: "toolbar", component: , positionMode: "fixed", position: { bottom: 20, left: "50%" }, style: { transform: "translateX(-50%)" }, zIndex: 200, }, ], [showMinimap], ); // Tablet layout: Full-width canvas with floating inspector const tabletGridConfig = React.useMemo( () => ({ areas: [["canvas"]], rows: [{ size: "1fr" }], columns: [{ size: "1fr" }], gap: "0", }), [], ); const tabletLayers = React.useMemo( () => [ { id: "canvas", component: , gridArea: "canvas", zIndex: 0, }, { id: "inspector", component: , positionMode: "absolute", position: { right: 20, top: 80 }, width: 300, height: "calc(100% - 100px)", zIndex: 100, draggable: true, pointerEvents: "auto", visible: showInspector, }, { id: "minimap", component: showMinimap ? : null, positionMode: "absolute", position: { left: 20, bottom: 20 }, zIndex: 100, width: 180, height: 120, draggable: true, }, { id: "toolbar", component: , positionMode: "fixed", position: { bottom: 20, left: "50%" }, style: { transform: "translateX(-50%)" }, zIndex: 200, }, ], [showInspector, showMinimap], ); // Mobile layout: Full-width canvas with drawer-based inspector const mobileGridConfig = React.useMemo( () => ({ areas: [["canvas"]], rows: [{ size: "1fr" }], columns: [{ size: "1fr" }], gap: "0", }), [], ); const mobileLayers = React.useMemo( () => [ { id: "canvas", component: , gridArea: "canvas", zIndex: 0, }, { id: "inspector", component: , drawer: { placement: "bottom", defaultOpen: showInspector, dismissible: true, showBackdrop: true, backdropOpacity: 0.4, size: "70%", onStateChange: handleInspectorStateChange, }, }, { id: "toolbar", component: , positionMode: "fixed", position: { bottom: 20, left: "50%" }, style: { transform: "translateX(-50%)" }, zIndex: 200, }, ], [showInspector, handleInspectorStateChange], ); // Select grid config and layers based on layout mode const { gridConfig, gridLayers } = React.useMemo(() => { switch (layoutMode) { case "mobile": return { gridConfig: mobileGridConfig, gridLayers: mobileLayers }; case "tablet": return { gridConfig: tabletGridConfig, gridLayers: tabletLayers }; case "desktop": return { gridConfig: desktopGridConfig, gridLayers: desktopLayers }; } }, [layoutMode, mobileGridConfig, mobileLayers, tabletGridConfig, tabletLayers, desktopGridConfig, desktopLayers]); return (
); };