2
0

feat(theme): Add chat theme settings

This commit is contained in:
Baptiste Arnaud
2022-01-24 15:07:09 +01:00
parent 619d10ae4e
commit b0abe5b8fa
37 changed files with 771 additions and 375 deletions

View File

@ -0,0 +1,40 @@
import { Flex, Text } from '@chakra-ui/react'
import { Background, BackgroundType } from 'models'
import React from 'react'
import { ColorPicker } from '../ColorPicker'
type BackgroundContentProps = {
background?: Background
onBackgroundContentChange: (content: string) => void
}
const defaultBackgroundColor = '#ffffff'
export const BackgroundContent = ({
background,
onBackgroundContentChange,
}: BackgroundContentProps) => {
const handleContentChange = (content: string) =>
onBackgroundContentChange(content)
switch (background?.type) {
case BackgroundType.COLOR:
return (
<Flex justify="space-between" align="center">
<Text>Background color:</Text>
<ColorPicker
initialColor={background.content ?? defaultBackgroundColor}
onColorChange={handleContentChange}
/>
</Flex>
)
case BackgroundType.IMAGE:
return (
<Flex>
<Text>Image</Text>
</Flex>
)
default:
return <></>
}
}

View File

@ -0,0 +1,37 @@
import { Stack, Text } from '@chakra-ui/react'
import { Background, BackgroundType } from 'models'
import React from 'react'
import { BackgroundContent } from './BackgroundContent'
import { BackgroundTypeRadioButtons } from './BackgroundTypeRadioButtons'
type Props = {
background?: Background
onBackgroundChange: (newBackground: Background) => void
}
const defaultBackgroundType = BackgroundType.NONE
export const BackgroundSelector = ({
background,
onBackgroundChange,
}: Props) => {
const handleBackgroundTypeChange = (type: BackgroundType) =>
background && onBackgroundChange({ ...background, type })
const handleBackgroundContentChange = (content: string) =>
background && onBackgroundChange({ ...background, content })
return (
<Stack spacing={4}>
<Text>Background</Text>
<BackgroundTypeRadioButtons
backgroundType={background?.type ?? defaultBackgroundType}
onBackgroundTypeChange={handleBackgroundTypeChange}
/>
<BackgroundContent
background={background}
onBackgroundContentChange={handleBackgroundContentChange}
/>
</Stack>
)
}

View File

@ -0,0 +1,73 @@
import {
Box,
Flex,
HStack,
useRadio,
useRadioGroup,
UseRadioProps,
} from '@chakra-ui/react'
import { BackgroundType } from 'models'
import { ReactNode } from 'react'
type Props = {
backgroundType: BackgroundType
onBackgroundTypeChange: (type: BackgroundType) => void
}
export const BackgroundTypeRadioButtons = ({
backgroundType,
onBackgroundTypeChange,
}: Props) => {
const options = ['Color', 'None']
const { getRootProps, getRadioProps } = useRadioGroup({
name: 'background-type',
defaultValue: backgroundType,
onChange: (nextVal: string) =>
onBackgroundTypeChange(nextVal as BackgroundType),
})
const group = getRootProps()
return (
<HStack {...group}>
{options.map((value) => {
const radio = getRadioProps({ value })
return (
<RadioCard key={value} {...radio}>
{value}
</RadioCard>
)
})}
</HStack>
)
}
export const RadioCard = (props: UseRadioProps & { children: ReactNode }) => {
const { getInputProps, getCheckboxProps } = useRadio(props)
const input = getInputProps()
const checkbox = getCheckboxProps()
return (
<Box as="label" flex="1">
<input {...input} />
<Flex
{...checkbox}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
_checked={{
bg: 'orange.400',
color: 'white',
borderColor: 'orange.400',
}}
px={5}
py={2}
transition="background-color 150ms, color 150ms, border 150ms"
justifyContent="center"
>
{props.children}
</Flex>
</Box>
)
}

View File

@ -0,0 +1 @@
export { BackgroundSelector } from './BackgroundSelector'

View File

