✨ 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:
@@ -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" />
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user