fix(engine): 🐛 Fix scrolling behavior and avatar position
This commit is contained in:
@ -154,6 +154,13 @@ textarea {
|
|||||||
.typebot-button {
|
.typebot-button {
|
||||||
color: var(--typebot-button-color);
|
color: var(--typebot-button-color);
|
||||||
background-color: var(--typebot-button-bg-color);
|
background-color: var(--typebot-button-bg-color);
|
||||||
|
border: 1px solid var(--typebot-button-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.typebot-button.selectable {
|
||||||
|
color: var(--typebot-host-bubble-color);
|
||||||
|
background-color: var(--typebot-host-bubble-bg-color);
|
||||||
|
border: 1px solid var(--typebot-button-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.typebot-host-bubble {
|
.typebot-host-bubble {
|
||||||
|
@ -38,10 +38,10 @@ export const AvatarSideContainer = () => {
|
|||||||
unmountOnExit
|
unmountOnExit
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="fixed w-6 h-6 xs:w-10 xs:h-10 mb-4 xs:mb-2 flex items-center top-0"
|
className="absolute w-6 h-6 xs:w-10 xs:h-10 mb-4 xs:mb-2 flex items-center top-0"
|
||||||
style={{
|
style={{
|
||||||
top: `calc(${topOffset}px - ${marginBottom}px)`,
|
top: `calc(${topOffset}px - ${marginBottom}px)`,
|
||||||
transition: 'top 500ms ease-out',
|
transition: 'top 350ms ease-out',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HostAvatar typebotName={typebot.name} />
|
<HostAvatar typebotName={typebot.name} />
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { animateScroll as scroll } from 'react-scroll'
|
|
||||||
import { TransitionGroup, CSSTransition } from 'react-transition-group'
|
import { TransitionGroup, CSSTransition } from 'react-transition-group'
|
||||||
import { ChatStep } from './ChatStep'
|
import { ChatStep } from './ChatStep'
|
||||||
import { AvatarSideContainer } from './AvatarSideContainer'
|
import { AvatarSideContainer } from './AvatarSideContainer'
|
||||||
@ -20,6 +19,7 @@ type ChatBlockProps = {
|
|||||||
steps: PublicStep[]
|
steps: PublicStep[]
|
||||||
startStepIndex: number
|
startStepIndex: number
|
||||||
blockIndex: number
|
blockIndex: number
|
||||||
|
onScroll: () => void
|
||||||
onBlockEnd: (edgeId?: string) => void
|
onBlockEnd: (edgeId?: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ export const ChatBlock = ({
|
|||||||
steps,
|
steps,
|
||||||
startStepIndex,
|
startStepIndex,
|
||||||
blockIndex,
|
blockIndex,
|
||||||
|
onScroll,
|
||||||
onBlockEnd,
|
onBlockEnd,
|
||||||
}: ChatBlockProps) => {
|
}: ChatBlockProps) => {
|
||||||
const { typebot, updateVariableValue } = useTypebot()
|
const { typebot, updateVariableValue } = useTypebot()
|
||||||
@ -41,7 +42,7 @@ export const ChatBlock = ({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
autoScrollToBottom()
|
onScroll()
|
||||||
onNewStepDisplayed()
|
onNewStepDisplayed()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [displayedSteps])
|
}, [displayedSteps])
|
||||||
@ -69,13 +70,6 @@ export const ChatBlock = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoScrollToBottom = () => {
|
|
||||||
scroll.scrollToBottom({
|
|
||||||
duration: 500,
|
|
||||||
containerId: 'scrollable-container',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayNextStep = (answerContent?: string) => {
|
const displayNextStep = (answerContent?: string) => {
|
||||||
const currentStep = [...displayedSteps].pop()
|
const currentStep = [...displayedSteps].pop()
|
||||||
if (currentStep) {
|
if (currentStep) {
|
||||||
|
@ -55,7 +55,9 @@ export const TextBubble = ({ step, onTransitionEnd }: Props) => {
|
|||||||
const sendAvatarOffset = () => {
|
const sendAvatarOffset = () => {
|
||||||
if (!messageContainer.current) return
|
if (!messageContainer.current) return
|
||||||
const containerDimensions = messageContainer.current.getBoundingClientRect()
|
const containerDimensions = messageContainer.current.getBoundingClientRect()
|
||||||
updateLastAvatarOffset(containerDimensions.top + containerDimensions.height)
|
updateLastAvatarOffset(
|
||||||
|
messageContainer.current.offsetTop + containerDimensions.height
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -44,8 +44,8 @@ export const ChoiceForm = ({ step, onSubmit }: ChoiceFormProps) => {
|
|||||||
className={
|
className={
|
||||||
'py-2 px-4 font-semibold rounded-md transition-all filter hover:brightness-90 active:brightness-75 duration-100 focus:outline-none mr-2 mb-2 typebot-button ' +
|
'py-2 px-4 font-semibold rounded-md transition-all filter hover:brightness-90 active:brightness-75 duration-100 focus:outline-none mr-2 mb-2 typebot-button ' +
|
||||||
(selectedIndices.includes(idx) || !step.options?.isMultipleChoice
|
(selectedIndices.includes(idx) || !step.options?.isMultipleChoice
|
||||||
? 'active'
|
? ''
|
||||||
: '')
|
: 'selectable')
|
||||||
}
|
}
|
||||||
data-testid="button"
|
data-testid="button"
|
||||||
>
|
>
|
||||||
|
@ -17,7 +17,7 @@ export const SendButton = ({
|
|||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
{...props}
|
{...props}
|
||||||
className={
|
className={
|
||||||
'py-2 px-4 font-semibold rounded-md text-white focus:outline-none flex items-center disabled:opacity-50 disabled:cursor-not-allowed disabled:brightness-100 transition-all filter hover:brightness-90 active:brightness-75 typebot-button active ' +
|
'py-2 px-4 font-semibold rounded-md text-white focus:outline-none flex items-center disabled:opacity-50 disabled:cursor-not-allowed disabled:brightness-100 transition-all filter hover:brightness-90 active:brightness-75 typebot-button ' +
|
||||||
props.className
|
props.className
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -7,6 +7,7 @@ import { useAnswers } from '../contexts/AnswersContext'
|
|||||||
import { deepEqual } from 'fast-equals'
|
import { deepEqual } from 'fast-equals'
|
||||||
import { Answer, Edge, PublicBlock, PublicTypebot } from 'models'
|
import { Answer, Edge, PublicBlock, PublicTypebot } from 'models'
|
||||||
import { byId } from 'utils'
|
import { byId } from 'utils'
|
||||||
|
import { animateScroll as scroll } from 'react-scroll'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
typebot: PublicTypebot
|
typebot: PublicTypebot
|
||||||
@ -27,6 +28,7 @@ export const ConversationContainer = ({
|
|||||||
const [localAnswer, setLocalAnswer] = useState<Answer | undefined>()
|
const [localAnswer, setLocalAnswer] = useState<Answer | undefined>()
|
||||||
const { answers } = useAnswers()
|
const { answers } = useAnswers()
|
||||||
const bottomAnchor = useRef<HTMLDivElement | null>(null)
|
const bottomAnchor = useRef<HTMLDivElement | null>(null)
|
||||||
|
const scrollableContainer = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
const displayNextBlock = (edgeId?: string) => {
|
const displayNextBlock = (edgeId?: string) => {
|
||||||
const nextEdge = typebot.edges.find(byId(edgeId))
|
const nextEdge = typebot.edges.find(byId(edgeId))
|
||||||
@ -60,10 +62,18 @@ export const ConversationContainer = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [answers])
|
}, [answers])
|
||||||
|
|
||||||
|
const autoScrollToBottom = () => {
|
||||||
|
if (!scrollableContainer.current) return
|
||||||
|
scroll.scrollToBottom({
|
||||||
|
duration: 500,
|
||||||
|
container: scrollableContainer.current,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={scrollableContainer}
|
||||||
className="overflow-y-scroll w-full lg:w-3/4 min-h-full rounded lg:px-5 px-3 pt-10 relative scrollable-container typebot-chat-view"
|
className="overflow-y-scroll w-full lg:w-3/4 min-h-full rounded lg:px-5 px-3 pt-10 relative scrollable-container typebot-chat-view"
|
||||||
id="scrollable-container"
|
|
||||||
>
|
>
|
||||||
{displayedBlocks.map((displayedBlock, idx) => (
|
{displayedBlocks.map((displayedBlock, idx) => (
|
||||||
<ChatBlock
|
<ChatBlock
|
||||||
@ -71,11 +81,12 @@ export const ConversationContainer = ({
|
|||||||
steps={displayedBlock.block.steps}
|
steps={displayedBlock.block.steps}
|
||||||
startStepIndex={displayedBlock.startStepIndex}
|
startStepIndex={displayedBlock.startStepIndex}
|
||||||
blockIndex={idx}
|
blockIndex={idx}
|
||||||
|
onScroll={autoScrollToBottom}
|
||||||
onBlockEnd={displayNextBlock}
|
onBlockEnd={displayNextBlock}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{/* We use a block to simulate padding because it makes iOS scroll flicker */}
|
{/* We use a block to simulate padding because it makes iOS scroll flicker */}
|
||||||
<div className="w-full" ref={bottomAnchor} />
|
<div className="w-full h-32" ref={bottomAnchor} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user