feat(editor): 🚸 Improve and unify inputs
This commit is contained in:
@ -9,10 +9,7 @@ import {
|
||||
PopoverContent,
|
||||
} from '@chakra-ui/react'
|
||||
import { ImageUploadContent } from 'components/shared/ImageUploadContent'
|
||||
import {
|
||||
InputWithVariableButton,
|
||||
TextareaWithVariableButton,
|
||||
} from 'components/shared/TextboxWithVariableButton'
|
||||
import { Input, Textarea } from 'components/shared/Textbox'
|
||||
|
||||
type Props = {
|
||||
typebotName: string
|
||||
@ -89,9 +86,9 @@ export const MetadataForm = ({
|
||||
<FormLabel mb="0" htmlFor="title">
|
||||
Title:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="title"
|
||||
initialValue={metadata.title ?? typebotName}
|
||||
defaultValue={metadata.title ?? typebotName}
|
||||
onChange={handleTitleChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -99,9 +96,9 @@ export const MetadataForm = ({
|
||||
<FormLabel mb="0" htmlFor="description">
|
||||
Description:
|
||||
</FormLabel>
|
||||
<TextareaWithVariableButton
|
||||
<Textarea
|
||||
id="description"
|
||||
initialValue={metadata.description}
|
||||
defaultValue={metadata.description}
|
||||
onChange={handleDescriptionChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,45 +0,0 @@
|
||||
import { Input, InputProps } from '@chakra-ui/react'
|
||||
import {
|
||||
ChangeEvent,
|
||||
ForwardedRef,
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
|
||||
type Props = Omit<InputProps, 'onChange' | 'value'> & {
|
||||
initialValue: string
|
||||
onChange: (debouncedValue: string) => void
|
||||
}
|
||||
|
||||
export const DebouncedInput = forwardRef(
|
||||
(
|
||||
{ onChange, initialValue, ...props }: Props,
|
||||
ref: ForwardedRef<HTMLInputElement>
|
||||
) => {
|
||||
const [currentValue, setCurrentValue] = useState(initialValue)
|
||||
const [debouncedValue] = useDebounce(
|
||||
currentValue,
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedValue === initialValue) return
|
||||
onChange(debouncedValue)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedValue])
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
setCurrentValue(e.target.value)
|
||||
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
ref={ref}
|
||||
value={currentValue}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
@ -1,39 +0,0 @@
|
||||
import { Textarea, TextareaProps } from '@chakra-ui/react'
|
||||
import { ChangeEvent, useEffect, useState } from 'react'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
|
||||
type Props = Omit<TextareaProps, 'onChange' | 'value'> & {
|
||||
initialValue: string
|
||||
onChange: (debouncedValue: string) => void
|
||||
}
|
||||
|
||||
export const DebouncedTextarea = ({
|
||||
onChange,
|
||||
initialValue,
|
||||
...props
|
||||
}: Props) => {
|
||||
const [currentValue, setCurrentValue] = useState(initialValue)
|
||||
const [debouncedValue] = useDebounce(
|
||||
currentValue,
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedValue === initialValue) return
|
||||
onChange(debouncedValue)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedValue])
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setCurrentValue(e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
{...props}
|
||||
value={currentValue}
|
||||
onChange={handleChange}
|
||||
resize={'vertical'}
|
||||
/>
|
||||
)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Stack, Text } from '@chakra-ui/react'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox/Input'
|
||||
import { VideoBubbleContent, VideoBubbleContentType } from 'models'
|
||||
import urlParser from 'js-video-url-parser/lib/base'
|
||||
import 'js-video-url-parser/lib/provider/vimeo'
|
||||
@ -24,9 +24,9 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
|
||||
}
|
||||
return (
|
||||
<Stack p="2">
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
placeholder="Paste the video link..."
|
||||
initialValue={content?.url ?? ''}
|
||||
defaultValue={content?.url ?? ''}
|
||||
onChange={handleUrlChange}
|
||||
/>
|
||||
<Text fontSize="sm" color="gray.400" textAlign="center">
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { ChoiceInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -34,9 +34,9 @@ export const ChoiceInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options?.buttonLabel ?? 'Send'}
|
||||
defaultValue={options?.buttonLabel ?? 'Send'}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormLabel, Stack, Text } from '@chakra-ui/react'
|
||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { CodeOptions } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
@ -20,10 +20,11 @@ export const CodeSettings = ({ options, onOptionsChange }: Props) => {
|
||||
<FormLabel mb="0" htmlFor="name">
|
||||
Name:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="name"
|
||||
initialValue={options.name}
|
||||
defaultValue={options.name}
|
||||
onChange={handleNameChange}
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Stack } from '@chakra-ui/react'
|
||||
import { DropdownList } from 'components/shared/DropdownList'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox/Input'
|
||||
import { TableListItemProps } from 'components/shared/TableList'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { Comparison, Variable, ComparisonOperators } from 'models'
|
||||
@ -39,8 +39,8 @@ export const ComparisonItem = ({
|
||||
placeholder="Select an operator"
|
||||
/>
|
||||
{item.comparisonOperator !== ComparisonOperators.IS_SET && (
|
||||
<InputWithVariableButton
|
||||
initialValue={item.value ?? ''}
|
||||
<Input
|
||||
defaultValue={item.value ?? ''}
|
||||
onChange={handleChangeValue}
|
||||
placeholder="Type a value..."
|
||||
/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { DateInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -46,9 +46,9 @@ export const DateInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="from">
|
||||
From label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="from"
|
||||
initialValue={options.labels.from}
|
||||
defaultValue={options.labels.from}
|
||||
onChange={handleFromChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -58,9 +58,9 @@ export const DateInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="to">
|
||||
To label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="to"
|
||||
initialValue={options.labels.to}
|
||||
defaultValue={options.labels.to}
|
||||
onChange={handleToChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -69,9 +69,9 @@ export const DateInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options.labels.button}
|
||||
defaultValue={options.labels.button}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { EmailInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -29,9 +28,9 @@ export const EmailInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="placeholder">
|
||||
Placeholder:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="placeholder"
|
||||
initialValue={options.labels.placeholder}
|
||||
defaultValue={options.labels.placeholder}
|
||||
onChange={handlePlaceholderChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -39,9 +38,9 @@ export const EmailInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options.labels.button}
|
||||
defaultValue={options.labels.button}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -49,9 +48,9 @@ export const EmailInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="retry">
|
||||
Retry message:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="retry"
|
||||
initialValue={options.retryMessageContent}
|
||||
defaultValue={options.retryMessageContent}
|
||||
onChange={handleRetryMessageChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -9,8 +9,7 @@ import {
|
||||
Stack,
|
||||
Tag,
|
||||
} from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { GoogleAnalyticsOptions } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
@ -47,9 +46,9 @@ export const GoogleAnalyticsSettings = ({
|
||||
<FormLabel mb="0" htmlFor="tracking-id">
|
||||
Tracking ID:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="tracking-id"
|
||||
initialValue={options?.trackingId ?? ''}
|
||||
defaultValue={options?.trackingId ?? ''}
|
||||
placeholder="G-123456..."
|
||||
onChange={handleTrackingIdChange}
|
||||
/>
|
||||
@ -58,9 +57,9 @@ export const GoogleAnalyticsSettings = ({
|
||||
<FormLabel mb="0" htmlFor="category">
|
||||
Event category:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="category"
|
||||
initialValue={options?.category ?? ''}
|
||||
defaultValue={options?.category ?? ''}
|
||||
placeholder="Example: Typebot"
|
||||
onChange={handleCategoryChange}
|
||||
/>
|
||||
@ -69,9 +68,9 @@ export const GoogleAnalyticsSettings = ({
|
||||
<FormLabel mb="0" htmlFor="action">
|
||||
Event action:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="action"
|
||||
initialValue={options?.action ?? ''}
|
||||
defaultValue={options?.action ?? ''}
|
||||
placeholder="Example: Submit email"
|
||||
onChange={handleActionChange}
|
||||
/>
|
||||
@ -91,9 +90,9 @@ export const GoogleAnalyticsSettings = ({
|
||||
<FormLabel mb="0" htmlFor="label">
|
||||
Event label <Tag>Optional</Tag>:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="label"
|
||||
initialValue={options?.label ?? ''}
|
||||
defaultValue={options?.label ?? ''}
|
||||
placeholder="Example: Campaign Z"
|
||||
onChange={handleLabelChange}
|
||||
/>
|
||||
@ -102,9 +101,9 @@ export const GoogleAnalyticsSettings = ({
|
||||
<FormLabel mb="0" htmlFor="value">
|
||||
Event value <Tag>Optional</Tag>:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="value"
|
||||
initialValue={options?.value?.toString() ?? ''}
|
||||
defaultValue={options?.value?.toString() ?? ''}
|
||||
placeholder="Example: 0"
|
||||
onChange={handleValueChange}
|
||||
/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Stack } from '@chakra-ui/react'
|
||||
import { DropdownList } from 'components/shared/DropdownList'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox/Input'
|
||||
import { TableListItemProps } from 'components/shared/TableList'
|
||||
import { Cell } from 'models'
|
||||
|
||||
@ -25,8 +25,8 @@ export const CellWithValueStack = ({
|
||||
items={columns}
|
||||
placeholder="Select a column"
|
||||
/>
|
||||
<InputWithVariableButton
|
||||
initialValue={item.value ?? ''}
|
||||
<Input
|
||||
defaultValue={item.value ?? ''}
|
||||
onChange={handleValueChange}
|
||||
placeholder="Type a value..."
|
||||
/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormLabel, HStack, Stack } from '@chakra-ui/react'
|
||||
import { SmartNumberInput } from 'components/shared/SmartNumberInput'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { NumberInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -35,9 +35,9 @@ export const NumberInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="placeholder">
|
||||
Placeholder:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="placeholder"
|
||||
initialValue={options.labels.placeholder}
|
||||
defaultValue={options.labels.placeholder}
|
||||
onChange={handlePlaceholderChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -45,9 +45,9 @@ export const NumberInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options?.labels?.button ?? 'Send'}
|
||||
defaultValue={options?.labels?.button ?? 'Send'}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { PhoneNumberInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -32,9 +31,9 @@ export const PhoneNumberSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="placeholder">
|
||||
Placeholder:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="placeholder"
|
||||
initialValue={options.labels.placeholder}
|
||||
defaultValue={options.labels.placeholder}
|
||||
onChange={handlePlaceholderChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -42,9 +41,9 @@ export const PhoneNumberSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options.labels.button}
|
||||
defaultValue={options.labels.button}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -61,9 +60,9 @@ export const PhoneNumberSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="retry">
|
||||
Retry message:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="retry"
|
||||
initialValue={options.retryMessageContent}
|
||||
defaultValue={options.retryMessageContent}
|
||||
onChange={handleRetryMessageChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { RedirectOptions } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
@ -21,9 +21,9 @@ export const RedirectSettings = ({ options, onOptionsChange }: Props) => {
|
||||
<FormLabel mb="0" htmlFor="tracking-id">
|
||||
Url:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="tracking-id"
|
||||
initialValue={options.url ?? ''}
|
||||
defaultValue={options.url ?? ''}
|
||||
placeholder="Type a URL..."
|
||||
onChange={handleUrlChange}
|
||||
/>
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { Stack, useDisclosure, Text } from '@chakra-ui/react'
|
||||
import { CredentialsDropdown } from 'components/shared/CredentialsDropdown'
|
||||
import {
|
||||
InputWithVariableButton,
|
||||
TextareaWithVariableButton,
|
||||
} from 'components/shared/TextboxWithVariableButton'
|
||||
import { Input, Textarea } from 'components/shared/Textbox'
|
||||
import { CredentialsType, SendEmailOptions } from 'models'
|
||||
import React, { useState } from 'react'
|
||||
import { SmtpConfigModal } from './SmtpConfigModal'
|
||||
@ -80,43 +77,43 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => {
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>To: </Text>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
onChange={handleToChange}
|
||||
initialValue={options.recipients.join(', ')}
|
||||
defaultValue={options.recipients.join(', ')}
|
||||
placeholder="email1@gmail.com, email2@gmail.com"
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Cc: </Text>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
onChange={handleCcChange}
|
||||
initialValue={options.cc?.join(', ') ?? ''}
|
||||
defaultValue={options.cc?.join(', ') ?? ''}
|
||||
placeholder="email1@gmail.com, email2@gmail.com"
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Bcc: </Text>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
onChange={handleBccChange}
|
||||
initialValue={options.bcc?.join(', ') ?? ''}
|
||||
defaultValue={options.bcc?.join(', ') ?? ''}
|
||||
placeholder="email1@gmail.com, email2@gmail.com"
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Subject: </Text>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
data-testid="subject-input"
|
||||
onChange={handleSubjectChange}
|
||||
initialValue={options.subject ?? ''}
|
||||
defaultValue={options.subject ?? ''}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Body: </Text>
|
||||
<TextareaWithVariableButton
|
||||
<Textarea
|
||||
data-testid="body-input"
|
||||
minH="300px"
|
||||
onChange={handleBodyChange}
|
||||
initialValue={options.body ?? ''}
|
||||
defaultValue={options.body ?? ''}
|
||||
/>
|
||||
</Stack>
|
||||
<SmtpConfigModal
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { FormControl, FormLabel, HStack, Stack } from '@chakra-ui/react'
|
||||
import { isDefined } from '@udecode/plate-common'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { SmartNumberInput } from 'components/shared/SmartNumberInput'
|
||||
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { SmtpCredentialsData } from 'models'
|
||||
import React from 'react'
|
||||
|
||||
@ -30,43 +30,48 @@ export const SmtpConfigForm = ({ config, onConfigChange }: Props) => {
|
||||
<Stack as="form" spacing={4}>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>From email:</FormLabel>
|
||||
<DebouncedInput
|
||||
initialValue={config.from.email ?? ''}
|
||||
<Input
|
||||
defaultValue={config.from.email ?? ''}
|
||||
onChange={handleFromEmailChange}
|
||||
placeholder="notifications@provider.com"
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>From name:</FormLabel>
|
||||
<DebouncedInput
|
||||
initialValue={config.from.name ?? ''}
|
||||
<Input
|
||||
defaultValue={config.from.name ?? ''}
|
||||
onChange={handleFromNameChange}
|
||||
placeholder="John Smith"
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Host:</FormLabel>
|
||||
<DebouncedInput
|
||||
initialValue={config.host ?? ''}
|
||||
<Input
|
||||
defaultValue={config.host ?? ''}
|
||||
onChange={handleHostChange}
|
||||
placeholder="mail.provider.com"
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Username / Email:</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
type="email"
|
||||
initialValue={config.username ?? ''}
|
||||
defaultValue={config.username ?? ''}
|
||||
onChange={handleUsernameChange}
|
||||
placeholder="user@provider.com"
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Password:</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
type="password"
|
||||
initialValue={config.password ?? ''}
|
||||
defaultValue={config.password ?? ''}
|
||||
onChange={handlePasswordChange}
|
||||
withVariableButton={false}
|
||||
/>
|
||||
</FormControl>
|
||||
<SwitchWithLabel
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedTextarea } from 'components/shared/DebouncedTextarea'
|
||||
import { Textarea } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { SetVariableOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -31,9 +31,9 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
|
||||
<FormLabel mb="0" htmlFor="expression">
|
||||
Value / Expression:
|
||||
</FormLabel>
|
||||
<DebouncedTextarea
|
||||
<Textarea
|
||||
id="expression"
|
||||
initialValue={options.expressionToEvaluate ?? ''}
|
||||
defaultValue={options.expressionToEvaluate ?? ''}
|
||||
onChange={handleExpressionChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { TextInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -35,9 +35,9 @@ export const TextInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="placeholder">
|
||||
Placeholder:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="placeholder"
|
||||
initialValue={options.labels.placeholder}
|
||||
defaultValue={options.labels.placeholder}
|
||||
onChange={handlePlaceholderChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -45,9 +45,9 @@ export const TextInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options.labels.button}
|
||||
defaultValue={options.labels.button}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { UrlInputOptions, Variable } from 'models'
|
||||
import React from 'react'
|
||||
@ -29,9 +28,9 @@ export const UrlInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="placeholder">
|
||||
Placeholder:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="placeholder"
|
||||
initialValue={options.labels.placeholder}
|
||||
defaultValue={options.labels.placeholder}
|
||||
onChange={handlePlaceholderChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -39,9 +38,9 @@ export const UrlInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="button">
|
||||
Button label:
|
||||
</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id="button"
|
||||
initialValue={options.labels.button}
|
||||
defaultValue={options.labels.button}
|
||||
onChange={handleButtonLabelChange}
|
||||
/>
|
||||
</Stack>
|
||||
@ -49,9 +48,9 @@ export const UrlInputSettingsBody = ({
|
||||
<FormLabel mb="0" htmlFor="retry">
|
||||
Retry message:
|
||||
</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id="retry"
|
||||
initialValue={options.retryMessageContent}
|
||||
defaultValue={options.retryMessageContent}
|
||||
onChange={handleRetryMessageChange}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Stack, FormControl, FormLabel } from '@chakra-ui/react'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { TableListItemProps } from 'components/shared/TableList'
|
||||
import { KeyValue } from 'models'
|
||||
|
||||
@ -40,18 +40,18 @@ export const KeyValueInputs = ({
|
||||
<Stack p="4" rounded="md" flex="1" borderWidth="1px">
|
||||
<FormControl>
|
||||
<FormLabel htmlFor={'key' + item.id}>Key:</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id={'key' + item.id}
|
||||
initialValue={item.key ?? ''}
|
||||
defaultValue={item.key ?? ''}
|
||||
onChange={handleKeyChange}
|
||||
placeholder={keyPlaceholder}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel htmlFor={'value' + item.id}>Value:</FormLabel>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
id={'value' + item.id}
|
||||
initialValue={item.value ?? ''}
|
||||
defaultValue={item.value ?? ''}
|
||||
onChange={handleValueChange}
|
||||
placeholder={valuePlaceholder}
|
||||
/>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Stack, FormControl, FormLabel } from '@chakra-ui/react'
|
||||
import { DebouncedInput } from 'components/shared/DebouncedInput'
|
||||
import { TableListItemProps } from 'components/shared/TableList'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
||||
import { VariableForTest, Variable } from 'models'
|
||||
|
||||
@ -26,9 +26,9 @@ export const VariableForTestInputs = ({
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel htmlFor={'value' + item.id}>Test value:</FormLabel>
|
||||
<DebouncedInput
|
||||
<Input
|
||||
id={'value' + item.id}
|
||||
initialValue={item.value ?? ''}
|
||||
defaultValue={item.value ?? ''}
|
||||
onChange={handleValueChange}
|
||||
/>
|
||||
</FormControl>
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
useToast,
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import {
|
||||
HttpMethod,
|
||||
@ -144,9 +144,9 @@ export const WebhookSettings = ({
|
||||
if (!localWebhook) return <Spinner />
|
||||
return (
|
||||
<Stack spacing={4}>
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
placeholder="Your Webhook URL..."
|
||||
initialValue={localWebhook.url ?? ''}
|
||||
defaultValue={localWebhook.url ?? ''}
|
||||
onChange={handleUrlChange}
|
||||
/>
|
||||
<SwitchWithLabel
|
||||
|
@ -5,7 +5,7 @@ import { UploadButton } from '../buttons/UploadButton'
|
||||
import { GiphySearch } from './GiphySearch'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
import { InputWithVariableButton } from '../TextboxWithVariableButton'
|
||||
import { Input } from '../Textbox'
|
||||
|
||||
type Props = {
|
||||
url?: string
|
||||
@ -105,10 +105,10 @@ const EmbedLinkContent = ({ initialUrl, onNewUrl }: ContentProps) => {
|
||||
|
||||
return (
|
||||
<Stack py="2">
|
||||
<InputWithVariableButton
|
||||
<Input
|
||||
placeholder={'Paste the image link...'}
|
||||
onChange={setImageUrl}
|
||||
initialValue={imageUrl}
|
||||
defaultValue={imageUrl}
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
|
7
apps/builder/components/shared/Textbox/Input.tsx
Normal file
7
apps/builder/components/shared/Textbox/Input.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import { Input as ChakraInput } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import { TextBox, TextBoxProps } from './TextBox'
|
||||
|
||||
export const Input = (props: Omit<TextBoxProps, 'TextBox'>) => (
|
||||
<TextBox TextBox={ChakraInput} {...props} />
|
||||
)
|
@ -12,38 +12,49 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { UserIcon } from 'assets/icons'
|
||||
import { Variable } from 'models'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { VariableSearchInput } from '../VariableSearchInput'
|
||||
|
||||
export type TextBoxWithVariableButtonProps = {
|
||||
initialValue: string
|
||||
export type TextBoxProps = {
|
||||
onChange: (value: string) => void
|
||||
TextBox:
|
||||
| ComponentWithAs<'textarea', TextareaProps>
|
||||
| ComponentWithAs<'input', InputProps>
|
||||
withVariableButton?: boolean
|
||||
} & Omit<InputProps & TextareaProps, 'onChange'>
|
||||
|
||||
export const TextBoxWithVariableButton = ({
|
||||
initialValue,
|
||||
export const TextBox = ({
|
||||
onChange,
|
||||
TextBox,
|
||||
withVariableButton = true,
|
||||
...props
|
||||
}: TextBoxWithVariableButtonProps) => {
|
||||
}: TextBoxProps) => {
|
||||
const textBoxRef = useRef<(HTMLInputElement & HTMLTextAreaElement) | null>(
|
||||
null
|
||||
)
|
||||
const [value, setValue] = useState(initialValue)
|
||||
const [debouncedValue] = useDebounce(
|
||||
value,
|
||||
const [carretPosition, setCarretPosition] = useState<number>(0)
|
||||
const [value, setValue] = useState(props.defaultValue)
|
||||
const debounced = useDebouncedCallback(
|
||||
(value) => {
|
||||
onChange(value)
|
||||
},
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000
|
||||
)
|
||||
const [carretPosition, setCarretPosition] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
onChange(value)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [debouncedValue])
|
||||
useEffect(
|
||||
() => () => {
|
||||
debounced.flush()
|
||||
},
|
||||
[debounced]
|
||||
)
|
||||
|
||||
const handleChange = (
|
||||
e: ChangeEvent<HTMLInputElement & HTMLTextAreaElement>
|
||||
) => {
|
||||
setValue(e.target.value)
|
||||
debounced(e.target.value)
|
||||
}
|
||||
|
||||
const handleVariableSelected = (variable?: Variable) => {
|
||||
if (!textBoxRef.current || !variable) return
|
||||
@ -56,11 +67,12 @@ export const TextBoxWithVariableButton = ({
|
||||
cursorPosition,
|
||||
textBoxRef.current.value.length
|
||||
)
|
||||
setValue(
|
||||
const newValue =
|
||||
textBeforeCursorPosition +
|
||||
`{{${variable.name}}}` +
|
||||
textAfterCursorPosition
|
||||
)
|
||||
`{{${variable.name}}}` +
|
||||
textAfterCursorPosition
|
||||
setValue(newValue)
|
||||
debounced(newValue)
|
||||
textBoxRef.current.focus()
|
||||
setTimeout(() => {
|
||||
if (!textBoxRef.current) return
|
||||
@ -75,17 +87,24 @@ export const TextBoxWithVariableButton = ({
|
||||
setCarretPosition(textBoxRef.current.selectionStart)
|
||||
}
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
) => setValue(e.target.value)
|
||||
|
||||
if (!withVariableButton) {
|
||||
return (
|
||||
<TextBox
|
||||
ref={textBoxRef}
|
||||
onChange={handleChange}
|
||||
bgColor={'white'}
|
||||
value={value}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<HStack spacing={0} align={'flex-end'}>
|
||||
<TextBox
|
||||
ref={textBoxRef}
|
||||
value={value}
|
||||
onKeyUp={handleKeyUp}
|
||||
onClick={handleKeyUp}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
bgColor={'white'}
|
||||
{...props}
|
7
apps/builder/components/shared/Textbox/Textarea.tsx
Normal file
7
apps/builder/components/shared/Textbox/Textarea.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import { Textarea as ChakraTextarea } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import { TextBox, TextBoxProps } from './TextBox'
|
||||
|
||||
export const Textarea = (props: Omit<TextBoxProps, 'TextBox'>) => (
|
||||
<TextBox TextBox={ChakraTextarea} {...props} />
|
||||
)
|
2
apps/builder/components/shared/Textbox/index.tsx
Normal file
2
apps/builder/components/shared/Textbox/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export { Input } from './Input'
|
||||
export { Textarea } from './Textarea'
|
@ -1,10 +0,0 @@
|
||||
import { Input } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import {
|
||||
TextBoxWithVariableButton,
|
||||
TextBoxWithVariableButtonProps,
|
||||
} from './TextboxWithVariableButton'
|
||||
|
||||
export const InputWithVariableButton = (
|
||||
props: Omit<TextBoxWithVariableButtonProps, 'TextBox'>
|
||||
) => <TextBoxWithVariableButton TextBox={Input} {...props} />
|
@ -1,10 +0,0 @@
|
||||
import { Textarea } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import {
|
||||
TextBoxWithVariableButton,
|
||||
TextBoxWithVariableButtonProps,
|
||||
} from './TextboxWithVariableButton'
|
||||
|
||||
export const TextareaWithVariableButton = (
|
||||
props: Omit<TextBoxWithVariableButtonProps, 'TextBox'>
|
||||
) => <TextBoxWithVariableButton TextBox={Textarea} {...props} />
|
@ -1,2 +0,0 @@
|
||||
export { InputWithVariableButton } from './InputWithVariableButton'
|
||||
export { TextareaWithVariableButton } from './TextareaWithVariableButton'
|
@ -1,2 +1,3 @@
|
||||
export * from './utils'
|
||||
export * from './useUndo'
|
||||
export * from './useRefState'
|
||||
|
10
apps/builder/services/utils/useRefState.ts
Normal file
10
apps/builder/services/utils/useRefState.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export const useRefState = (initialValue: string) => {
|
||||
const [state, setState] = useState(initialValue)
|
||||
const stateRef = useRef<string>(state)
|
||||
useEffect(() => {
|
||||
stateRef.current = state
|
||||
}, [state])
|
||||
return [stateRef, setState]
|
||||
}
|
Reference in New Issue
Block a user