@@ -18,7 +18,7 @@ type Props = {
|
|||||||
isMouseOver: boolean
|
isMouseOver: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ButtonNodeContent = ({ item, indices, isMouseOver }: Props) => {
|
export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
|
||||||
const { deleteItem, updateItem, createItem } = useTypebot()
|
const { deleteItem, updateItem, createItem } = useTypebot()
|
||||||
const [initialContent] = useState(item.content ?? '')
|
const [initialContent] = useState(item.content ?? '')
|
||||||
const [itemValue, setItemValue] = useState(item.content ?? 'Click to edit')
|
const [itemValue, setItemValue] = useState(item.content ?? 'Click to edit')
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './ButtonsItemNode'
|
||||||
|
export * from './ButtonsIcon'
|
||||||
|
export * from './ButtonsOptionsForm'
|
||||||
@@ -1,3 +1 @@
|
|||||||
export { ButtonsOptionsForm } from './components/ButtonsOptionsForm'
|
export * from './components'
|
||||||
export { ButtonNodeContent } from './components/ButtonNodeContent'
|
|
||||||
export { ButtonsInputIcon } from './components/ButtonsInputIcon'
|
|
||||||
|
|||||||
@@ -1,26 +1,16 @@
|
|||||||
import { Flex } from '@chakra-ui/react'
|
import { Flex } from '@chakra-ui/react'
|
||||||
import { DropdownList } from '@/components/DropdownList'
|
import { DropdownList } from '@/components/DropdownList'
|
||||||
import {
|
import { Comparison, ConditionItem, LogicalOperator } from 'models'
|
||||||
Comparison,
|
|
||||||
ConditionItem,
|
|
||||||
ConditionBlock,
|
|
||||||
LogicalOperator,
|
|
||||||
} from 'models'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ComparisonItem } from './ComparisonsItem'
|
import { ComparisonItem } from './ComparisonItem'
|
||||||
import { TableList } from '@/components/TableList'
|
import { TableList } from '@/components/TableList'
|
||||||
|
|
||||||
type ConditionSettingsBodyProps = {
|
type Props = {
|
||||||
block: ConditionBlock
|
itemContent: ConditionItem['content']
|
||||||
onItemChange: (updates: Partial<ConditionItem>) => void
|
onItemChange: (updates: Partial<ConditionItem>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConditionSettingsBody = ({
|
export const ConditionItemForm = ({ itemContent, onItemChange }: Props) => {
|
||||||
block,
|
|
||||||
onItemChange,
|
|
||||||
}: ConditionSettingsBodyProps) => {
|
|
||||||
const itemContent = block.items[0].content
|
|
||||||
|
|
||||||
const handleComparisonsChange = (comparisons: Comparison[]) =>
|
const handleComparisonsChange = (comparisons: Comparison[]) =>
|
||||||
onItemChange({ content: { ...itemContent, comparisons } })
|
onItemChange({ content: { ...itemContent, comparisons } })
|
||||||
const handleLogicalOperatorChange = (logicalOperator: LogicalOperator) =>
|
const handleLogicalOperatorChange = (logicalOperator: LogicalOperator) =>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './ConditionItemForm'
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import {
|
||||||
|
Stack,
|
||||||
|
Tag,
|
||||||
|
Text,
|
||||||
|
Flex,
|
||||||
|
Wrap,
|
||||||
|
Fade,
|
||||||
|
IconButton,
|
||||||
|
PopoverTrigger,
|
||||||
|
Popover,
|
||||||
|
Portal,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverArrow,
|
||||||
|
PopoverBody,
|
||||||
|
useEventListener,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
|
import { useTypebot } from '@/features/editor'
|
||||||
|
import {
|
||||||
|
Comparison,
|
||||||
|
ConditionItem,
|
||||||
|
ComparisonOperators,
|
||||||
|
ItemType,
|
||||||
|
ItemIndices,
|
||||||
|
} from 'models'
|
||||||
|
import React, { useRef } from 'react'
|
||||||
|
import { byId, isNotDefined } from 'utils'
|
||||||
|
import { PlusIcon } from '@/components/icons'
|
||||||
|
import { ConditionItemForm } from './ConditionItemForm'
|
||||||
|
import { useGraph } from '@/features/graph'
|
||||||
|
import cuid from 'cuid'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
item: ConditionItem
|
||||||
|
isMouseOver: boolean
|
||||||
|
indices: ItemIndices
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConditionItemNode = ({ item, isMouseOver, indices }: Props) => {
|
||||||
|
const { typebot, createItem, updateItem } = useTypebot()
|
||||||
|
const { openedItemId, setOpenedItemId } = useGraph()
|
||||||
|
const ref = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
|
const handleMouseDown = (e: React.MouseEvent) => e.stopPropagation()
|
||||||
|
|
||||||
|
const openPopover = () => {
|
||||||
|
setOpenedItemId(item.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleItemChange = (updates: Partial<ConditionItem>) => {
|
||||||
|
updateItem(indices, { ...item, ...updates })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePlusClick = (event: React.MouseEvent) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
const itemIndex = indices.itemIndex + 1
|
||||||
|
const newItemId = cuid()
|
||||||
|
createItem(
|
||||||
|
{
|
||||||
|
blockId: item.blockId,
|
||||||
|
type: ItemType.CONDITION,
|
||||||
|
id: newItemId,
|
||||||
|
},
|
||||||
|
{ ...indices, itemIndex }
|
||||||
|
)
|
||||||
|
setOpenedItemId(newItemId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseWheel = (e: WheelEvent) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
useEventListener('wheel', handleMouseWheel, ref.current)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
placement="left"
|
||||||
|
isLazy
|
||||||
|
isOpen={openedItemId === item.id}
|
||||||
|
closeOnBlur={false}
|
||||||
|
>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Flex p={3} pos="relative" w="full" onClick={openPopover}>
|
||||||
|
{item.content.comparisons.length === 0 ||
|
||||||
|
comparisonIsEmpty(item.content.comparisons[0]) ? (
|
||||||
|
<Text color={'gray.500'}>Configure...</Text>
|
||||||
|
) : (
|
||||||
|
<Stack maxW="170px">
|
||||||
|
{item.content.comparisons.map((comparison, idx) => {
|
||||||
|
const variable = typebot?.variables.find(
|
||||||
|
byId(comparison.variableId)
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Wrap key={comparison.id} spacing={1} noOfLines={1}>
|
||||||
|
{idx > 0 && (
|
||||||
|
<Text>{item.content.logicalOperator ?? ''}</Text>
|
||||||
|
)}
|
||||||
|
{variable?.name && (
|
||||||
|
<Tag bgColor="orange.400" color="white">
|
||||||
|
{variable.name}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{comparison.comparisonOperator && (
|
||||||
|
<Text>
|
||||||
|
{parseComparisonOperatorSymbol(
|
||||||
|
comparison.comparisonOperator
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{comparison?.value && (
|
||||||
|
<Tag bgColor={'gray.200'}>
|
||||||
|
<Text noOfLines={1}>{comparison.value}</Text>
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</Wrap>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
<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>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<Portal>
|
||||||
|
<PopoverContent pos="relative" onMouseDown={handleMouseDown}>
|
||||||
|
<PopoverArrow />
|
||||||
|
<PopoverBody
|
||||||
|
py="6"
|
||||||
|
overflowY="scroll"
|
||||||
|
maxH="400px"
|
||||||
|
shadow="lg"
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<ConditionItemForm
|
||||||
|
itemContent={item.content}
|
||||||
|
onItemChange={handleItemChange}
|
||||||
|
/>
|
||||||
|
</PopoverBody>
|
||||||
|
</PopoverContent>
|
||||||
|
</Portal>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const comparisonIsEmpty = (comparison: Comparison) =>
|
||||||
|
isNotDefined(comparison.comparisonOperator) &&
|
||||||
|
isNotDefined(comparison.value) &&
|
||||||
|
isNotDefined(comparison.variableId)
|
||||||
|
|
||||||
|
const parseComparisonOperatorSymbol = (operator: ComparisonOperators) => {
|
||||||
|
switch (operator) {
|
||||||
|
case ComparisonOperators.CONTAINS:
|
||||||
|
return 'contains'
|
||||||
|
case ComparisonOperators.EQUAL:
|
||||||
|
return '='
|
||||||
|
case ComparisonOperators.GREATER:
|
||||||
|
return '>'
|
||||||
|
case ComparisonOperators.IS_SET:
|
||||||
|
return 'is set'
|
||||||
|
case ComparisonOperators.LESS:
|
||||||
|
return '<'
|
||||||
|
case ComparisonOperators.NOT_EQUAL:
|
||||||
|
return '!='
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import { Stack, Tag, Text, Flex, Wrap } from '@chakra-ui/react'
|
|
||||||
import { useTypebot } from '@/features/editor'
|
|
||||||
import { Comparison, ConditionItem, ComparisonOperators } from 'models'
|
|
||||||
import React from 'react'
|
|
||||||
import { byId, isNotDefined } from 'utils'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
item: ConditionItem
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConditionNodeContent = ({ item }: Props) => {
|
|
||||||
const { typebot } = useTypebot()
|
|
||||||
return (
|
|
||||||
<Flex px={2} py={2}>
|
|
||||||
{item.content.comparisons.length === 0 ||
|
|
||||||
comparisonIsEmpty(item.content.comparisons[0]) ? (
|
|
||||||
<Text color={'gray.500'}>Configure...</Text>
|
|
||||||
) : (
|
|
||||||
<Stack maxW="170px">
|
|
||||||
{item.content.comparisons.map((comparison, idx) => {
|
|
||||||
const variable = typebot?.variables.find(
|
|
||||||
byId(comparison.variableId)
|
|
||||||
)
|
|
||||||
return (
|
|
||||||
<Wrap key={comparison.id} spacing={1} noOfLines={1}>
|
|
||||||
{idx > 0 && <Text>{item.content.logicalOperator ?? ''}</Text>}
|
|
||||||
{variable?.name && (
|
|
||||||
<Tag bgColor="orange.400" color="white">
|
|
||||||
{variable.name}
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{comparison.comparisonOperator && (
|
|
||||||
<Text>
|
|
||||||
{parseComparisonOperatorSymbol(
|
|
||||||
comparison.comparisonOperator
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
{comparison?.value && (
|
|
||||||
<Tag bgColor={'gray.200'}>
|
|
||||||
<Text noOfLines={1}>{comparison.value}</Text>
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Wrap>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const comparisonIsEmpty = (comparison: Comparison) =>
|
|
||||||
isNotDefined(comparison.comparisonOperator) &&
|
|
||||||
isNotDefined(comparison.value) &&
|
|
||||||
isNotDefined(comparison.variableId)
|
|
||||||
|
|
||||||
const parseComparisonOperatorSymbol = (operator: ComparisonOperators) => {
|
|
||||||
switch (operator) {
|
|
||||||
case ComparisonOperators.CONTAINS:
|
|
||||||
return 'contains'
|
|
||||||
case ComparisonOperators.EQUAL:
|
|
||||||
return '='
|
|
||||||
case ComparisonOperators.GREATER:
|
|
||||||
return '>'
|
|
||||||
case ComparisonOperators.IS_SET:
|
|
||||||
return 'is set'
|
|
||||||
case ComparisonOperators.LESS:
|
|
||||||
return '<'
|
|
||||||
case ComparisonOperators.NOT_EQUAL:
|
|
||||||
return '!='
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { ConditionSettingsBody } from './ConditonSettingsBody'
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export { ConditionSettingsBody } from './components/ConditionSettingsBody'
|
export * from './components/ConditionItemNode'
|
||||||
export { ConditionNodeContent } from './components/ConditionNodeContent'
|
export * from './components/ConditionIcon'
|
||||||
export { ConditionIcon } from './components/ConditionIcon'
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
ItemIndices,
|
ItemIndices,
|
||||||
Item,
|
Item,
|
||||||
InputBlockType,
|
|
||||||
BlockWithItems,
|
BlockWithItems,
|
||||||
ButtonItem,
|
defaultConditionContent,
|
||||||
|
ItemType,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { SetTypebot } from '../TypebotProvider'
|
import { SetTypebot } from '../TypebotProvider'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
@@ -12,10 +12,7 @@ import { byId, blockHasItems } from 'utils'
|
|||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
|
||||||
export type ItemsActions = {
|
export type ItemsActions = {
|
||||||
createItem: (
|
createItem: (item: Item | Omit<Item, 'id'>, indices: ItemIndices) => void
|
||||||
item: ButtonItem | Omit<ButtonItem, 'id'>,
|
|
||||||
indices: ItemIndices
|
|
||||||
) => void
|
|
||||||
updateItem: (indices: ItemIndices, updates: Partial<Omit<Item, 'id'>>) => void
|
updateItem: (indices: ItemIndices, updates: Partial<Omit<Item, 'id'>>) => void
|
||||||
detachItemFromBlock: (indices: ItemIndices) => void
|
detachItemFromBlock: (indices: ItemIndices) => void
|
||||||
deleteItem: (indices: ItemIndices) => void
|
deleteItem: (indices: ItemIndices) => void
|
||||||
@@ -23,18 +20,23 @@ export type ItemsActions = {
|
|||||||
|
|
||||||
const itemsAction = (setTypebot: SetTypebot): ItemsActions => ({
|
const itemsAction = (setTypebot: SetTypebot): ItemsActions => ({
|
||||||
createItem: (
|
createItem: (
|
||||||
item: ButtonItem | Omit<ButtonItem, 'id'>,
|
item: Item | Omit<Item, 'id'>,
|
||||||
{ groupIndex, blockIndex, itemIndex }: ItemIndices
|
{ groupIndex, blockIndex, itemIndex }: ItemIndices
|
||||||
) =>
|
) =>
|
||||||
setTypebot((typebot) =>
|
setTypebot((typebot) =>
|
||||||
produce(typebot, (typebot) => {
|
produce(typebot, (typebot) => {
|
||||||
const block = typebot.groups[groupIndex].blocks[blockIndex]
|
const block = typebot.groups[groupIndex].blocks[
|
||||||
if (block.type !== InputBlockType.CHOICE) return
|
blockIndex
|
||||||
|
] as BlockWithItems
|
||||||
|
|
||||||
const newItem = {
|
const newItem = {
|
||||||
...item,
|
|
||||||
blockId: block.id,
|
|
||||||
id: 'id' in item ? item.id : cuid(),
|
id: 'id' in item ? item.id : cuid(),
|
||||||
}
|
content:
|
||||||
|
item.type === ItemType.CONDITION
|
||||||
|
? defaultConditionContent
|
||||||
|
: undefined,
|
||||||
|
...item,
|
||||||
|
} as Item
|
||||||
if (item.outgoingEdgeId) {
|
if (item.outgoingEdgeId) {
|
||||||
const edgeIndex = typebot.edges.findIndex(byId(item.outgoingEdgeId))
|
const edgeIndex = typebot.edges.findIndex(byId(item.outgoingEdgeId))
|
||||||
edgeIndex !== -1
|
edgeIndex !== -1
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const Graph = ({
|
|||||||
const {
|
const {
|
||||||
setGraphPosition: setGlobalGraphPosition,
|
setGraphPosition: setGlobalGraphPosition,
|
||||||
setOpenedBlockId,
|
setOpenedBlockId,
|
||||||
|
setOpenedItemId,
|
||||||
setPreviewingEdge,
|
setPreviewingEdge,
|
||||||
connectingIds,
|
connectingIds,
|
||||||
} = useGraph()
|
} = useGraph()
|
||||||
@@ -126,6 +127,7 @@ export const Graph = ({
|
|||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
setOpenedBlockId(undefined)
|
setOpenedBlockId(undefined)
|
||||||
|
setOpenedItemId(undefined)
|
||||||
setPreviewingEdge(undefined)
|
setPreviewingEdge(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import React, { useEffect, useRef, useState } from 'react'
|
|||||||
import {
|
import {
|
||||||
BubbleBlock,
|
BubbleBlock,
|
||||||
BubbleBlockContent,
|
BubbleBlockContent,
|
||||||
ConditionBlock,
|
|
||||||
DraggableBlock,
|
DraggableBlock,
|
||||||
Block,
|
Block,
|
||||||
BlockWithOptions,
|
BlockWithOptions,
|
||||||
TextBubbleContent,
|
TextBubbleContent,
|
||||||
TextBubbleBlock,
|
TextBubbleBlock,
|
||||||
|
LogicBlockType,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { isBubbleBlock, isTextBubbleBlock } from 'utils'
|
import { isBubbleBlock, isTextBubbleBlock } from 'utils'
|
||||||
import { BlockNodeContent } from './BlockNodeContent/BlockNodeContent'
|
import { BlockNodeContent } from './BlockNodeContent/BlockNodeContent'
|
||||||
@@ -28,7 +28,12 @@ import { BlockSettings } from './SettingsPopoverContent/SettingsPopoverContent'
|
|||||||
import { TextBubbleEditor } from '../../../../blocks/bubbles/textBubble/components/TextBubbleEditor'
|
import { TextBubbleEditor } from '../../../../blocks/bubbles/textBubble/components/TextBubbleEditor'
|
||||||
import { TargetEndpoint } from '../../Endpoints'
|
import { TargetEndpoint } from '../../Endpoints'
|
||||||
import { MediaBubblePopoverContent } from './MediaBubblePopoverContent'
|
import { MediaBubblePopoverContent } from './MediaBubblePopoverContent'
|
||||||
import { NodePosition, useDragDistance, useGraph } from '../../../providers'
|
import {
|
||||||
|
NodePosition,
|
||||||
|
useBlockDnd,
|
||||||
|
useDragDistance,
|
||||||
|
useGraph,
|
||||||
|
} from '../../../providers'
|
||||||
import { ContextMenu } from '@/components/ContextMenu'
|
import { ContextMenu } from '@/components/ContextMenu'
|
||||||
import { setMultipleRefs } from '@/utils/helpers'
|
import { setMultipleRefs } from '@/utils/helpers'
|
||||||
import { hasDefaultConnector } from '../../../utils'
|
import { hasDefaultConnector } from '../../../utils'
|
||||||
@@ -53,6 +58,7 @@ export const BlockNode = ({
|
|||||||
setFocusedGroupId,
|
setFocusedGroupId,
|
||||||
previewingEdge,
|
previewingEdge,
|
||||||
} = useGraph()
|
} = useGraph()
|
||||||
|
const { mouseOverBlock, setMouseOverBlock, draggedItem } = useBlockDnd()
|
||||||
const { typebot, updateBlock } = useTypebot()
|
const { typebot, updateBlock } = useTypebot()
|
||||||
const [isConnecting, setIsConnecting] = useState(false)
|
const [isConnecting, setIsConnecting] = useState(false)
|
||||||
const [isPopoverOpened, setIsPopoverOpened] = useState(
|
const [isPopoverOpened, setIsPopoverOpened] = useState(
|
||||||
@@ -99,6 +105,8 @@ export const BlockNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
|
if (draggedItem !== undefined)
|
||||||
|
setMouseOverBlock({ id: block.id, ref: blockRef })
|
||||||
if (connectingIds)
|
if (connectingIds)
|
||||||
setConnectingIds({
|
setConnectingIds({
|
||||||
...connectingIds,
|
...connectingIds,
|
||||||
@@ -107,6 +115,7 @@ export const BlockNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
|
if (mouseOverBlock) setMouseOverBlock(undefined)
|
||||||
if (connectingIds?.target)
|
if (connectingIds?.target)
|
||||||
setConnectingIds({
|
setConnectingIds({
|
||||||
...connectingIds,
|
...connectingIds,
|
||||||
@@ -238,9 +247,8 @@ export const BlockNode = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasSettingsPopover = (
|
const hasSettingsPopover = (block: Block): block is BlockWithOptions =>
|
||||||
block: Block
|
!isBubbleBlock(block) && block.type !== LogicBlockType.CONDITION
|
||||||
): block is BlockWithOptions | ConditionBlock => !isBubbleBlock(block)
|
|
||||||
|
|
||||||
const isMediaBubbleBlock = (
|
const isMediaBubbleBlock = (
|
||||||
block: Block
|
block: Block
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import {
|
|||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { ExpandIcon } from '@/components/icons'
|
import { ExpandIcon } from '@/components/icons'
|
||||||
import {
|
import {
|
||||||
ConditionItem,
|
|
||||||
ConditionBlock,
|
|
||||||
InputBlockType,
|
InputBlockType,
|
||||||
IntegrationBlockType,
|
IntegrationBlockType,
|
||||||
LogicBlockType,
|
LogicBlockType,
|
||||||
@@ -34,7 +32,6 @@ import { SendEmailSettings } from '@/features/blocks/integrations/sendEmail'
|
|||||||
import { WebhookSettings } from '@/features/blocks/integrations/webhook'
|
import { WebhookSettings } from '@/features/blocks/integrations/webhook'
|
||||||
import { ZapierSettings } from '@/features/blocks/integrations/zapier'
|
import { ZapierSettings } from '@/features/blocks/integrations/zapier'
|
||||||
import { CodeSettings } from '@/features/blocks/logic/code'
|
import { CodeSettings } from '@/features/blocks/logic/code'
|
||||||
import { ConditionSettingsBody } from '@/features/blocks/logic/condition'
|
|
||||||
import { RedirectSettings } from '@/features/blocks/logic/redirect'
|
import { RedirectSettings } from '@/features/blocks/logic/redirect'
|
||||||
import { SetVariableSettings } from '@/features/blocks/logic/setVariable'
|
import { SetVariableSettings } from '@/features/blocks/logic/setVariable'
|
||||||
import { TypebotLinkSettingsForm } from '@/features/blocks/logic/typebotLink'
|
import { TypebotLinkSettingsForm } from '@/features/blocks/logic/typebotLink'
|
||||||
@@ -42,7 +39,7 @@ import { ButtonsOptionsForm } from '@/features/blocks/inputs/buttons'
|
|||||||
import { ChatwootSettingsForm } from '@/features/blocks/integrations/chatwoot'
|
import { ChatwootSettingsForm } from '@/features/blocks/integrations/chatwoot'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
block: BlockWithOptions | ConditionBlock
|
block: BlockWithOptions
|
||||||
webhook?: Webhook
|
webhook?: Webhook
|
||||||
onExpandClick: () => void
|
onExpandClick: () => void
|
||||||
onBlockChange: (updates: Partial<Block>) => void
|
onBlockChange: (updates: Partial<Block>) => void
|
||||||
@@ -88,18 +85,14 @@ export const BlockSettings = ({
|
|||||||
block,
|
block,
|
||||||
onBlockChange,
|
onBlockChange,
|
||||||
}: {
|
}: {
|
||||||
block: BlockWithOptions | ConditionBlock
|
block: BlockWithOptions
|
||||||
webhook?: Webhook
|
webhook?: Webhook
|
||||||
onBlockChange: (block: Partial<Block>) => void
|
onBlockChange: (block: Partial<Block>) => void
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const handleOptionsChange = (options: BlockOptions) => {
|
const handleOptionsChange = (options: BlockOptions) => {
|
||||||
onBlockChange({ options } as Partial<Block>)
|
onBlockChange({ options } as Partial<Block>)
|
||||||
}
|
}
|
||||||
const handleItemChange = (updates: Partial<ConditionItem>) => {
|
|
||||||
onBlockChange({
|
|
||||||
items: [{ ...(block as ConditionBlock).items[0], ...updates }],
|
|
||||||
} as Partial<Block>)
|
|
||||||
}
|
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case InputBlockType.TEXT: {
|
case InputBlockType.TEXT: {
|
||||||
return (
|
return (
|
||||||
@@ -189,11 +182,6 @@ export const BlockSettings = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case LogicBlockType.CONDITION: {
|
|
||||||
return (
|
|
||||||
<ConditionSettingsBody block={block} onItemChange={handleItemChange} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case LogicBlockType.REDIRECT: {
|
case LogicBlockType.REDIRECT: {
|
||||||
return (
|
return (
|
||||||
<RedirectSettings
|
<RedirectSettings
|
||||||
|
|||||||
@@ -6,13 +6,7 @@ import {
|
|||||||
useDragDistance,
|
useDragDistance,
|
||||||
} from '../../../providers'
|
} from '../../../providers'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import {
|
import { ChoiceInputBlock, Item, ItemIndices } from 'models'
|
||||||
ButtonItem,
|
|
||||||
ChoiceInputBlock,
|
|
||||||
Item,
|
|
||||||
ItemIndices,
|
|
||||||
ItemType,
|
|
||||||
} from 'models'
|
|
||||||
import React, { useRef, useState } from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { SourceEndpoint } from '../../Endpoints/SourceEndpoint'
|
import { SourceEndpoint } from '../../Endpoints/SourceEndpoint'
|
||||||
import { ItemNodeContent } from './ItemNodeContent'
|
import { ItemNodeContent } from './ItemNodeContent'
|
||||||
@@ -25,30 +19,37 @@ type Props = {
|
|||||||
indices: ItemIndices
|
indices: ItemIndices
|
||||||
onMouseDown?: (
|
onMouseDown?: (
|
||||||
blockNodePosition: { absolute: Coordinates; relative: Coordinates },
|
blockNodePosition: { absolute: Coordinates; relative: Coordinates },
|
||||||
item: ButtonItem
|
item: Item
|
||||||
) => void
|
) => void
|
||||||
|
connectionDisabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ItemNode = ({ item, indices, onMouseDown }: Props) => {
|
export const ItemNode = ({
|
||||||
|
item,
|
||||||
|
indices,
|
||||||
|
onMouseDown,
|
||||||
|
connectionDisabled,
|
||||||
|
}: Props) => {
|
||||||
const { typebot } = useTypebot()
|
const { typebot } = useTypebot()
|
||||||
const { previewingEdge } = useGraph()
|
const { previewingEdge } = useGraph()
|
||||||
const [isMouseOver, setIsMouseOver] = useState(false)
|
const [isMouseOver, setIsMouseOver] = useState(false)
|
||||||
const itemRef = useRef<HTMLDivElement | null>(null)
|
const itemRef = useRef<HTMLDivElement | null>(null)
|
||||||
const isPreviewing = previewingEdge?.from.itemId === item.id
|
const isPreviewing = previewingEdge?.from.itemId === item.id
|
||||||
const isConnectable = !(
|
const isConnectable =
|
||||||
typebot?.groups[indices.groupIndex].blocks[
|
!connectionDisabled &&
|
||||||
indices.blockIndex
|
!(
|
||||||
] as ChoiceInputBlock
|
typebot?.groups[indices.groupIndex].blocks[
|
||||||
)?.options?.isMultipleChoice
|
indices.blockIndex
|
||||||
const isReadOnly = item.type === ItemType.CONDITION
|
] as ChoiceInputBlock
|
||||||
|
)?.options?.isMultipleChoice
|
||||||
const onDrag = (position: NodePosition) => {
|
const onDrag = (position: NodePosition) => {
|
||||||
if (!onMouseDown || item.type !== ItemType.BUTTON) return
|
if (!onMouseDown) return
|
||||||
onMouseDown(position, item)
|
onMouseDown(position, item)
|
||||||
}
|
}
|
||||||
useDragDistance({
|
useDragDistance({
|
||||||
ref: itemRef,
|
ref: itemRef,
|
||||||
onDrag,
|
onDrag,
|
||||||
isDisabled: !onMouseDown || item.type !== ItemType.BUTTON,
|
isDisabled: !onMouseDown,
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleMouseEnter = () => setIsMouseOver(true)
|
const handleMouseEnter = () => setIsMouseOver(true)
|
||||||
@@ -63,19 +64,19 @@ export const ItemNode = ({ item, indices, onMouseDown }: Props) => {
|
|||||||
data-testid="item"
|
data-testid="item"
|
||||||
pos="relative"
|
pos="relative"
|
||||||
ref={setMultipleRefs([ref, itemRef])}
|
ref={setMultipleRefs([ref, itemRef])}
|
||||||
|
w="full"
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
align="center"
|
align="center"
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
shadow="sm"
|
shadow="sm"
|
||||||
_hover={isReadOnly ? {} : { shadow: 'md' }}
|
_hover={{ shadow: 'md' }}
|
||||||
transition="box-shadow 200ms, border-color 200ms"
|
transition="box-shadow 200ms, border-color 200ms"
|
||||||
rounded="md"
|
rounded="md"
|
||||||
borderWidth={isOpened || isPreviewing ? '2px' : '1px'}
|
borderWidth={isOpened || isPreviewing ? '2px' : '1px'}
|
||||||
borderColor={isOpened || isPreviewing ? 'blue.400' : 'gray.100'}
|
borderColor={isOpened || isPreviewing ? 'blue.400' : 'gray.100'}
|
||||||
margin={isOpened || isPreviewing ? '-1px' : 0}
|
margin={isOpened || isPreviewing ? '-1px' : 0}
|
||||||
pointerEvents={isReadOnly ? 'none' : 'all'}
|
|
||||||
bgColor="white"
|
bgColor="white"
|
||||||
w="full"
|
w="full"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ButtonNodeContent } from '@/features/blocks/inputs/buttons'
|
import { ButtonsItemNode } from '@/features/blocks/inputs/buttons'
|
||||||
import { ConditionNodeContent } from '@/features/blocks/logic/condition'
|
import { ConditionItemNode } from '@/features/blocks/logic/condition'
|
||||||
import { Item, ItemIndices, ItemType } from 'models'
|
import { Item, ItemIndices, ItemType } from 'models'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
@@ -13,13 +13,19 @@ export const ItemNodeContent = ({ item, indices, isMouseOver }: Props) => {
|
|||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case ItemType.BUTTON:
|
case ItemType.BUTTON:
|
||||||
return (
|
return (
|
||||||
<ButtonNodeContent
|
<ButtonsItemNode
|
||||||
item={item}
|
item={item}
|
||||||
isMouseOver={isMouseOver}
|
isMouseOver={isMouseOver}
|
||||||
indices={indices}
|
indices={indices}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case ItemType.CONDITION:
|
case ItemType.CONDITION:
|
||||||
return <ConditionNodeContent item={item} />
|
return (
|
||||||
|
<ConditionItemNode
|
||||||
|
item={item}
|
||||||
|
isMouseOver={isMouseOver}
|
||||||
|
indices={indices}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,10 @@ import {
|
|||||||
useGraph,
|
useGraph,
|
||||||
} from '../../../providers'
|
} from '../../../providers'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import {
|
import { BlockIndices, BlockWithItems, LogicBlockType, Item } from 'models'
|
||||||
ButtonItem,
|
|
||||||
BlockIndices,
|
|
||||||
BlockWithItems,
|
|
||||||
LogicBlockType,
|
|
||||||
} from 'models'
|
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { ItemNode } from './ItemNode'
|
import { ItemNode } from './ItemNode'
|
||||||
import { SourceEndpoint } from '../../Endpoints'
|
import { SourceEndpoint } from '../../Endpoints'
|
||||||
import { ItemNodeOverlay } from './ItemNodeOverlay'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
block: BlockWithItems
|
block: BlockWithItems
|
||||||
@@ -27,14 +21,13 @@ export const ItemNodesList = ({
|
|||||||
indices: { groupIndex, blockIndex },
|
indices: { groupIndex, blockIndex },
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { typebot, createItem, detachItemFromBlock } = useTypebot()
|
const { typebot, createItem, detachItemFromBlock } = useTypebot()
|
||||||
const { draggedItem, setDraggedItem, mouseOverGroup } = useBlockDnd()
|
const { draggedItem, setDraggedItem, mouseOverBlock } = useBlockDnd()
|
||||||
const placeholderRefs = useRef<HTMLDivElement[]>([])
|
const placeholderRefs = useRef<HTMLDivElement[]>([])
|
||||||
const { graphPosition } = useGraph()
|
const { graphPosition } = useGraph()
|
||||||
const groupId = typebot?.groups[groupIndex].id
|
const isDraggingOnCurrentBlock =
|
||||||
const isDraggingOnCurrentGroup =
|
(draggedItem && mouseOverBlock?.id === block.id) ?? false
|
||||||
(draggedItem && mouseOverGroup?.id === groupId) ?? false
|
const showPlaceholders =
|
||||||
const isReadOnly = block.type === LogicBlockType.CONDITION
|
draggedItem !== undefined && block.items[0].type === draggedItem.type
|
||||||
const showPlaceholders = draggedItem && !isReadOnly
|
|
||||||
|
|
||||||
const isLastBlock =
|
const isLastBlock =
|
||||||
typebot?.groups[groupIndex].blocks[blockIndex + 1] === undefined
|
typebot?.groups[groupIndex].blocks[blockIndex + 1] === undefined
|
||||||
@@ -60,29 +53,37 @@ export const ItemNodesList = ({
|
|||||||
useEventListener('mousemove', handleGlobalMouseMove)
|
useEventListener('mousemove', handleGlobalMouseMove)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mouseOverGroup?.id !== block.groupId)
|
if (!showPlaceholders) return
|
||||||
|
if (mouseOverBlock?.id !== block.id) {
|
||||||
setExpandedPlaceholderIndex(undefined)
|
setExpandedPlaceholderIndex(undefined)
|
||||||
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [mouseOverGroup?.id])
|
}, [mouseOverBlock?.id, showPlaceholders])
|
||||||
|
|
||||||
const handleMouseMoveOnGroup = (event: MouseEvent) => {
|
const handleMouseMoveOnBlock = (event: MouseEvent) => {
|
||||||
if (!isDraggingOnCurrentGroup || isReadOnly) return
|
if (!isDraggingOnCurrentBlock) return
|
||||||
const index = computeNearestPlaceholderIndex(event.pageY, placeholderRefs)
|
const index = computeNearestPlaceholderIndex(event.pageY, placeholderRefs)
|
||||||
setExpandedPlaceholderIndex(index)
|
setExpandedPlaceholderIndex(index)
|
||||||
}
|
}
|
||||||
useEventListener(
|
useEventListener(
|
||||||
'mousemove',
|
'mousemove',
|
||||||
handleMouseMoveOnGroup,
|
handleMouseMoveOnBlock,
|
||||||
mouseOverGroup?.ref.current
|
mouseOverBlock?.ref.current
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMouseUpOnGroup = (e: MouseEvent) => {
|
const handleMouseUpOnGroup = (e: MouseEvent) => {
|
||||||
|
if (
|
||||||
|
!showPlaceholders ||
|
||||||
|
!isDraggingOnCurrentBlock ||
|
||||||
|
!draggedItem ||
|
||||||
|
mouseOverBlock?.id !== block.id
|
||||||
|
)
|
||||||
|
return
|
||||||
setExpandedPlaceholderIndex(undefined)
|
setExpandedPlaceholderIndex(undefined)
|
||||||
if (!isDraggingOnCurrentGroup) return
|
|
||||||
const itemIndex = computeNearestPlaceholderIndex(e.pageY, placeholderRefs)
|
const itemIndex = computeNearestPlaceholderIndex(e.pageY, placeholderRefs)
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
setDraggedItem(undefined)
|
setDraggedItem(undefined)
|
||||||
createItem(draggedItem as ButtonItem, {
|
createItem(draggedItem, {
|
||||||
groupIndex,
|
groupIndex,
|
||||||
blockIndex,
|
blockIndex,
|
||||||
itemIndex,
|
itemIndex,
|
||||||
@@ -91,7 +92,7 @@ export const ItemNodesList = ({
|
|||||||
useEventListener(
|
useEventListener(
|
||||||
'mouseup',
|
'mouseup',
|
||||||
handleMouseUpOnGroup,
|
handleMouseUpOnGroup,
|
||||||
mouseOverGroup?.ref.current,
|
mouseOverBlock?.ref.current,
|
||||||
{
|
{
|
||||||
capture: true,
|
capture: true,
|
||||||
}
|
}
|
||||||
@@ -101,9 +102,9 @@ export const ItemNodesList = ({
|
|||||||
(itemIndex: number) =>
|
(itemIndex: number) =>
|
||||||
(
|
(
|
||||||
{ absolute, relative }: { absolute: Coordinates; relative: Coordinates },
|
{ absolute, relative }: { absolute: Coordinates; relative: Coordinates },
|
||||||
item: ButtonItem
|
item: Item
|
||||||
) => {
|
) => {
|
||||||
if (!typebot || isReadOnly) return
|
if (!typebot || block.items.length <= 1) return
|
||||||
placeholderRefs.current.splice(itemIndex + 1, 1)
|
placeholderRefs.current.splice(itemIndex + 1, 1)
|
||||||
detachItemFromBlock({ groupIndex, blockIndex, itemIndex })
|
detachItemFromBlock({ groupIndex, blockIndex, itemIndex })
|
||||||
setPosition(absolute)
|
setPosition(absolute)
|
||||||
@@ -119,13 +120,7 @@ export const ItemNodesList = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack flex={1} spacing={1} maxW="full" onClick={stopPropagating}>
|
||||||
flex={1}
|
|
||||||
spacing={1}
|
|
||||||
maxW="full"
|
|
||||||
onClick={stopPropagating}
|
|
||||||
pointerEvents={isReadOnly ? 'none' : 'all'}
|
|
||||||
>
|
|
||||||
<Flex
|
<Flex
|
||||||
ref={handlePushElementRef(0)}
|
ref={handlePushElementRef(0)}
|
||||||
h={showPlaceholders && expandedPlaceholderIndex === 0 ? '50px' : '2px'}
|
h={showPlaceholders && expandedPlaceholderIndex === 0 ? '50px' : '2px'}
|
||||||
@@ -161,13 +156,15 @@ export const ItemNodesList = ({
|
|||||||
py="2"
|
py="2"
|
||||||
borderWidth="1px"
|
borderWidth="1px"
|
||||||
borderColor="gray.300"
|
borderColor="gray.300"
|
||||||
bgColor={isReadOnly ? '' : 'gray.50'}
|
bgColor={'gray.50'}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
pos="relative"
|
pos="relative"
|
||||||
align="center"
|
align="center"
|
||||||
cursor={isReadOnly ? 'pointer' : 'not-allowed'}
|
cursor="not-allowed"
|
||||||
>
|
>
|
||||||
<Text color={isReadOnly ? 'inherit' : 'gray.500'}>Default</Text>
|
<Text color="gray.500">
|
||||||
|
{block.type === LogicBlockType.CONDITION ? 'Else' : 'Default'}
|
||||||
|
</Text>
|
||||||
<SourceEndpoint
|
<SourceEndpoint
|
||||||
source={{
|
source={{
|
||||||
groupId: block.groupId,
|
groupId: block.groupId,
|
||||||
@@ -181,16 +178,23 @@ export const ItemNodesList = ({
|
|||||||
|
|
||||||
{draggedItem && draggedItem.blockId === block.id && (
|
{draggedItem && draggedItem.blockId === block.id && (
|
||||||
<Portal>
|
<Portal>
|
||||||
<ItemNodeOverlay
|
<Flex
|
||||||
item={draggedItem}
|
pointerEvents="none"
|
||||||
pos="fixed"
|
pos="fixed"
|
||||||
top="0"
|
top="0"
|
||||||
left="0"
|
left="0"
|
||||||
style={{
|
style={{
|
||||||
transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg) scale(${graphPosition.scale})`,
|
transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg) scale(${graphPosition.scale})`,
|
||||||
}}
|
}}
|
||||||
|
w="220px"
|
||||||
transformOrigin="0 0 0"
|
transformOrigin="0 0 0"
|
||||||
/>
|
>
|
||||||
|
<ItemNode
|
||||||
|
item={draggedItem}
|
||||||
|
indices={{ groupIndex, blockIndex, itemIndex: 0 }}
|
||||||
|
connectionDisabled
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
</Portal>
|
</Portal>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEventListener } from '@chakra-ui/react'
|
import { useEventListener } from '@chakra-ui/react'
|
||||||
import { ButtonItem, DraggableBlock, DraggableBlockType } from 'models'
|
import { DraggableBlock, DraggableBlockType, Item } from 'models'
|
||||||
import {
|
import {
|
||||||
createContext,
|
createContext,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { Coordinates } from './GraphProvider'
|
import { Coordinates } from './GraphProvider'
|
||||||
|
|
||||||
type GroupInfo = {
|
type NodeInfo = {
|
||||||
id: string
|
id: string
|
||||||
ref: React.MutableRefObject<HTMLDivElement | null>
|
ref: React.MutableRefObject<HTMLDivElement | null>
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,12 @@ const graphDndContext = createContext<{
|
|||||||
setDraggedBlockType: Dispatch<SetStateAction<DraggableBlockType | undefined>>
|
setDraggedBlockType: Dispatch<SetStateAction<DraggableBlockType | undefined>>
|
||||||
draggedBlock?: DraggableBlock
|
draggedBlock?: DraggableBlock
|
||||||
setDraggedBlock: Dispatch<SetStateAction<DraggableBlock | undefined>>
|
setDraggedBlock: Dispatch<SetStateAction<DraggableBlock | undefined>>
|
||||||
draggedItem?: ButtonItem
|
draggedItem?: Item
|
||||||
setDraggedItem: Dispatch<SetStateAction<ButtonItem | undefined>>
|
setDraggedItem: Dispatch<SetStateAction<Item | undefined>>
|
||||||
mouseOverGroup?: GroupInfo
|
mouseOverGroup?: NodeInfo
|
||||||
setMouseOverGroup: Dispatch<SetStateAction<GroupInfo | undefined>>
|
setMouseOverGroup: Dispatch<SetStateAction<NodeInfo | undefined>>
|
||||||
|
mouseOverBlock?: NodeInfo
|
||||||
|
setMouseOverBlock: Dispatch<SetStateAction<NodeInfo | undefined>>
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
}>({})
|
}>({})
|
||||||
@@ -36,8 +38,9 @@ export const GraphDndProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
const [draggedBlockType, setDraggedBlockType] = useState<
|
const [draggedBlockType, setDraggedBlockType] = useState<
|
||||||
DraggableBlockType | undefined
|
DraggableBlockType | undefined
|
||||||
>()
|
>()
|
||||||
const [draggedItem, setDraggedItem] = useState<ButtonItem | undefined>()
|
const [draggedItem, setDraggedItem] = useState<Item | undefined>()
|
||||||
const [mouseOverGroup, setMouseOverGroup] = useState<GroupInfo>()
|
const [mouseOverGroup, setMouseOverGroup] = useState<NodeInfo>()
|
||||||
|
const [mouseOverBlock, setMouseOverBlock] = useState<NodeInfo>()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<graphDndContext.Provider
|
<graphDndContext.Provider
|
||||||
@@ -50,6 +53,8 @@ export const GraphDndProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
setDraggedItem,
|
setDraggedItem,
|
||||||
mouseOverGroup,
|
mouseOverGroup,
|
||||||
setMouseOverGroup,
|
setMouseOverGroup,
|
||||||
|
mouseOverBlock,
|
||||||
|
setMouseOverBlock,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ const graphContext = createContext<{
|
|||||||
addTargetEndpoint: (endpoint: Endpoint) => void
|
addTargetEndpoint: (endpoint: Endpoint) => void
|
||||||
openedBlockId?: string
|
openedBlockId?: string
|
||||||
setOpenedBlockId: Dispatch<SetStateAction<string | undefined>>
|
setOpenedBlockId: Dispatch<SetStateAction<string | undefined>>
|
||||||
|
openedItemId?: string
|
||||||
|
setOpenedItemId: Dispatch<SetStateAction<string | undefined>>
|
||||||
isReadOnly: boolean
|
isReadOnly: boolean
|
||||||
focusedGroupId?: string
|
focusedGroupId?: string
|
||||||
setFocusedGroupId: Dispatch<SetStateAction<string | undefined>>
|
setFocusedGroupId: Dispatch<SetStateAction<string | undefined>>
|
||||||
@@ -92,6 +94,7 @@ export const GraphProvider = ({
|
|||||||
const [sourceEndpoints, setSourceEndpoints] = useState<IdMap<Endpoint>>({})
|
const [sourceEndpoints, setSourceEndpoints] = useState<IdMap<Endpoint>>({})
|
||||||
const [targetEndpoints, setTargetEndpoints] = useState<IdMap<Endpoint>>({})
|
const [targetEndpoints, setTargetEndpoints] = useState<IdMap<Endpoint>>({})
|
||||||
const [openedBlockId, setOpenedBlockId] = useState<string>()
|
const [openedBlockId, setOpenedBlockId] = useState<string>()
|
||||||
|
const [openedItemId, setOpenedItemId] = useState<string>()
|
||||||
const [focusedGroupId, setFocusedGroupId] = useState<string>()
|
const [focusedGroupId, setFocusedGroupId] = useState<string>()
|
||||||
|
|
||||||
const addSourceEndpoint = (endpoint: Endpoint) => {
|
const addSourceEndpoint = (endpoint: Endpoint) => {
|
||||||
@@ -123,6 +126,8 @@ export const GraphProvider = ({
|
|||||||
addTargetEndpoint,
|
addTargetEndpoint,
|
||||||
openedBlockId,
|
openedBlockId,
|
||||||
setOpenedBlockId,
|
setOpenedBlockId,
|
||||||
|
openedItemId,
|
||||||
|
setOpenedItemId,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
focusedGroupId,
|
focusedGroupId,
|
||||||
setFocusedGroupId,
|
setFocusedGroupId,
|
||||||
|
|||||||
@@ -53,8 +53,7 @@
|
|||||||
"id": "eMk84KvFM53sBxchTeackR",
|
"id": "eMk84KvFM53sBxchTeackR",
|
||||||
"blocks": [
|
"blocks": [
|
||||||
{
|
{
|
||||||
"id": "s5hz7HQki66cwELvk2738MJ",
|
"id": "sv8uvEXgYWQNMfZWcdbfyCs",
|
||||||
"groupId": "eMk84KvFM53sBxchTeackR",
|
|
||||||
"type": "Condition",
|
"type": "Condition",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
@@ -70,13 +69,7 @@
|
|||||||
"logicalOperator": "AND"
|
"logicalOperator": "AND"
|
||||||
},
|
},
|
||||||
"outgoingEdgeId": "nDjMjM11xPQF7c9Be6ukdY"
|
"outgoingEdgeId": "nDjMjM11xPQF7c9Be6ukdY"
|
||||||
}
|
},
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sv8uvEXgYWQNMfZWcdbfyCs",
|
|
||||||
"type": "Condition",
|
|
||||||
"items": [
|
|
||||||
{
|
{
|
||||||
"id": "ijYfW38tGhCMRrCtmR3bcr",
|
"id": "ijYfW38tGhCMRrCtmR3bcr",
|
||||||
"type": 1,
|
"type": 1,
|
||||||
|
|||||||
@@ -13,14 +13,15 @@ export const executeCondition = (
|
|||||||
block: ConditionBlock,
|
block: ConditionBlock,
|
||||||
{ typebot: { variables } }: LogicState
|
{ typebot: { variables } }: LogicState
|
||||||
): EdgeId | undefined => {
|
): EdgeId | undefined => {
|
||||||
const { content } = block.items[0]
|
const passedCondition = block.items.find((item) => {
|
||||||
const isConditionPassed =
|
const { content } = item
|
||||||
content.logicalOperator === LogicalOperator.AND
|
const isConditionPassed =
|
||||||
? content.comparisons.every(executeComparison(variables))
|
content.logicalOperator === LogicalOperator.AND
|
||||||
: content.comparisons.some(executeComparison(variables))
|
? content.comparisons.every(executeComparison(variables))
|
||||||
return isConditionPassed
|
: content.comparisons.some(executeComparison(variables))
|
||||||
? block.items[0].outgoingEdgeId
|
return isConditionPassed
|
||||||
: block.outgoingEdgeId
|
})
|
||||||
|
return passedCondition ? passedCondition.outgoingEdgeId : block.outgoingEdgeId
|
||||||
}
|
}
|
||||||
|
|
||||||
const executeComparison =
|
const executeComparison =
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type ItemIndices = {
|
|||||||
groupIndex: number
|
groupIndex: number
|
||||||
itemIndex: number
|
itemIndex: number
|
||||||
}
|
}
|
||||||
const itemScema = buttonItemSchema.or(conditionItemSchema)
|
const itemSchema = buttonItemSchema.or(conditionItemSchema)
|
||||||
|
|
||||||
export type ItemBase = z.infer<typeof itemBaseSchema>
|
export type ItemBase = z.infer<typeof itemBaseSchema>
|
||||||
export type Item = z.infer<typeof itemScema>
|
export type Item = z.infer<typeof itemSchema>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export const toggle = () => {
|
|||||||
? closePopup(existingPopup)
|
? closePopup(existingPopup)
|
||||||
: openPopup(existingPopup)
|
: openPopup(existingPopup)
|
||||||
const existingBubble = document.querySelector('#typebot-bubble')
|
const existingBubble = document.querySelector('#typebot-bubble')
|
||||||
console.log(existingBubble)
|
|
||||||
if (existingBubble)
|
if (existingBubble)
|
||||||
isIframeOpened(existingBubble)
|
isIframeOpened(existingBubble)
|
||||||
? closeIframe(existingBubble)
|
? closeIframe(existingBubble)
|
||||||
|
|||||||
Reference in New Issue
Block a user