Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ (theme) Add progress bar option #1276

Merged
merged 4 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions apps/builder/src/components/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import {
Box,
} from '@chakra-ui/react'
import { useTranslate } from '@tolgee/react'
import React, { ChangeEvent, useState } from 'react'
import React, { useState } from 'react'
import tinyColor from 'tinycolor2'
import { useDebouncedCallback } from 'use-debounce'

const colorsSelection: `#${string}`[] = [
'#666460',
Expand All @@ -42,9 +43,9 @@ export const ColorPicker = ({ value, defaultValue, onColorChange }: Props) => {
const [color, setColor] = useState(defaultValue ?? '')
const displayedValue = value ?? color

const handleColorChange = (e: ChangeEvent<HTMLInputElement>) => {
setColor(e.target.value)
onColorChange(e.target.value)
const handleColorChange = (color: string) => {
setColor(color)
onColorChange(color)
}

const handleClick = (color: string) => () => {
Expand Down Expand Up @@ -103,7 +104,7 @@ export const ColorPicker = ({ value, defaultValue, onColorChange }: Props) => {
aria-label={t('colorPicker.colorValue.ariaLabel')}
size="sm"
value={displayedValue}
onChange={handleColorChange}
onChange={(e) => handleColorChange(e.target.value)}
/>
<NativeColorPicker
size="sm"
Expand All @@ -124,8 +125,12 @@ const NativeColorPicker = ({
...props
}: {
color: string
onColorChange: (e: ChangeEvent<HTMLInputElement>) => void
onColorChange: (color: string) => void
} & ButtonProps) => {
const debouncedOnColorChange = useDebouncedCallback((color: string) => {
onColorChange(color)
}, 200)

return (
<>
<Button as="label" htmlFor="native-picker" {...props}>
Expand All @@ -136,7 +141,7 @@ const NativeColorPicker = ({
display="none"
id="native-picker"
value={color}
onChange={onColorChange}
onChange={(e) => debouncedOnColorChange(e.target.value)}
/>
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TypebotHeader } from '@/features/editor/components/TypebotHeader'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { TypebotNotFoundPage } from '@/features/editor/components/TypebotNotFoundPage'
import { env } from '@typebot.io/env'
import { headerHeight } from '@/features/editor/constants'

export const SettingsPage = () => {
const { typebot, is404 } = useTypebot()
Expand All @@ -15,7 +16,7 @@ export const SettingsPage = () => {
<Flex overflow="hidden" h="100vh" flexDir="column">
<Seo title={typebot?.name ? `${typebot.name} | Settings` : 'Settings'} />
<TypebotHeader />
<Flex h="full" w="full">
<Flex height={`calc(100vh - ${headerHeight}px)`} w="full">
<SettingsSideMenu />
<Flex flex="1">
{typebot && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { GeneralSettingsForm } from './GeneralSettingsForm'
import { MetadataForm } from './MetadataForm'
import { TypingEmulationForm } from './TypingEmulationForm'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { headerHeight } from '@/features/editor/constants'
import { SecurityForm } from './SecurityForm'

export const SettingsSideMenu = () => {
Expand Down Expand Up @@ -52,7 +51,7 @@ export const SettingsSideMenu = () => {
<Stack
flex="1"
maxW="400px"
height={`calc(100vh - ${headerHeight}px)`}
height="full"
borderRightWidth={1}
pt={10}
spacing={10}
Expand Down
3 changes: 2 additions & 1 deletion apps/builder/src/features/theme/components/ThemePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Flex } from '@chakra-ui/react'
import { Standard } from '@typebot.io/nextjs'
import { ThemeSideMenu } from './ThemeSideMenu'
import { TypebotNotFoundPage } from '@/features/editor/components/TypebotNotFoundPage'
import { headerHeight } from '@/features/editor/constants'

export const ThemePage = () => {
const { typebot, is404 } = useTypebot()
Expand All @@ -14,7 +15,7 @@ export const ThemePage = () => {
<Flex overflow="hidden" h="100vh" flexDir="column">
<Seo title={typebot?.name ? `${typebot.name} | Theme` : 'Theme'} />
<TypebotHeader />
<Flex h="full" w="full">
<Flex w="full" height={`calc(100vh - ${headerHeight}px)`}>
<ThemeSideMenu />
<Flex flex="1">
{typebot && (
Expand Down
3 changes: 1 addition & 2 deletions apps/builder/src/features/theme/components/ThemeSideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ChatTheme, GeneralTheme, ThemeTemplate } from '@typebot.io/schemas'
import React from 'react'
import { CustomCssSettings } from './CustomCssSettings'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { headerHeight } from '@/features/editor/constants'
import { ChatThemeSettings } from './chat/ChatThemeSettings'
import { GeneralSettings } from './general/GeneralSettings'
import { ThemeTemplates } from './ThemeTemplates'
Expand Down Expand Up @@ -61,7 +60,7 @@ export const ThemeSideMenu = () => {
<Stack
flex="1"
maxW="400px"
height={`calc(100vh - ${headerHeight}px)`}
h="full"
borderRightWidth={1}
pt={10}
spacing={10}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,56 +34,61 @@ export const BackgroundContent = ({
const handleContentChange = (content: string) =>
onBackgroundContentChange(content)

switch (background?.type) {
case BackgroundType.COLOR:
return (
<Flex justify="space-between" align="center">
<Text>{t('theme.sideMenu.global.background.color')}</Text>
<ColorPicker
value={
background.content ?? defaultTheme.general.background.content
}
onColorChange={handleContentChange}
/>
</Flex>
)
case BackgroundType.IMAGE:
if (!typebot) return null
return (
<Popover isLazy placement="top">
<PopoverTrigger>
{isNotEmpty(background.content) ? (
<Image
src={background.content}
alt={t('theme.sideMenu.global.background.image.alt')}
cursor="pointer"
_hover={{ filter: 'brightness(.9)' }}
transition="filter 200ms"
rounded="md"
maxH="200px"
objectFit="cover"
/>
) : (
<Button>
{t('theme.sideMenu.global.background.image.button')}
</Button>
)}
</PopoverTrigger>
<Portal>
<PopoverContent p="4" w="500px">
<ImageUploadContent
uploadFileProps={{
workspaceId: typebot.workspaceId,
typebotId: typebot.id,
fileName: 'background',
}}
defaultUrl={background.content}
onSubmit={handleContentChange}
excludedTabs={['giphy', 'icon']}
/>
</PopoverContent>
</Portal>
</Popover>
)
if (
(background?.type ?? defaultTheme.general.background.type) ===
BackgroundType.IMAGE
) {
if (!typebot) return null
return (
<Popover isLazy placement="top">
<PopoverTrigger>
{isNotEmpty(background?.content) ? (
<Image
src={background?.content}
alt={t('theme.sideMenu.global.background.image.alt')}
cursor="pointer"
_hover={{ filter: 'brightness(.9)' }}
transition="filter 200ms"
rounded="md"
maxH="200px"
objectFit="cover"
/>
) : (
<Button>
{t('theme.sideMenu.global.background.image.button')}
</Button>
)}
</PopoverTrigger>
<Portal>
<PopoverContent p="4" w="500px">
<ImageUploadContent
uploadFileProps={{
workspaceId: typebot.workspaceId,
typebotId: typebot.id,
fileName: 'background',
}}
defaultUrl={background?.content}
onSubmit={handleContentChange}
excludedTabs={['giphy', 'icon']}
/>
</PopoverContent>
</Portal>
</Popover>
)
}
if (
(background?.type ?? defaultTheme.general.background.type) ===
BackgroundType.COLOR
) {
return (
<Flex justify="space-between" align="center">
<Text>{t('theme.sideMenu.global.background.color')}</Text>
<ColorPicker
value={background?.content ?? defaultTheme.general.background.content}
onColorChange={handleContentChange}
/>
</Flex>
)
}
return null
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@ import { Stack, Text } from '@chakra-ui/react'
import { Background } from '@typebot.io/schemas'
import React from 'react'
import { BackgroundContent } from './BackgroundContent'
import { BackgroundType } from '@typebot.io/schemas/features/typebot/theme/constants'
import {
BackgroundType,
defaultTheme,
} from '@typebot.io/schemas/features/typebot/theme/constants'
import { useTranslate } from '@tolgee/react'

type Props = {
background?: Background
onBackgroundChange: (newBackground: Background) => void
}

const defaultBackgroundType = BackgroundType.NONE

export const BackgroundSelector = ({
background,
onBackgroundChange,
}: Props) => {
const { t } = useTranslate()

const handleBackgroundTypeChange = (type: BackgroundType) =>
background &&
onBackgroundChange({ ...background, type, content: undefined })

const handleBackgroundContentChange = (content: string) =>
background && onBackgroundChange({ ...background, content })
onBackgroundChange({ ...background, content })

return (
<Stack spacing={4}>
Expand All @@ -44,7 +44,7 @@ export const BackgroundSelector = ({
value: BackgroundType.NONE,
},
]}
value={background?.type ?? defaultBackgroundType}
value={background?.type ?? defaultTheme.general.background.type}
onSelect={handleBackgroundTypeChange}
/>
<BackgroundContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { GoogleFontForm } from './GoogleFontForm'
import { CustomFontForm } from './CustomFontForm'

type Props = {
font: Font
font: Font | undefined
onFontChange: (font: Font) => void
}

export const FontForm = ({ font, onFontChange }: Props) => {
if (typeof font === 'string' || font.type === 'Google')
if (!font || typeof font === 'string' || font?.type === 'Google')
return <GoogleFontForm font={font} onFontChange={onFontChange} />
if (font.type === 'Custom')
return <CustomFontForm font={font} onFontChange={onFontChange} />
return null
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
useDisclosure,
Text,
} from '@chakra-ui/react'
import { Background, Font, Theme } from '@typebot.io/schemas'
import { Background, Font, ProgressBar, Theme } from '@typebot.io/schemas'
import React from 'react'
import { BackgroundSelector } from './BackgroundSelector'
import { LockTag } from '@/features/billing/components/LockTag'
Expand All @@ -24,6 +24,7 @@ import { env } from '@typebot.io/env'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { RadioButtons } from '@/components/inputs/RadioButtons'
import { FontForm } from './FontForm'
import { ProgressBarForm } from './ProgressBarForm'

type Props = {
isBrandingEnabled: boolean
Expand Down Expand Up @@ -63,6 +64,9 @@ export const GeneralSettings = ({
const handleBackgroundChange = (background: Background) =>
onGeneralThemeChange({ ...generalTheme, background })

const updateProgressBar = (progressBar: ProgressBar) =>
onGeneralThemeChange({ ...generalTheme, progressBar })

const updateBranding = () => {
if (isBrandingEnabled && isWorkspaceFreePlan) return
if (
Expand Down Expand Up @@ -118,15 +122,16 @@ export const GeneralSettings = ({
defaultValue={fontType}
onSelect={updateFontType}
/>
<FontForm
font={generalTheme?.font ?? defaultTheme.general.font}
onFontChange={updateFont}
/>
<FontForm font={generalTheme?.font} onFontChange={updateFont} />
</Stack>
<BackgroundSelector
background={generalTheme?.background ?? defaultTheme.general.background}
background={generalTheme?.background}
onBackgroundChange={handleBackgroundChange}
/>
<ProgressBarForm
progressBar={generalTheme?.progressBar}
onProgressBarChange={updateProgressBar}
/>
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Select } from '@/components/inputs/Select'
import { env } from '@typebot.io/env'
import { GoogleFont } from '@typebot.io/schemas'
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { useState, useEffect } from 'react'

type Props = {
font: GoogleFont | string
font: GoogleFont | string | undefined
onFontChange: (font: GoogleFont) => void
}

export const GoogleFontForm = ({ font, onFontChange }: Props) => {
const [currentFont, setCurrentFont] = useState(
typeof font === 'string' ? font : font.family
(typeof font === 'string' ? font : font?.family) ??
defaultTheme.general.font.family
)
const [googleFonts, setGoogleFonts] = useState<string[]>([])

Expand Down
Loading