From 7a9ac2b253b0aed40639cc84b17fb1fc5cc6f3fb Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Mon, 30 Aug 2021 15:08:38 +0100 Subject: [PATCH 1/3] Added a graph-only route for viewing on-demand graphs via query string --- src/pages/graph-only.tsx | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/pages/graph-only.tsx diff --git a/src/pages/graph-only.tsx b/src/pages/graph-only.tsx new file mode 100644 index 00000000..00f74d7d --- /dev/null +++ b/src/pages/graph-only.tsx @@ -0,0 +1,78 @@ +import { Box, ChakraProvider } from '@chakra-ui/react'; +import { useInterpret } from '@xstate/react'; +import { useRouter } from 'next/router'; +import { useMemo } from 'react'; +import { createMachine } from 'xstate'; +import { CanvasContainer } from '../CanvasContainer'; +import { CanvasProvider } from '../CanvasContext'; +import { canvasMachine } from '../canvasMachine'; +import { toDirectedGraph } from '../directedGraph'; +import { theme } from '../theme'; +import { Graph } from '../Graph'; +import { SimulationProvider } from '../SimulationContext'; +import { simulationMachine } from '../simulationMachine'; +import { AppHead } from '../AppHead'; +import { isOnClientSide } from '../isOnClientSide'; + +const GraphOnly = () => { + const router = useRouter(); + + const config = JSON.parse((router.query.config as string) || '{}'); + + const digraph = useMemo( + () => + toDirectedGraph( + createMachine({ + after: { + 500: { + actions: 'sayHello', + }, + }, + initial: 'cool', + states: { + cool: {}, + }, + }), + ), + [config], + ); + + const canvasService = useInterpret(canvasMachine); + const simService = useInterpret(simulationMachine); + + return ( + <> + + {isOnClientSide() && ( + + + + + + {digraph && } + + + + + + )} + + ); +}; + +export default GraphOnly; From 3517e3c8989ffcad0fd32d0a8b9d63ec1d185a7a Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Mon, 30 Aug 2021 15:10:02 +0100 Subject: [PATCH 2/3] Added dummy machine --- src/pages/graph-only.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/graph-only.tsx b/src/pages/graph-only.tsx index 00f74d7d..ee3259b4 100644 --- a/src/pages/graph-only.tsx +++ b/src/pages/graph-only.tsx @@ -22,6 +22,7 @@ const GraphOnly = () => { const digraph = useMemo( () => toDirectedGraph( + // Dummy machine to test createMachine({ after: { 500: { From 560641c8b615adda8c0f9d5da864a2611f087ba2 Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Tue, 31 Aug 2021 09:17:48 +0100 Subject: [PATCH 3/3] Made it work --- src/CanvasContext.tsx | 7 +- src/pages/graph-only.tsx | 167 +++++++++++++++++++++++++++++---------- 2 files changed, 129 insertions(+), 45 deletions(-) diff --git a/src/CanvasContext.tsx b/src/CanvasContext.tsx index bf45a9c7..f96db479 100644 --- a/src/CanvasContext.tsx +++ b/src/CanvasContext.tsx @@ -1,7 +1,6 @@ import { InterpreterFrom } from 'xstate'; import { canvasMachine } from './canvasMachine'; -import { createRequiredContext } from './utils'; +import { createInterpreterContext, createRequiredContext } from './utils'; -export const [CanvasProvider, useCanvas] = createRequiredContext< - InterpreterFrom ->('Canvas'); +export const [CanvasProvider, useCanvas, createCanvasMachineSelector] = + createInterpreterContext>('Canvas'); diff --git a/src/pages/graph-only.tsx b/src/pages/graph-only.tsx index ee3259b4..52299f7f 100644 --- a/src/pages/graph-only.tsx +++ b/src/pages/graph-only.tsx @@ -1,46 +1,87 @@ -import { Box, ChakraProvider } from '@chakra-ui/react'; -import { useInterpret } from '@xstate/react'; +import { AddIcon, MinusIcon, RepeatIcon } from '@chakra-ui/icons'; +import { Box, ButtonGroup, ChakraProvider, IconButton } from '@chakra-ui/react'; +import { useInterpret, useSelector } from '@xstate/react'; import { useRouter } from 'next/router'; -import { useMemo } from 'react'; -import { createMachine } from 'xstate'; +import React, { useEffect, useMemo } from 'react'; +import { createMachine, StateFrom } from 'xstate'; +import { AppHead } from '../AppHead'; import { CanvasContainer } from '../CanvasContainer'; -import { CanvasProvider } from '../CanvasContext'; -import { canvasMachine } from '../canvasMachine'; +import { CanvasProvider, createCanvasMachineSelector } from '../CanvasContext'; +import { + canvasMachine, + getShouldEnableZoomInButton, + getShouldEnableZoomOutButton, +} from '../canvasMachine'; import { toDirectedGraph } from '../directedGraph'; -import { theme } from '../theme'; import { Graph } from '../Graph'; +import { isOnClientSide } from '../isOnClientSide'; import { SimulationProvider } from '../SimulationContext'; import { simulationMachine } from '../simulationMachine'; -import { AppHead } from '../AppHead'; -import { isOnClientSide } from '../isOnClientSide'; +import { theme } from '../theme'; +import { CompressIcon } from '../Icons'; + +const getCurrentSimService = (state: StateFrom) => { + if (!state.context.currentSessionId) return; + return state.context.serviceDataMap[state.context.currentSessionId]; +}; + +const getIsVisualizing = (state: StateFrom) => { + return state.matches('visualizing.ready'); +}; + +const getIsReadyForFitToScreen = (state: StateFrom) => { + return Boolean(state.context.elkGraph); +}; const GraphOnly = () => { const router = useRouter(); - const config = JSON.parse((router.query.config as string) || '{}'); + const parseResult = useMemo(() => { + if (!router.query.config) return undefined; + const config = JSON.parse((router.query.config as string) || '{}'); + const machine = createMachine(config || {}); - const digraph = useMemo( - () => - toDirectedGraph( - // Dummy machine to test - createMachine({ - after: { - 500: { - actions: 'sayHello', - }, - }, - initial: 'cool', - states: { - cool: {}, - }, - }), - ), - [config], - ); + return { + machine, + digraph: toDirectedGraph(machine), + }; + }, [Boolean(router.query.config)]); const canvasService = useInterpret(canvasMachine); const simService = useInterpret(simulationMachine); + useEffect(() => { + if (parseResult?.machine) { + simService.send({ + type: 'MACHINES.REGISTER', + machines: [parseResult?.machine], + }); + } + }, [Boolean(parseResult?.machine)]); + + const shouldEnableZoomOutButton = useSelector( + canvasService, + getShouldEnableZoomOutButton, + ); + + const shouldEnableZoomInButton = useSelector( + canvasService, + getShouldEnableZoomInButton, + ); + + const currentSimService = useSelector(simService, getCurrentSimService); + + const isReadyForFitToScreen = useSelector( + canvasService, + getIsReadyForFitToScreen, + ); + + useEffect(() => { + if (isReadyForFitToScreen) { + canvasService.send('FIT_TO_VIEW'); + } + }, [isReadyForFitToScreen]); + return ( <> { importPrettier={false} ogTitle="" > - {isOnClientSide() && ( + {isOnClientSide() && parseResult && ( - + - - {digraph && } - + + + + {parseResult?.digraph && currentSimService && ( + + )} + + + + + } + disabled={!shouldEnableZoomOutButton} + onClick={() => canvasService.send('ZOOM.OUT')} + variant="secondary" + /> + } + disabled={!shouldEnableZoomInButton} + onClick={() => canvasService.send('ZOOM.IN')} + variant="secondary" + /> + } + onClick={() => canvasService.send('FIT_TO_VIEW')} + variant="secondary" + /> + } + onClick={() => canvasService.send('POSITION.RESET')} + variant="secondary" + /> + + +