@ -0,0 +1,100 @@
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverArrow,
PopoverCloseButton,
PopoverHeader,
Center,
PopoverBody,
SimpleGrid,
Input,
Button,
} from '@chakra-ui/react'
import React, { useEffect, useState } from 'react'
const colorsSelection: `#${string}`[] = [
'#264653',
'#e9c46a',
'#2a9d8f',
'#7209b7',
'#023e8a',
'#ffe8d6',
'#d8f3dc',
'#4ea8de',
'#ffb4a2',
]
type Props = {
initialColor: string
onColorChange: (color: string) => void
}
export const ColorPicker = ({ initialColor, onColorChange }: Props) => {
const [color, setColor] = useState(initialColor)
useEffect(() => {
onColorChange(color)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [color])
return (
<Popover variant="picker">
<PopoverTrigger>
<Button
aria-label={'Pick a color'}
background={color}
height="22px"
width="22px"
padding={0}
borderRadius={3}
borderWidth={1}
/>
</PopoverTrigger>
<PopoverContent width="170px">
<PopoverArrow bg={color} />
<PopoverCloseButton color="white" />
<PopoverHeader
height="100px"
backgroundColor={color}
borderTopLeftRadius={5}
borderTopRightRadius={5}
color={color === '#ffffff' ? 'gray.800' : 'white'}
>
<Center height="100%">{color}</Center>
</PopoverHeader>
<PopoverBody height="120px">
<SimpleGrid columns={5} spacing={2}>
{colorsSelection.map((c) => (
<Button
key={c}
aria-label={c}
background={c}
height="22px"
width="22px"
padding={0}
minWidth="unset"
borderRadius={3}
_hover={{ background: c }}
onClick={() => {
setColor(c)
}}
></Button>
))}
</SimpleGrid>
<Input
borderRadius={3}
marginTop={3}
placeholder="#2a9d8f"
aria-label="Color value"
size="sm"
value={color}
onChange={(e) => {
setColor(e.target.value)
}}
/>
</PopoverBody>
</PopoverContent>
</Popover>
)
}

View File

@ -0,0 +1,46 @@
import React, { useEffect, useState } from 'react'
import { Text, HStack } from '@chakra-ui/react'
import { SearchableDropdown } from '../../../shared/SearchableDropdown'
type FontSelectorProps = {
activeFont?: string
onSelectFont: (font: string) => void
}
export const FontSelector = ({
activeFont,
onSelectFont,
}: FontSelectorProps) => {
const [currentFont, setCurrentFont] = useState(activeFont)
const [googleFonts, setGoogleFonts] = useState<string[]>([])
useEffect(() => {
fetchPopularFonts().then(setGoogleFonts)
}, [])
const fetchPopularFonts = async () => {
const response = await fetch(
`https://www.googleapis.com/webfonts/v1/webfonts?key=AIzaSyD2YAiipBLNYg058Wm-sPE-e2dPDn_zX8w&sort=popularity`
)
return (await response.json()).items.map(
(item: { family: string }) => item.family
)
}
const handleFontSelected = (nextFont: string) => {
if (nextFont == currentFont) return
setCurrentFont(nextFont)
onSelectFont(nextFont)
}
return (
<HStack justify="space-between" align="center">
<Text>Font</Text>
<SearchableDropdown
selectedItem={activeFont}
items={googleFonts}
onValueChange={handleFontSelected}
/>
</HStack>
)
}

View File

@ -0,0 +1 @@
export { FontSelector } from './FontSelector'

View File

@ -0,0 +1,36 @@
import { Stack } from '@chakra-ui/react'
import { Background, BackgroundType, GeneralTheme } from 'models'
import React from 'react'
import { BackgroundSelector } from './BackgroundSelector'
import { FontSelector } from './FontSelector'
type Props = {
generalTheme?: GeneralTheme
onGeneralThemeChange: (general: GeneralTheme) => void
}
const defaultFont = 'Open Sans'
export const GeneralSettings = ({
generalTheme,
onGeneralThemeChange,
}: Props) => {
const handleSelectFont = (font: string) =>
onGeneralThemeChange({ ...generalTheme, font })
const handleBackgroundChange = (background: Background) =>
onGeneralThemeChange({ ...generalTheme, background })
return (
<Stack spacing={6}>
<FontSelector
activeFont={generalTheme?.font ?? defaultFont}
onSelectFont={handleSelectFont}
/>
<BackgroundSelector
background={generalTheme?.background ?? { type: BackgroundType.NONE }}
onBackgroundChange={handleBackgroundChange}
/>
</Stack>
)
}

View File

@ -0,0 +1 @@
export { GeneralSettings } from './GeneralSettings'