2
0

Add dark mode (#191)

Closes #189
This commit is contained in:
Baptiste Arnaud
2022-12-20 16:55:43 +01:00
committed by GitHub
parent 054cbb3585
commit 3394fa5e0a
77 changed files with 1782 additions and 601 deletions

View File

@ -1,5 +1,11 @@
import { Flex, HStack, StackProps, Text, Tooltip } from '@chakra-ui/react'
import { BlockType, DraggableBlockType } from 'models'
import {
Flex,
HStack,
Text,
Tooltip,
useColorModeValue,
} from '@chakra-ui/react'
import { DraggableBlockType } from 'models'
import { useBlockDnd } from '@/features/graph'
import React, { useEffect, useState } from 'react'
import { BlockIcon } from './BlockIcon'
@ -28,17 +34,17 @@ export const BlockCard = ({
<Flex pos="relative">
<HStack
borderWidth="1px"
borderColor="gray.200"
borderColor={useColorModeValue('gray.200', 'gray.800')}
rounded="lg"
flex="1"
cursor={'grab'}
opacity={isMouseDown || isDisabled ? '0.4' : '1'}
onMouseDown={handleMouseDown}
bgColor="gray.50"
bgColor={useColorModeValue('gray.50', 'gray.850')}
px="4"
py="2"
_hover={{ shadow: 'md' }}
transition="box-shadow 200ms"
_hover={useColorModeValue({ shadow: 'md' }, { bgColor: 'gray.800' })}
transition="box-shadow 200ms, background-color 200ms"
pointerEvents={isDisabled ? 'none' : 'auto'}
>
{!isMouseDown ? (
@ -46,38 +52,9 @@ export const BlockCard = ({
<BlockIcon type={type} />
<BlockTypeLabel type={type} />
</>
) : (
<Text color="white" userSelect="none">
Placeholder
</Text>
)}
) : null}
</HStack>
</Flex>
</Tooltip>
)
}
export const BlockCardOverlay = ({
type,
...props
}: StackProps & { type: BlockType }) => {
return (
<HStack
borderWidth="1px"
rounded="lg"
cursor={'grabbing'}
w="147px"
transition="none"
pointerEvents="none"
px="4"
py="2"
bgColor="white"
shadow="xl"
zIndex={2}
{...props}
>
<BlockIcon type={type} />
<BlockTypeLabel type={type} />
</HStack>
)
}

View File

@ -0,0 +1,30 @@
import { StackProps, HStack, useColorModeValue } from '@chakra-ui/react'
import { BlockType } from 'models'
import { BlockIcon } from './BlockIcon'
import { BlockTypeLabel } from './BlockTypeLabel'
export const BlockCardOverlay = ({
type,
...props
}: StackProps & { type: BlockType }) => {
return (
<HStack
borderWidth="1px"
rounded="lg"
cursor={'grabbing'}
w="147px"
transition="none"
pointerEvents="none"
px="4"
py="2"
borderColor={useColorModeValue('gray.200', 'gray.800')}
bgColor={useColorModeValue('gray.50', 'gray.850')}
shadow="xl"
zIndex={2}
{...props}
>
<BlockIcon type={type} />
<BlockTypeLabel type={type} />
</HStack>
)
}

View File

@ -1,4 +1,4 @@
import { IconProps } from '@chakra-ui/react'
import { IconProps, useColorModeValue } from '@chakra-ui/react'
import {
BubbleBlockType,
InputBlockType,
@ -40,47 +40,50 @@ import { AudioBubbleIcon } from '@/features/blocks/bubbles/audio'
type BlockIconProps = { type: BlockType } & IconProps
export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
const blue = useColorModeValue('blue.500', 'blue.300')
const orange = useColorModeValue('orange.500', 'orange.300')
const purple = useColorModeValue('purple.500', 'purple.300')
switch (type) {
case BubbleBlockType.TEXT:
return <TextBubbleIcon {...props} />
return <TextBubbleIcon color={blue} {...props} />
case BubbleBlockType.IMAGE:
return <ImageBubbleIcon {...props} />
return <ImageBubbleIcon color={blue} {...props} />
case BubbleBlockType.VIDEO:
return <VideoBubbleIcon {...props} />
return <VideoBubbleIcon color={blue} {...props} />
case BubbleBlockType.EMBED:
return <EmbedBubbleIcon {...props} />
return <EmbedBubbleIcon color={blue} {...props} />
case BubbleBlockType.AUDIO:
return <AudioBubbleIcon {...props} />
return <AudioBubbleIcon color={blue} {...props} />
case InputBlockType.TEXT:
return <TextInputIcon {...props} />
return <TextInputIcon color={orange} {...props} />
case InputBlockType.NUMBER:
return <NumberInputIcon {...props} />
return <NumberInputIcon color={orange} {...props} />
case InputBlockType.EMAIL:
return <EmailInputIcon {...props} />
return <EmailInputIcon color={orange} {...props} />
case InputBlockType.URL:
return <UrlInputIcon {...props} />
return <UrlInputIcon color={orange} {...props} />
case InputBlockType.DATE:
return <DateInputIcon {...props} />
return <DateInputIcon color={orange} {...props} />
case InputBlockType.PHONE:
return <PhoneInputIcon {...props} />
return <PhoneInputIcon color={orange} {...props} />
case InputBlockType.CHOICE:
return <ButtonsInputIcon {...props} />
return <ButtonsInputIcon color={orange} {...props} />
case InputBlockType.PAYMENT:
return <PaymentInputIcon {...props} />
return <PaymentInputIcon color={orange} {...props} />
case InputBlockType.RATING:
return <RatingInputIcon {...props} />
return <RatingInputIcon color={orange} {...props} />
case InputBlockType.FILE:
return <FileInputIcon {...props} />
return <FileInputIcon color={orange} {...props} />
case LogicBlockType.SET_VARIABLE:
return <SetVariableIcon {...props} />
return <SetVariableIcon color={purple} {...props} />
case LogicBlockType.CONDITION:
return <ConditionIcon {...props} />
return <ConditionIcon color={purple} {...props} />
case LogicBlockType.REDIRECT:
return <RedirectIcon {...props} />
return <RedirectIcon color={purple} {...props} />
case LogicBlockType.CODE:
return <CodeIcon {...props} />
return <CodeIcon color={purple} {...props} />
case LogicBlockType.TYPEBOT_LINK:
return <TypebotLinkIcon {...props} />
return <TypebotLinkIcon color={purple} {...props} />
case IntegrationBlockType.GOOGLE_SHEETS:
return <GoogleSheetsLogo {...props} />
case IntegrationBlockType.GOOGLE_ANALYTICS:

View File

@ -8,6 +8,7 @@ import {
IconButton,
Tooltip,
Fade,
useColorModeValue,
} from '@chakra-ui/react'
import {
BubbleBlockType,
@ -18,9 +19,10 @@ import {
} from 'models'
import { useBlockDnd } from '@/features/graph'
import React, { useState } from 'react'
import { BlockCard, BlockCardOverlay } from './BlockCard'
import { BlockCard } from './BlockCard'
import { LockedIcon, UnlockedIcon } from '@/components/icons'
import { headerHeight } from '../../constants'
import { BlockCardOverlay } from './BlockCardOverlay'
export const BlocksSideBar = () => {
const { setDraggedBlockType, draggedBlockType } = useBlockDnd()
@ -93,7 +95,7 @@ export const BlocksSideBar = () => {
pt="2"
pb="10"
px="4"
bgColor="white"
bgColor={useColorModeValue('white', 'gray.900')}
spacing={6}
userSelect="none"
overflowY="scroll"
@ -105,14 +107,13 @@ export const BlocksSideBar = () => {
icon={isLocked ? <LockedIcon /> : <UnlockedIcon />}
aria-label={isLocked ? 'Unlock' : 'Lock'}
size="sm"
variant="outline"
onClick={handleLockClick}
/>
</Tooltip>
</Flex>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
<Text fontSize="sm" fontWeight="semibold">
Bubbles
</Text>
<SimpleGrid columns={2} spacing="3">
@ -123,7 +124,7 @@ export const BlocksSideBar = () => {
</Stack>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
<Text fontSize="sm" fontWeight="semibold">
Inputs
</Text>
<SimpleGrid columns={2} spacing="3">
@ -134,7 +135,7 @@ export const BlocksSideBar = () => {
</Stack>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
<Text fontSize="sm" fontWeight="semibold">
Logic
</Text>
<SimpleGrid columns={2} spacing="3">
@ -145,7 +146,7 @@ export const BlocksSideBar = () => {
</Stack>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
<Text fontSize="sm" fontWeight="semibold">
Integrations
</Text>
<SimpleGrid columns={2} spacing="3">

View File

@ -1,10 +1,12 @@
import {
Flex,
FlexProps,
IconButton,
Menu,
MenuButton,
MenuButtonProps,
MenuItem,
MenuList,
useColorModeValue,
useDisclosure,
} from '@chakra-ui/react'
import assert from 'assert'
@ -21,7 +23,7 @@ import { isNotDefined } from 'utils'
import { EditorSettingsModal } from './EditorSettingsModal'
import { parseDefaultPublicId } from '@/features/publish'
export const BoardMenuButton = (props: MenuButtonProps) => {
export const BoardMenuButton = (props: FlexProps) => {
const { query } = useRouter()
const { typebot } = useTypebot()
const { user } = useUser()
@ -55,25 +57,30 @@ export const BoardMenuButton = (props: MenuButtonProps) => {
setIsDownloading(false)
}
return (
<Menu>
<MenuButton
as={IconButton}
bgColor="white"
icon={<MoreVerticalIcon transform={'rotate(90deg)'} />}
isLoading={isDownloading}
size="sm"
shadow="lg"
{...props}
/>
<MenuList>
<MenuItem icon={<SettingsIcon />} onClick={onOpen}>
Editor settings
</MenuItem>
<MenuItem icon={<DownloadIcon />} onClick={downloadFlow}>
Export flow
</MenuItem>
</MenuList>
<EditorSettingsModal isOpen={isOpen} onClose={onClose} />
</Menu>
<Flex
bgColor={useColorModeValue('white', 'gray.900')}
rounded="md"
{...props}
>
<Menu>
<MenuButton
as={IconButton}
icon={<MoreVerticalIcon transform={'rotate(90deg)'} />}
isLoading={isDownloading}
size="sm"
shadow="lg"
bgColor={useColorModeValue('white', undefined)}
/>
<MenuList>
<MenuItem icon={<SettingsIcon />} onClick={onOpen}>
Editor settings
</MenuItem>
<MenuItem icon={<DownloadIcon />} onClick={downloadFlow}>
Export flow
</MenuItem>
</MenuList>
<EditorSettingsModal isOpen={isOpen} onClose={onClose} />
</Menu>
</Flex>
)
}

View File

@ -5,7 +5,7 @@ import {
GraphProvider,
GroupsCoordinatesProvider,
} from '@/features/graph'
import { Flex, Spinner } from '@chakra-ui/react'
import { Flex, Spinner, useColorModeValue } from '@chakra-ui/react'
import {
EditorProvider,
useEditor,
@ -31,8 +31,11 @@ export const EditTypebotPage = () => {
flex="1"
pos="relative"
h="full"
background="#f4f5f8"
backgroundImage="radial-gradient(#c6d0e1 1px, transparent 0)"
bgColor={useColorModeValue('#f4f5f8', 'gray.850')}
backgroundImage={useColorModeValue(
'radial-gradient(#c6d0e1 1px, transparent 0)',
'radial-gradient(#2f2f39 1px, transparent 0)'
)}
backgroundSize="40px 40px"
backgroundPosition="-19px -19px"
>
@ -48,12 +51,7 @@ export const EditTypebotPage = () => {
</GraphProvider>
</GraphDndProvider>
) : (
<Flex
justify="center"
align="center"
boxSize="full"
bgColor="rgba(255, 255, 255, 0.5)"
>
<Flex justify="center" align="center" boxSize="full">
<Spinner color="gray" />
</Flex>
)}

View File

@ -1,77 +0,0 @@
import {
Stack,
Heading,
HStack,
Text,
Radio,
RadioGroup,
VStack,
} from '@chakra-ui/react'
import { MouseIcon, LaptopIcon } from '@/components/icons'
import { useUser } from '@/features/account'
import { GraphNavigation } from 'db'
import React, { useState } from 'react'
type Props = {
defaultGraphNavigation: GraphNavigation
}
export const EditorSettingsForm = ({ defaultGraphNavigation }: Props) => {
const { saveUser } = useUser()
const [value, setValue] = useState<string>(defaultGraphNavigation)
const changeEditorNavigation = (value: string) => {
setValue(value)
saveUser({ graphNavigation: value as GraphNavigation }).then()
}
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">Editor Navigation</Heading>
<RadioGroup onChange={changeEditorNavigation} 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>
)
}

View File

@ -1,4 +1,4 @@
import { useUser } from '@/features/account'
import { UserPreferencesForm } from '@/features/account'
import {
Modal,
ModalBody,
@ -6,31 +6,21 @@ import {
ModalContent,
ModalOverlay,
} from '@chakra-ui/react'
import { GraphNavigation } from 'db'
import React from 'react'
import { EditorSettingsForm } from './EditorSettingsForm'
type Props = {
isOpen: boolean
onClose: () => void
}
export const EditorSettingsModal = ({ isOpen, onClose }: Props) => {
const { user } = useUser()
return (
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody pt="12" pb="8" px="8">
<EditorSettingsForm
defaultGraphNavigation={
user?.graphNavigation ?? GraphNavigation.TRACKPAD
}
/>
</ModalBody>
</ModalContent>
</Modal>
)
}
export const EditorSettingsModal = ({ isOpen, onClose }: Props) => (
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody pt="12" pb="8" px="8">
<UserPreferencesForm />
</ModalBody>
</ModalContent>
</Modal>
)

View File

@ -127,10 +127,9 @@ export const GettingStartedModal = () => {
height="315"
src="https://www.youtube.com/embed/jp3ggg_42-M"
title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{ borderRadius: '0.5rem' }}
style={{ borderRadius: '0.5rem', border: 'none' }}
/>
<Accordion allowToggle>
<AccordionItem>
@ -146,20 +145,18 @@ export const GettingStartedModal = () => {
height="315"
src="https://www.youtube.com/embed/6BudIC4GYNk"
title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{ borderRadius: '0.5rem' }}
style={{ borderRadius: '0.5rem', border: 'none' }}
/>
<iframe
width="100%"
height="315"
src="https://www.youtube.com/embed/ZuyDwFLRbfQ"
title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{ borderRadius: '0.5rem' }}
style={{ borderRadius: '0.5rem', border: 'none' }}
/>
</AccordionPanel>
</AccordionItem>

View File

@ -5,6 +5,7 @@ import {
Fade,
Flex,
FlexProps,
useColorMode,
useEventListener,
UseToastOptions,
VStack,
@ -21,6 +22,7 @@ import { headerHeight } from '../constants'
import { parseTypebotToPublicTypebot } from '@/features/publish'
export const PreviewDrawer = () => {
const isDark = useColorMode().colorMode === 'dark'
const { typebot } = useTypebot()
const { setRightPanel, startPreviewAtGroup } = useEditor()
const { setPreviewingEdge } = useGraph()
@ -68,7 +70,8 @@ export const PreviewDrawer = () => {
top={`0`}
h={`100%`}
w={`${width}px`}
bgColor="white"
bgColor={isDark ? 'gray.900' : 'white'}
borderLeftWidth={'1px'}
shadow="lg"
borderLeftRadius={'lg'}
onMouseOver={() => setIsResizeHandleVisible(true)}
@ -78,6 +81,7 @@ export const PreviewDrawer = () => {
>
<Fade in={isResizeHandleVisible}>
<ResizeHandle
isDark={isDark}
pos="absolute"
left="-7.5px"
top={`calc(50% - ${headerHeight}px)`}
@ -107,6 +111,7 @@ export const PreviewDrawer = () => {
onNewLog={handleNewLog}
startGroupId={startPreviewAtGroup}
isPreview
style={{ borderRadius: '10px' }}
/>
</Flex>
)}
@ -115,20 +120,26 @@ export const PreviewDrawer = () => {
)
}
const ResizeHandle = (props: FlexProps) => {
const ResizeHandle = (props: FlexProps & { isDark: boolean }) => {
return (
<Flex
w="15px"
h="50px"
borderWidth={'1px'}
bgColor={'white'}
bgColor={props.isDark ? 'gray.800' : 'white'}
cursor={'col-resize'}
justifyContent={'center'}
align={'center'}
borderRadius={'sm'}
{...props}
>
<Box w="2px" bgColor={'gray.300'} h="70%" mr="0.5" />
<Box w="2px" bgColor={'gray.300'} h="70%" />
<Box
w="2px"
bgColor={props.isDark ? 'gray.600' : 'gray.300'}
h="70%"
mr="0.5"
/>
<Box w="2px" bgColor={props.isDark ? 'gray.600' : 'gray.300'} h="70%" />
</Flex>
)
}

View File

@ -39,7 +39,7 @@ export const EditableTypebotName = ({
fontSize="14px"
minW="30px"
minH="20px"
bgColor={currentName === '' ? 'gray.100' : 'white'}
bgColor={currentName === '' ? 'gray.100' : 'inherit'}
/>
<EditableInput fontSize="14px" />
</Editable>

View File

@ -6,6 +6,7 @@ import {
Tooltip,
Spinner,
Text,
useColorModeValue,
} from '@chakra-ui/react'
import {
BuoyIcon,
@ -66,7 +67,7 @@ export const TypebotHeader = () => {
h={`${headerHeight}px`}
zIndex={100}
pos="relative"
bgColor="white"
bgColor={useColorModeValue('white', 'gray.900')}
flexShrink={0}
>
<HStack
@ -201,6 +202,7 @@ export const TypebotHeader = () => {
<CollaborationMenuButton isLoading={isNotDefined(typebot)} />
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
<Button
colorScheme="gray"
onClick={handlePreviewClick}
isLoading={isNotDefined(typebot)}
size="sm"

View File

@ -1,7 +1,6 @@
export { TypebotProvider, useTypebot } from './providers/TypebotProvider'
export { TypebotHeader } from './components/TypebotHeader'
export { EditTypebotPage } from './components/EditTypebotPage'
export { EditorSettingsForm } from './components/EditorSettingsForm'
export { headerHeight } from './constants'
export { BlockIcon } from './components/BlocksSideBar/BlockIcon'
export { RightPanel, useEditor } from './providers/EditorProvider'