2
0

(js) Add placement option for bubble embed

Allows you to place the bubble embed on the bottom left corner
of your site
This commit is contained in:
Baptiste Arnaud
2023-05-31 10:22:58 +02:00
parent b2ea8fc059
commit 57f3e5c004
9 changed files with 74 additions and 26 deletions

View File

@ -4,17 +4,24 @@ import {
AccordionIcon,
AccordionItem,
AccordionPanel,
Button,
HStack,
Menu,
MenuButton,
MenuItem,
MenuList,
Stack,
Text,
} from '@chakra-ui/react'
import { BubbleProps } from '@typebot.io/js'
import {
BubbleTheme,
ButtonTheme,
PreviewMessageTheme,
} from '@typebot.io/js/dist/features/bubble/types'
import { ButtonThemeSettings } from './ButtonThemeSettings'
import { PreviewMessageThemeSettings } from './PreviewMessageThemeSettings'
import { ChevronDownIcon } from '@/components/icons'
type Props = {
isPreviewMessageEnabled: boolean
@ -41,6 +48,13 @@ export const ThemeSettings = ({
})
}
const updatePlacement = (placement: BubbleTheme['placement']) => {
onChange({
...theme,
placement,
})
}
return (
<Accordion allowMultiple>
<AccordionItem>
@ -51,6 +65,22 @@ export const ThemeSettings = ({
<AccordionIcon />
</AccordionButton>
<AccordionPanel as={Stack} pb={4} spacing={4} px="0">
<HStack justify="space-between">
<Text>Placement</Text>
<Menu>
<MenuButton as={Button} size="sm" rightIcon={<ChevronDownIcon />}>
{theme?.placement ?? 'right'}
</MenuButton>
<MenuList>
<MenuItem onClick={() => updatePlacement('right')}>
right
</MenuItem>
<MenuItem onClick={() => updatePlacement('left')}>
left
</MenuItem>
</MenuList>
</Menu>
</HStack>
<ButtonThemeSettings
buttonTheme={theme?.button}
onChange={updateButtonTheme}

View File

@ -72,7 +72,8 @@ const parseBubbleTheme = (theme: BubbleProps['theme']): string => {
const buttonThemeLine = parseButtonTheme(button)
const previewMessageThemeLine = parsePreviewMessageTheme(previewMessage)
const chatWindowThemeLine = parseChatWindowTheme(theme.chatWindow)
const line = `theme: {${buttonThemeLine}${previewMessageThemeLine}${chatWindowThemeLine}},`
const placementLine = parseStringParam('placement', theme.placement, 'right')
const line = `theme: {${placementLine}${buttonThemeLine}${previewMessageThemeLine}${chatWindowThemeLine}},`
if (line === 'theme: {},') return ''
return line
}

View File

@ -6,8 +6,15 @@ import { Typebot } from '@typebot.io/schemas'
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
import packageJson from '../../../../../../../../packages/embeds/js/package.json'
export const parseStringParam = (fieldName: string, fieldValue?: string) =>
fieldValue ? `${fieldName}: "${fieldValue}",` : ``
export const parseStringParam = (
fieldName: string,
fieldValue?: string,
defaultValue?: string
) => {
if (!fieldValue) return ''
if (isDefined(defaultValue) && fieldValue === defaultValue) return ''
return `${fieldName}: "${fieldValue}",`
}
export const parseNumberOrBoolParam = (
fieldName: string,

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.0.59",
"version": "0.0.60",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",

View File

@ -129,6 +129,7 @@ export const Bubble = (props: BubbleProps) => {
<Show when={isPreviewMessageDisplayed()}>
<PreviewMessage
{...previewMessage()}
placement={bubbleProps.theme?.placement}
previewMessageTheme={bubbleProps.theme?.previewMessage}
buttonSize={bubbleProps.theme?.button?.size}
onClick={handlePreviewMessageClick}
@ -137,6 +138,7 @@ export const Bubble = (props: BubbleProps) => {
</Show>
<BubbleButton
{...bubbleProps.theme?.button}
placement={bubbleProps.theme?.placement}
toggleBot={toggleBot}
isBotOpened={isBotOpened()}
/>
@ -146,16 +148,20 @@ export const Bubble = (props: BubbleProps) => {
height: 'calc(100% - 80px)',
transition:
'transform 200ms cubic-bezier(0, 1.2, 1, 1), opacity 150ms ease-out',
'transform-origin': 'bottom right',
'transform-origin':
props.theme?.placement === 'left' ? 'bottom left' : 'bottom right',
transform: isBotOpened() ? 'scale3d(1, 1, 1)' : 'scale3d(0, 0, 1)',
'box-shadow': 'rgb(0 0 0 / 16%) 0px 5px 40px',
'background-color': bubbleProps.theme?.chatWindow?.backgroundColor,
'z-index': 42424242,
}}
class={
'fixed sm:right-5 rounded-lg w-full sm:w-[400px] max-h-[704px]' +
'fixed rounded-lg w-full sm:w-[400px] max-h-[704px]' +
(isBotOpened() ? ' opacity-1' : ' opacity-0 pointer-events-none') +
(props.theme?.button?.size === 'large' ? ' bottom-24' : ' bottom-20')
(props.theme?.button?.size === 'large'
? ' bottom-24'
: ' bottom-20') +
(props.theme?.placement === 'left' ? ' left-5' : ' right-5')
}
>
<Show when={isBotStarted()}>

View File

@ -1,12 +1,13 @@
import { Show } from 'solid-js'
import { isNotDefined, isSvgSrc } from '@typebot.io/lib'
import { ButtonTheme } from '../types'
import { BubbleTheme, ButtonTheme } from '../types'
import { isLight } from '@typebot.io/lib/hexToRgb'
type Props = ButtonTheme & {
isBotOpened: boolean
toggleBot: () => void
}
type Props = Pick<BubbleTheme, 'placement'> &
ButtonTheme & {
isBotOpened: boolean
toggleBot: () => void
}
const defaultButtonColor = '#0042DA'
const defaultDarkIconColor = '#27272A'
@ -20,8 +21,9 @@ export const BubbleButton = (props: Props) => (
part="button"
onClick={() => props.toggleBot()}
class={
'fixed bottom-5 right-5 shadow-md rounded-full hover:scale-110 active:scale-95 transition-transform duration-200 flex justify-center items-center animate-fade-in' +
(props.size === 'large' ? ' w-16 h-16' : ' w-12 h-12')
'fixed bottom-5 shadow-md rounded-full hover:scale-110 active:scale-95 transition-transform duration-200 flex justify-center items-center animate-fade-in' +
(props.size === 'large' ? ' w-16 h-16' : ' w-12 h-12') +
(props.placement === 'left' ? ' left-5' : ' right-5')
}
style={{
'background-color': props.backgroundColor ?? defaultButtonColor,
@ -49,6 +51,7 @@ export const BubbleButton = (props: Props) => (
</Show>
<Show when={props.customIconSrc && isImageSrc(props.customIconSrc)}>
<img
part="button-icon"
src={props.customIconSrc}
class={
'duration-200 transition' +

View File

@ -1,19 +1,18 @@
import { createSignal, Show } from 'solid-js'
import {
BubbleTheme,
ButtonTheme,
PreviewMessageParams,
PreviewMessageTheme,
} from '../types'
export type PreviewMessageProps = Pick<
PreviewMessageParams,
'avatarUrl' | 'message'
> & {
buttonSize: ButtonTheme['size']
previewMessageTheme?: PreviewMessageTheme
onClick: () => void
onCloseClick: () => void
}
export type PreviewMessageProps = Pick<BubbleTheme, 'placement'> &
Pick<PreviewMessageParams, 'avatarUrl' | 'message'> & {
buttonSize: ButtonTheme['size']
previewMessageTheme?: PreviewMessageTheme
onClick: () => void
onCloseClick: () => void
}
const defaultBackgroundColor = '#F7F8FF'
const defaultTextColor = '#303235'
@ -27,8 +26,9 @@ export const PreviewMessage = (props: PreviewMessageProps) => {
part="preview-message"
onClick={() => props.onClick()}
class={
'fixed right-5 max-w-[256px] rounded-md duration-200 flex items-center gap-4 shadow-md animate-fade-in cursor-pointer hover:shadow-lg p-4' +
(props.buttonSize === 'large' ? ' bottom-24' : ' bottom-20')
'fixed max-w-[256px] rounded-md duration-200 flex items-center gap-4 shadow-md animate-fade-in cursor-pointer hover:shadow-lg p-4' +
(props.buttonSize === 'large' ? ' bottom-24' : ' bottom-20') +
(props.placement === 'left' ? ' left-5' : ' right-5')
}
style={{
'background-color':

View File

@ -8,6 +8,7 @@ export type BubbleTheme = {
chatWindow?: ChatWindowTheme
button?: ButtonTheme
previewMessage?: PreviewMessageTheme
placement?: 'left' | 'right'
}
export type ChatWindowTheme = {

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.0.59",
"version": "0.0.60",
"description": "React library to display typebots on your website",
"main": "dist/index.js",
"types": "dist/index.d.ts",