@@ -1,4 +1,12 @@
|
|||||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionButton,
|
||||||
|
AccordionIcon,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionPanel,
|
||||||
|
FormLabel,
|
||||||
|
Stack,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
import { CodeEditor } from '@/components/inputs/CodeEditor'
|
import { CodeEditor } from '@/components/inputs/CodeEditor'
|
||||||
import { FileInputBlock, Variable } from '@typebot.io/schemas'
|
import { FileInputBlock, Variable } from '@typebot.io/schemas'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@@ -45,6 +53,24 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
|
|||||||
visibility: (typeof fileVisibilityOptions)[number]
|
visibility: (typeof fileVisibilityOptions)[number]
|
||||||
) => onOptionsChange({ ...options, visibility })
|
) => onOptionsChange({ ...options, visibility })
|
||||||
|
|
||||||
|
const updateSingleFileSuccessLabel = (single: string) =>
|
||||||
|
onOptionsChange({
|
||||||
|
...options,
|
||||||
|
labels: {
|
||||||
|
...options?.labels,
|
||||||
|
success: { ...options?.labels?.success, single },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateMultipleFilesSuccessLabel = (multiple: string) =>
|
||||||
|
onOptionsChange({
|
||||||
|
...options,
|
||||||
|
labels: {
|
||||||
|
...options?.labels,
|
||||||
|
success: { ...options?.labels?.success, multiple },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
<SwitchWithLabel
|
<SwitchWithLabel
|
||||||
@@ -75,30 +101,66 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
|
|||||||
withVariableButton={false}
|
withVariableButton={false}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<TextInput
|
<Accordion allowToggle>
|
||||||
label={t('blocks.inputs.settings.button.label')}
|
<AccordionItem>
|
||||||
defaultValue={
|
<AccordionButton justifyContent="space-between">
|
||||||
options?.labels?.button ?? defaultFileInputOptions.labels.button
|
Labels
|
||||||
}
|
<AccordionIcon />
|
||||||
onChange={handleButtonLabelChange}
|
</AccordionButton>
|
||||||
withVariableButton={false}
|
<AccordionPanel as={Stack} spacing={4}>
|
||||||
/>
|
<TextInput
|
||||||
<TextInput
|
label={t('blocks.inputs.settings.button.label')}
|
||||||
label={t('blocks.inputs.file.settings.clear.label')}
|
defaultValue={
|
||||||
defaultValue={
|
options?.labels?.button ?? defaultFileInputOptions.labels.button
|
||||||
options?.labels?.clear ?? defaultFileInputOptions.labels.clear
|
}
|
||||||
}
|
onChange={handleButtonLabelChange}
|
||||||
onChange={updateClearButtonLabel}
|
withVariableButton={false}
|
||||||
withVariableButton={false}
|
/>
|
||||||
/>
|
{options?.isMultipleAllowed && (
|
||||||
<TextInput
|
<TextInput
|
||||||
label={t('blocks.inputs.file.settings.skip.label')}
|
label={t('blocks.inputs.file.settings.clear.label')}
|
||||||
defaultValue={
|
defaultValue={
|
||||||
options?.labels?.skip ?? defaultFileInputOptions.labels.skip
|
options?.labels?.clear ?? defaultFileInputOptions.labels.clear
|
||||||
}
|
}
|
||||||
onChange={updateSkipButtonLabel}
|
onChange={updateClearButtonLabel}
|
||||||
withVariableButton={false}
|
withVariableButton={false}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{!(options?.isRequired ?? defaultFileInputOptions.isRequired) && (
|
||||||
|
<TextInput
|
||||||
|
label={t('blocks.inputs.file.settings.skip.label')}
|
||||||
|
defaultValue={
|
||||||
|
options?.labels?.skip ?? defaultFileInputOptions.labels.skip
|
||||||
|
}
|
||||||
|
onChange={updateSkipButtonLabel}
|
||||||
|
withVariableButton={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<TextInput
|
||||||
|
label="Single file success"
|
||||||
|
defaultValue={
|
||||||
|
options?.labels?.success?.single ??
|
||||||
|
defaultFileInputOptions.labels.success.single
|
||||||
|
}
|
||||||
|
onChange={updateSingleFileSuccessLabel}
|
||||||
|
withVariableButton={false}
|
||||||
|
/>
|
||||||
|
{options?.isMultipleAllowed && (
|
||||||
|
<TextInput
|
||||||
|
label="Multi files success"
|
||||||
|
moreInfoTooltip="Include {total} to show the total number of files uploaded"
|
||||||
|
defaultValue={
|
||||||
|
options?.labels?.success?.multiple ??
|
||||||
|
defaultFileInputOptions.labels.success.multiple
|
||||||
|
}
|
||||||
|
onChange={updateMultipleFilesSuccessLabel}
|
||||||
|
withVariableButton={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
<DropdownList
|
<DropdownList
|
||||||
label="Visibility:"
|
label="Visibility:"
|
||||||
moreInfoTooltip='This setting determines who can see the uploaded files. "Public" means that anyone who has the link can see the files. "Private" means that only a members of this workspace can see the files.'
|
moreInfoTooltip='This setting determines who can see the uploaded files. "Public" means that anyone who has the link can see the files. "Private" means that only a members of this workspace can see the files.'
|
||||||
|
|||||||
@@ -2,4 +2,6 @@ const urlRegex =
|
|||||||
/^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]:[0-9]*\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]:[0-9]*\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})$/
|
/^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]:[0-9]*\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]:[0-9]*\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})$/
|
||||||
|
|
||||||
export const validateUrl = (url: string) =>
|
export const validateUrl = (url: string) =>
|
||||||
url.startsWith('http://localhost') || urlRegex.test(url)
|
url.startsWith('http://localhost') ||
|
||||||
|
url.startsWith('http://fake-upload-url.com') ||
|
||||||
|
urlRegex.test(url)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.2.39",
|
"version": "0.2.40",
|
||||||
"description": "Javascript library to display typebots on your website",
|
"description": "Javascript library to display typebots on your website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ export const FileUploadForm = (props: Props) => {
|
|||||||
const startSingleFileUpload = async (file: File) => {
|
const startSingleFileUpload = async (file: File) => {
|
||||||
if (props.context.isPreview || !props.context.resultId)
|
if (props.context.isPreview || !props.context.resultId)
|
||||||
return props.onSubmit({
|
return props.onSubmit({
|
||||||
label: `File uploaded`,
|
label:
|
||||||
|
props.block.options?.labels?.success?.single ??
|
||||||
|
defaultFileInputOptions.labels.success.single,
|
||||||
value: 'http://fake-upload-url.com',
|
value: 'http://fake-upload-url.com',
|
||||||
})
|
})
|
||||||
setIsUploading(true)
|
setIsUploading(true)
|
||||||
@@ -70,7 +72,9 @@ export const FileUploadForm = (props: Props) => {
|
|||||||
setIsUploading(false)
|
setIsUploading(false)
|
||||||
if (urls.length)
|
if (urls.length)
|
||||||
return props.onSubmit({
|
return props.onSubmit({
|
||||||
label: `File uploaded`,
|
label:
|
||||||
|
props.block.options?.labels?.success?.single ??
|
||||||
|
defaultFileInputOptions.labels.success.single,
|
||||||
value: urls[0] ? encodeUrl(urls[0]) : '',
|
value: urls[0] ? encodeUrl(urls[0]) : '',
|
||||||
})
|
})
|
||||||
setErrorMessage('An error occured while uploading the file')
|
setErrorMessage('An error occured while uploading the file')
|
||||||
@@ -79,7 +83,14 @@ export const FileUploadForm = (props: Props) => {
|
|||||||
const resultId = props.context.resultId
|
const resultId = props.context.resultId
|
||||||
if (props.context.isPreview || !resultId)
|
if (props.context.isPreview || !resultId)
|
||||||
return props.onSubmit({
|
return props.onSubmit({
|
||||||
label: `${files.length} file${files.length > 1 ? 's' : ''} uploaded`,
|
label:
|
||||||
|
files.length > 1
|
||||||
|
? (
|
||||||
|
props.block.options?.labels?.success?.multiple ??
|
||||||
|
defaultFileInputOptions.labels.success.multiple
|
||||||
|
).replaceAll('{total}', files.length.toString())
|
||||||
|
: props.block.options?.labels?.success?.single ??
|
||||||
|
defaultFileInputOptions.labels.success.single,
|
||||||
value: files
|
value: files
|
||||||
.map((_, idx) => `http://fake-upload-url.com/${idx}`)
|
.map((_, idx) => `http://fake-upload-url.com/${idx}`)
|
||||||
.join(', '),
|
.join(', '),
|
||||||
@@ -101,7 +112,14 @@ export const FileUploadForm = (props: Props) => {
|
|||||||
if (urls.length !== files.length)
|
if (urls.length !== files.length)
|
||||||
return setErrorMessage('An error occured while uploading the files')
|
return setErrorMessage('An error occured while uploading the files')
|
||||||
props.onSubmit({
|
props.onSubmit({
|
||||||
label: `${urls.length} file${urls.length > 1 ? 's' : ''} uploaded`,
|
label:
|
||||||
|
urls.length > 1
|
||||||
|
? (
|
||||||
|
props.block.options?.labels?.success?.multiple ??
|
||||||
|
defaultFileInputOptions.labels.success.multiple
|
||||||
|
).replaceAll('{total}', urls.length.toString())
|
||||||
|
: props.block.options?.labels?.success?.single ??
|
||||||
|
defaultFileInputOptions.labels.success.single,
|
||||||
value: urls.filter(isDefined).map(encodeUrl).join(', '),
|
value: urls.filter(isDefined).map(encodeUrl).join(', '),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/nextjs",
|
"name": "@typebot.io/nextjs",
|
||||||
"version": "0.2.39",
|
"version": "0.2.40",
|
||||||
"description": "Convenient library to display typebots on your Next.js website",
|
"description": "Convenient library to display typebots on your Next.js website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/react",
|
"name": "@typebot.io/react",
|
||||||
"version": "0.2.39",
|
"version": "0.2.40",
|
||||||
"description": "Convenient library to display typebots on your React app",
|
"description": "Convenient library to display typebots on your React app",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ export const defaultFileInputOptions = {
|
|||||||
button: 'Upload',
|
button: 'Upload',
|
||||||
clear: 'Clear',
|
clear: 'Clear',
|
||||||
skip: 'Skip',
|
skip: 'Skip',
|
||||||
|
success: {
|
||||||
|
single: 'File uploaded',
|
||||||
|
multiple: '{total} files uploaded',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies FileInputBlock['options']
|
} as const satisfies FileInputBlock['options']
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ const fileInputOptionsV5Schema = optionBaseSchema.merge(
|
|||||||
button: z.string().optional(),
|
button: z.string().optional(),
|
||||||
clear: z.string().optional(),
|
clear: z.string().optional(),
|
||||||
skip: z.string().optional(),
|
skip: z.string().optional(),
|
||||||
|
success: z
|
||||||
|
.object({
|
||||||
|
single: z.string().optional(),
|
||||||
|
multiple: z.string().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
sizeLimit: z.number().optional(),
|
sizeLimit: z.number().optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user