feat(editor): ♿️ Improve graph navigation setting
This commit is contained in:
78
apps/builder/components/account/EditorSection.tsx
Normal file
78
apps/builder/components/account/EditorSection.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
Stack,
|
||||||
|
Heading,
|
||||||
|
HStack,
|
||||||
|
Text,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
VStack,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
|
import { MouseIcon, LaptopIcon } from 'assets/icons'
|
||||||
|
import { useUser } from 'contexts/UserContext'
|
||||||
|
import { GraphNavigation } from 'db'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
export const EditorSection = () => <EditorSettings />
|
||||||
|
|
||||||
|
export const EditorSettings = () => {
|
||||||
|
const { user, saveUser } = useUser()
|
||||||
|
const [value, setValue] = useState<string>(
|
||||||
|
user?.graphNavigation ?? GraphNavigation.TRACKPAD
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user?.graphNavigation === value) return
|
||||||
|
saveUser({ graphNavigation: value as GraphNavigation }).then()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
value: GraphNavigation.MOUSE,
|
||||||
|
label: 'Mouse',
|
||||||
|
description:
|
||||||
|
'Move by dragging the board and zoom in/out using the scroll wheel',
|
||||||
|
icon: <MouseIcon boxSize="35px" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: GraphNavigation.TRACKPAD,
|
||||||
|
label: 'Trackpad',
|
||||||
|
description: 'Move the board using 2 fingers and zoom in/out by pinching',
|
||||||
|
icon: <LaptopIcon boxSize="35px" />,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={6}>
|
||||||
|
<Heading size="md">Navigation</Heading>
|
||||||
|
<RadioGroup onChange={setValue} value={value}>
|
||||||
|
<HStack spacing={4} w="full" align="stretch">
|
||||||
|
{options.map((option) => (
|
||||||
|
<VStack
|
||||||
|
key={option.value}
|
||||||
|
as="label"
|
||||||
|
htmlFor={option.label}
|
||||||
|
cursor="pointer"
|
||||||
|
borderWidth="1px"
|
||||||
|
borderRadius="md"
|
||||||
|
w="full"
|
||||||
|
p="6"
|
||||||
|
spacing={6}
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<VStack spacing={6}>
|
||||||
|
{option.icon}
|
||||||
|
<Stack>
|
||||||
|
<Text fontWeight="bold">{option.label}</Text>
|
||||||
|
<Text>{option.description}</Text>
|
||||||
|
</Stack>
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
<Radio value={option.value} id={option.label} />
|
||||||
|
</VStack>
|
||||||
|
))}
|
||||||
|
</HStack>
|
||||||
|
</RadioGroup>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
@ -10,15 +10,30 @@ import {
|
|||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import { DownloadIcon, MoreVerticalIcon, SettingsIcon } from 'assets/icons'
|
import { DownloadIcon, MoreVerticalIcon, SettingsIcon } from 'assets/icons'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import React, { useState } from 'react'
|
import { useUser } from 'contexts/UserContext'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
import { parseDefaultPublicId } from 'services/typebots'
|
import { parseDefaultPublicId } from 'services/typebots'
|
||||||
|
import { isNotDefined } from 'utils'
|
||||||
import { EditorSettingsModal } from './EditorSettingsModal'
|
import { EditorSettingsModal } from './EditorSettingsModal'
|
||||||
|
|
||||||
export const BoardMenuButton = (props: MenuButtonProps) => {
|
export const BoardMenuButton = (props: MenuButtonProps) => {
|
||||||
|
const { query } = useRouter()
|
||||||
const { typebot } = useTypebot()
|
const { typebot } = useTypebot()
|
||||||
|
const { user } = useUser()
|
||||||
const [isDownloading, setIsDownloading] = useState(false)
|
const [isDownloading, setIsDownloading] = useState(false)
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
user &&
|
||||||
|
isNotDefined(user.graphNavigation) &&
|
||||||
|
isNotDefined(query.isFirstBot)
|
||||||
|
)
|
||||||
|
onOpen()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
const downloadFlow = () => {
|
const downloadFlow = () => {
|
||||||
assert(typebot)
|
assert(typebot)
|
||||||
setIsDownloading(true)
|
setIsDownloading(true)
|
||||||
|
@ -1,21 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
Heading,
|
|
||||||
Modal,
|
Modal,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
ModalCloseButton,
|
ModalCloseButton,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
Radio,
|
|
||||||
VStack,
|
|
||||||
RadioGroup,
|
|
||||||
HStack,
|
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { LaptopIcon, MouseIcon } from 'assets/icons'
|
import { EditorSettings } from 'components/account/EditorSection'
|
||||||
import { useUser } from 'contexts/UserContext'
|
import React from 'react'
|
||||||
import { GraphNavigation } from 'db'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
@ -35,63 +26,3 @@ export const EditorSettingsModal = ({ isOpen, onClose }: Props) => {
|
|||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditorSettings = () => {
|
|
||||||
const { user, saveUser } = useUser()
|
|
||||||
const [value, setValue] = useState<string>(
|
|
||||||
user?.graphNavigation ?? GraphNavigation.TRACKPAD
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user?.graphNavigation === value) return
|
|
||||||
saveUser({ graphNavigation: value as GraphNavigation }).then()
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [value])
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
value: GraphNavigation.MOUSE,
|
|
||||||
label: 'Mouse',
|
|
||||||
description:
|
|
||||||
'Move by dragging the board and zoom in/out using the scroll wheel',
|
|
||||||
icon: <MouseIcon boxSize="35px" />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: GraphNavigation.TRACKPAD,
|
|
||||||
label: 'Trackpad',
|
|
||||||
description: 'Move the board using 2 fingers and zoom in/out by pinching',
|
|
||||||
icon: <LaptopIcon boxSize="35px" />,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack spacing={4}>
|
|
||||||
<Heading size="md">Navigation</Heading>
|
|
||||||
<RadioGroup onChange={setValue} value={value}>
|
|
||||||
<HStack spacing={4} w="full">
|
|
||||||
{options.map((option) => (
|
|
||||||
<VStack
|
|
||||||
key={option.value}
|
|
||||||
as="label"
|
|
||||||
htmlFor={option.label}
|
|
||||||
cursor="pointer"
|
|
||||||
borderWidth="1px"
|
|
||||||
borderRadius="md"
|
|
||||||
w="full"
|
|
||||||
p="6"
|
|
||||||
spacing={6}
|
|
||||||
>
|
|
||||||
{option.icon}
|
|
||||||
<Stack>
|
|
||||||
<Text fontWeight="bold">{option.label}</Text>
|
|
||||||
<Text>{option.description}</Text>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Radio value={option.value} id={option.label} />
|
|
||||||
</VStack>
|
|
||||||
))}
|
|
||||||
</HStack>
|
|
||||||
</RadioGroup>
|
|
||||||
</Stack>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -55,7 +55,12 @@ export const CreateNewTypebotButtons = () => {
|
|||||||
if (data)
|
if (data)
|
||||||
router.push({
|
router.push({
|
||||||
pathname: `/typebots/${data.id}/edit`,
|
pathname: `/typebots/${data.id}/edit`,
|
||||||
query: { isFirstBot: router.query.isFirstBot },
|
query:
|
||||||
|
router.query.isFirstBot === 'true'
|
||||||
|
? {
|
||||||
|
isFirstBot: 'true',
|
||||||
|
}
|
||||||
|
: {},
|
||||||
})
|
})
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@ import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { PersonalInfoForm } from 'components/account/PersonalInfoForm'
|
import { PersonalInfoForm } from 'components/account/PersonalInfoForm'
|
||||||
import { BillingSection } from 'components/account/BillingSection'
|
import { BillingSection } from 'components/account/BillingSection'
|
||||||
|
import { EditorSection } from 'components/account/EditorSection'
|
||||||
|
|
||||||
export const AccountContent = () => {
|
export const AccountContent = () => {
|
||||||
return (
|
return (
|
||||||
<Flex h="full" w="full" justifyContent="center" align="flex-start">
|
<Flex h="full" w="full" justifyContent="center" align="flex-start" pb="20">
|
||||||
<Stack maxW="600px" w="full" pt="4" spacing={10}>
|
<Stack maxW="600px" w="full" pt="4" spacing={10}>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Button
|
<Button
|
||||||
@ -28,6 +29,8 @@ export const AccountContent = () => {
|
|||||||
<PersonalInfoForm />
|
<PersonalInfoForm />
|
||||||
<Divider />
|
<Divider />
|
||||||
<BillingSection />
|
<BillingSection />
|
||||||
|
<Divider />
|
||||||
|
<EditorSection />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user