💄 (embed) Improve avatar alignment and audio auto load
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"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",
|
||||||
|
|||||||
@@ -76,10 +76,10 @@
|
|||||||
|
|
||||||
@keyframes chatBubbles {
|
@keyframes chatBubbles {
|
||||||
0% {
|
0% {
|
||||||
transform: translateY(0);
|
transform: translateY(2.5);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-2.5px);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { TypingBubble } from '@/components'
|
import { TypingBubble } from '@/components'
|
||||||
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
import type { AudioBubbleContent } from '@typebot.io/schemas'
|
import type { AudioBubbleContent } from '@typebot.io/schemas'
|
||||||
import { createSignal, onCleanup, onMount } from 'solid-js'
|
import { createSignal, onCleanup, onMount } from 'solid-js'
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const showAnimationDuration = 400
|
const showAnimationDuration = 400
|
||||||
const typingDuration = 500
|
const typingDuration = 100
|
||||||
|
|
||||||
let typingTimeout: NodeJS.Timeout
|
let typingTimeout: NodeJS.Timeout
|
||||||
|
|
||||||
@@ -18,7 +19,8 @@ export const AudioBubble = (props: Props) => {
|
|||||||
let audioElement: HTMLAudioElement | undefined
|
let audioElement: HTMLAudioElement | undefined
|
||||||
const [isTyping, setIsTyping] = createSignal(true)
|
const [isTyping, setIsTyping] = createSignal(true)
|
||||||
|
|
||||||
const endTyping = () => {
|
onMount(() => {
|
||||||
|
typingTimeout = setTimeout(() => {
|
||||||
if (isPlayed) return
|
if (isPlayed) return
|
||||||
isPlayed = true
|
isPlayed = true
|
||||||
setIsTyping(false)
|
setIsTyping(false)
|
||||||
@@ -26,21 +28,7 @@ export const AudioBubble = (props: Props) => {
|
|||||||
() => props.onTransitionEnd(ref?.offsetTop),
|
() => props.onTransitionEnd(ref?.offsetTop),
|
||||||
showAnimationDuration
|
showAnimationDuration
|
||||||
)
|
)
|
||||||
}
|
}, typingDuration)
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
typingTimeout = setTimeout(endTyping, typingDuration)
|
|
||||||
audioElement?.addEventListener(
|
|
||||||
'canplay',
|
|
||||||
() => {
|
|
||||||
clearTimeout(typingTimeout)
|
|
||||||
audioElement
|
|
||||||
?.play()
|
|
||||||
.catch((e) => console.warn("Couldn't autoplay audio", e))
|
|
||||||
endTyping()
|
|
||||||
},
|
|
||||||
{ once: true }
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
@@ -63,11 +51,14 @@ export const AudioBubble = (props: Props) => {
|
|||||||
<audio
|
<audio
|
||||||
ref={audioElement}
|
ref={audioElement}
|
||||||
src={props.url}
|
src={props.url}
|
||||||
|
autoplay
|
||||||
class={
|
class={
|
||||||
'z-10 text-fade-in m-2 ' +
|
'z-10 text-fade-in ' +
|
||||||
(isTyping() ? 'opacity-0' : 'opacity-100')
|
(isTyping() ? 'opacity-0' : 'opacity-100 m-2')
|
||||||
}
|
}
|
||||||
style={{ height: isTyping() ? '32px' : 'revert' }}
|
style={{
|
||||||
|
height: isTyping() ? (isMobile() ? '32px' : '36px') : 'revert',
|
||||||
|
}}
|
||||||
controls
|
controls
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { TypingBubble } from '@/components'
|
import { TypingBubble } from '@/components'
|
||||||
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
import type { EmbedBubbleContent } from '@typebot.io/schemas'
|
import type { EmbedBubbleContent } from '@typebot.io/schemas'
|
||||||
import { createSignal, onCleanup, onMount } from 'solid-js'
|
import { createSignal, onCleanup, onMount } from 'solid-js'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
content: EmbedBubbleContent
|
content: EmbedBubbleContent
|
||||||
@@ -43,19 +45,27 @@ export const EmbedBubble = (props: Props) => {
|
|||||||
>
|
>
|
||||||
{isTyping() && <TypingBubble />}
|
{isTyping() && <TypingBubble />}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class={clsx(
|
||||||
|
'p-4 z-20 text-fade-in w-full',
|
||||||
|
isTyping() ? 'opacity-0' : 'opacity-100 p-4'
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
height: isTyping()
|
||||||
|
? isMobile()
|
||||||
|
? '32px'
|
||||||
|
: '36px'
|
||||||
|
: `${props.content.height}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<iframe
|
<iframe
|
||||||
id="embed-bubble-content"
|
id="embed-bubble-content"
|
||||||
src={props.content.url}
|
src={props.content.url}
|
||||||
class={
|
class={'w-full h-full '}
|
||||||
'w-full z-20 p-4 text-fade-in ' +
|
|
||||||
(isTyping() ? 'opacity-0' : 'opacity-100')
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
height: isTyping() ? '32px' : `${props.content.height}px`,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { TypingBubble } from '@/components'
|
import { TypingBubble } from '@/components'
|
||||||
import type { ImageBubbleContent } from '@typebot.io/schemas'
|
import type { ImageBubbleContent } from '@typebot.io/schemas'
|
||||||
import { createSignal, onCleanup, onMount } from 'solid-js'
|
import { createSignal, onCleanup, onMount } from 'solid-js'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
content: ImageBubbleContent
|
content: ImageBubbleContent
|
||||||
@@ -73,12 +75,20 @@ export const ImageBubble = (props: Props) => {
|
|||||||
<a
|
<a
|
||||||
href={props.content.clickLink.url}
|
href={props.content.clickLink.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="p-4 z-10"
|
class={clsx('z-10', isTyping() ? 'h-8' : 'p-4')}
|
||||||
>
|
>
|
||||||
{Image}
|
{Image}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<figure class="p-4 z-10">{Image}</figure>
|
<figure
|
||||||
|
class={clsx(
|
||||||
|
'z-10',
|
||||||
|
!isTyping() && 'p-4',
|
||||||
|
isTyping() ? (isMobile() ? 'h-8' : 'h-9') : ''
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{Image}
|
||||||
|
</figure>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { For, createSignal, onCleanup, onMount } from 'solid-js'
|
|||||||
import { computeTypingDuration } from '../helpers/computeTypingDuration'
|
import { computeTypingDuration } from '../helpers/computeTypingDuration'
|
||||||
import { PlateBlock } from './plate/PlateBlock'
|
import { PlateBlock } from './plate/PlateBlock'
|
||||||
import { computePlainText } from '../helpers/convertRichTextToPlainText'
|
import { computePlainText } from '../helpers/convertRichTextToPlainText'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
content: TextBubbleContent
|
content: TextBubbleContent
|
||||||
@@ -65,10 +67,13 @@ export const TextBubble = (props: Props) => {
|
|||||||
{isTyping() && <TypingBubble />}
|
{isTyping() && <TypingBubble />}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class={
|
class={clsx(
|
||||||
'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative text-ellipsis ' +
|
'overflow-hidden text-fade-in mx-4 my-2 whitespace-pre-wrap slate-html-container relative text-ellipsis',
|
||||||
(isTyping() ? 'opacity-0 h-6' : 'opacity-100 h-full')
|
isTyping() ? 'opacity-0' : 'opacity-100'
|
||||||
}
|
)}
|
||||||
|
style={{
|
||||||
|
height: isTyping() ? (isMobile() ? '16px' : '20px') : '100%',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<For each={props.content.richText}>
|
<For each={props.content.richText}>
|
||||||
{(element) => <PlateBlock element={element} />}
|
{(element) => <PlateBlock element={element} />}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { TypingBubble } from '@/components'
|
import { TypingBubble } from '@/components'
|
||||||
|
import { isMobile } from '@/utils/isMobileSignal'
|
||||||
import type { VideoBubbleContent } from '@typebot.io/schemas'
|
import type { VideoBubbleContent } from '@typebot.io/schemas'
|
||||||
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/enums'
|
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/enums'
|
||||||
import { createSignal, Match, onCleanup, onMount, Switch } from 'solid-js'
|
import { createSignal, Match, onCleanup, onMount, Switch } from 'solid-js'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
content: VideoBubbleContent
|
content: VideoBubbleContent
|
||||||
@@ -13,24 +15,23 @@ let typingTimeout: NodeJS.Timeout
|
|||||||
|
|
||||||
export const VideoBubble = (props: Props) => {
|
export const VideoBubble = (props: Props) => {
|
||||||
let ref: HTMLDivElement | undefined
|
let ref: HTMLDivElement | undefined
|
||||||
let videoElement: HTMLVideoElement | undefined
|
|
||||||
const [isTyping, setIsTyping] = createSignal(true)
|
const [isTyping, setIsTyping] = createSignal(true)
|
||||||
|
|
||||||
const onTypingEnd = () => {
|
onMount(() => {
|
||||||
const videoElement = ref?.querySelector('video')
|
const typingDuration =
|
||||||
if (videoElement)
|
props.content?.type &&
|
||||||
videoElement
|
[VideoBubbleContentType.VIMEO, VideoBubbleContentType.YOUTUBE].includes(
|
||||||
.play()
|
props.content?.type
|
||||||
.catch((e) => console.warn('Could not autoplay the video:', e))
|
)
|
||||||
|
? 2000
|
||||||
|
: 100
|
||||||
|
typingTimeout = setTimeout(() => {
|
||||||
if (!isTyping()) return
|
if (!isTyping()) return
|
||||||
setIsTyping(false)
|
setIsTyping(false)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
props.onTransitionEnd(ref?.offsetTop)
|
props.onTransitionEnd(ref?.offsetTop)
|
||||||
}, showAnimationDuration)
|
}, showAnimationDuration)
|
||||||
}
|
}, typingDuration)
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
typingTimeout = setTimeout(onTypingEnd, 2000)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
@@ -40,7 +41,7 @@ export const VideoBubble = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div class="flex flex-col animate-fade-in" ref={ref}>
|
<div class="flex flex-col animate-fade-in" ref={ref}>
|
||||||
<div class="flex w-full items-center">
|
<div class="flex w-full items-center">
|
||||||
<div class={'flex relative z-10 items-start typebot-host-bubble'}>
|
<div class="flex relative z-10 items-start typebot-host-bubble overflow-hidden">
|
||||||
<div
|
<div
|
||||||
class="flex items-center absolute px-4 py-2 bubble-typing z-10 "
|
class="flex items-center absolute px-4 py-2 bubble-typing z-10 "
|
||||||
style={{
|
style={{
|
||||||
@@ -58,7 +59,7 @@ export const VideoBubble = (props: Props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
ref={videoElement}
|
autoplay
|
||||||
src={props.content.url}
|
src={props.content.url}
|
||||||
controls
|
controls
|
||||||
class={
|
class={
|
||||||
@@ -66,7 +67,7 @@ export const VideoBubble = (props: Props) => {
|
|||||||
(isTyping() ? 'opacity-0' : 'opacity-100')
|
(isTyping() ? 'opacity-0' : 'opacity-100')
|
||||||
}
|
}
|
||||||
style={{
|
style={{
|
||||||
height: isTyping() ? '32px' : 'auto',
|
height: isTyping() ? (isMobile() ? '32px' : '36px') : 'auto',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
@@ -78,6 +79,15 @@ export const VideoBubble = (props: Props) => {
|
|||||||
VideoBubbleContentType.YOUTUBE,
|
VideoBubbleContentType.YOUTUBE,
|
||||||
].includes(props.content.type)
|
].includes(props.content.type)
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class={clsx(
|
||||||
|
'p-4 z-10 text-fade-in w-full',
|
||||||
|
isTyping() ? 'opacity-0' : 'opacity-100 p-4'
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
height: isTyping() ? (isMobile() ? '32px' : '36px') : '200px',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<iframe
|
<iframe
|
||||||
src={`${
|
src={`${
|
||||||
@@ -85,14 +95,11 @@ export const VideoBubble = (props: Props) => {
|
|||||||
? 'https://player.vimeo.com/video'
|
? 'https://player.vimeo.com/video'
|
||||||
: 'https://www.youtube.com/embed'
|
: 'https://www.youtube.com/embed'
|
||||||
}/${props.content.id}`}
|
}/${props.content.id}`}
|
||||||
class={
|
class={'w-full h-full'}
|
||||||
'w-full p-4 text-fade-in z-10 ' +
|
|
||||||
(isTyping() ? 'opacity-0' : 'opacity-100')
|
|
||||||
}
|
|
||||||
height={isTyping() ? '32px' : '200px'}
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/nextjs",
|
"name": "@typebot.io/nextjs",
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"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.1.12",
|
"version": "0.1.13",
|
||||||
"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",
|
||||||
|
|||||||
Reference in New Issue
Block a user