diff --git a/packages/web/src/components/Editor/index.tsx b/packages/web/src/components/Editor/index.tsx index bb4d76b6..3d3372dc 100644 --- a/packages/web/src/components/Editor/index.tsx +++ b/packages/web/src/components/Editor/index.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { useMutation } from '@apollo/client'; -import { ApolloCache, FetchResult } from '@apollo/client'; import Box from '@mui/material/Box'; import IconButton from '@mui/material/IconButton'; import AddIcon from '@mui/icons-material/Add'; @@ -10,7 +9,6 @@ import { CREATE_STEP } from 'graphql/mutations/create-step'; import { UPDATE_STEP } from 'graphql/mutations/update-step'; import FlowStep from 'components/FlowStep'; import type { Flow } from 'types/flow'; -import type { Step } from 'types/step'; type EditorProps = { flow: Flow; @@ -42,7 +40,7 @@ function updateHandlerFactory(flowId: string, previousStepId: string) { export default function Editor(props: EditorProps): React.ReactElement { const [updateStep] = useMutation(UPDATE_STEP); - const [createStep] = useMutation(CREATE_STEP); + const [createStep, { loading: creationInProgress }] = useMutation(CREATE_STEP); const [currentStep, setCurrentStep] = React.useState(0); const { flow } = props; @@ -96,7 +94,7 @@ export default function Editor(props: EditorProps): React.ReactElement { alignItems="center" alignSelf="center" py={3} - gap={2} + gap={1} > {flow?.steps?.map((step, index) => ( @@ -110,7 +108,7 @@ export default function Editor(props: EditorProps): React.ReactElement { onChange={onStepChange} /> - addStep(step.id)} color="primary"> + addStep(step.id)} color="primary" disabled={creationInProgress}> diff --git a/packages/web/src/components/FlowStep/index.tsx b/packages/web/src/components/FlowStep/index.tsx index 390bda65..eac83311 100644 --- a/packages/web/src/components/FlowStep/index.tsx +++ b/packages/web/src/components/FlowStep/index.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useQuery } from '@apollo/client'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Collapse from '@mui/material/Collapse'; import List from '@mui/material/List'; @@ -9,7 +10,10 @@ import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autocomplete'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import IconButton from '@mui/material/IconButton'; +import FlowStepContextMenu from 'components/FlowStepContextMenu'; import AppIcon from 'components/AppIcon'; import { GET_APPS } from 'graphql/queries/get-apps'; import useFormatMessage from 'hooks/useFormatMessage'; @@ -49,7 +53,9 @@ const parseStep = (step: any) => { export default function FlowStep(props: FlowStepProps): React.ReactElement | null { const { collapsed, index, onChange } = props; + const contextButtonRef = React.useRef(null); const [step, setStep] = React.useState(() => parseStep(props.step)); + const [anchorEl, setAnchorEl] = React.useState(null); const isTrigger = step.type === StepType.Trigger; const isAction = step.type === StepType.Action; const initialRender = React.useRef(true); @@ -97,6 +103,14 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul if (!apps) return null; + const onContextMenuClose = (event: React.SyntheticEvent) => { + event.stopPropagation(); + setAnchorEl(null); + } + const onContextMenuClick = (event: React.SyntheticEvent) => { + event.stopPropagation(); + setAnchorEl(contextButtonRef.current); + } const onOpen = () => collapsed && props.onOpen?.(); const onClose = () => props.onClose?.(); @@ -121,6 +135,13 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul {index}. {app?.name} + + + {/* as there are no other actions besides "delete step", we hide the context menu. */} + {!isTrigger && + + } + @@ -170,6 +191,13 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul )} + + {anchorEl && } ) }; diff --git a/packages/web/src/components/FlowStepContextMenu/index.tsx b/packages/web/src/components/FlowStepContextMenu/index.tsx new file mode 100644 index 00000000..8857f0c5 --- /dev/null +++ b/packages/web/src/components/FlowStepContextMenu/index.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { useMutation } from '@apollo/client'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import type { PopoverProps } from '@mui/material/Popover'; + +import { DELETE_STEP } from 'graphql/mutations/delete-step'; +import useFormatMessage from 'hooks/useFormatMessage'; + +type FlowStepContextMenuProps = { + stepId: string; + onClose: PopoverProps['onClose']; + anchorEl: HTMLButtonElement; + deletable: boolean; +}; + +function FlowStepContextMenu(props: FlowStepContextMenuProps): React.ReactElement { + const { stepId, onClose, anchorEl, deletable } = props; + const [deleteStep] = useMutation(DELETE_STEP); + const formatMessage = useFormatMessage(); + + const deleteActionHandler = React.useCallback(async (event: React.SyntheticEvent) => { + event.stopPropagation(); + await deleteStep({ variables: { id: stepId }}); + }, [stepId]); + + return ( + + {deletable && + {formatMessage('connection.delete')} + } + + ); +}; + +export default FlowStepContextMenu; diff --git a/packages/web/src/graphql/mutations/create-step.ts b/packages/web/src/graphql/mutations/create-step.ts index 35159985..5dedcb5f 100644 --- a/packages/web/src/graphql/mutations/create-step.ts +++ b/packages/web/src/graphql/mutations/create-step.ts @@ -7,6 +7,7 @@ export const CREATE_STEP = gql` type key appKey + parameters connection { id } diff --git a/packages/web/src/graphql/mutations/delete-step.ts b/packages/web/src/graphql/mutations/delete-step.ts new file mode 100644 index 00000000..e2291b7c --- /dev/null +++ b/packages/web/src/graphql/mutations/delete-step.ts @@ -0,0 +1,15 @@ +import { gql } from '@apollo/client'; + +export const DELETE_STEP = gql` + mutation DeleteStep($id: String!) { + deleteStep(id: $id) { + id + flow { + id + steps { + id + } + } + } + } +`; diff --git a/packages/web/src/graphql/queries/get-app.ts b/packages/web/src/graphql/queries/get-app.ts index ebf829a8..9a8c87bd 100644 --- a/packages/web/src/graphql/queries/get-app.ts +++ b/packages/web/src/graphql/queries/get-app.ts @@ -8,7 +8,6 @@ export const GET_APP = gql` iconUrl docUrl primaryColor - connectionCount fields { key label