2
0

refactor: ♻️ Rename step to block

This commit is contained in:
Baptiste Arnaud
2022-06-11 07:27:38 +02:00
parent 8751766d0e
commit 2df8338505
297 changed files with 4292 additions and 3989 deletions

View File

@ -0,0 +1,83 @@
import { Flex, HStack, StackProps, Text, Tooltip } from '@chakra-ui/react'
import { BlockType, DraggableBlockType } from 'models'
import { useBlockDnd } from 'contexts/GraphDndContext'
import React, { useEffect, useState } from 'react'
import { BlockIcon } from './BlockIcon'
import { BlockTypeLabel } from './BlockTypeLabel'
export const BlockCard = ({
type,
onMouseDown,
isDisabled = false,
}: {
type: DraggableBlockType
isDisabled?: boolean
onMouseDown: (e: React.MouseEvent, type: DraggableBlockType) => void
}) => {
const { draggedBlockType } = useBlockDnd()
const [isMouseDown, setIsMouseDown] = useState(false)
useEffect(() => {
setIsMouseDown(draggedBlockType === type)
}, [draggedBlockType, type])
const handleMouseDown = (e: React.MouseEvent) => onMouseDown(e, type)
return (
<Tooltip label="Coming soon!" isDisabled={!isDisabled}>
<Flex pos="relative">
<HStack
borderWidth="1px"
borderColor="gray.200"
rounded="lg"
flex="1"
cursor={'grab'}
opacity={isMouseDown || isDisabled ? '0.4' : '1'}
onMouseDown={handleMouseDown}
bgColor="gray.50"
px="4"
py="2"
_hover={{ shadow: 'md' }}
transition="box-shadow 200ms"
pointerEvents={isDisabled ? 'none' : 'auto'}
>
{!isMouseDown ? (
<>
<BlockIcon type={type} />
<BlockTypeLabel type={type} />
</>
) : (
<Text color="white" userSelect="none">
Placeholder
</Text>
)}
</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,98 @@
import { IconProps } from '@chakra-ui/react'
import {
BoxIcon,
CalendarIcon,
ChatIcon,
CheckSquareIcon,
CodeIcon,
CreditCardIcon,
EditIcon,
EmailIcon,
ExternalLinkIcon,
FilmIcon,
FilterIcon,
FlagIcon,
GlobeIcon,
ImageIcon,
LayoutIcon,
NumberIcon,
PhoneIcon,
SendEmailIcon,
StarIcon,
TextIcon,
WebhookIcon,
} from 'assets/icons'
import {
GoogleAnalyticsLogo,
GoogleSheetsLogo,
MakeComLogo,
PabblyConnectLogo,
ZapierLogo,
} from 'assets/logos'
import {
BubbleBlockType,
InputBlockType,
IntegrationBlockType,
LogicBlockType,
BlockType,
} from 'models'
import React from 'react'
type BlockIconProps = { type: BlockType } & IconProps
export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
switch (type) {
case BubbleBlockType.TEXT:
return <ChatIcon color="blue.500" {...props} />
case BubbleBlockType.IMAGE:
return <ImageIcon color="blue.500" {...props} />
case BubbleBlockType.VIDEO:
return <FilmIcon color="blue.500" {...props} />
case BubbleBlockType.EMBED:
return <LayoutIcon color="blue.500" {...props} />
case InputBlockType.TEXT:
return <TextIcon color="orange.500" {...props} />
case InputBlockType.NUMBER:
return <NumberIcon color="orange.500" {...props} />
case InputBlockType.EMAIL:
return <EmailIcon color="orange.500" {...props} />
case InputBlockType.URL:
return <GlobeIcon color="orange.500" {...props} />
case InputBlockType.DATE:
return <CalendarIcon color="orange.500" {...props} />
case InputBlockType.PHONE:
return <PhoneIcon color="orange.500" {...props} />
case InputBlockType.CHOICE:
return <CheckSquareIcon color="orange.500" {...props} />
case InputBlockType.PAYMENT:
return <CreditCardIcon color="orange.500" {...props} />
case InputBlockType.RATING:
return <StarIcon color="orange.500" {...props} />
case LogicBlockType.SET_VARIABLE:
return <EditIcon color="purple.500" {...props} />
case LogicBlockType.CONDITION:
return <FilterIcon color="purple.500" {...props} />
case LogicBlockType.REDIRECT:
return <ExternalLinkIcon color="purple.500" {...props} />
case LogicBlockType.CODE:
return <CodeIcon color="purple.500" {...props} />
case LogicBlockType.TYPEBOT_LINK:
return <BoxIcon color="purple.500" {...props} />
case IntegrationBlockType.GOOGLE_SHEETS:
return <GoogleSheetsLogo {...props} />
case IntegrationBlockType.GOOGLE_ANALYTICS:
return <GoogleAnalyticsLogo {...props} />
case IntegrationBlockType.WEBHOOK:
return <WebhookIcon {...props} />
case IntegrationBlockType.ZAPIER:
return <ZapierLogo {...props} />
case IntegrationBlockType.MAKE_COM:
return <MakeComLogo {...props} />
case IntegrationBlockType.PABBLY_CONNECT:
return <PabblyConnectLogo {...props} />
case IntegrationBlockType.EMAIL:
return <SendEmailIcon {...props} />
case 'start':
return <FlagIcon {...props} />
}
}

View File

@ -0,0 +1,87 @@
import { Text, Tooltip } from '@chakra-ui/react'
import {
BubbleBlockType,
InputBlockType,
IntegrationBlockType,
LogicBlockType,
BlockType,
} from 'models'
import React from 'react'
type Props = { type: BlockType }
export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
switch (type) {
case 'start':
return <Text>Start</Text>
case BubbleBlockType.TEXT:
case InputBlockType.TEXT:
return <Text>Text</Text>
case BubbleBlockType.IMAGE:
return <Text>Image</Text>
case BubbleBlockType.VIDEO:
return <Text>Video</Text>
case BubbleBlockType.EMBED:
return (
<Tooltip label="Embed a pdf, an iframe, a website...">
<Text>Embed</Text>
</Tooltip>
)
case InputBlockType.NUMBER:
return <Text>Number</Text>
case InputBlockType.EMAIL:
return <Text>Email</Text>
case InputBlockType.URL:
return <Text>Website</Text>
case InputBlockType.DATE:
return <Text>Date</Text>
case InputBlockType.PHONE:
return <Text>Phone</Text>
case InputBlockType.CHOICE:
return <Text>Button</Text>
case InputBlockType.PAYMENT:
return <Text>Payment</Text>
case InputBlockType.RATING:
return <Text>Rating</Text>
case LogicBlockType.SET_VARIABLE:
return <Text>Set variable</Text>
case LogicBlockType.CONDITION:
return <Text>Condition</Text>
case LogicBlockType.REDIRECT:
return <Text>Redirect</Text>
case LogicBlockType.CODE:
return (
<Tooltip label="Run Javascript code">
<Text>Code</Text>
</Tooltip>
)
case LogicBlockType.TYPEBOT_LINK:
return (
<Tooltip label="Link to another of your typebots">
<Text>Typebot</Text>
</Tooltip>
)
case IntegrationBlockType.GOOGLE_SHEETS:
return (
<Tooltip label="Google Sheets">
<Text>Sheets</Text>
</Tooltip>
)
case IntegrationBlockType.GOOGLE_ANALYTICS:
return (
<Tooltip label="Google Analytics">
<Text>Analytics</Text>
</Tooltip>
)
case IntegrationBlockType.WEBHOOK:
return <Text>Webhook</Text>
case IntegrationBlockType.ZAPIER:
return <Text>Zapier</Text>
case IntegrationBlockType.MAKE_COM:
return <Text>Make.com</Text>
case IntegrationBlockType.PABBLY_CONNECT:
return <Text>Pabbly</Text>
case IntegrationBlockType.EMAIL:
return <Text>Email</Text>
}
}

