feat(inputs): improve input focus behavior
This commit is contained in:
@ -17,7 +17,6 @@ export const SendButton = ({
|
|||||||
}: SendButtonProps) => {
|
}: SendButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
|
||||||
disabled={isDisabled || isLoading}
|
disabled={isDisabled || isLoading}
|
||||||
{...props}
|
{...props}
|
||||||
className={
|
className={
|
||||||
|
@ -6,10 +6,10 @@ import {
|
|||||||
TextInputBlock,
|
TextInputBlock,
|
||||||
UrlInputBlock,
|
UrlInputBlock,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import React, { FormEvent, useState } from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { InputSubmitContent } from '../../InputChatBlock'
|
import { InputSubmitContent } from '../../InputChatBlock'
|
||||||
import { SendButton } from '../SendButton'
|
import { SendButton } from '../SendButton'
|
||||||
import { TextInput } from './TextInputContent'
|
import { TextInput } from './TextInput'
|
||||||
|
|
||||||
type TextFormProps = {
|
type TextFormProps = {
|
||||||
block:
|
block:
|
||||||
@ -30,6 +30,7 @@ export const TextForm = ({
|
|||||||
hasGuestAvatar,
|
hasGuestAvatar,
|
||||||
}: TextFormProps) => {
|
}: TextFormProps) => {
|
||||||
const [inputValue, setInputValue] = useState(defaultValue ?? '')
|
const [inputValue, setInputValue] = useState(defaultValue ?? '')
|
||||||
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
const isLongText = block.type === InputBlockType.TEXT && block.options?.isLong
|
const isLongText = block.type === InputBlockType.TEXT && block.options?.isLong
|
||||||
|
|
||||||
@ -42,29 +43,41 @@ export const TextForm = ({
|
|||||||
setInputValue(inputValue)
|
setInputValue(inputValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: FormEvent) => {
|
const checkIfInputIsValid = () =>
|
||||||
e.preventDefault()
|
inputValue !== '' && inputRef.current?.reportValidity()
|
||||||
if (inputValue === '') return
|
|
||||||
onSubmit({ value: inputValue })
|
const submit = () => {
|
||||||
|
if (checkIfInputIsValid()) onSubmit({ value: inputValue })
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitWhenEnter = (e: React.KeyboardEvent) => {
|
||||||
|
if (e.key === 'Enter') submit()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<div
|
||||||
className="flex items-end justify-between rounded-lg pr-2 typebot-input w-full"
|
className={
|
||||||
onSubmit={handleSubmit}
|
'flex items-end justify-between rounded-lg pr-2 typebot-input w-full'
|
||||||
|
}
|
||||||
data-testid="input"
|
data-testid="input"
|
||||||
autoComplete={block.type === InputBlockType.TEXT ? 'off' : 'on'}
|
|
||||||
style={{
|
style={{
|
||||||
marginRight: hasGuestAvatar ? '50px' : '0.5rem',
|
marginRight: hasGuestAvatar ? '50px' : '0.5rem',
|
||||||
maxWidth: isLongText ? undefined : '350px',
|
maxWidth: isLongText ? undefined : '350px',
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={submitWhenEnter}
|
||||||
>
|
>
|
||||||
<TextInput block={block} onChange={handleChange} value={inputValue} />
|
<TextInput
|
||||||
|
inputRef={inputRef}
|
||||||
|
block={block}
|
||||||
|
onChange={handleChange}
|
||||||
|
value={inputValue}
|
||||||
|
/>
|
||||||
<SendButton
|
<SendButton
|
||||||
label={block.options?.labels?.button ?? 'Send'}
|
label={block.options?.labels?.button ?? 'Send'}
|
||||||
isDisabled={inputValue === ''}
|
isDisabled={inputValue === ''}
|
||||||
className="my-2 ml-2"
|
className="my-2 ml-2"
|
||||||
|
onClick={submit}
|
||||||
/>
|
/>
|
||||||
</form>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,11 @@ import {
|
|||||||
UrlInputBlock,
|
UrlInputBlock,
|
||||||
PhoneNumberInputBlock,
|
PhoneNumberInputBlock,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import React, {
|
import React, { ChangeEvent, ChangeEventHandler } from 'react'
|
||||||
ChangeEvent,
|
|
||||||
ChangeEventHandler,
|
|
||||||
RefObject,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
} from 'react'
|
|
||||||
import PhoneInput, { Value, Country } from 'react-phone-number-input'
|
import PhoneInput, { Value, Country } from 'react-phone-number-input'
|
||||||
import { isMobile } from 'services/utils'
|
|
||||||
|
|
||||||
type TextInputProps = {
|
type TextInputProps = {
|
||||||
|
inputRef: React.RefObject<any>
|
||||||
block:
|
block:
|
||||||
| TextInputBlock
|
| TextInputBlock
|
||||||
| EmailInputBlock
|
| EmailInputBlock
|
||||||
@ -27,13 +21,12 @@ type TextInputProps = {
|
|||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TextInput = ({ block, value, onChange }: TextInputProps) => {
|
export const TextInput = ({
|
||||||
const inputRef = useRef<HTMLInputElement & HTMLTextAreaElement>(null)
|
inputRef,
|
||||||
|
block,
|
||||||
useEffect(() => {
|
value,
|
||||||
setTimeout(() => inputRef.current?.focus(), isMobile ? 500 : 0)
|
onChange,
|
||||||
}, [])
|
}: TextInputProps) => {
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||||
) => onChange(e.target.value)
|
) => onChange(e.target.value)
|
||||||
@ -46,7 +39,7 @@ export const TextInput = ({ block, value, onChange }: TextInputProps) => {
|
|||||||
case InputBlockType.TEXT: {
|
case InputBlockType.TEXT: {
|
||||||
return block.options?.isLong ? (
|
return block.options?.isLong ? (
|
||||||
<LongTextInput
|
<LongTextInput
|
||||||
ref={inputRef as unknown as RefObject<HTMLTextAreaElement>}
|
ref={inputRef}
|
||||||
value={value}
|
value={value}
|
||||||
placeholder={
|
placeholder={
|
||||||
block.options?.labels?.placeholder ?? 'Type your answer...'
|
block.options?.labels?.placeholder ?? 'Type your answer...'
|
||||||
@ -61,7 +54,6 @@ export const TextInput = ({ block, value, onChange }: TextInputProps) => {
|
|||||||
block.options?.labels?.placeholder ?? 'Type your answer...'
|
block.options?.labels?.placeholder ?? 'Type your answer...'
|
||||||
}
|
}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoComplete="new-typebot-answer-value"
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -111,14 +103,14 @@ export const TextInput = ({ block, value, onChange }: TextInputProps) => {
|
|||||||
case InputBlockType.PHONE: {
|
case InputBlockType.PHONE: {
|
||||||
return (
|
return (
|
||||||
<PhoneInput
|
<PhoneInput
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
ref={inputRef}
|
||||||
ref={inputRef as any}
|
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handlePhoneNumberChange}
|
onChange={handlePhoneNumberChange}
|
||||||
placeholder={
|
placeholder={
|
||||||
block.options.labels.placeholder ?? 'Your phone number...'
|
block.options.labels.placeholder ?? 'Your phone number...'
|
||||||
}
|
}
|
||||||
defaultCountry={block.options.defaultCountryCode as Country}
|
defaultCountry={block.options.defaultCountryCode as Country}
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -136,6 +128,8 @@ const ShortTextInput = React.forwardRef(
|
|||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
style={{ fontSize: '16px' }}
|
style={{ fontSize: '16px' }}
|
||||||
|
autoComplete="do-not-autofill"
|
||||||
|
autoFocus
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -157,6 +151,7 @@ const LongTextInput = React.forwardRef(
|
|||||||
data-testid="textarea"
|
data-testid="textarea"
|
||||||
required
|
required
|
||||||
style={{ fontSize: '16px' }}
|
style={{ fontSize: '16px' }}
|
||||||
|
autoFocus
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
Reference in New Issue
Block a user