2
0

feat(inputs): Add buttons input

This commit is contained in:
Baptiste Arnaud
2022-01-12 09:10:59 +01:00
parent b20bcb1408
commit c02c61cd8b
47 changed files with 1109 additions and 243 deletions

View File

@ -1,24 +1,17 @@
import { useEventListener } from '@chakra-ui/hooks'
import { Coordinates } from '@dnd-kit/core/dist/types'
import { headerHeight } from 'components/shared/TypebotHeader/TypebotHeader'
import {
blockWidth,
firstStepOffsetY,
spaceBetweenSteps,
stubLength,
useGraph,
} from 'contexts/GraphContext'
import { useGraph, ConnectingIds } from 'contexts/GraphContext'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { Target } from 'models'
import React, { useMemo, useState } from 'react'
import {
computeFlowChartConnectorPath,
getAnchorsPosition,
computeDrawingConnectedPath,
computeDrawingPathToMouse,
} from 'services/graph'
import { roundCorners } from 'svg-round-corners'
export const DrawingEdge = () => {
const { graphPosition, setConnectingIds, connectingIds } = useGraph()
const { typebot, updateStep } = useTypebot()
const { typebot, updateStep, updateChoiceItem } = useTypebot()
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 })
const sourceBlock = useMemo(
@ -28,33 +21,21 @@ export const DrawingEdge = () => {
)
const path = useMemo(() => {
if (!sourceBlock || !typebot) return ``
if (connectingIds?.target) {
const targetedBlock = typebot?.blocks.byId[connectingIds.target.blockId]
const targetedStepIndex = connectingIds.target.stepId
? targetedBlock.stepIds.findIndex(
(stepId) => stepId === connectingIds.target?.stepId
)
: undefined
const anchorsPosition = getAnchorsPosition(
sourceBlock,
targetedBlock,
sourceBlock?.stepIds.findIndex(
(stepId) => stepId === connectingIds?.source.stepId
),
targetedStepIndex
)
return computeFlowChartConnectorPath(anchorsPosition)
}
return computeConnectingEdgePath(
sourceBlock?.graphCoordinates,
mousePosition,
sourceBlock.stepIds.findIndex(
(stepId) => stepId === connectingIds?.source.stepId
)
)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sourceBlock, mousePosition])
if (!sourceBlock || !typebot || !connectingIds) return ``
return connectingIds?.target
? computeDrawingConnectedPath(
connectingIds as Omit<ConnectingIds, 'target'> & { target: Target },
sourceBlock,
typebot
)
: computeDrawingPathToMouse(
sourceBlock,
connectingIds,
mousePosition,
typebot.steps
)
}, [sourceBlock, typebot, connectingIds, mousePosition])
const handleMouseMove = (e: MouseEvent) => {
setMousePosition({
@ -64,11 +45,19 @@ export const DrawingEdge = () => {
}
useEventListener('mousemove', handleMouseMove)
useEventListener('mouseup', () => {
if (connectingIds?.target)
updateStep(connectingIds.source.stepId, { target: connectingIds.target })
if (connectingIds?.target) createNewEdge(connectingIds)
setConnectingIds(null)
})
const createNewEdge = (connectingIds: ConnectingIds) =>
connectingIds.source.choiceItemId
? updateChoiceItem(connectingIds.source.choiceItemId, {
target: connectingIds.target,
})
: updateStep(connectingIds.source.stepId, {
target: connectingIds.target,
})
if ((mousePosition.x === 0 && mousePosition.y === 0) || !connectingIds)
return <></>
return (
@ -81,44 +70,3 @@ export const DrawingEdge = () => {
/>
)
}
const computeConnectingEdgePath = (
blockPosition: Coordinates,
mousePosition: Coordinates,
stepIndex: number
): string => {
const sourcePosition = {
x:
mousePosition.x - blockPosition.x > blockWidth / 2
? blockPosition.x + blockWidth - 40
: blockPosition.x + 40,
y: blockPosition.y + firstStepOffsetY + stepIndex * spaceBetweenSteps,
}
const sourceType =
mousePosition.x - blockPosition.x > blockWidth / 2 ? 'right' : 'left'
const segments = computeThreeSegments(
sourcePosition,
mousePosition,
sourceType
)
return roundCorners(
`M${sourcePosition.x},${sourcePosition.y} ${segments}`,
10
).path
}
const computeThreeSegments = (
sourcePosition: Coordinates,
targetPosition: Coordinates,
sourceType: 'right' | 'left'
) => {
const segments = []
const firstSegmentX =
sourceType === 'right'
? sourcePosition.x + stubLength + 40
: sourcePosition.x - stubLength - 40
segments.push(`L${firstSegmentX},${sourcePosition.y}`)
segments.push(`L${firstSegmentX},${targetPosition.y}`)
segments.push(`L${targetPosition.x},${targetPosition.y}`)
return segments.join(' ')
}

View File

@ -1,10 +1,15 @@
import { isDefined } from '@udecode/plate-core'
import assert from 'assert'
import { Coordinates, useGraph } from 'contexts/GraphContext'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { ChoiceItem } from 'models'
import React, { useMemo } from 'react'
import {
getAnchorsPosition,
computeFlowChartConnectorPath,
getSourceChoiceItemIndex,
} from 'services/graph'
import { isChoiceInput } from 'utils'
export type AnchorsPositionProps = {
sourcePosition: Coordinates
@ -13,7 +18,13 @@ export type AnchorsPositionProps = {
totalSegments: number
}
export const Edge = ({ stepId }: { stepId: string }) => {
export const Edge = ({
stepId,
item,
}: {
stepId: string
item?: ChoiceItem
}) => {
const { typebot } = useTypebot()
const { previewingIds } = useGraph()
const step = typebot?.steps.byId[stepId]
@ -27,29 +38,35 @@ export const Edge = ({ stepId }: { stepId: string }) => {
const { sourceBlock, targetBlock, targetStepIndex } = useMemo(() => {
if (!typebot) return {}
const step = typebot.steps.byId[stepId]
if (!step.target) return {}
const sourceBlock = typebot.blocks.byId[step.blockId]
const targetBlock = typebot.blocks.byId[step.target.blockId]
const targetStepIndex = step.target.stepId
? targetBlock.stepIds.indexOf(step.target.stepId)
const targetBlockId = item?.target?.blockId ?? step.target?.blockId
assert(isDefined(targetBlockId))
const targetBlock = typebot.blocks.byId[targetBlockId]
const targetStepId = item?.target?.stepId ?? step.target?.stepId
const targetStepIndex = targetStepId
? targetBlock.stepIds.indexOf(targetStepId)
: undefined
return {
sourceBlock,
targetBlock,
targetStepIndex,
}
}, [stepId, typebot])
}, [item?.target?.blockId, item?.target?.stepId, stepId, typebot])
const path = useMemo(() => {
if (!sourceBlock || !targetBlock) return ``
const anchorsPosition = getAnchorsPosition(
if (!sourceBlock || !targetBlock || !step) return ``
const sourceChoiceItemIndex = isChoiceInput(step)
? getSourceChoiceItemIndex(step, item?.id)
: undefined
const anchorsPosition = getAnchorsPosition({
sourceBlock,
targetBlock,
sourceBlock.stepIds.indexOf(stepId),
targetStepIndex
)
sourceStepIndex: sourceBlock.stepIds.indexOf(stepId),
targetStepIndex,
sourceChoiceItemIndex,
})
return computeFlowChartConnectorPath(anchorsPosition)
}, [sourceBlock, stepId, targetBlock, targetStepIndex])
}, [item, sourceBlock, step, stepId, targetBlock, targetStepIndex])
return (
<path

View File

@ -1,7 +1,8 @@
import { chakra } from '@chakra-ui/system'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { ChoiceItem } from 'models'
import React, { useMemo } from 'react'
import { isDefined } from 'utils'
import { isDefined, isSingleChoiceInput } from 'utils'
import { DrawingEdge } from './DrawingEdge'
import { Edge } from './Edge'
@ -13,6 +14,18 @@ export const Edges = () => {
isDefined(typebot.steps.byId[stepId].target)
)
}, [typebot])
const singleChoiceItemsWithTarget: ChoiceItem[] = useMemo(() => {
if (!typebot) return []
return typebot.choiceItems.allIds
.filter(
(itemId) =>
isDefined(typebot.choiceItems.byId[itemId].target) &&
isSingleChoiceInput(
typebot.steps.byId[typebot.choiceItems.byId[itemId].stepId]
)
)
.map((itemId) => typebot.choiceItems.byId[itemId])
}, [typebot])
return (
<chakra.svg
@ -27,6 +40,9 @@ export const Edges = () => {
{stepIdsWithTarget.map((stepId) => (
<Edge key={stepId} stepId={stepId} />
))}
{singleChoiceItemsWithTarget.map((item) => (
<Edge key={item.id} stepId={item.stepId} item={item} />
))}
<marker
id={'arrow'}
refX="8"