View File

@ -0,0 +1,194 @@
import {
Stack,
Text,
SimpleGrid,
useEventListener,
Portal,
Flex,
IconButton,
Tooltip,
Fade,
} from '@chakra-ui/react'
import {
BubbleBlockType,
DraggableBlockType,
InputBlockType,
IntegrationBlockType,
LogicBlockType,
} from 'models'
import { useBlockDnd } from 'contexts/GraphDndContext'
import React, { useState } from 'react'
import { BlockCard, BlockCardOverlay } from './BlockCard'
import { LockedIcon, UnlockedIcon } from 'assets/icons'
import { headerHeight } from 'components/shared/TypebotHeader'
export const BlocksSideBar = () => {
const { setDraggedBlockType, draggedBlockType } = useBlockDnd()
const [position, setPosition] = useState({
x: 0,
y: 0,
})
const [relativeCoordinates, setRelativeCoordinates] = useState({ x: 0, y: 0 })
const [isLocked, setIsLocked] = useState(true)
const [isExtended, setIsExtended] = useState(true)
const handleMouseMove = (event: MouseEvent) => {
if (!draggedBlockType) return
const { clientX, clientY } = event
setPosition({
...position,
x: clientX - relativeCoordinates.x,
y: clientY - relativeCoordinates.y,
})
}
useEventListener('mousemove', handleMouseMove)
const handleMouseDown = (e: React.MouseEvent, type: DraggableBlockType) => {
const element = e.currentTarget as HTMLDivElement
const rect = element.getBoundingClientRect()
setPosition({ x: rect.left, y: rect.top })
const x = e.clientX - rect.left
const y = e.clientY - rect.top
setRelativeCoordinates({ x, y })
setDraggedBlockType(type)
}
const handleMouseUp = () => {
if (!draggedBlockType) return
setDraggedBlockType(undefined)
setPosition({
x: 0,
y: 0,
})
}
useEventListener('mouseup', handleMouseUp)
const handleLockClick = () => setIsLocked(!isLocked)
const handleDockBarEnter = () => setIsExtended(true)
const handleMouseLeave = () => {
if (isLocked) return
setIsExtended(false)
}
return (
<Flex
w="360px"
pos="absolute"
left="0"
h={`calc(100vh - ${headerHeight}px)`}
zIndex="2"
pl="4"
py="4"
onMouseLeave={handleMouseLeave}
transform={isExtended ? 'translateX(0)' : 'translateX(-350px)'}
transition="transform 350ms cubic-bezier(0.075, 0.82, 0.165, 1) 0s"
>
<Stack
w="full"
rounded="lg"
shadow="xl"
borderWidth="1px"
pt="2"
pb="10"
px="4"
bgColor="white"
spacing={6}
userSelect="none"
overflowY="scroll"
className="hide-scrollbar"
>
<Flex justifyContent="flex-end">
<Tooltip label={isLocked ? 'Unlock sidebar' : 'Lock sidebar'}>
<IconButton
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">
Bubbles
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(BubbleBlockType).map((type) => (
<BlockCard key={type} type={type} onMouseDown={handleMouseDown} />
))}
</SimpleGrid>
</Stack>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
Inputs
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(InputBlockType).map((type) => (
<BlockCard key={type} type={type} onMouseDown={handleMouseDown} />
))}
</SimpleGrid>
</Stack>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
Logic
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(LogicBlockType).map((type) => (
<BlockCard key={type} type={type} onMouseDown={handleMouseDown} />
))}
</SimpleGrid>
</Stack>
<Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600">
Integrations
</Text>
<SimpleGrid columns={2} spacing="3">
{Object.values(IntegrationBlockType).map((type) => (
<BlockCard
key={type}
type={type}
onMouseDown={handleMouseDown}
isDisabled={type === IntegrationBlockType.MAKE_COM}
/>
))}
</SimpleGrid>
</Stack>
{draggedBlockType && (
<Portal>
<BlockCardOverlay
type={draggedBlockType}
onMouseUp={handleMouseUp}
pos="fixed"
top="0"
left="0"
style={{
transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg)`,
}}
/>
</Portal>
)}
</Stack>
<Fade in={!isLocked} unmountOnExit>
<Flex
pos="absolute"
h="100%"
right="-50px"
w="50px"
top="0"
justify="center"
align="center"
onMouseEnter={handleDockBarEnter}
>
<Flex w="5px" h="20px" bgColor="gray.400" rounded="md" />
</Flex>
</Fade>
</Flex>
)
}

View File

@ -0,0 +1 @@
export { BlocksSideBar } from './BlocksSideBar'