2
0

refactor(♻️ Add defaults everywhere (+ settings page)):

This commit is contained in:
Baptiste Arnaud
2022-01-25 18:19:37 +01:00
parent 21448bcc8a
commit c5aaa323d1
115 changed files with 1436 additions and 720 deletions

View File

@@ -1,22 +1,27 @@
import { ChangeEvent, FormEvent, useState } from 'react'
import { ChangeEvent, FormEvent, useEffect, useState } from 'react'
import { Button, HStack, Input, Stack } from '@chakra-ui/react'
import { SearchContextManager } from '@giphy/react-components'
import { UploadButton } from '../buttons/UploadButton'
import { GiphySearch } from './GiphySearch'
import { useTypebot } from 'contexts/TypebotContext'
import { ImageBubbleContent } from 'models'
import { useDebounce } from 'use-debounce'
type Props = {
content?: ImageBubbleContent
onSubmit: (content: ImageBubbleContent) => void
url?: string
onSubmit: (url: string) => void
isGiphyEnabled?: boolean
}
export const ImageUploadContent = ({ content, onSubmit }: Props) => {
export const ImageUploadContent = ({
url,
onSubmit,
isGiphyEnabled = true,
}: Props) => {
const [currentTab, setCurrentTab] = useState<'link' | 'upload' | 'giphy'>(
'upload'
)
const handleSubmit = (url: string) => onSubmit({ url })
const handleSubmit = (url: string) => onSubmit(url)
return (
<Stack>
<HStack>
@@ -34,7 +39,7 @@ export const ImageUploadContent = ({ content, onSubmit }: Props) => {
>
Embed link
</Button>
{process.env.NEXT_PUBLIC_GIPHY_API_KEY && (
{process.env.NEXT_PUBLIC_GIPHY_API_KEY && isGiphyEnabled && (
<Button
variant={currentTab === 'giphy' ? 'solid' : 'ghost'}
onClick={() => setCurrentTab('giphy')}
@@ -45,11 +50,7 @@ export const ImageUploadContent = ({ content, onSubmit }: Props) => {
)}
</HStack>
<BodyContent
tab={currentTab}
onSubmit={handleSubmit}
url={content?.url}
/>
<BodyContent tab={currentTab} onSubmit={handleSubmit} url={url} />
</Stack>
)
}
@@ -93,26 +94,23 @@ const UploadFileContent = ({ onNewUrl }: ContentProps) => {
const EmbedLinkContent = ({ initialUrl, onNewUrl }: ContentProps) => {
const [imageUrl, setImageUrl] = useState(initialUrl ?? '')
const [debouncedImageUrl] = useDebounce(imageUrl, 100)
useEffect(() => {
if (initialUrl === debouncedImageUrl) return
onNewUrl(imageUrl)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedImageUrl])
const handleImageUrlChange = (e: ChangeEvent<HTMLInputElement>) =>
setImageUrl(e.target.value)
const handleUrlSubmit = (e: FormEvent) => {
e.preventDefault()
onNewUrl(imageUrl)
}
return (
<Stack as="form" onSubmit={handleUrlSubmit}>
<Input
placeholder={'Paste the image link...'}
onChange={handleImageUrlChange}
value={imageUrl}
/>
<Button type="submit" disabled={imageUrl === ''} colorScheme="blue">
Embed image
</Button>
</Stack>
<Input
placeholder={'Paste the image link...'}
onChange={handleImageUrlChange}
value={imageUrl}
/>
)
}

View File

@@ -0,0 +1,9 @@
import { Alert, AlertIcon, AlertProps } from '@chakra-ui/react'
import React from 'react'
export const Info = (props: AlertProps) => (
<Alert status="info" bgColor={'blue.50'} rounded="md" {...props}>
<AlertIcon />
{props.children}
</Alert>
)

View File

@@ -0,0 +1,39 @@
import {
NumberInputProps,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
} from '@chakra-ui/react'
import { useState } from 'react'
export const SmartNumberInput = ({
value,
onValueChange,
...props
}: {
value?: number
onValueChange: (value?: number) => void
} & NumberInputProps) => {
const [currentValue, setCurrentValue] = useState(value?.toString() ?? '')
const handleValueChange = (value: string) => {
setCurrentValue(value)
if (value.endsWith('.') || value.endsWith(',')) return
if (value === '') return onValueChange(undefined)
const newValue = parseFloat(value)
if (isNaN(newValue)) return
onValueChange(newValue)
}
return (
<NumberInput onChange={handleValueChange} value={currentValue} {...props}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)
}

View File

@@ -0,0 +1,10 @@
import { Input } from '@chakra-ui/react'
import React from 'react'
import {
TextBoxWithVariableButton,
TextBoxWithVariableButtonProps,
} from './TextboxWithVariableButton'
export const InputWithVariableButton = (
props: Omit<TextBoxWithVariableButtonProps, 'TextBox'>
) => <TextBoxWithVariableButton TextBox={Input} {...props} />

View File

@@ -0,0 +1,10 @@
import { Textarea } from '@chakra-ui/react'
import React from 'react'
import {
TextBoxWithVariableButton,
TextBoxWithVariableButtonProps,
} from './TextboxWithVariableButton'
export const TextareaWithVariableButton = (
props: Omit<TextBoxWithVariableButtonProps, 'TextBox'>
) => <TextBoxWithVariableButton TextBox={Textarea} {...props} />

View File

@@ -1,31 +1,40 @@
import {
ComponentWithAs,
Flex,
HStack,
IconButton,
Input,
InputProps,
Popover,
PopoverContent,
PopoverTrigger,
TextareaProps,
Tooltip,
} from '@chakra-ui/react'
import { UserIcon } from 'assets/icons'
import { Variable } from 'models'
import React, { useEffect, useRef, useState } from 'react'
import { useDebounce } from 'use-debounce'
import { VariableSearchInput } from './VariableSearchInput'
import { VariableSearchInput } from '../VariableSearchInput'
export const InputWithVariableButton = ({
initialValue,
onChange,
delay,
...props
}: {
export type TextBoxWithVariableButtonProps = {
initialValue: string
onChange: (value: string) => void
delay?: number
} & Omit<InputProps, 'onChange'>) => {
const inputRef = useRef<HTMLInputElement | null>(null)
TextBox:
| ComponentWithAs<'textarea', TextareaProps>
| ComponentWithAs<'input', InputProps>
} & Omit<InputProps & TextareaProps, 'onChange'>
export const TextBoxWithVariableButton = ({
initialValue,
onChange,
delay,
TextBox,
...props
}: TextBoxWithVariableButtonProps) => {
const textBoxRef = useRef<(HTMLInputElement & HTMLTextAreaElement) | null>(
null
)
const [value, setValue] = useState(initialValue)
const [debouncedValue] = useDebounce(value, delay ?? 100)
const [carretPosition, setCarretPosition] = useState<number>(0)
@@ -36,48 +45,49 @@ export const InputWithVariableButton = ({
}, [debouncedValue])
const handleVariableSelected = (variable?: Variable) => {
if (!inputRef.current || !variable) return
if (!textBoxRef.current || !variable) return
const cursorPosition = carretPosition
const textBeforeCursorPosition = inputRef.current.value.substring(
const textBeforeCursorPosition = textBoxRef.current.value.substring(
0,
cursorPosition
)
const textAfterCursorPosition = inputRef.current.value.substring(
const textAfterCursorPosition = textBoxRef.current.value.substring(
cursorPosition,
inputRef.current.value.length
textBoxRef.current.value.length
)
setValue(
textBeforeCursorPosition +
`{{${variable.name}}}` +
textAfterCursorPosition
)
inputRef.current.focus()
textBoxRef.current.focus()
setTimeout(() => {
if (!inputRef.current) return
inputRef.current.selectionStart = inputRef.current.selectionEnd =
if (!textBoxRef.current) return
textBoxRef.current.selectionStart = textBoxRef.current.selectionEnd =
carretPosition + `{{${variable.name}}}`.length
setCarretPosition(inputRef.current.selectionStart)
setCarretPosition(textBoxRef.current.selectionStart)
}, 100)
}
const handleKeyUp = () => {
if (!inputRef.current?.selectionStart) return
setCarretPosition(inputRef.current.selectionStart)
if (!textBoxRef.current?.selectionStart) return
setCarretPosition(textBoxRef.current.selectionStart)
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) =>
setValue(e.target.value)
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => setValue(e.target.value)
return (
<HStack spacing={0}>
<Input
ref={inputRef}
<HStack spacing={0} align={'flex-end'}>
<TextBox
ref={textBoxRef}
onKeyUp={handleKeyUp}
onClick={handleKeyUp}
value={value}
onChange={handleChange}
{...props}
bgColor={'white'}
{...props}
/>
<Popover matchWidth isLazy>
<PopoverTrigger>

View File

@@ -0,0 +1,2 @@
export { InputWithVariableButton } from './InputWithVariableButton'
export { TextareaWithVariableButton } from './TextareaWithVariableButton'