♻️ (builder) Change to features-centric folder structure

This commit is contained in:
Baptiste Arnaud
2022-11-15 09:35:48 +01:00
committed by Baptiste Arnaud
parent 3686465a85
commit 643571fe7d
683 changed files with 3907 additions and 3643 deletions

View File

@@ -0,0 +1,7 @@
import { Input as ChakraInput } from '@chakra-ui/react'
import React from 'react'
import { TextBox, TextBoxProps } from './TextBox'
export const Input = (props: Omit<TextBoxProps, 'TextBox'>) => (
<TextBox TextBox={ChakraInput} {...props} />
)

View File

@@ -0,0 +1,55 @@
import {
NumberInputProps,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
} from '@chakra-ui/react'
import { useEffect, useState } from 'react'
import { useDebouncedCallback } from 'use-debounce'
import { env } from 'utils'
export const SmartNumberInput = ({
value,
onValueChange,
debounceTimeout = 1000,
...props
}: {
value?: number
debounceTimeout?: number
onValueChange: (value?: number) => void
} & NumberInputProps) => {
const [currentValue, setCurrentValue] = useState(value?.toString() ?? '')
const debounced = useDebouncedCallback(
onValueChange,
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
)
useEffect(
() => () => {
debounced.flush()
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
)
const handleValueChange = (value: string) => {
setCurrentValue(value)
if (value.endsWith('.') || value.endsWith(',')) return
if (value === '') return debounced(undefined)
const newValue = parseFloat(value)
if (isNaN(newValue)) return
debounced(newValue)
}
return (
<NumberInput onChange={handleValueChange} value={currentValue} {...props}>
<NumberInputField placeholder={props.placeholder} />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)
}

View File

@@ -0,0 +1,134 @@
import {
ComponentWithAs,
FormControl,
FormLabel,
HStack,
InputProps,
TextareaProps,
} from '@chakra-ui/react'
import { Variable } from 'models'
import React, { ChangeEvent, useEffect, useRef, useState } from 'react'
import { useDebouncedCallback } from 'use-debounce'
import { env } from 'utils'
import { VariablesButton } from '../../features/variables/components/VariablesButton'
import { MoreInfoTooltip } from '../MoreInfoTooltip'
export type TextBoxProps = {
onChange: (value: string) => void
TextBox:
| ComponentWithAs<'textarea', TextareaProps>
| ComponentWithAs<'input', InputProps>
withVariableButton?: boolean
debounceTimeout?: number
label?: string
moreInfoTooltip?: string
} & Omit<InputProps & TextareaProps, 'onChange'>
export const TextBox = ({
onChange,
TextBox,
withVariableButton = true,
debounceTimeout = 1000,
label,
moreInfoTooltip,
...props
}: TextBoxProps) => {
const textBoxRef = useRef<(HTMLInputElement & HTMLTextAreaElement) | null>(
null
)
const [carretPosition, setCarretPosition] = useState<number>(0)
const [value, setValue] = useState(props.defaultValue ?? '')
const [isTouched, setIsTouched] = useState(false)
const debounced = useDebouncedCallback(
(value) => {
onChange(value)
},
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
)
useEffect(() => {
if (props.defaultValue !== value && value === '' && !isTouched)
setValue(props.defaultValue ?? '')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.defaultValue])
useEffect(
() => () => {
debounced.flush()
},
[debounced]
)
const handleChange = (
e: ChangeEvent<HTMLInputElement & HTMLTextAreaElement>
) => {
setIsTouched(true)
setValue(e.target.value)
debounced(e.target.value)
}
const handleVariableSelected = (variable?: Variable) => {
if (!textBoxRef.current || !variable) return
setIsTouched(true)
const cursorPosition = carretPosition
const textBeforeCursorPosition = textBoxRef.current.value.substring(
0,
cursorPosition
)
const textAfterCursorPosition = textBoxRef.current.value.substring(
cursorPosition,
textBoxRef.current.value.length
)
const newValue =
textBeforeCursorPosition +
`{{${variable.name}}}` +
textAfterCursorPosition
setValue(newValue)
debounced(newValue)
textBoxRef.current.focus()
setTimeout(() => {
if (!textBoxRef.current) return
textBoxRef.current.selectionStart = textBoxRef.current.selectionEnd =
carretPosition + `{{${variable.name}}}`.length
setCarretPosition(textBoxRef.current.selectionStart)
}, 100)
}
const handleKeyUp = () => {
if (!textBoxRef.current?.selectionStart) return
setCarretPosition(textBoxRef.current.selectionStart)
}
const Input = (
<TextBox
ref={textBoxRef}
value={value}
onKeyUp={handleKeyUp}
onClick={handleKeyUp}
onChange={handleChange}
bgColor={'white'}
{...props}
/>
)
return (
<FormControl isRequired={props.isRequired}>
{label && (
<FormLabel>
{label}{' '}
{moreInfoTooltip && (
<MoreInfoTooltip>{moreInfoTooltip}</MoreInfoTooltip>
)}
</FormLabel>
)}
{withVariableButton ? (
<HStack spacing={0} align={'flex-end'}>
{Input}
<VariablesButton onSelectVariable={handleVariableSelected} />
</HStack>
) : (
Input
)}
</FormControl>
)
}

View File

@@ -0,0 +1,7 @@
import { Textarea as ChakraTextarea } from '@chakra-ui/react'
import React from 'react'
import { TextBox, TextBoxProps } from './TextBox'
export const Textarea = (props: Omit<TextBoxProps, 'TextBox'>) => (
<TextBox TextBox={ChakraTextarea} {...props} />
)

View File

@@ -0,0 +1,3 @@
export { Input } from './Input'
export { Textarea } from './Textarea'
export { SmartNumberInput } from './SmartNumberInput'