11import { enumFromJson , isEnumJson } from '@bufbuild/protobuf' ;
22import { createClient } from '@connectrpc/connect' ;
3- import { createFileRoute , useMatchRoute , useNavigate , useRouteContext } from '@tanstack/react-router' ;
3+ import { createFileRoute , useBlocker , useMatchRoute , useNavigate , useRouteContext } from '@tanstack/react-router' ;
44import {
55 Background ,
66 BackgroundVariant ,
@@ -19,7 +19,7 @@ import { Array, Boolean, HashMap, Match, MutableHashMap, Option, pipe, Predicate
1919import { Ulid } from 'id128' ;
2020import { PropsWithChildren , Suspense , use , useCallback , useMemo , useRef , useState } from 'react' ;
2121import { useDrop } from 'react-aria' ;
22- import { MenuTrigger , useDragAndDrop } from 'react-aria-components' ;
22+ import { Dialog , MenuTrigger , useDragAndDrop } from 'react-aria-components' ;
2323import { FiClock , FiMinus , FiMoreHorizontal , FiPlus , FiStopCircle , FiX } from 'react-icons/fi' ;
2424import { Panel , PanelGroup } from 'react-resizable-panels' ;
2525import { Example } from '@the-dev-tools/spec/collection/item/example/v1/example_pb' ;
@@ -55,6 +55,7 @@ import { Button, ButtonAsLink } from '@the-dev-tools/ui/button';
5555import { DataTable , useReactTable } from '@the-dev-tools/ui/data-table' ;
5656import { PlayCircleIcon , Spinner } from '@the-dev-tools/ui/icons' ;
5757import { Menu , MenuItem , useContextMenuState } from '@the-dev-tools/ui/menu' ;
58+ import { Modal } from '@the-dev-tools/ui/modal' ;
5859import { PanelResizeHandle } from '@the-dev-tools/ui/resizable-panel' ;
5960import { Separator } from '@the-dev-tools/ui/separator' ;
6061import { tw } from '@the-dev-tools/ui/tailwind-literal' ;
@@ -445,6 +446,12 @@ const ActionBar = () => {
445446
446447 const makeNode = useMakeNode ( ) ;
447448
449+ const { proceed, reset, status } = useBlocker ( {
450+ disabled : ! controller ,
451+ shouldBlockFn : ( _ ) => _ . current . pathname !== _ . next . pathname ,
452+ withResolver : true ,
453+ } ) ;
454+
448455 const onRun = async ( ) => {
449456 const controller = new AbortController ( ) ;
450457 setController ( controller ) ;
@@ -531,48 +538,72 @@ const ActionBar = () => {
531538 } ;
532539
533540 return (
534- < RFPanel
535- className = { tw `mb-4 flex items-center gap-2 rounded-lg bg-slate-900 p-1 shadow-sm` }
536- position = 'bottom-center'
537- >
538- { /* <Button variant='ghost dark' className={tw`p-1`}>
539- <TextBoxIcon className={tw`size-5 text-slate-300`} />
540- </Button> */ }
541-
542- { /* <Button variant='ghost dark' className={tw`p-1`}>
543- <ChatAddIcon className={tw`size-5 text-slate-300`} />
544- </Button> */ }
545-
546- { /* <div className={tw`mx-2 h-5 w-px bg-white/20`} /> */ }
547-
548- < Button
549- className = { tw `px-1.5 py-1` }
550- onPress = { async ( ) => {
551- const { domNode } = storeApi . getState ( ) ;
552- if ( ! domNode ) return ;
553- const box = domNode . getBoundingClientRect ( ) ;
554- const position = flow . screenToFlowPosition ( { x : box . x + box . width / 2 , y : box . y + box . height * 0.1 } ) ;
555- const node = await makeNode ( { kind : NodeKind . NO_OP , noOp : NodeNoOpKind . CREATE , position } ) ;
556- pipe ( node , Node . fromDTO , flow . addNodes ) ;
557- } }
558- variant = 'ghost dark'
541+ < >
542+ < Modal isOpen = { status === 'blocked' } modalClassName = { tw `h-auto w-sm` } >
543+ < Dialog className = { tw `grid grid-cols-2 gap-4 p-6` } >
544+ < div className = { tw `col-span-full` } >
545+ Leaving the flow will stop the execution, are you sure you want to proceed?
546+ </ div >
547+
548+ < Button onPress = { ( ) => reset ?.( ) } variant = 'secondary' >
549+ Cancel
550+ </ Button >
551+
552+ < Button
553+ onPress = { ( ) => {
554+ onStop ( ) ;
555+ proceed ?.( ) ;
556+ } }
557+ variant = 'primary'
558+ >
559+ Continue
560+ </ Button >
561+ </ Dialog >
562+ </ Modal >
563+
564+ < RFPanel
565+ className = { tw `mb-4 flex items-center gap-2 rounded-lg bg-slate-900 p-1 shadow-sm` }
566+ position = 'bottom-center'
559567 >
560- < FiPlus className = { tw `size-5 text-slate-300` } />
561- Add Node
562- </ Button >
563-
564- { controller ? (
565- < Button onPress = { onStop } variant = 'primary' >
566- < FiStopCircle className = { tw `size-4` } />
567- Stop
568- </ Button >
569- ) : (
570- < Button onPress = { onRun } variant = 'primary' >
571- < PlayCircleIcon className = { tw `size-4` } />
572- Run
568+ { /* <Button variant='ghost dark' className={tw`p-1`}>
569+ <TextBoxIcon className={tw`size-5 text-slate-300`} />
570+ </Button> */ }
571+
572+ { /* <Button variant='ghost dark' className={tw`p-1`}>
573+ <ChatAddIcon className={tw`size-5 text-slate-300`} />
574+ </Button> */ }
575+
576+ { /* <div className={tw`mx-2 h-5 w-px bg-white/20`} /> */ }
577+
578+ < Button
579+ className = { tw `px-1.5 py-1` }
580+ onPress = { async ( ) => {
581+ const { domNode } = storeApi . getState ( ) ;
582+ if ( ! domNode ) return ;
583+ const box = domNode . getBoundingClientRect ( ) ;
584+ const position = flow . screenToFlowPosition ( { x : box . x + box . width / 2 , y : box . y + box . height * 0.1 } ) ;
585+ const node = await makeNode ( { kind : NodeKind . NO_OP , noOp : NodeNoOpKind . CREATE , position } ) ;
586+ pipe ( node , Node . fromDTO , flow . addNodes ) ;
587+ } }
588+ variant = 'ghost dark'
589+ >
590+ < FiPlus className = { tw `size-5 text-slate-300` } />
591+ Add Node
573592 </ Button >
574- ) }
575- </ RFPanel >
593+
594+ { controller ? (
595+ < Button onPress = { onStop } variant = 'primary' >
596+ < FiStopCircle className = { tw `size-4` } />
597+ Stop
598+ </ Button >
599+ ) : (
600+ < Button onPress = { onRun } variant = 'primary' >
601+ < PlayCircleIcon className = { tw `size-4` } />
602+ Run
603+ </ Button >
604+ ) }
605+ </ RFPanel >
606+ </ >
576607 ) ;
577608} ;
578609
0 commit comments