import { isEmptyContent } from 'hero-editor'; import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import { Animated, Easing } from 'react-native'; import type { ReactElement, Ref } from 'react'; import type { LayoutChangeEvent } from 'react-native'; import { useTheme } from '../../theme'; import { StyledAsteriskLabelInsideTextInput, StyledBorderBackDrop, StyledContainer, StyledError, StyledErrorAndHelpTextContainer, StyledErrorAndMaxLengthContainer, StyledErrorContainer, StyledHelperText, StyledInputContentContainer, StyledLabelContainerInsideTextInput, StyledLabelInsideTextInput, StyledTextInputAndLabelContainer, StyledTextInputContainer, } from '../TextInput/StyledTextInput'; import { LABEL_ANIMATION_DURATION } from '../TextInput'; import Typography from '../Typography'; import RichTextEditorInput from './RichTextEditorInput'; import type { RichTextEditorInputProps, RichTextEditorRef, } from './RichTextEditorInput'; type EditorValue = { type: string; children: any; }[]; export interface RichTextEditorProps extends Omit { /** * Field label */ label: string; /** * Field helper text */ helpText?: string; /** * Whether the input is required, if true, an asterisk will be appended to the label. * */ required?: boolean; /** * Testing ID of the component */ testID?: string; /** * Imperative ref to expose the component method */ forwardedRef?: Ref; } const defaultValue: EditorValue = [ { type: 'paragraph', children: [{ text: '' }], }, ]; const RichTextEditor = ({ autoFocus = true, name, placeholder = '', onChange, onCursorChange, error = '', style = {}, label, helpText, required, testID, onFocus, onBlur, forwardedRef, value = defaultValue, }: RichTextEditorProps): ReactElement => { const theme = useTheme(); const [isFocused, setIsFocused] = useState(false); const isEmptyValue = useMemo(() => isEmptyContent(value), [value]); const state = useMemo(() => { if (error) { return 'error'; } if (isEmptyValue) { return 'filled'; } return 'default'; }, [isFocused, error, isEmptyValue]); const [containerHeight, setContainerHeight] = React.useState(0); const focusAnimation = useRef(new Animated.Value(0)).current; useEffect(() => { Animated.timing(focusAnimation, { toValue: isFocused || !isEmptyValue ? 1 : 0, duration: LABEL_ANIMATION_DURATION, easing: Easing.bezier(0.4, 0, 0.2, 1), useNativeDriver: true, }).start(); }, [focusAnimation, isEmptyValue, isFocused]); const onLayout = useCallback((event: LayoutChangeEvent) => { setContainerHeight(event.nativeEvent.layout.height); }, []); const backgroundColor = theme.__hd__.textInput.colors.containerBackground; const handleEditorFocus = useCallback(() => { onFocus?.(); setIsFocused(true); }, [onFocus]); const handleEditorBlur = useCallback(() => { onBlur?.(); setIsFocused(false); }, [onBlur]); return ( {!!label && ( {required && ( * )} {label} )} {error ? ( {error} ) : ( !!helpText && {helpText} )} ); }; const RichTextEditorWithRef = forwardRef< RichTextEditorRef, Omit >((props, ref) => ); RichTextEditorWithRef.displayName = 'RichTextEditor'; export default RichTextEditorWithRef;