diff --git a/apps/builder/src/features/account/components/GraphNavigationRadioGroup.tsx b/apps/builder/src/features/account/components/GraphNavigationRadioGroup.tsx
deleted file mode 100644
index e66205032..000000000
--- a/apps/builder/src/features/account/components/GraphNavigationRadioGroup.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { MouseIcon, LaptopIcon } from '@/components/icons'
-import { useTranslate } from '@tolgee/react'
-import {
- HStack,
- Radio,
- RadioGroup,
- Stack,
- VStack,
- Text,
-} from '@chakra-ui/react'
-import { GraphNavigation } from '@typebot.io/prisma'
-
-type Props = {
- defaultValue: string
- onChange: (value: string) => void
-}
-export const GraphNavigationRadioGroup = ({
- defaultValue,
- onChange,
-}: Props) => {
- const { t } = useTranslate()
- const graphNavigationData = [
- {
- value: GraphNavigation.MOUSE,
- label: t('account.preferences.graphNavigation.mouse.label'),
- description: t('account.preferences.graphNavigation.mouse.description'),
- icon: ,
- },
- {
- value: GraphNavigation.TRACKPAD,
- label: t('account.preferences.graphNavigation.trackpad.label'),
- description: t(
- 'account.preferences.graphNavigation.trackpad.description'
- ),
- icon: ,
- },
- ]
- return (
-
-
- {graphNavigationData.map((option) => (
-
-
- {option.icon}
-
- {option.label}
- {option.description}
-
-
-
-
-
- ))}
-
-
- )
-}
diff --git a/apps/builder/src/features/account/components/UserPreferencesForm.tsx b/apps/builder/src/features/account/components/UserPreferencesForm.tsx
index 52b3abc85..0604b4631 100644
--- a/apps/builder/src/features/account/components/UserPreferencesForm.tsx
+++ b/apps/builder/src/features/account/components/UserPreferencesForm.tsx
@@ -11,7 +11,6 @@ import {
} from '@chakra-ui/react'
import { GraphNavigation } from '@typebot.io/prisma'
import React, { useEffect } from 'react'
-import { GraphNavigationRadioGroup } from './GraphNavigationRadioGroup'
import { AppearanceRadioGroup } from './AppearanceRadioGroup'
import { useUser } from '../hooks/useUser'
import { ChevronDownIcon } from '@/components/icons'
@@ -42,10 +41,6 @@ export const UserPreferencesForm = () => {
updateUser({ graphNavigation: GraphNavigation.TRACKPAD })
}, [updateUser, user?.graphNavigation])
- const changeGraphNavigation = async (value: string) => {
- updateUser({ graphNavigation: value as GraphNavigation })
- }
-
const changeAppearance = async (value: string) => {
updateUser({ preferredAppAppearance: value })
}
@@ -99,15 +94,7 @@ export const UserPreferencesForm = () => {
)}
-
-
- {t('account.preferences.graphNavigation.heading')}
-
-
-
+
{t('account.preferences.appearance.heading')}
diff --git a/apps/builder/src/features/graph/components/Graph.tsx b/apps/builder/src/features/graph/components/Graph.tsx
index 67ad470e1..047adce7e 100644
--- a/apps/builder/src/features/graph/components/Graph.tsx
+++ b/apps/builder/src/features/graph/components/Graph.tsx
@@ -63,6 +63,8 @@ export const Graph = ({
setPreviewingEdge,
connectingIds,
} = useGraph()
+ const isDraggingGraph = useGroupsStore((state) => state.isDraggingGraph)
+ const setIsDraggingGraph = useGroupsStore((state) => state.setIsDraggingGraph)
const focusedGroups = useGroupsStore(
useShallow((state) => state.focusedGroups)
)
@@ -107,7 +109,6 @@ export const Graph = ({
const [lastMouseClickPosition, setLastMouseClickPosition] = useState<
Coordinates | undefined
>()
- const [isSpacePressed, setIsSpacePressed] = useState(false)
const [isDragging, setIsDragging] = useState(false)
const graphContainerRef = useRef(null)
@@ -172,6 +173,7 @@ export const Graph = ({
}
const handlePointerUp = (e: PointerEvent) => {
+ if (isDraggingGraph) return
if (
!selectBoxCoordinates ||
Math.abs(selectBoxCoordinates?.dimension.width) +
@@ -192,7 +194,7 @@ export const Graph = ({
useGesture(
{
onDrag: (props) => {
- if (isSpacePressed) {
+ if (isDraggingGraph) {
if (props.first) setIsDragging(true)
if (props.last) setIsDragging(false)
setGraphPosition({
@@ -333,11 +335,11 @@ export const Graph = ({
})
useEventListener('keydown', (e) => {
- if (e.key === ' ') setIsSpacePressed(true)
+ if (e.key === ' ') setIsDraggingGraph(true)
})
useEventListener('keyup', (e) => {
if (e.key === ' ') {
- setIsSpacePressed(false)
+ setIsDraggingGraph(false)
setIsDragging(false)
}
})
@@ -357,7 +359,7 @@ export const Graph = ({
const zoomIn = () => zoom({ delta: zoomButtonsScaleBlock })
const zoomOut = () => zoom({ delta: -zoomButtonsScaleBlock })
- const cursor = isSpacePressed ? (isDragging ? 'grabbing' : 'grab') : 'auto'
+ const cursor = isDraggingGraph ? (isDragging ? 'grabbing' : 'grab') : 'auto'
return (
state.isDraggingGraph)
+
const onDrag = (position: NodePosition) => {
if (!onMouseDown) return
onMouseDown(position, block)
@@ -212,7 +215,7 @@ export const BlockNode = ({
data-testid={`block ${block.id}`}
w="full"
className="prevent-group-drag"
- pointerEvents={isReadOnly ? 'none' : 'auto'}
+ pointerEvents={isReadOnly || isDraggingGraph ? 'none' : 'auto'}
>
(null)
const [debouncedEventPosition] = useDebounce(currentCoordinates, 100)
const [isFocused, setIsFocused] = useState(false)
+ const isDraggingGraph = useGroupsStore((state) => state.isDraggingGraph)
useOutsideClick({
handler: () => setIsFocused(false),
@@ -172,6 +174,7 @@ const NonMemoizedDraggableEventNode = ({
shadow="md"
_hover={{ shadow: 'lg' }}
zIndex={isFocused ? 10 : 1}
+ pointerEvents={isDraggingGraph ? 'none' : 'auto'}
>
{
isNotDefined(previewingEdge.to.blockId))))
const groupRef = useRef(null)
+ const isDraggingGraph = useGroupsStore((state) => state.isDraggingGraph)
const focusedGroups = useGroupsStore(
useShallow((state) => state.focusedGroups)
)
@@ -192,6 +193,7 @@ export const GroupNode = ({ group, groupIndex }: Props) => {
_hover={{ shadow: 'lg' }}
zIndex={isFocused ? 10 : 1}
spacing={isEmpty(group.title) ? '0' : '2'}
+ pointerEvents={isDraggingGraph ? 'none' : 'auto'}
>
CoordinatesMap | undefined
focusGroup: (groupId: string, isAppending?: boolean) => void
@@ -15,12 +16,14 @@ type Store = {
setGroupsCoordinates: (groups: Group[] | undefined) => void
updateGroupCoordinates: (groupId: string, newCoord: Coordinates) => void
copyGroups: (groups: GroupV6[], edges: Edge[]) => void
+ setIsDraggingGraph: (isDragging: boolean) => void
}
export const useGroupsStore = createWithEqualityFn((set, get) => ({
focusedGroups: [],
groupsCoordinates: undefined,
groupsInClipboard: undefined,
+ isDraggingGraph: false,
getGroupsCoordinates: () => get().groupsCoordinates,
focusGroup: (groupId, isShiftKeyPressed) =>
set((state) => ({
@@ -80,4 +83,5 @@ export const useGroupsStore = createWithEqualityFn((set, get) => ({
edges,
},
}),
+ setIsDraggingGraph: (isDragging) => set({ isDraggingGraph: isDragging }),
}))
diff --git a/apps/docs/editor/graph.mdx b/apps/docs/editor/graph.mdx
new file mode 100644
index 000000000..74eda6ad0
--- /dev/null
+++ b/apps/docs/editor/graph.mdx
@@ -0,0 +1,18 @@
+---
+title: Graph
+icon: game-board
+---
+
+import { LoomVideo } from '/snippets/loom-video.mdx'
+
+The Graph is where you arrange your conversation flow and connect the Typebot [blocks](./blocks/overview) together.
+
+## Gestures
+
+**Select**: `Left click` + `drag`
+
+**Zoom**: `Ctrl` + `Mouse wheel` on a mouse or `pinch` on a trackpad
+
+**Pan**: `Space` + `Mouse wheel` on a mouse or `two-finger drag` on a trackpad
+
+
diff --git a/apps/docs/mint.json b/apps/docs/mint.json
index 5ed8a464a..d1d4cb9d9 100644
--- a/apps/docs/mint.json
+++ b/apps/docs/mint.json
@@ -76,6 +76,7 @@
{
"group": "Flow",
"pages": [
+ "editor/graph",
{
"group": "Blocks",
"icon": "block",