Add conditional choice items

Allows you to conditonnally display an item from the Buttons or the Picture choice input

Closes #546
This commit is contained in:
Baptiste Arnaud
2023-06-05 17:33:43 +02:00
parent acaa1c6223
commit ef0a2d9dc6
18 changed files with 404 additions and 157 deletions

View File

@@ -5,12 +5,22 @@ import {
Fade,
IconButton,
Flex,
Popover,
PopoverAnchor,
PopoverArrow,
PopoverBody,
PopoverContent,
Portal,
useColorModeValue,
SlideFade,
} from '@chakra-ui/react'
import { PlusIcon } from '@/components/icons'
import { PlusIcon, SettingsIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { ButtonItem, Item, ItemIndices, ItemType } from '@typebot.io/schemas'
import React, { useRef, useState } from 'react'
import { isNotDefined } from '@typebot.io/lib'
import { useGraph } from '@/features/graph/providers/GraphProvider'
import { ButtonsItemSettings } from './ButtonsItemSettings'
type Props = {
item: ButtonItem
@@ -20,8 +30,12 @@ type Props = {
export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
const { deleteItem, updateItem, createItem } = useTypebot()
const { openedItemId, setOpenedItemId } = useGraph()
const [itemValue, setItemValue] = useState(item.content ?? 'Click to edit')
const editableRef = useRef<HTMLDivElement | null>(null)
const ref = useRef<HTMLDivElement | null>(null)
const handleMouseDown = (e: React.MouseEvent) => e.stopPropagation()
const handleInputSubmit = () => {
if (itemValue === '') deleteItem(indices)
@@ -45,44 +59,101 @@ export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
)
}
const updateItemSettings = (settings: Omit<ButtonItem, 'content'>) => {
updateItem(indices, { ...item, ...settings })
}
return (
<Flex px={4} py={2} justify="center" w="90%" pos="relative">
<Editable
ref={editableRef}
flex="1"
startWithEditView={isNotDefined(item.content)}
value={itemValue}
onChange={setItemValue}
onSubmit={handleInputSubmit}
onKeyDownCapture={handleKeyPress}
maxW="180px"
>
<EditablePreview
w="full"
color={item.content !== 'Click to edit' ? 'inherit' : 'gray.500'}
cursor="pointer"
/>
<EditableInput />
</Editable>
<Fade
in={isMouseOver}
style={{
position: 'absolute',
bottom: '-15px',
zIndex: 3,
left: '90px',
}}
unmountOnExit
>
<IconButton
aria-label="Add item"
icon={<PlusIcon />}
size="xs"
shadow="md"
colorScheme="gray"
onClick={handlePlusClick}
/>
</Fade>
</Flex>
<Popover
placement="right"
isLazy
isOpen={openedItemId === item.id}
closeOnBlur={false}
>
<PopoverAnchor>
<Flex px={4} py={2} justify="center" w="90%" pos="relative">
<Editable
ref={editableRef}
flex="1"
startWithEditView={isNotDefined(item.content)}
value={itemValue}
onChange={setItemValue}
onSubmit={handleInputSubmit}
onKeyDownCapture={handleKeyPress}
maxW="180px"
>
<EditablePreview
w="full"
color={item.content !== 'Click to edit' ? 'inherit' : 'gray.500'}
cursor="pointer"
/>
<EditableInput />
</Editable>
<HitboxExtension />
<SlideFade
offsetY="0px"
offsetX="-10px"
in={isMouseOver}
style={{
position: 'absolute',
right: '-60px',
zIndex: 3,
}}
unmountOnExit
>
<IconButton
aria-label="Open settings"
icon={<SettingsIcon />}
bgColor={useColorModeValue('white', 'gray.800')}
variant="ghost"
size="sm"
shadow="md"
colorScheme="gray"
onClick={() => setOpenedItemId(item.id)}
/>
</SlideFade>
<Fade
in={isMouseOver}
style={{
position: 'absolute',
bottom: '-15px',
zIndex: 3,
left: '90px',
}}
unmountOnExit
>
<IconButton
aria-label="Add item"
icon={<PlusIcon />}
size="xs"
shadow="md"
colorScheme="gray"
onClick={handlePlusClick}
/>
</Fade>
</Flex>
</PopoverAnchor>
<Portal>
<PopoverContent pos="relative" onMouseDown={handleMouseDown}>
<PopoverArrow />
<PopoverBody
py="6"
overflowY="scroll"
maxH="400px"
shadow="lg"
ref={ref}
>
<ButtonsItemSettings
item={item}
onSettingsChange={updateItemSettings}
/>
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
)
}
const HitboxExtension = () => (
<Flex h="full" w="50px" pos="absolute" top="0" right="-70px" />
)

View File

@@ -0,0 +1,50 @@
import React from 'react'
import { Stack } from '@chakra-ui/react'
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
import { ConditionForm } from '@/features/blocks/logic/condition/components/ConditionForm'
import { ButtonItem, Condition, LogicalOperator } from '@typebot.io/schemas'
type Props = {
item: ButtonItem
onSettingsChange: (updates: Omit<ButtonItem, 'content'>) => void
}
export const ButtonsItemSettings = ({ item, onSettingsChange }: Props) => {
const updateIsDisplayConditionEnabled = (isEnabled: boolean) =>
onSettingsChange({
...item,
displayCondition: {
...item.displayCondition,
isEnabled,
},
})
const updateDisplayCondition = (condition: Condition) =>
onSettingsChange({
...item,
displayCondition: {
...item.displayCondition,
condition,
},
})
return (
<Stack spacing={4}>
<SwitchWithRelatedSettings
label="Display condition"
initialValue={item.displayCondition?.isEnabled ?? false}
onCheckChange={updateIsDisplayConditionEnabled}
>
<ConditionForm
condition={
item.displayCondition?.condition ?? {
comparisons: [],
logicalOperator: LogicalOperator.AND,
}
}
onConditionChange={updateDisplayCondition}
/>
</SwitchWithRelatedSettings>
</Stack>
)
}

View File

@@ -11,6 +11,9 @@ import {
Text,
} from '@chakra-ui/react'
import { ImageUploadContent } from '@/components/ImageUploadContent'
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
import { ConditionForm } from '@/features/blocks/logic/condition/components/ConditionForm'
import { Condition, LogicalOperator } from '@typebot.io/schemas'
type Props = {
typebotId: string
@@ -32,15 +35,35 @@ export const PictureChoiceItemSettings = ({
const updateDescription = (description: string) =>
onItemChange({ ...item, description })
const updateIsDisplayConditionEnabled = (isEnabled: boolean) =>
onItemChange({
...item,
displayCondition: {
...item.displayCondition,
isEnabled,
},
})
const updateDisplayCondition = (condition: Condition) =>
onItemChange({
...item,
displayCondition: {
...item.displayCondition,
condition,
},
})
return (
<Stack>
<Stack spacing={4}>
<HStack>
<Text fontWeight="medium">Image:</Text>
<Popover isLazy>
{({ onClose }) => (
<>
<PopoverTrigger>
<Button size="sm">Pick an image</Button>
<Button size="sm">
{item.pictureSrc ? 'Change image' : 'Pick an image'}
</Button>
</PopoverTrigger>
<PopoverContent p="4" w="500px">
<ImageUploadContent
@@ -67,6 +90,21 @@ export const PictureChoiceItemSettings = ({
defaultValue={item.description}
onChange={updateDescription}
/>
<SwitchWithRelatedSettings
label="Display condition"
initialValue={item.displayCondition?.isEnabled ?? false}
onCheckChange={updateIsDisplayConditionEnabled}
>
<ConditionForm
condition={
item.displayCondition?.condition ?? {
comparisons: [],
logicalOperator: LogicalOperator.AND,
}
}
onConditionChange={updateDisplayCondition}
/>
</SwitchWithRelatedSettings>
</Stack>
)
}