feat(inputs): ✨ Add number input
This commit is contained in:
@ -1,16 +1,19 @@
|
||||
import { Button, ButtonProps, Flex, HStack } from '@chakra-ui/react'
|
||||
import { StepType } from 'models'
|
||||
import { BubbleStepType, InputStepType, StepType } from 'models'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { StepIcon } from './StepIcon'
|
||||
import { StepLabel } from './StepLabel'
|
||||
import { StepTypeLabel } from './StepTypeLabel'
|
||||
|
||||
export const StepCard = ({
|
||||
type,
|
||||
onMouseDown,
|
||||
}: {
|
||||
type: StepType
|
||||
onMouseDown: (e: React.MouseEvent, type: StepType) => void
|
||||
type: BubbleStepType | InputStepType
|
||||
onMouseDown: (
|
||||
e: React.MouseEvent,
|
||||
type: BubbleStepType | InputStepType
|
||||
) => void
|
||||
}) => {
|
||||
const { draggedStepType } = useDnd()
|
||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||
@ -35,7 +38,7 @@ export const StepCard = ({
|
||||
{!isMouseDown && (
|
||||
<>
|
||||
<StepIcon type={type} />
|
||||
<StepLabel type={type} />
|
||||
<StepTypeLabel type={type} />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@ -62,7 +65,7 @@ export const StepCardOverlay = ({
|
||||
{...props}
|
||||
>
|
||||
<StepIcon type={type} />
|
||||
<StepLabel type={type} />
|
||||
<StepTypeLabel type={type} />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
@ -1,22 +1,25 @@
|
||||
import { ChatIcon, FlagIcon, TextIcon } from 'assets/icons'
|
||||
import { StepType } from 'models'
|
||||
import { ChatIcon, FlagIcon, NumberIcon, TextIcon } from 'assets/icons'
|
||||
import { BubbleStepType, InputStepType, StepType } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
type StepIconProps = { type: StepType }
|
||||
|
||||
export const StepIcon = ({ type }: StepIconProps) => {
|
||||
switch (type) {
|
||||
case StepType.TEXT: {
|
||||
case BubbleStepType.TEXT: {
|
||||
return <ChatIcon />
|
||||
}
|
||||
case StepType.TEXT: {
|
||||
case InputStepType.TEXT: {
|
||||
return <TextIcon />
|
||||
}
|
||||
case StepType.START: {
|
||||
case InputStepType.NUMBER: {
|
||||
return <NumberIcon />
|
||||
}
|
||||
case 'start': {
|
||||
return <FlagIcon />
|
||||
}
|
||||
default: {
|
||||
return <TextIcon />
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import { StepType } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
type Props = { type: StepType }
|
||||
|
||||
export const StepLabel = ({ type }: Props) => {
|
||||
switch (type) {
|
||||
case StepType.TEXT: {
|
||||
return <Text>Text</Text>
|
||||
}
|
||||
case StepType.TEXT_INPUT: {
|
||||
return <Text>Text</Text>
|
||||
}
|
||||
default: {
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import { BubbleStepType, InputStepType, StepType } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
type Props = { type: StepType }
|
||||
|
||||
export const StepTypeLabel = ({ type }: Props) => {
|
||||
switch (type) {
|
||||
case BubbleStepType.TEXT:
|
||||
case InputStepType.TEXT: {
|
||||
return <Text>Text</Text>
|
||||
}
|
||||
case InputStepType.NUMBER: {
|
||||
return <Text>Number</Text>
|
||||
}
|
||||
default: {
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
}
|
@ -5,19 +5,11 @@ import {
|
||||
SimpleGrid,
|
||||
useEventListener,
|
||||
} from '@chakra-ui/react'
|
||||
import { StepType } from 'models'
|
||||
import { BubbleStepType, InputStepType } from 'models'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import React, { useState } from 'react'
|
||||
import { StepCard, StepCardOverlay } from './StepCard'
|
||||
|
||||
export const stepListItems: {
|
||||
bubbles: { type: StepType }[]
|
||||
inputs: { type: StepType }[]
|
||||
} = {
|
||||
bubbles: [{ type: StepType.TEXT }],
|
||||
inputs: [{ type: StepType.TEXT_INPUT }],
|
||||
}
|
||||
|
||||
export const StepTypesList = () => {
|
||||
const { setDraggedStepType, draggedStepType } = useDnd()
|
||||
const [position, setPosition] = useState({
|
||||
@ -37,7 +29,10 @@ export const StepTypesList = () => {
|
||||
}
|
||||
useEventListener('mousemove', handleMouseMove)
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent, type: StepType) => {
|
||||
const handleMouseDown = (
|
||||
e: React.MouseEvent,
|
||||
type: BubbleStepType | InputStepType
|
||||
) => {
|
||||
const element = e.currentTarget as HTMLDivElement
|
||||
const rect = element.getBoundingClientRect()
|
||||
const relativeX = e.clientX - rect.left
|
||||
@ -77,8 +72,8 @@ export const StepTypesList = () => {
|
||||
Bubbles
|
||||
</Text>
|
||||
<SimpleGrid columns={2} spacing="2">
|
||||
{stepListItems.bubbles.map((props) => (
|
||||
<StepCard key={props.type} onMouseDown={handleMouseDown} {...props} />
|
||||
{Object.values(BubbleStepType).map((type) => (
|
||||
<StepCard key={type} type={type} onMouseDown={handleMouseDown} />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
|
||||
@ -86,8 +81,8 @@ export const StepTypesList = () => {
|
||||
Inputs
|
||||
</Text>
|
||||
<SimpleGrid columns={2} spacing="2">
|
||||
{stepListItems.inputs.map((props) => (
|
||||
<StepCard key={props.type} onMouseDown={handleMouseDown} {...props} />
|
||||
{Object.values(InputStepType).map((type) => (
|
||||
<StepCard key={type} type={type} onMouseDown={handleMouseDown} />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
{draggedStepType && (
|
||||
|
@ -0,0 +1,84 @@
|
||||
import { FormLabel, HStack, Stack } from '@chakra-ui/react'
|
||||
import { SmartNumberInput } from 'components/settings/SmartNumberInput'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { NumberInputOptions } from 'models'
|
||||
import React from 'react'
|
||||
import { removeUndefinedFields } from 'services/utils'
|
||||
|
||||
type NumberInputSettingsBodyProps = {
|
||||
options?: NumberInputOptions
|
||||
onOptionsChange: (options: NumberInputOptions) => void
|
||||
}
|
||||
|
||||
export const NumberInputSettingsBody = ({
|
||||
options,
|
||||
onOptionsChange,
|
||||
}: NumberInputSettingsBodyProps) => {
|
||||
const handlePlaceholderChange = (placeholder: string) =>
|
||||
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
|
||||
const handleButtonLabelChange = (button: string) =>
|
||||
onOptionsChange({ ...options, labels: { ...options?.labels, button } })
|
||||
const handleMinChange = (min?: number) =>
|
||||
onOptionsChange(removeUndefinedFields({ ...options, min }))
|
||||
const handleMaxChange = (max?: number) =>
|
||||
onOptionsChange(removeUndefinedFields({ ...options, max }))
|
||||
const handleStepChange = (step?: number) =>
|
||||
onOptionsChange(removeUndefinedFields({ ...options, step }))
|
||||
|
||||
return (
|
||||
<Stack spacing={4}>
|
||||
<Stack>
|
||||
<FormLabel mb="0" htmlFor="placeholder">
|
||||
Placeholder:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
id="placeholder"
|
||||
initialValue={options?.labels?.placeholder ?? 'Type your answer...'}
|
||||
delay={100}
|
||||
onChange={handlePlaceholderChange}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
id="button"
|
||||
initialValue={options?.labels?.button ?? 'Send'}
|
||||
delay={100}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
<HStack justifyContent="space-between">
|
||||
<FormLabel mb="0" htmlFor="min">
|
||||
Min:
|
||||
</FormLabel>
|
||||
<SmartNumberInput
|
||||
id="min"
|
||||
initialValue={options?.min}
|
||||
onValueChange={handleMinChange}
|
||||
/>
|
||||
</HStack>
|
||||
<HStack justifyContent="space-between">
|
||||
<FormLabel mb="0" htmlFor="max">
|
||||
Max:
|
||||
</FormLabel>
|
||||
<SmartNumberInput
|
||||
id="max"
|
||||
initialValue={options?.max}
|
||||
onValueChange={handleMaxChange}
|
||||
/>
|
||||
</HStack>
|
||||
<HStack justifyContent="space-between">
|
||||
<FormLabel mb="0" htmlFor="step">
|
||||
Step:
|
||||
</FormLabel>
|
||||
<SmartNumberInput
|
||||
id="step"
|
||||
initialValue={options?.step}
|
||||
onValueChange={handleStepChange}
|
||||
/>
|
||||
</HStack>
|
||||
</Stack>
|
||||
)
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { PopoverContent, PopoverArrow, PopoverBody } from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { Step, StepType, TextInputOptions } from 'models'
|
||||
import { InputStepType, Step, TextInputOptions } from 'models'
|
||||
import { NumberInputSettingsBody } from './NumberInputSettingsBody'
|
||||
import { TextInputSettingsBody } from './TextInputSettingsBody'
|
||||
|
||||
type Props = {
|
||||
@ -25,7 +26,7 @@ const SettingsPopoverBodyContent = ({ step }: Props) => {
|
||||
updateStep(step.id, { options } as Partial<Step>)
|
||||
|
||||
switch (step.type) {
|
||||
case StepType.TEXT_INPUT: {
|
||||
case InputStepType.TEXT: {
|
||||
return (
|
||||
<TextInputSettingsBody
|
||||
options={step.options}
|
||||
@ -33,6 +34,14 @@ const SettingsPopoverBodyContent = ({ step }: Props) => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
case InputStepType.NUMBER: {
|
||||
return (
|
||||
<NumberInputSettingsBody
|
||||
options={step.options}
|
||||
onOptionsChange={handleOptionsChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default: {
|
||||
return <></>
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { Flex, Text } from '@chakra-ui/react'
|
||||
import { Step, StartStep, StepType } from 'models'
|
||||
|
||||
export const StepContent = (props: Step | StartStep) => {
|
||||
switch (props.type) {
|
||||
case StepType.TEXT: {
|
||||
return (
|
||||
<Flex
|
||||
flexDir={'column'}
|
||||
opacity={props.content.html === '' ? '0.5' : '1'}
|
||||
className="slate-html-container"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
props.content.html === ''
|
||||
? `<p>Click to edit...</p>`
|
||||
: props.content.html,
|
||||
}}
|
||||
></Flex>
|
||||
)
|
||||
}
|
||||
case StepType.TEXT_INPUT: {
|
||||
return <Text color={'gray.500'}>Type your answer...</Text>
|
||||
}
|
||||
case StepType.START: {
|
||||
return <Text>{props.label}</Text>
|
||||
}
|
||||
default: {
|
||||
return <Text>No input</Text>
|
||||
}
|
||||
}
|
||||
}
|
@ -11,15 +11,15 @@ import { Block, Step } from 'models'
|
||||
import { SourceEndpoint } from './SourceEndpoint'
|
||||
import { useGraph } from 'contexts/GraphContext'
|
||||
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
|
||||
import { isDefined } from 'utils'
|
||||
import { isDefined, isTextBubbleStep } from 'utils'
|
||||
import { Coordinates } from '@dnd-kit/core/dist/types'
|
||||
import { TextEditor } from './TextEditor/TextEditor'
|
||||
import { StepContent } from './StepContent'
|
||||
import { StepNodeLabel } from './StepNodeLabel'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { ContextMenu } from 'components/shared/ContextMenu'
|
||||
import { StepNodeContextMenu } from './RightClickMenu'
|
||||
import { SettingsPopoverContent } from './SettingsPopoverContent'
|
||||
import { isStepText } from 'services/typebots'
|
||||
import { DraggableStep } from 'contexts/DndContext'
|
||||
|
||||
export const StepNode = ({
|
||||
step,
|
||||
@ -34,7 +34,7 @@ export const StepNode = ({
|
||||
onMouseMoveTopOfElement?: () => void
|
||||
onMouseDown?: (
|
||||
stepNodePosition: { absolute: Coordinates; relative: Coordinates },
|
||||
step: Step
|
||||
step: DraggableStep
|
||||
) => void
|
||||
}) => {
|
||||
const { setConnectingIds, connectingIds } = useGraph()
|
||||
@ -43,7 +43,7 @@ export const StepNode = ({
|
||||
const [mouseDownEvent, setMouseDownEvent] =
|
||||
useState<{ absolute: Coordinates; relative: Coordinates }>()
|
||||
const [isEditing, setIsEditing] = useState<boolean>(
|
||||
isStepText(step) && step.content.plainText === ''
|
||||
isTextBubbleStep(step) && step.content.plainText === ''
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@ -102,8 +102,8 @@ export const StepNode = ({
|
||||
mouseDownEvent &&
|
||||
onMouseDown &&
|
||||
(event.movementX > 0 || event.movementY > 0)
|
||||
if (isMovingAndIsMouseDown) {
|
||||
onMouseDown(mouseDownEvent, step as Step)
|
||||
if (isMovingAndIsMouseDown && step.type !== 'start') {
|
||||
onMouseDown(mouseDownEvent, step)
|
||||
deleteStep(step.id)
|
||||
setMouseDownEvent(undefined)
|
||||
}
|
||||
@ -142,7 +142,7 @@ export const StepNode = ({
|
||||
connectingIds?.target?.blockId,
|
||||
])
|
||||
|
||||
return isEditing && isStepText(step) ? (
|
||||
return isEditing && isTextBubbleStep(step) ? (
|
||||
<TextEditor
|
||||
stepId={step.id}
|
||||
initialValue={step.content.richText}
|
||||
@ -186,7 +186,7 @@ export const StepNode = ({
|
||||
bgColor="white"
|
||||
>
|
||||
<StepIcon type={step.type} />
|
||||
<StepContent {...step} />
|
||||
<StepNodeLabel {...step} />
|
||||
{isConnectable && (
|
||||
<SourceEndpoint
|
||||
onConnectionDragStart={handleConnectionDragStart}
|
||||
|
@ -0,0 +1,42 @@
|
||||
import { Flex, Text } from '@chakra-ui/react'
|
||||
import { Step, StartStep, BubbleStepType, InputStepType } from 'models'
|
||||
|
||||
export const StepNodeLabel = (props: Step | StartStep) => {
|
||||
switch (props.type) {
|
||||
case BubbleStepType.TEXT: {
|
||||
return (
|
||||
<Flex
|
||||
flexDir={'column'}
|
||||
opacity={props.content.html === '' ? '0.5' : '1'}
|
||||
className="slate-html-container"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
props.content.html === ''
|
||||
? `<p>Click to edit...</p>`
|
||||
: props.content.html,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case InputStepType.TEXT: {
|
||||
return (
|
||||
<Text color={'gray.500'}>
|
||||
{props.options?.labels?.placeholder ?? 'Type your answer...'}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
case InputStepType.NUMBER: {
|
||||
return (
|
||||
<Text color={'gray.500'}>
|
||||
{props.options?.labels?.placeholder ?? 'Type your answer...'}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
case 'start': {
|
||||
return <Text>{props.label}</Text>
|
||||
}
|
||||
default: {
|
||||
return <Text>No input</Text>
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { StackProps, HStack } from '@chakra-ui/react'
|
||||
import { StartStep, Step } from 'models'
|
||||
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
|
||||
import { StepContent } from './StepContent'
|
||||
import { StepNodeLabel } from './StepNodeLabel'
|
||||
|
||||
export const StepNodeOverlay = ({
|
||||
step,
|
||||
@ -19,7 +19,7 @@ export const StepNodeOverlay = ({
|
||||
{...props}
|
||||
>
|
||||
<StepIcon type={step.type} />
|
||||
<StepContent {...step} />
|
||||
<StepNodeLabel {...step} />
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useEventListener, Stack, Flex, Portal } from '@chakra-ui/react'
|
||||
import { Step, Table } from 'models'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import { DraggableStep, useDnd } from 'contexts/DndContext'
|
||||
import { Coordinates } from 'contexts/GraphContext'
|
||||
import { useState } from 'react'
|
||||
import { StepNode, StepNodeOverlay } from './StepNode'
|
||||
@ -54,7 +54,7 @@ export const StepsList = ({
|
||||
|
||||
const handleStepMouseDown = (
|
||||
{ absolute, relative }: { absolute: Coordinates; relative: Coordinates },
|
||||
step: Step
|
||||
step: DraggableStep
|
||||
) => {
|
||||
setPosition(absolute)
|
||||
setRelativeCoordinates(relative)
|
||||
|
@ -2,11 +2,10 @@ import { Flex, FlexProps, useEventListener } from '@chakra-ui/react'
|
||||
import React, { useRef, useMemo } from 'react'
|
||||
import { blockWidth, useGraph } from 'contexts/GraphContext'
|
||||
import { BlockNode } from './BlockNode/BlockNode'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import { DraggableStepType, useDnd } from 'contexts/DndContext'
|
||||
import { Edges } from './Edges'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { headerHeight } from 'components/shared/TypebotHeader/TypebotHeader'
|
||||
import { StepType } from 'models'
|
||||
|
||||
const Graph = ({ ...props }: FlexProps) => {
|
||||
const { draggedStepType, setDraggedStepType, draggedStep, setDraggedStep } =
|
||||
@ -44,7 +43,7 @@ const Graph = ({ ...props }: FlexProps) => {
|
||||
createBlock({
|
||||
x: e.clientX - graphPosition.x - blockWidth / 3,
|
||||
y: e.clientY - graphPosition.y - 20 - headerHeight,
|
||||
step: draggedStep ?? (draggedStepType as StepType),
|
||||
step: draggedStep ?? (draggedStepType as DraggableStepType),
|
||||
})
|
||||
setDraggedStep(undefined)
|
||||
setDraggedStepType(undefined)
|
||||
|
Reference in New Issue
Block a user