mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
fixed ref forwarding on textareas
This commit is contained in:
parent
2b7b756212
commit
140e36b6b7
5 changed files with 58 additions and 44 deletions
|
@ -25,7 +25,7 @@ interface IProps {
|
|||
}
|
||||
|
||||
const CommentForm: FC<IProps> = ({ comment, nodeId, saveComment, onCancelEdit }) => {
|
||||
const [textarea, setTextarea] = useState<HTMLTextAreaElement>();
|
||||
const [textarea, setTextArea] = useState<HTMLTextAreaElement | null>(null);
|
||||
const uploader = useFileUploader(
|
||||
UPLOAD_SUBJECTS.COMMENT,
|
||||
UPLOAD_TARGETS.COMMENTS,
|
||||
|
@ -55,7 +55,7 @@ const CommentForm: FC<IProps> = ({ comment, nodeId, saveComment, onCancelEdit })
|
|||
}, [formik]);
|
||||
|
||||
const error = formik.status || formik.errors.text;
|
||||
useInputPasteUpload(textarea, uploader.uploadFiles);
|
||||
const onPaste = useInputPasteUpload(uploader.uploadFiles);
|
||||
|
||||
return (
|
||||
<UploadDropzone onUpload={uploader.uploadFiles}>
|
||||
|
@ -63,7 +63,7 @@ const CommentForm: FC<IProps> = ({ comment, nodeId, saveComment, onCancelEdit })
|
|||
<FormikProvider value={formik}>
|
||||
<FileUploaderProvider value={uploader}>
|
||||
<div className={styles.input}>
|
||||
<LocalCommentFormTextarea setRef={setTextarea} />
|
||||
<LocalCommentFormTextarea onPaste={onPaste} ref={setTextArea} />
|
||||
|
||||
{!!error && (
|
||||
<div className={styles.error} onClick={clearError}>
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import React, { FC, KeyboardEventHandler, useCallback } from 'react';
|
||||
import React, {
|
||||
forwardRef,
|
||||
KeyboardEventHandler,
|
||||
TextareaHTMLAttributes,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { Textarea } from '~/components/input/Textarea';
|
||||
import { useCommentFormContext } from '~/hooks/comments/useCommentFormFormik';
|
||||
import { useRandomPhrase } from '~/constants/phrases';
|
||||
|
||||
interface IProps {
|
||||
interface IProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
isLoading?: boolean;
|
||||
setRef?: (r: HTMLTextAreaElement) => void;
|
||||
}
|
||||
|
||||
const LocalCommentFormTextarea: FC<IProps> = ({ setRef }) => {
|
||||
const LocalCommentFormTextarea = forwardRef<HTMLTextAreaElement, IProps>(({ ...rest }, ref) => {
|
||||
const { values, handleChange, handleSubmit, isSubmitting } = useCommentFormContext();
|
||||
|
||||
const onKeyDown = useCallback<KeyboardEventHandler<HTMLTextAreaElement>>(
|
||||
|
@ -22,14 +26,15 @@ const LocalCommentFormTextarea: FC<IProps> = ({ setRef }) => {
|
|||
|
||||
return (
|
||||
<Textarea
|
||||
{...rest}
|
||||
ref={ref}
|
||||
value={values.text}
|
||||
handler={handleChange('text')}
|
||||
onKeyDown={onKeyDown}
|
||||
disabled={isSubmitting}
|
||||
placeholder={placeholder}
|
||||
ref={setRef}
|
||||
/>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export { LocalCommentFormTextarea };
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import React, {
|
||||
ChangeEvent,
|
||||
DetailedHTMLProps,
|
||||
memo,
|
||||
forwardRef,
|
||||
TextareaHTMLAttributes,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
@ -13,6 +12,7 @@ import autosize from 'autosize';
|
|||
import styles from './styles.module.scss';
|
||||
|
||||
import { InputWrapper } from '~/components/input/InputWrapper';
|
||||
import { useForwardRef } from '~/hooks/dom/useForwardRef';
|
||||
|
||||
type IProps = DetailedHTMLProps<
|
||||
TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
|
@ -27,8 +27,9 @@ type IProps = DetailedHTMLProps<
|
|||
title?: string;
|
||||
};
|
||||
|
||||
const Textarea = memo<IProps>(
|
||||
({
|
||||
const Textarea = forwardRef<HTMLTextAreaElement, IProps>(
|
||||
(
|
||||
{
|
||||
placeholder,
|
||||
error,
|
||||
minRows = 3,
|
||||
|
@ -38,9 +39,11 @@ const Textarea = memo<IProps>(
|
|||
title = '',
|
||||
value,
|
||||
...props
|
||||
}) => {
|
||||
},
|
||||
forwardRef
|
||||
) => {
|
||||
const ref = useForwardRef(forwardRef);
|
||||
const [focused, setFocused] = useState(false);
|
||||
const ref = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const onInput = useCallback(
|
||||
({ target }: ChangeEvent<HTMLTextAreaElement>) => handler(target.value),
|
||||
|
@ -52,20 +55,21 @@ const Textarea = memo<IProps>(
|
|||
|
||||
useEffect(() => {
|
||||
if (!ref?.current) return;
|
||||
autosize(ref.current);
|
||||
return () => autosize.destroy(ref.current);
|
||||
autosize(ref?.current);
|
||||
return () => autosize.destroy(ref);
|
||||
}, [ref]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
if (!ref?.current) return;
|
||||
|
||||
autosize.update(ref.current);
|
||||
}, [value]);
|
||||
autosize.update(ref);
|
||||
}, [ref, value, forwardRef]);
|
||||
|
||||
return (
|
||||
<InputWrapper title={title} error={error} focused={focused} notEmpty={!!value}>
|
||||
<textarea
|
||||
{...props}
|
||||
ref={ref}
|
||||
style={{
|
||||
...props.style,
|
||||
minHeight: minRows * 20,
|
||||
|
@ -74,7 +78,6 @@ const Textarea = memo<IProps>(
|
|||
placeholder={placeholder}
|
||||
className={classNames(styles.textarea, className)}
|
||||
onChange={onInput}
|
||||
ref={ref}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
|
|
17
src/hooks/dom/useForwardRef.ts
Normal file
17
src/hooks/dom/useForwardRef.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ForwardedRef, useEffect, useRef } from 'react';
|
||||
|
||||
export const useForwardRef = <T>(ref: ForwardedRef<T | null>) => {
|
||||
const targetRef = useRef<T | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref) return;
|
||||
|
||||
if (typeof ref === 'function') {
|
||||
(ref as any)(targetRef.current);
|
||||
} else {
|
||||
(ref as any).current = targetRef.current;
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
return targetRef;
|
||||
};
|
|
@ -1,12 +1,9 @@
|
|||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { getImageFromPaste } from '~/utils/uploader';
|
||||
|
||||
// useInputPasteUpload attaches event listener to input, that calls onUpload if user pasted any image
|
||||
export const useInputPasteUpload = (
|
||||
input: HTMLTextAreaElement | HTMLInputElement | undefined,
|
||||
onUpload: (files: File[]) => void
|
||||
) => {
|
||||
const onPaste = useCallback(
|
||||
export const useInputPasteUpload = (onUpload: (files: File[]) => void) => {
|
||||
return useCallback(
|
||||
async event => {
|
||||
const image = await getImageFromPaste(event);
|
||||
|
||||
|
@ -16,12 +13,4 @@ export const useInputPasteUpload = (
|
|||
},
|
||||
[onUpload]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!input) return;
|
||||
|
||||
input.addEventListener('paste', onPaste);
|
||||
|
||||
return () => input.removeEventListener('paste', onPaste);
|
||||
}, [input, onPaste]);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue