From 124f350aa27fdf33063d98266169b75e8065c4c4 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Wed, 26 Apr 2023 15:59:22 +0200 Subject: [PATCH] :lipstick: (buttons) Improve multiple choice form UI --- .../blocks/inputs/buttons/buttons.spec.ts | 9 +- apps/builder/src/features/theme/theme.spec.ts | 14 +-- .../src/features/results/results.spec.ts | 2 +- packages/embeds/js/package.json | 2 +- packages/embeds/js/src/assets/index.css | 38 ++++++++ packages/embeds/js/src/components/Button.tsx | 33 +++++++ .../AvatarSideContainer.tsx | 4 +- .../ConversationContainer/ChatChunk.tsx | 95 +++++++++---------- .../ConversationContainer.tsx | 4 +- .../PopupBlockedToast.tsx | 15 +-- .../js/src/components/InputChatBlock.tsx | 31 ++++-- .../embeds/js/src/components/SendButton.tsx | 57 +++-------- packages/embeds/js/src/components/Spinner.tsx | 26 +++++ .../embeds/js/src/components/TypingBubble.tsx | 6 +- .../js/src/components/bubbles/GuestBubble.tsx | 4 +- .../src/components/bubbles/LoadingBubble.tsx | 2 +- .../js/src/components/icons/CheckIcon.tsx | 16 ++++ .../bubbles/audio/components/AudioBubble.tsx | 2 +- .../bubbles/embed/components/EmbedBubble.tsx | 2 +- .../bubbles/image/components/ImageBubble.tsx | 2 +- .../textBubble/components/TextBubble.tsx | 2 +- .../bubbles/video/components/VideoBubble.tsx | 2 +- .../inputs/buttons/components/Buttons.tsx | 41 ++++++++ .../inputs/buttons/components/Checkbox.tsx | 18 ++++ .../inputs/buttons/components/ChoiceForm.tsx | 82 ---------------- .../components/MultipleChoicesForm.tsx | 84 ++++++++++++++++ .../features/blocks/inputs/buttons/index.ts | 1 - .../inputs/date/components/DateForm.tsx | 4 +- .../fileUpload/components/FileUploadForm.tsx | 38 +++----- .../inputs/rating/components/RatingForm.tsx | 31 +++--- packages/embeds/js/src/utils/hexToRgb.ts | 20 ++++ .../js/src/utils/setCssVariablesValue.ts | 27 +++++- packages/embeds/react/package.json | 2 +- 33 files changed, 454 insertions(+), 262 deletions(-) create mode 100644 packages/embeds/js/src/components/Button.tsx create mode 100644 packages/embeds/js/src/components/Spinner.tsx create mode 100644 packages/embeds/js/src/components/icons/CheckIcon.tsx create mode 100644 packages/embeds/js/src/features/blocks/inputs/buttons/components/Buttons.tsx create mode 100644 packages/embeds/js/src/features/blocks/inputs/buttons/components/Checkbox.tsx delete mode 100644 packages/embeds/js/src/features/blocks/inputs/buttons/components/ChoiceForm.tsx create mode 100644 packages/embeds/js/src/features/blocks/inputs/buttons/components/MultipleChoicesForm.tsx delete mode 100644 packages/embeds/js/src/features/blocks/inputs/buttons/index.ts create mode 100644 packages/embeds/js/src/utils/hexToRgb.ts diff --git a/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts b/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts index 2cbb3cdf74..b43f570135 100644 --- a/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts +++ b/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts @@ -45,9 +45,8 @@ test.describe.parallel('Buttons input block', () => { await expect(page.locator('text=Item 2')).toBeHidden() await page.click('text=Preview') - const item3Button = page.locator('button >> text=Item 3') - await item3Button.click() - await expect(item3Button).toBeHidden() + await page.getByRole('button', { name: 'Item 3' }).click() + await expect(page.getByRole('button', { name: 'Item 3' })).toBeHidden() await expect(page.getByTestId('guest-bubble')).toHaveText('Item 3') await page.click('button[aria-label="Close"]') @@ -67,8 +66,8 @@ test.describe.parallel('Buttons input block', () => { await page.click('text=Preview') - await page.locator('button >> text="Item 3"').click() - await page.locator('button >> text="Item 1"').click() + await page.getByRole('checkbox', { name: 'Item 3' }).click() + await page.getByRole('checkbox', { name: 'Item 1' }).click() await page.locator('text=Go').click() await expect(page.locator('text="Item 3, Item 1"')).toBeVisible() diff --git a/apps/builder/src/features/theme/theme.spec.ts b/apps/builder/src/features/theme/theme.spec.ts index 748a957209..55e82a5bd7 100644 --- a/apps/builder/src/features/theme/theme.spec.ts +++ b/apps/builder/src/features/theme/theme.spec.ts @@ -18,7 +18,7 @@ test.describe.parallel('Theme page', () => { id: typebotId, }) await page.goto(`/typebots/${typebotId}/theme`) - await expect(page.locator('button >> text="Go"')).toBeVisible() + await expect(page.getByRole('button', { name: 'Go' })).toBeVisible() // Font await page.getByRole('button', { name: 'Font & Background' }).click() @@ -70,7 +70,7 @@ test.describe.parallel('Theme page', () => { } await page.goto(`/typebots/${typebotId}/theme`) - await expect(page.locator('button >> text="Go"')).toBeVisible() + await expect(page.getByRole('button', { name: 'Go' })).toBeVisible() await page.click('button:has-text("Chat")') // Host avatar @@ -81,7 +81,7 @@ test.describe.parallel('Theme page', () => { 'input[placeholder="Paste the image link..."]', hostAvatarUrl ) - await page.locator('button >> text="Go"').click() + await page.getByRole('button', { name: 'Go' }).click() await expect(page.locator('.typebot-container img')).toHaveAttribute( 'src', @@ -155,7 +155,7 @@ test.describe.parallel('Theme page', () => { '[data-testid="guest-bubbles-theme"] >> [aria-label="Pick a color"] >> nth=1' ) await page.fill('input[value="#FFFFFF"]', '#264653') - await page.locator('button >> text="Go"').click() + await page.getByRole('button', { name: 'Go' }).click() const guestBubble = page.locator('[data-testid="guest-bubble"] >> nth=-1') await expect(guestBubble).toHaveCSS( 'background-color', @@ -173,7 +173,7 @@ test.describe.parallel('Theme page', () => { await page .locator('input[placeholder="Paste the image link..."]') .fill(guestAvatarUrl) - await page.locator('button >> text="Go"').click() + await page.getByRole('button', { name: 'Go' }).click() await expect(page.locator('.typebot-container img')).toHaveAttribute( 'src', guestAvatarUrl @@ -202,7 +202,7 @@ test.describe.parallel('Theme page', () => { id: typebotId, }) await page.goto(`/typebots/${typebotId}/theme`) - await expect(page.locator('button >> text="Go"')).toBeVisible() + await expect(page.getByRole('button', { name: 'Go' })).toBeVisible() await page.click('button:has-text("Custom CSS")') await page.fill( 'div[role="textbox"]', @@ -222,7 +222,7 @@ test.describe.parallel('Theme page', () => { id: typebotId, }) await page.goto(`/typebots/${typebotId}/theme`) - await expect(page.locator('button >> text="Go"')).toBeVisible() + await expect(page.getByRole('button', { name: 'Go' })).toBeVisible() await page.getByRole('button', { name: 'Templates New!' }).click() await page.getByRole('button', { name: 'Save current theme' }).click() await page.getByPlaceholder('My template').fill('My awesome theme') diff --git a/apps/viewer/src/features/results/results.spec.ts b/apps/viewer/src/features/results/results.spec.ts index caf9b3d5a2..cd52c2100c 100644 --- a/apps/viewer/src/features/results/results.spec.ts +++ b/apps/viewer/src/features/results/results.spec.ts @@ -14,7 +14,7 @@ test('Big groups should work as expected', async ({ page }) => { await page.locator('input').press('Enter') await page.locator('input').fill('26') await page.locator('input').press('Enter') - await page.locator('button >> text=Yes').click() + await page.getByRole('button', { name: 'Yes' }).click() await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) await expect(page.locator('text="Baptiste"')).toBeVisible() await expect(page.locator('text="26"')).toBeVisible() diff --git a/packages/embeds/js/package.json b/packages/embeds/js/package.json index a27669938e..58e3de35b9 100644 --- a/packages/embeds/js/package.json +++ b/packages/embeds/js/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/js", - "version": "0.0.39", + "version": "0.0.40", "description": "Javascript library to display typebots on your website", "type": "module", "main": "dist/index.js", diff --git a/packages/embeds/js/src/assets/index.css b/packages/embeds/js/src/assets/index.css index 6295671ade..ac4ca45a1f 100644 --- a/packages/embeds/js/src/assets/index.css +++ b/packages/embeds/js/src/assets/index.css @@ -6,10 +6,14 @@ --typebot-container-bg-image: none; --typebot-container-bg-color: transparent; --typebot-container-font-family: 'Open Sans'; + --typebot-container-color: #303235; --typebot-button-bg-color: #0042da; + --typebot-button-bg-color-rgb: 0, 66, 218; --typebot-button-color: #ffffff; + --typebot-checkbox-bg-color: #ffffff; + --typebot-host-bubble-bg-color: #f7f8ff; --typebot-host-bubble-color: #303235; @@ -134,6 +138,7 @@ textarea { background-color: var(--typebot-button-bg-color); border: 1px solid var(--typebot-button-bg-color); border-radius: var(--typebot-border-radius); + transition: all 0.3s ease; } .typebot-button.selectable { @@ -142,6 +147,38 @@ textarea { border: 1px solid var(--typebot-button-bg-color); } +.typebot-selectable { + border: 1px solid rgba(var(--typebot-button-bg-color-rgb), 0.25); + border-radius: var(--typebot-border-radius); + color: var(--typebot-container-color); + background-color: rgba(var(--typebot-button-bg-color-rgb), 0.08); + transition: all 0.3s ease; +} + +.typebot-selectable:hover { + background-color: rgba(var(--typebot-button-bg-color-rgb), 0.12); + border-color: rgba(var(--typebot-button-bg-color-rgb), 0.3); +} + +.typebot-selectable.selected { + background-color: rgba(var(--typebot-button-bg-color-rgb), 0.18); + border-color: rgba(var(--typebot-button-bg-color-rgb), 0.35); +} + +.typebot-checkbox { + border: 1px solid var(--typebot-button-bg-color); + border-radius: var(--typebot-border-radius); + background-color: var(--typebot-checkbox-bg-color); + color: var(--typebot-button-color); + padding: 1px; + border-radius: 2px; + transition: all 0.3s ease; +} + +.typebot-checkbox.checked { + background-color: var(--typebot-button-bg-color); +} + .typebot-host-bubble { color: var(--typebot-host-bubble-color); } @@ -220,6 +257,7 @@ textarea { .typebot-upload-input { transition: border-color 100ms ease-out; + border-radius: var(--typebot-border-radius); } .typebot-upload-input.dragging-over { diff --git a/packages/embeds/js/src/components/Button.tsx b/packages/embeds/js/src/components/Button.tsx new file mode 100644 index 0000000000..61b7c4574c --- /dev/null +++ b/packages/embeds/js/src/components/Button.tsx @@ -0,0 +1,33 @@ +import { children, JSX, Show, splitProps } from 'solid-js' +import { Spinner } from './Spinner' + +type Props = { + variant?: 'primary' | 'secondary' + children: JSX.Element + isDisabled?: boolean + isLoading?: boolean +} & JSX.ButtonHTMLAttributes + +export const Button = (props: Props) => { + const childrenReturn = children(() => props.children) + const [local, buttonProps] = splitProps(props, ['disabled', 'class']) + + return ( + + ) +} diff --git a/packages/embeds/js/src/components/ConversationContainer/AvatarSideContainer.tsx b/packages/embeds/js/src/components/ConversationContainer/AvatarSideContainer.tsx index b506ea3a53..b9e3056c39 100644 --- a/packages/embeds/js/src/components/ConversationContainer/AvatarSideContainer.tsx +++ b/packages/embeds/js/src/components/ConversationContainer/AvatarSideContainer.tsx @@ -28,13 +28,13 @@ export const AvatarSideContainer = (props: Props) => {
{ } return ( -
-
-
- 0 - } - > - - -
- - {(message) => ( - - )} - -
-
- {props.input && displayedMessageIndex() === props.messages.length && ( - +
+ 0 + } + > + - )} + +
+ + {(message) => ( + + )} + +
+ {props.input && displayedMessageIndex() === props.messages.length && ( + + )}
) } diff --git a/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx b/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx index 97ab3b08f7..338afb170a 100644 --- a/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx +++ b/packages/embeds/js/src/components/ConversationContainer/ConversationContainer.tsx @@ -184,7 +184,7 @@ export const ConversationContainer = (props: Props) => { return (
{(chatChunk, index) => ( @@ -228,5 +228,5 @@ type BottomSpacerProps = { ref: HTMLDivElement | undefined } const BottomSpacer = (props: BottomSpacerProps) => { - return
+ return
} diff --git a/packages/embeds/js/src/components/ConversationContainer/PopupBlockedToast.tsx b/packages/embeds/js/src/components/ConversationContainer/PopupBlockedToast.tsx index 54f0733824..0ab147c341 100644 --- a/packages/embeds/js/src/components/ConversationContainer/PopupBlockedToast.tsx +++ b/packages/embeds/js/src/components/ConversationContainer/PopupBlockedToast.tsx @@ -9,17 +9,18 @@ export const PopupBlockedToast = (props: Props) => { class="w-full max-w-xs p-4 text-gray-500 bg-white shadow flex flex-col gap-2 typebot-popup-blocked-toast" role="alert" > - - Popup blocked - -
- The bot wants to open a new tab but it was blocked by your broswer. It - needs a manual approval. +
+ Popup blocked +
+ The bot wants to open a new tab but it was blocked by your broswer. It + needs a manual approval. +
+ props.onLinkClick()} > diff --git a/packages/embeds/js/src/components/InputChatBlock.tsx b/packages/embeds/js/src/components/InputChatBlock.tsx index 72e5ff11dc..b56ae78c84 100644 --- a/packages/embeds/js/src/components/InputChatBlock.tsx +++ b/packages/embeds/js/src/components/InputChatBlock.tsx @@ -22,13 +22,14 @@ import { EmailInput } from '@/features/blocks/inputs/email' import { UrlInput } from '@/features/blocks/inputs/url' import { PhoneInput } from '@/features/blocks/inputs/phone' import { DateForm } from '@/features/blocks/inputs/date' -import { ChoiceForm } from '@/features/blocks/inputs/buttons' import { RatingForm } from '@/features/blocks/inputs/rating' import { FileUploadForm } from '@/features/blocks/inputs/fileUpload' import { createSignal, Switch, Match } from 'solid-js' import { isNotDefined } from '@typebot.io/lib' import { isMobile } from '@/utils/isMobileSignal' import { PaymentForm } from '@/features/blocks/inputs/payment' +import { MultipleChoicesForm } from '@/features/blocks/inputs/buttons/components/MultipleChoicesForm' +import { Buttons } from '@/features/blocks/inputs/buttons/components/Buttons' type Props = { block: NonNullable @@ -66,13 +67,13 @@ export const InputChatBlock = (props: Props) => {
{props.hasHostAvatar && (
@@ -158,10 +159,28 @@ const Input = (props: { onSubmit={onSubmit} /> - - + + + + diff --git a/packages/embeds/js/src/components/SendButton.tsx b/packages/embeds/js/src/components/SendButton.tsx index 17fa208279..ddac0fa83d 100644 --- a/packages/embeds/js/src/components/SendButton.tsx +++ b/packages/embeds/js/src/components/SendButton.tsx @@ -1,7 +1,8 @@ import { isMobile } from '@/utils/isMobileSignal' -import { Show } from 'solid-js' +import { splitProps } from 'solid-js' import { JSX } from 'solid-js/jsx-runtime' import { SendIcon } from './icons' +import { Button } from './Button' type SendButtonProps = { isDisabled?: boolean @@ -10,50 +11,16 @@ type SendButtonProps = { } & JSX.ButtonHTMLAttributes export const SendButton = (props: SendButtonProps) => { + const [local, others] = splitProps(props, ['disableIcon']) return ( - + ) } - -export const Spinner = (props: JSX.SvgSVGAttributes) => ( - - - - -) diff --git a/packages/embeds/js/src/components/Spinner.tsx b/packages/embeds/js/src/components/Spinner.tsx new file mode 100644 index 0000000000..eb42c1d7c0 --- /dev/null +++ b/packages/embeds/js/src/components/Spinner.tsx @@ -0,0 +1,26 @@ +import { JSX } from 'solid-js' + +export const Spinner = (props: JSX.SvgSVGAttributes) => ( + + + + +) diff --git a/packages/embeds/js/src/components/TypingBubble.tsx b/packages/embeds/js/src/components/TypingBubble.tsx index b388c297d9..6bf2579077 100644 --- a/packages/embeds/js/src/components/TypingBubble.tsx +++ b/packages/embeds/js/src/components/TypingBubble.tsx @@ -1,7 +1,7 @@ export const TypingBubble = () => ( -
-
-
+
+
+
) diff --git a/packages/embeds/js/src/components/bubbles/GuestBubble.tsx b/packages/embeds/js/src/components/bubbles/GuestBubble.tsx index d14fbf14b6..68a3ff1380 100644 --- a/packages/embeds/js/src/components/bubbles/GuestBubble.tsx +++ b/packages/embeds/js/src/components/bubbles/GuestBubble.tsx @@ -9,11 +9,11 @@ type Props = { export const GuestBubble = (props: Props) => (
{props.message} diff --git a/packages/embeds/js/src/components/bubbles/LoadingBubble.tsx b/packages/embeds/js/src/components/bubbles/LoadingBubble.tsx index 12720b26ab..9ddaf48207 100644 --- a/packages/embeds/js/src/components/bubbles/LoadingBubble.tsx +++ b/packages/embeds/js/src/components/bubbles/LoadingBubble.tsx @@ -2,7 +2,7 @@ import { TypingBubble } from '@/components' export const LoadingBubble = () => (
-
+
) => ( + + + +) diff --git a/packages/embeds/js/src/features/blocks/bubbles/audio/components/AudioBubble.tsx b/packages/embeds/js/src/features/blocks/bubbles/audio/components/AudioBubble.tsx index a99a6bc286..857cfa4ae9 100644 --- a/packages/embeds/js/src/features/blocks/bubbles/audio/components/AudioBubble.tsx +++ b/packages/embeds/js/src/features/blocks/bubbles/audio/components/AudioBubble.tsx @@ -30,7 +30,7 @@ export const AudioBubble = (props: Props) => { return (
-
+
{ return (
-
+
diff --git a/packages/embeds/js/src/features/blocks/bubbles/image/components/ImageBubble.tsx b/packages/embeds/js/src/features/blocks/bubbles/image/components/ImageBubble.tsx index 6b4e76c0ab..7821f435a0 100644 --- a/packages/embeds/js/src/features/blocks/bubbles/image/components/ImageBubble.tsx +++ b/packages/embeds/js/src/features/blocks/bubbles/image/components/ImageBubble.tsx @@ -55,7 +55,7 @@ export const ImageBubble = (props: Props) => { return (
-
+
{ return (
-
+
{ return (
-
+
void +} + +export const Buttons = (props: Props) => { + // eslint-disable-next-line solid/reactivity + const handleClick = (itemIndex: number) => () => + props.onSubmit({ value: props.items[itemIndex].content ?? '' }) + + return ( +
+ + {(item, index) => ( + + + {props.inputIndex === 0 && props.items.length === 1 && ( + + + + + )} + + )} + +
+ ) +} diff --git a/packages/embeds/js/src/features/blocks/inputs/buttons/components/Checkbox.tsx b/packages/embeds/js/src/features/blocks/inputs/buttons/components/Checkbox.tsx new file mode 100644 index 0000000000..a64babaa1d --- /dev/null +++ b/packages/embeds/js/src/features/blocks/inputs/buttons/components/Checkbox.tsx @@ -0,0 +1,18 @@ +import { CheckIcon } from '@/components/icons/CheckIcon' +import { Show } from 'solid-js' + +type Props = { + isChecked: boolean +} + +export const Checkbox = (props: Props) => { + return ( +
+ + + +
+ ) +} diff --git a/packages/embeds/js/src/features/blocks/inputs/buttons/components/ChoiceForm.tsx b/packages/embeds/js/src/features/blocks/inputs/buttons/components/ChoiceForm.tsx deleted file mode 100644 index d24640b510..0000000000 --- a/packages/embeds/js/src/features/blocks/inputs/buttons/components/ChoiceForm.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { SendButton } from '@/components/SendButton' -import { InputSubmitContent } from '@/types' -import type { ChoiceInputBlock } from '@typebot.io/schemas' -import { createSignal, For } from 'solid-js' - -type Props = { - inputIndex: number - block: ChoiceInputBlock - onSubmit: (value: InputSubmitContent) => void -} - -export const ChoiceForm = (props: Props) => { - const [selectedIndices, setSelectedIndices] = createSignal([]) - - const handleClick = (itemIndex: number) => { - if (props.block.options?.isMultipleChoice) - toggleSelectedItemIndex(itemIndex) - else props.onSubmit({ value: props.block.items[itemIndex].content ?? '' }) - } - - const toggleSelectedItemIndex = (itemIndex: number) => { - const existingIndex = selectedIndices().indexOf(itemIndex) - if (existingIndex !== -1) { - setSelectedIndices((selectedIndices) => - selectedIndices.filter((index) => index !== itemIndex) - ) - } else { - setSelectedIndices((selectedIndices) => [...selectedIndices, itemIndex]) - } - } - - const handleSubmit = () => - props.onSubmit({ - value: selectedIndices() - .map((itemIndex) => props.block.items[itemIndex].content) - .join(', '), - }) - - return ( -
-
- - {(item, index) => ( - - - {props.inputIndex === 0 && props.block.items.length === 1 && ( - - - - - )} - - )} - -
-
- {selectedIndices().length > 0 && ( - - {props.block.options?.buttonLabel ?? 'Send'} - - )} -
-
- ) -} diff --git a/packages/embeds/js/src/features/blocks/inputs/buttons/components/MultipleChoicesForm.tsx b/packages/embeds/js/src/features/blocks/inputs/buttons/components/MultipleChoicesForm.tsx new file mode 100644 index 0000000000..95f162f180 --- /dev/null +++ b/packages/embeds/js/src/features/blocks/inputs/buttons/components/MultipleChoicesForm.tsx @@ -0,0 +1,84 @@ +import { SendButton } from '@/components/SendButton' +import { InputSubmitContent } from '@/types' +import { isMobile } from '@/utils/isMobileSignal' +import type { ChoiceInputBlock } from '@typebot.io/schemas' +import { createSignal, For } from 'solid-js' +import { Checkbox } from './Checkbox' + +type Props = { + inputIndex: number + items: ChoiceInputBlock['items'] + options: ChoiceInputBlock['options'] + onSubmit: (value: InputSubmitContent) => void +} + +export const MultipleChoicesForm = (props: Props) => { + const [selectedIndices, setSelectedIndices] = createSignal([]) + + const handleClick = (itemIndex: number) => { + toggleSelectedItemIndex(itemIndex) + } + + const toggleSelectedItemIndex = (itemIndex: number) => { + const existingIndex = selectedIndices().indexOf(itemIndex) + if (existingIndex !== -1) { + setSelectedIndices((selectedIndices) => + selectedIndices.filter((index) => index !== itemIndex) + ) + } else { + setSelectedIndices((selectedIndices) => [...selectedIndices, itemIndex]) + } + } + + const handleSubmit = () => + props.onSubmit({ + value: selectedIndices() + .map((itemIndex) => props.items[itemIndex].content) + .join(', '), + }) + + return ( +
+
+ + {(item, index) => ( + +
selectedIndex === index() + )} + on:click={() => handleClick(index())} + class={ + 'w-full py-2 px-4 font-semibold focus:outline-none cursor-pointer select-none typebot-selectable' + + (selectedIndices().some( + (selectedIndex) => selectedIndex === index() + ) + ? ' selected' + : '') + } + data-itemid={item.id} + > +
+ selectedIndex === index() + )} + /> + {item.content} +
+
+
+ )} +
+
+
+ {selectedIndices().length > 0 && ( + + {props.options?.buttonLabel ?? 'Send'} + + )} +
+
+ ) +} diff --git a/packages/embeds/js/src/features/blocks/inputs/buttons/index.ts b/packages/embeds/js/src/features/blocks/inputs/buttons/index.ts deleted file mode 100644 index 6ab5fadf07..0000000000 --- a/packages/embeds/js/src/features/blocks/inputs/buttons/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ChoiceForm } from './components/ChoiceForm' diff --git a/packages/embeds/js/src/features/blocks/inputs/date/components/DateForm.tsx b/packages/embeds/js/src/features/blocks/inputs/date/components/DateForm.tsx index e4e3d408dd..265694bdfe 100644 --- a/packages/embeds/js/src/features/blocks/inputs/date/components/DateForm.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/date/components/DateForm.tsx @@ -36,11 +36,11 @@ export const DateForm = (props: Props) => {
{props.options?.isRange && ( -

+

{props.options.labels?.from ?? 'From:'}

)} diff --git a/packages/embeds/js/src/features/blocks/inputs/fileUpload/components/FileUploadForm.tsx b/packages/embeds/js/src/features/blocks/inputs/fileUpload/components/FileUploadForm.tsx index 2f7d9ab2c7..93a29b4c71 100644 --- a/packages/embeds/js/src/features/blocks/inputs/fileUpload/components/FileUploadForm.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/fileUpload/components/FileUploadForm.tsx @@ -1,10 +1,12 @@ -import { SendButton, Spinner } from '@/components/SendButton' +import { SendButton } from '@/components/SendButton' import { BotContext, InputSubmitContent } from '@/types' import { guessApiHost } from '@/utils/guessApiHost' import { FileInputBlock } from '@typebot.io/schemas' import { defaultFileInputOptions } from '@typebot.io/schemas/features/blocks/inputs/file' import { createSignal, Match, Show, Switch } from 'solid-js' import { uploadFiles } from '@typebot.io/lib' +import { Button } from '@/components/Button' +import { Spinner } from '@/components/Spinner' type Props = { context: BotContext @@ -111,12 +113,17 @@ export const FileUploadForm = (props: Props) => { const clearFiles = () => setSelectedFiles([]) + const skip = () => + props.onSkip( + props.block.options.labels.skip ?? defaultFileInputOptions.labels.skip + ) + return ( -
+