1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Rewrite emoji component logic to be more generic

This commit is contained in:
xmflsct
2022-09-18 01:02:25 +02:00
parent 535268c680
commit 8a054f2205
12 changed files with 367 additions and 455 deletions

View File

@ -1,99 +1,55 @@
import { StyleConstants } from '@utils/styles/constants'
import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager'
import React, {
Dispatch,
SetStateAction,
useEffect,
useRef,
useState
} from 'react'
import React, { Dispatch, forwardRef, RefObject, SetStateAction, useRef } from 'react'
import { Platform, TextInput, TextInputProps, View } from 'react-native'
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
import { ComponentEmojis, EmojisButton, EmojisList } from './Emojis'
import EmojisContext from './Emojis/helpers/EmojisContext'
import CustomText from './Text'
export interface Props {
autoFocus?: boolean
export type Props = {
value: string
setValue: Dispatch<SetStateAction<string>>
selectionRange?: { start: number; end: number }
title?: string
multiline?: boolean
} & Omit<
TextInputProps,
| 'style'
| 'onChangeText'
| 'onSelectionChange'
| 'keyboardAppearance'
| 'textAlignVertical'
| 'multiline'
>
emoji?: boolean
const ComponentInput = forwardRef(
(
{ title, multiline = false, value, setValue, selectionRange, ...props }: Props,
ref: RefObject<TextInput>
) => {
const { colors, mode } = useTheme()
value?: string
setValue:
| Dispatch<SetStateAction<string | undefined>>
| Dispatch<SetStateAction<string>>
options?: Omit<
TextInputProps,
| 'autoFocus'
| 'onFocus'
| 'onBlur'
| 'style'
| 'onChangeText'
| 'onSelectionChange'
| 'keyboardAppearance'
| 'textAlignVertical'
>
}
const Input: React.FC<Props> = ({
autoFocus = true,
title,
multiline = false,
emoji = false,
value,
setValue,
options
}) => {
const { colors, mode } = useTheme()
const animateTitle = useAnimatedStyle(() => {
if (value) {
return {
fontSize: withTiming(StyleConstants.Font.Size.S),
paddingHorizontal: withTiming(StyleConstants.Spacing.XS),
left: withTiming(StyleConstants.Spacing.S),
top: withTiming(-(StyleConstants.Font.Size.S / 2) - 2),
backgroundColor: withTiming(colors.backgroundDefault)
}
} else {
return {
fontSize: withTiming(StyleConstants.Font.Size.M),
paddingHorizontal: withTiming(0),
left: withTiming(StyleConstants.Spacing.S),
top: withTiming(StyleConstants.Spacing.S + 1),
backgroundColor: withTiming(colors.backgroundDefaultTransparent)
}
}
}, [mode, value])
const selectionRange = useRef<{ start: number; end: number }>(
value
? {
start: value.length,
end: value.length
const animateTitle = useAnimatedStyle(() => {
if (value) {
return {
fontSize: withTiming(StyleConstants.Font.Size.S),
paddingHorizontal: withTiming(StyleConstants.Spacing.XS),
left: withTiming(StyleConstants.Spacing.S),
top: withTiming(-(StyleConstants.Font.Size.S / 2) - 2),
backgroundColor: withTiming(colors.backgroundDefault)
}
: { start: 0, end: 0 }
)
} else {
return {
fontSize: withTiming(StyleConstants.Font.Size.M),
paddingHorizontal: withTiming(0),
left: withTiming(StyleConstants.Spacing.S),
top: withTiming(StyleConstants.Spacing.S + 1),
backgroundColor: withTiming(colors.backgroundDefaultTransparent)
}
}
}, [mode, value])
const [inputFocused, setInputFocused] = useState(false)
useEffect(() => {
layoutAnimation()
}, [inputFocused])
return (
<ComponentEmojis
enabled={emoji}
value={value}
setValue={setValue}
selectionRange={selectionRange}
maxLength={options?.maxLength}
>
return (
<View
style={{
borderWidth: 1,
@ -104,51 +60,35 @@ const Input: React.FC<Props> = ({
alignItems: 'stretch'
}}
>
<EmojisContext.Consumer>
{({ emojisDispatch }) => (
<TextInput
autoFocus={autoFocus}
onFocus={() => setInputFocused(true)}
onBlur={() => {
setInputFocused(false)
emojisDispatch({ type: 'activate', payload: false })
}}
style={{
flex: 1,
fontSize: StyleConstants.Font.Size.M,
color: colors.primaryDefault,
minHeight:
Platform.OS === 'ios' && multiline
? StyleConstants.Font.LineHeight.M * 5
: undefined
}}
onChangeText={setValue}
onSelectionChange={({ nativeEvent: { selection } }) =>
(selectionRange.current = selection)
}
value={value}
{...(multiline && {
multiline,
numberOfLines: Platform.OS === 'android' ? 5 : undefined
})}
keyboardAppearance={mode}
textAlignVertical='top'
{...options}
/>
)}
</EmojisContext.Consumer>
<TextInput
ref={ref}
style={{
flex: 1,
fontSize: StyleConstants.Font.Size.M,
color: colors.primaryDefault,
minHeight:
Platform.OS === 'ios' && multiline ? StyleConstants.Font.LineHeight.M * 5 : undefined
}}
onChangeText={setValue}
onSelectionChange={({ nativeEvent: { selection } }) => (selectionRange = selection)}
value={value}
{...(multiline && {
multiline,
numberOfLines: Platform.OS === 'android' ? 5 : undefined
})}
keyboardAppearance={mode}
textAlignVertical='top'
{...props}
/>
{title ? (
<Animated.Text
style={[
animateTitle,
{ position: 'absolute', color: colors.secondary }
]}
>
<Animated.Text style={[animateTitle, { position: 'absolute', color: colors.secondary }]}>
{title}
</Animated.Text>
) : null}
<View style={{ flexDirection: 'row', alignSelf: 'flex-end' }}>
{options?.maxLength && value?.length ? (
{props?.maxLength && value?.length ? (
<CustomText
fontStyle='S'
style={{
@ -156,15 +96,13 @@ const Input: React.FC<Props> = ({
color: colors.secondary
}}
>
{value?.length} / {options.maxLength}
{value?.length} / {props.maxLength}
</CustomText>
) : null}
{inputFocused ? <EmojisButton /> : null}
</View>
</View>
<EmojisList />
</ComponentEmojis>
)
}
)
}
)
export default Input
export default ComponentInput