From 1f19eb8763b2553feb849075eda8a3c8adec6fa9 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Wed, 15 Nov 2023 14:10:41 +0100 Subject: [PATCH] :children_crossing: Auto scroll once picture choice images are fully loaded --- packages/embeds/js/package.json | 2 +- .../ConversationContainer/ChatChunk.tsx | 3 +++ .../js/src/components/InputChatBlock.tsx | 6 ++++++ .../inputs/payment/components/PaymentForm.tsx | 2 ++ .../payment/components/StripePaymentForm.tsx | 6 +++++- .../pictureChoice/MultiplePictureChoice.tsx | 17 ++++++++++++++++- .../pictureChoice/SinglePictureChoice.tsx | 19 +++++++++++++++++-- packages/embeds/nextjs/package.json | 2 +- packages/embeds/react/package.json | 2 +- 9 files changed, 52 insertions(+), 7 deletions(-) diff --git a/packages/embeds/js/package.json b/packages/embeds/js/package.json index 0335d634f..78c30e581 100644 --- a/packages/embeds/js/package.json +++ b/packages/embeds/js/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/js", - "version": "0.2.19", + "version": "0.2.20", "description": "Javascript library to display typebots on your website", "type": "module", "main": "dist/index.js", diff --git a/packages/embeds/js/src/components/ConversationContainer/ChatChunk.tsx b/packages/embeds/js/src/components/ConversationContainer/ChatChunk.tsx index 82654983b..435f2d323 100644 --- a/packages/embeds/js/src/components/ConversationContainer/ChatChunk.tsx +++ b/packages/embeds/js/src/components/ConversationContainer/ChatChunk.tsx @@ -27,6 +27,7 @@ type Props = Pick & { export const ChatChunk = (props: Props) => { let inputRef: HTMLDivElement | undefined const [displayedMessageIndex, setDisplayedMessageIndex] = createSignal(0) + const [lastBubbleOffsetTop, setLastBubbleOffsetTop] = createSignal() onMount(() => { if (props.streamingMessageId) return @@ -48,6 +49,7 @@ export const ChatChunk = (props: Props) => { ) props.onScrollToBottom(bubbleOffsetTop) if (displayedMessageIndex() === props.messages.length) { + setLastBubbleOffsetTop(bubbleOffsetTop) props.onAllBubblesDisplayed() } } @@ -111,6 +113,7 @@ export const ChatChunk = (props: Props) => { defaultSettings.general.isInputPrefillEnabled } hasError={props.hasError} + onTransitionEnd={() => props.onScrollToBottom(lastBubbleOffsetTop())} onSubmit={props.onSubmit} onSkip={props.onSkip} /> diff --git a/packages/embeds/js/src/components/InputChatBlock.tsx b/packages/embeds/js/src/components/InputChatBlock.tsx index 6e3c4cb2c..3982e6eae 100644 --- a/packages/embeds/js/src/components/InputChatBlock.tsx +++ b/packages/embeds/js/src/components/InputChatBlock.tsx @@ -46,6 +46,7 @@ type Props = { context: BotContext isInputPrefillEnabled: boolean hasError: boolean + onTransitionEnd: () => void onSubmit: (answer: string) => void onSkip: () => void } @@ -102,6 +103,7 @@ export const InputChatBlock = (props: Props) => { block={props.block} inputIndex={props.inputIndex} isInputPrefillEnabled={props.isInputPrefillEnabled} + onTransitionEnd={props.onTransitionEnd} onSubmit={handleSubmit} onSkip={handleSkip} /> @@ -116,6 +118,7 @@ const Input = (props: { block: NonNullable inputIndex: number isInputPrefillEnabled: boolean + onTransitionEnd: () => void onSubmit: (answer: InputSubmitContent) => void onSkip: (label: string) => void }) => { @@ -208,6 +211,7 @@ const Input = (props: { defaultItems={block.items} options={block.options} onSubmit={onSubmit} + onTransitionEnd={props.onTransitionEnd} /> @@ -215,6 +219,7 @@ const Input = (props: { defaultItems={block.items} options={block.options} onSubmit={onSubmit} + onTransitionEnd={props.onTransitionEnd} /> @@ -245,6 +250,7 @@ const Input = (props: { } as PaymentInputBlock['options'] & RuntimeOptions } onSuccess={submitPaymentSuccess} + onTransitionEnd={props.onTransitionEnd} /> diff --git a/packages/embeds/js/src/features/blocks/inputs/payment/components/PaymentForm.tsx b/packages/embeds/js/src/features/blocks/inputs/payment/components/PaymentForm.tsx index 1273e423e..18ed57ff7 100644 --- a/packages/embeds/js/src/features/blocks/inputs/payment/components/PaymentForm.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/payment/components/PaymentForm.tsx @@ -6,6 +6,7 @@ type Props = { context: BotContext options: PaymentInputBlock['options'] & RuntimeOptions onSuccess: () => void + onTransitionEnd: () => void } export const PaymentForm = (props: Props) => ( @@ -13,5 +14,6 @@ export const PaymentForm = (props: Props) => ( onSuccess={props.onSuccess} options={props.options} context={props.context} + onTransitionEnd={props.onTransitionEnd} /> ) diff --git a/packages/embeds/js/src/features/blocks/inputs/payment/components/StripePaymentForm.tsx b/packages/embeds/js/src/features/blocks/inputs/payment/components/StripePaymentForm.tsx index 292c53764..f45371926 100644 --- a/packages/embeds/js/src/features/blocks/inputs/payment/components/StripePaymentForm.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/payment/components/StripePaymentForm.tsx @@ -14,6 +14,7 @@ type Props = { context: BotContext options: PaymentInputBlock['options'] & RuntimeOptions onSuccess: () => void + onTransitionEnd: () => void } const slotName = 'stripe-payment-form' @@ -48,7 +49,10 @@ export const StripePaymentForm = (props: Props) => { layout: 'tabs', }) paymentElement.mount('#payment-element') - setTimeout(() => setIsMounted(true), 1000) + setTimeout(() => { + setIsMounted(true) + props.onTransitionEnd() + }, 1000) }) const handleSubmit = async (event: Event & { submitter: HTMLElement }) => { diff --git a/packages/embeds/js/src/features/blocks/inputs/pictureChoice/MultiplePictureChoice.tsx b/packages/embeds/js/src/features/blocks/inputs/pictureChoice/MultiplePictureChoice.tsx index 9097ac73b..a5769b1e4 100644 --- a/packages/embeds/js/src/features/blocks/inputs/pictureChoice/MultiplePictureChoice.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/pictureChoice/MultiplePictureChoice.tsx @@ -1,6 +1,6 @@ import { InputSubmitContent } from '@/types' import { PictureChoiceBlock } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice' -import { For, Show, createSignal, onMount } from 'solid-js' +import { For, Show, createEffect, createSignal, onMount } from 'solid-js' import { Checkbox } from '../buttons/components/Checkbox' import { SendButton } from '@/components' import { isDefined, isEmpty, isSvgSrc } from '@typebot.io/lib' @@ -12,12 +12,14 @@ type Props = { defaultItems: PictureChoiceBlock['items'] options: PictureChoiceBlock['options'] onSubmit: (value: InputSubmitContent) => void + onTransitionEnd: () => void } export const MultiplePictureChoice = (props: Props) => { let inputRef: HTMLInputElement | undefined const [filteredItems, setFilteredItems] = createSignal(props.defaultItems) const [selectedItemIds, setSelectedItemIds] = createSignal([]) + const [totalLoadedImages, setTotalLoadedImages] = createSignal(0) onMount(() => { if (!isMobile() && inputRef) inputRef.focus() @@ -64,6 +66,18 @@ export const MultiplePictureChoice = (props: Props) => { ) } + createEffect(() => { + if ( + totalLoadedImages() === + props.defaultItems.filter((item) => isDefined(item.pictureSrc)).length + ) + props.onTransitionEnd() + }) + + const onImageLoad = () => { + setTotalLoadedImages((acc) => acc + 1) + } + return (
@@ -112,6 +126,7 @@ export const MultiplePictureChoice = (props: Props) => { elementtiming={`Picture choice ${index() + 1}`} fetchpriority={'high'} class="m-auto" + onLoad={onImageLoad} />
void + onTransitionEnd: () => void } export const SinglePictureChoice = (props: Props) => { let inputRef: HTMLInputElement | undefined const [filteredItems, setFilteredItems] = createSignal(props.defaultItems) + const [totalLoadedImages, setTotalLoadedImages] = createSignal(0) onMount(() => { if (!isMobile() && inputRef) inputRef.focus() @@ -41,6 +43,18 @@ export const SinglePictureChoice = (props: Props) => { ) } + createEffect(() => { + if ( + totalLoadedImages() === + props.defaultItems.filter((item) => isDefined(item.pictureSrc)).length + ) + props.onTransitionEnd() + }) + + const onImageLoad = () => { + setTotalLoadedImages((acc) => acc + 1) + } + return (
@@ -77,6 +91,7 @@ export const SinglePictureChoice = (props: Props) => { elementtiming={`Picture choice ${index() + 1}`} fetchpriority={'high'} class="m-auto" + onLoad={onImageLoad} />