🚸 (textInput) Add Input mode option

Closes #2101
This commit is contained in:
Baptiste Arnaud 2025-06-05 15:47:14 +02:00
parent c43ad4bfe7
commit b76dad18b7
No known key found for this signature in database
8 changed files with 81 additions and 2 deletions

View File

@ -1,6 +1,7 @@
import { DropdownList } from "@/components/DropdownList";
import { SwitchWithRelatedSettings } from "@/components/SwitchWithRelatedSettings";
import { TextInput } from "@/components/inputs";
import { Select } from "@/components/inputs/Select";
import { SwitchWithLabel } from "@/components/inputs/SwitchWithLabel";
import { VariableSearchInput } from "@/components/inputs/VariableSearchInput";
import { FormLabel, Stack } from "@chakra-ui/react";
@ -8,6 +9,7 @@ import { useTranslate } from "@tolgee/react";
import { fileVisibilityOptions } from "@typebot.io/blocks-inputs/file/constants";
import { defaultTextInputOptions } from "@typebot.io/blocks-inputs/text/constants";
import type { TextInputBlock } from "@typebot.io/blocks-inputs/text/schema";
import { inputModeOptions } from "@typebot.io/blocks-inputs/text/schema";
import type { Variable } from "@typebot.io/variables/schemas";
import React from "react";
@ -73,6 +75,12 @@ export const TextInputSettings = ({ options, onOptionsChange }: Props) => {
audioClip: { ...options?.audioClip, visibility },
});
const updateInputMode = (inputMode?: string) =>
onOptionsChange({
...options,
inputMode: inputMode as (typeof inputModeOptions)[number] | undefined,
});
return (
<Stack spacing={4}>
<SwitchWithLabel
@ -95,6 +103,17 @@ export const TextInputSettings = ({ options, onOptionsChange }: Props) => {
}
onChange={updateButtonLabel}
/>
<Stack>
<FormLabel mb="0" htmlFor="input-mode">
Input mode
</FormLabel>
<Select
selectedItem={options?.inputMode ?? "text"}
items={inputModeOptions}
onSelect={updateInputMode}
placeholder="Select input mode..."
/>
</Stack>
<SwitchWithRelatedSettings
label={"Allow audio clip"}
initialValue={

View File

@ -53,6 +53,25 @@ You can also ask your user for a longer text answer by enabling it in the input
</Tab>
</Tabs>
## Input mode
The input mode option allows you to specify the type of virtual keyboard that should be displayed on mobile devices. This provides a better user experience by showing the most appropriate keyboard for the expected input.
Available input modes:
- **text**: Shows a standard keyboard
- **decimal**: Shows a numeric keypad with decimal support
- **numeric**: Shows a numeric keypad for whole numbers
- **tel**: Shows a telephone keypad
- **search**: Shows a keyboard optimized for search inputs
- **email**: Shows a keyboard optimized for email addresses
- **url**: Shows a keyboard optimized for URL entry
<Note>
Input mode is a hint to the browser and may not be supported on all devices or browsers. It's particularly useful for mobile devices.
</Note>
For more information, see the [MDN documentation](https://developer.mozilla.org/docs/Web/HTML/Reference/Global_attributes/inputmode).
## Allow attachments
This option, when enabled, allows users to attach files to their message. This is useful when you want to ask for a document or a picture attached to the user messages.

View File

@ -14482,6 +14482,18 @@
"isLong": {
"type": "boolean"
},
"inputMode": {
"type": "string",
"enum": [
"text",
"decimal",
"numeric",
"tel",
"search",
"email",
"url"
]
},
"audioClip": {
"type": "object",
"properties": {

View File

@ -6790,6 +6790,18 @@
"isLong": {
"type": "boolean"
},
"inputMode": {
"type": "string",
"enum": [
"text",
"decimal",
"numeric",
"tel",
"search",
"email",
"url"
]
},
"audioClip": {
"type": "object",
"properties": {

View File

@ -15,11 +15,22 @@ export const textInputOptionsBaseSchema = z.object({
.optional(),
});
export const inputModeOptions = [
"text",
"decimal",
"numeric",
"tel",
"search",
"email",
"url",
] as const;
export const textInputOptionsSchema = textInputOptionsBaseSchema
.merge(optionBaseSchema)
.merge(
z.object({
isLong: z.boolean().optional(),
inputMode: z.enum(inputModeOptions).optional(),
audioClip: z
.object({
isEnabled: z.boolean().optional(),

View File

@ -4,16 +4,18 @@ import type { JSX } from "solid-js/jsx-runtime";
type ShortTextInputProps = {
ref: HTMLInputElement | undefined;
onInput: (value: string) => void;
inputmode?: string;
} & Omit<JSX.InputHTMLAttributes<HTMLInputElement>, "onInput">;
export const ShortTextInput = (props: ShortTextInputProps) => {
const [local, others] = splitProps(props, ["ref", "onInput"]);
const [local, others] = splitProps(props, ["ref", "onInput", "inputmode"]);
return (
<input
ref={props.ref}
class="focus:outline-none bg-transparent px-4 py-4 flex-1 w-full text-input"
type="text"
inputmode={local.inputmode}
onInput={(e) => local.onInput(e.currentTarget.value)}
{...others}
/>

View File

@ -4,10 +4,11 @@ import { type JSX, splitProps } from "solid-js";
type TextareaProps = {
ref: HTMLTextAreaElement | undefined;
onInput: (value: string) => void;
inputmode?: string;
} & Omit<JSX.TextareaHTMLAttributes<HTMLTextAreaElement>, "onInput">;
export const Textarea = (props: TextareaProps) => {
const [local, others] = splitProps(props, ["ref", "onInput"]);
const [local, others] = splitProps(props, ["ref", "onInput", "inputmode"]);
const isMobile = guessDeviceIsMobile();
@ -19,6 +20,7 @@ export const Textarea = (props: TextareaProps) => {
data-testid="textarea"
required
autofocus={!isMobile}
inputmode={local.inputmode}
onInput={(e) => local.onInput(e.currentTarget.value)}
{...others}
/>

View File

@ -313,6 +313,7 @@ export const TextInput = (props: Props) => {
onInput={handleInput}
onKeyDown={submitIfCtrlEnter}
value={inputValue()}
inputmode={props.block.options?.inputMode}
placeholder={
props.block.options?.labels?.placeholder ??
defaultTextInputOptions.labels.placeholder
@ -323,6 +324,7 @@ export const TextInput = (props: Props) => {
ref={inputRef as HTMLInputElement}
onInput={handleInput}
value={inputValue()}
inputmode={props.block.options?.inputMode}
placeholder={
props.block.options?.labels?.placeholder ??
defaultTextInputOptions.labels.placeholder