Skip to content

Commit

Permalink
⚡ (embed) Add size and icon picker in bubble settings (#508)
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno authored May 15, 2023
1 parent 123926f commit 0f91b34
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 106 deletions.
17 changes: 9 additions & 8 deletions apps/builder/src/components/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ import tinyColor from 'tinycolor2'

const colorsSelection: `#${string}`[] = [
'#666460',
'#AFABA3',
'#FFFFFF',
'#A87964',
'#D09C46',
'#DE8031',
'#598E71',
'#4A8BB2',
'#9B74B7',
'#C75F96',
'#C75F96',
'#0042DA',
]

type Props = {
Expand Down Expand Up @@ -78,18 +78,19 @@ export const ColorPicker = ({ value, defaultValue, onColorChange }: Props) => {
</PopoverHeader>
<PopoverBody as={Stack}>
<SimpleGrid columns={5} spacing={2}>
{colorsSelection.map((c) => (
{colorsSelection.map((color) => (
<Button
key={c}
aria-label={c}
background={c}
key={color}
aria-label={color}
background={color}
height="22px"
width="22px"
padding={0}
minWidth="unset"
borderRadius={3}
_hover={{ background: c }}
onClick={handleClick(c)}
borderWidth={color === '#FFFFFF' ? 1 : undefined}
_hover={{ background: color }}
onClick={handleClick(color)}
/>
))}
</SimpleGrid>
Expand Down
5 changes: 2 additions & 3 deletions apps/builder/src/components/EditableEmojiOrImageIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@ export const EditableEmojiOrImageIcon = ({
filePath={uploadFilePath}
defaultUrl={icon ?? ''}
onSubmit={onChangeIcon}
isGiphyEnabled={false}
isUnsplashEnabled={false}
isEmojiEnabled={true}
excludedTabs={['giphy', 'unsplash']}
onClose={onClose}
initialTab="icon"
/>
</PopoverContent>
</>
Expand Down
17 changes: 12 additions & 5 deletions apps/builder/src/components/ImageUploadContent/IconPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export const IconPicker = ({ onIconSelected }: Props) => {
)
const searchQuery = useRef<string>('')
const [selectedColor, setSelectedColor] = useState(initialIconColor)
const isWhite = useMemo(
() =>
selectedColor.toLowerCase() === '#ffffff' ||
selectedColor.toLowerCase() === '#fff' ||
selectedColor === 'white',
[selectedColor]
)

useEffect(() => {
if (!bottomElement.current) return
Expand Down Expand Up @@ -68,10 +75,9 @@ export const IconPicker = ({ onIconSelected }: Props) => {

const selectIcon = async (iconName: string) => {
const svg = await (await fetch(`/icons/${iconName}.svg`)).text()
const dataUri = `data:image/svg+xml;utf8,${svg.replace(
'<svg',
`<svg fill='${encodeURIComponent(selectedColor)}'`
)}`
const dataUri = `data:image/svg+xml;utf8,${svg
.replace('<svg', `<svg fill='${encodeURIComponent(selectedColor)}'`)
.replace(/"/g, "'")}`
onIconSelected(dataUri)
}

Expand All @@ -96,8 +102,9 @@ export const IconPicker = ({ onIconSelected }: Props) => {
>
{displayedIconNames.map((iconName) => (
<Button
variant="ghost"
size="sm"
variant={isWhite ? 'solid' : 'ghost'}
colorScheme={isWhite ? 'blackAlpha' : undefined}
fontSize="xl"
w="38px"
h="38px"
Expand Down
100 changes: 65 additions & 35 deletions apps/builder/src/components/ImageUploadContent/ImageUploadContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,52 @@ import { IconPicker } from './IconPicker'
type Tabs = 'link' | 'upload' | 'giphy' | 'emoji' | 'unsplash' | 'icon'

type Props = {
filePath: string
filePath: string | undefined
includeFileName?: boolean
defaultUrl?: string
isEmojiEnabled?: boolean
isGiphyEnabled?: boolean
isUnsplashEnabled?: boolean
imageSize?: 'small' | 'regular' | 'thumb'
initialTab?: Tabs
onSubmit: (url: string) => void
onClose?: () => void
}
} & (
| {
includedTabs?: Tabs[]
}
| {
excludedTabs?: Tabs[]
}
)

const defaultDisplayedTabs: Tabs[] = [
'link',
'upload',
'giphy',
'emoji',
'unsplash',
'icon',
]

export const ImageUploadContent = ({
filePath,
includeFileName,
defaultUrl,
onSubmit,
isEmojiEnabled = false,
isGiphyEnabled = true,
isUnsplashEnabled = true,
imageSize = 'regular',
onClose,
initialTab,
...props
}: Props) => {
const includedTabs =
'includedTabs' in props
? props.includedTabs ?? defaultDisplayedTabs
: defaultDisplayedTabs
const excludedTabs = 'excludedTabs' in props ? props.excludedTabs ?? [] : []
const displayedTabs = defaultDisplayedTabs.filter(
(tab) => !excludedTabs.includes(tab) && includedTabs.includes(tab)
)

const [currentTab, setCurrentTab] = useState<Tabs>(
isEmojiEnabled ? 'emoji' : 'link'
initialTab ?? displayedTabs[0]
)

const handleSubmit = (url: string) => {
Expand All @@ -44,7 +66,25 @@ export const ImageUploadContent = ({
return (
<Stack>
<HStack>
{isEmojiEnabled && (
{displayedTabs.includes('link') && (
<Button
variant={currentTab === 'link' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('link')}
size="sm"
>
Link
</Button>
)}
{displayedTabs.includes('upload') && (
<Button
variant={currentTab === 'upload' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('upload')}
size="sm"
>
Upload
</Button>
)}
{displayedTabs.includes('emoji') && (
<Button
variant={currentTab === 'emoji' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('emoji')}
Expand All @@ -53,21 +93,7 @@ export const ImageUploadContent = ({
Emoji
</Button>
)}
<Button
variant={currentTab === 'link' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('link')}
size="sm"
>
Link
</Button>
<Button
variant={currentTab === 'upload' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('upload')}
size="sm"
>
Upload
</Button>
{isGiphyEnabled && (
{displayedTabs.includes('giphy') && (
<Button
variant={currentTab === 'giphy' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('giphy')}
Expand All @@ -76,7 +102,7 @@ export const ImageUploadContent = ({
Giphy
</Button>
)}
{isUnsplashEnabled && (
{displayedTabs.includes('unsplash') && (
<Button
variant={currentTab === 'unsplash' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('unsplash')}
Expand All @@ -85,13 +111,15 @@ export const ImageUploadContent = ({
Unsplash
</Button>
)}
<Button
variant={currentTab === 'icon' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('icon')}
size="sm"
>
Icon
</Button>
{displayedTabs.includes('icon') && (
<Button
variant={currentTab === 'icon' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('icon')}
size="sm"
>
Icon
</Button>
)}
</HStack>

<BodyContent
Expand All @@ -115,21 +143,23 @@ const BodyContent = ({
onSubmit,
}: {
includeFileName?: boolean
filePath: string
filePath: string | undefined
tab: Tabs
defaultUrl?: string
imageSize: 'small' | 'regular' | 'thumb'
onSubmit: (url: string) => void
}) => {
switch (tab) {
case 'upload':
case 'upload': {
if (!filePath) return null
return (
<UploadFileContent
filePath={filePath}
includeFileName={includeFileName}
onNewUrl={onSubmit}
/>
)
}
case 'link':
return <EmbedLinkContent defaultUrl={defaultUrl} onNewUrl={onSubmit} />
case 'giphy':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ import { JavascriptBubbleSnippet } from '../JavascriptBubbleSnippet'
export const parseDefaultBubbleTheme = (typebot?: Typebot) => ({
button: {
backgroundColor: typebot?.theme.chat.buttons.backgroundColor,
iconColor: typebot?.theme.chat.buttons.color,
},
previewMessage: {
backgroundColor: typebot?.theme.general.background.content ?? 'white',
textColor: 'black',
},
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { Stack, Heading, HStack, Flex, Text, Image } from '@chakra-ui/react'
import {
Stack,
Heading,
HStack,
Flex,
Text,
Image,
chakra,
} from '@chakra-ui/react'
import { BubbleProps } from '@typebot.io/js'
import { isDefined } from '@typebot.io/lib'
import { isDefined, isSvgSrc } from '@typebot.io/lib'
import { PreviewMessageSettings } from './PreviewMessageSettings'
import { ThemeSettings } from './ThemeSettings'
import { isLight } from '@typebot.io/lib/hexToRgb'

type Props = {
defaultPreviewMessageAvatar: string
Expand Down Expand Up @@ -79,24 +88,72 @@ export const BubbleSettings = ({
<Flex
align="center"
justifyContent="center"
boxSize="3rem"
transition="all 0.2s ease-in-out"
boxSize={theme?.button?.size === 'large' ? '64px' : '48px'}
bgColor={theme?.button?.backgroundColor}
rounded="full"
boxShadow="0 0 #0000,0 0 #0000,0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1)"
>
<svg
viewBox="0 0 24 24"
style={{
stroke: theme?.button?.iconColor,
}}
width="30px"
strokeWidth="2px"
fill="transparent"
>
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
</svg>
<BubbleIcon buttonTheme={theme?.button} />
</Flex>
</Stack>
</Stack>
</Stack>
)
}

const BubbleIcon = ({
buttonTheme,
}: {
buttonTheme: NonNullable<BubbleProps['theme']>['button']
}) => {
if (!buttonTheme?.customIconSrc)
return (
<svg
viewBox="0 0 24 24"
style={{
stroke: buttonTheme?.backgroundColor
? isLight(buttonTheme?.backgroundColor)
? '#000'
: '#fff'
: '#fff',
transition: 'all 0.2s ease-in-out',
}}
width={buttonTheme?.size === 'large' ? '36px' : '28px'}
strokeWidth="2px"
fill="transparent"
>
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
</svg>
)

if (
buttonTheme.customIconSrc.startsWith('http') ||
buttonTheme.customIconSrc.startsWith('data:image/svg+xml')
)
return (
<Image
src={buttonTheme.customIconSrc}
transition="all 0.2s ease-in-out"
boxSize={
isSvgSrc(buttonTheme.customIconSrc)
? buttonTheme?.size === 'large'
? '36px'
: '28px'
: '90%'
}
rounded={isSvgSrc(buttonTheme.customIconSrc) ? undefined : 'full'}
alt="Bubble button icon"
objectFit={isSvgSrc(buttonTheme.customIconSrc) ? undefined : 'cover'}
/>
)
return (
<chakra.span
transition="all 0.2s ease-in-out"
fontSize={buttonTheme.size === 'large' ? '36px' : '24px'}
lineHeight={buttonTheme.size === 'large' ? '40px' : '32px'}
>
{buttonTheme.customIconSrc}
</chakra.span>
)
}
Loading

4 comments on commit 0f91b34

@vercel
Copy link

@vercel vercel bot commented on 0f91b34 May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 0f91b34 May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-git-main-typebot-io.vercel.app
app.typebot.io
builder-v2-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 0f91b34 May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

chatbot.repplai.com
demo.botscientis.us
demo.wemakebots.xyz
hrbot.robomotion.io
inearephones.cr8.ai
kbsub.wpwakanda.com
limitenahora.com.br
live.botscientis.us
mentoria.omelhor.vc
nutrisamirbayde.com
order.maitempah.com
profileadscloud.com
query.forgetsql.com
quest.wpwakanda.com
support.wawplus.com
survey1.digienge.io
surveys.essiell.com
test.botscientis.us
test.getreview.help
test.reventepro.com
typebot.stillio.com
wordsandimagery.com
88584434.therpm.club
92109660.therpm.club
abbonamento.bwell.it
assistent.m-vogel.de
bium.gratirabbit.com
bot.ansuraniphone.my
bot.barrettamario.it
bot.cotemeuplano.com
bot.leadbooster.help
bot.mycompay.reviews
chat.hayurihijab.com
chatbee.agfunnel.com
click.sevenoways.com
connect.growthguy.in
forms.bonanza.design
hello.advergreen.com
kuiz.sistemniaga.com
menu.numero-primo.it
menukb.wpwakanda.com
offer.botscientis.us
sellmycarglasgow.com
talkbot.agfunnel.com
tenorioadvogados.com
uppity.wpwakanda.com
type.opaulovieira.com.br
aibot.angrybranding.co.uk
app.photosbackupin1.click
bot.aidigitalmarketing.kr
bot.amicidisanfaustino.it
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
bot.fusionstarreviews.com
boyfriend-breakup.riku.ai
register.algorithmpress.com
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
open.campus.aalen.university
piazzatorre.barrettamario.it
poll.mosaicohairboutique.com
type.cookieacademyonline.com
upload.atlasoutfittersk9.com
bot.brigadeirosemdrama.com.br
tuttirecepcao.fratucci.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
recepcao.tutti.fratucci.com.br
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
baleia.testeeventos.progenbr.com
bot.cabin-rentals-of-georgia.net
open.campus.bot.aalen.university
sondaggio.mosaicohairboutique.it
baleia.testegabinete.progenbr.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
sondaggio.mosaicohairboutique.com
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
chrome-os-inquiry-system.itschromeos.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com

@vercel
Copy link

@vercel vercel bot commented on 0f91b34 May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs.typebot.io
docs-typebot-io.vercel.app

Please sign in to comment.