[WEB-1024] fix: textarea component auto-resize (#4221)

* chore: updated resize hook logic

* fix: page title overflow issue

* chore: add length validation to page title
This commit is contained in:
Aaryan Khandelwal 2024-04-17 19:41:34 +05:30 committed by GitHub
parent 10ed12e589
commit f0cb48006f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 36 deletions

View File

@ -1,6 +1,8 @@
import * as React from "react";
import React, { useRef } from "react";
// helpers
import { cn } from "../../helpers";
// hooks
import { useAutoResizeTextArea } from "../hooks/use-auto-resize-textarea";
export interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
mode?: "primary" | "transparent";
@ -8,21 +10,6 @@ export interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextArea
className?: string;
}
// Updates the height of a <textarea> when the value changes.
const useAutoSizeTextArea = (textAreaRef: HTMLTextAreaElement | null, value: any) => {
React.useEffect(() => {
if (textAreaRef) {
// We need to reset the height momentarily to get the correct scrollHeight for the textarea
textAreaRef.style.height = "0px";
const scrollHeight = textAreaRef.scrollHeight;
// We then set the height directly, outside of the render loop
// Trying to set this with state or a ref will product an incorrect value.
textAreaRef.style.height = scrollHeight + "px";
}
}, [textAreaRef, value]);
};
const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>((props, ref) => {
const {
id,
@ -35,10 +22,10 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>((props, re
className = "",
...rest
} = props;
const textAreaRef = React.useRef<any>(ref);
useAutoSizeTextArea(textAreaRef?.current, value);
// refs
const textAreaRef = useRef<any>(ref);
// auto re-size
useAutoResizeTextArea(textAreaRef);
return (
<textarea

View File

@ -0,0 +1,24 @@
import { useEffect } from "react";
export const useAutoResizeTextArea = (textAreaRef: React.RefObject<HTMLTextAreaElement>) => {
useEffect(() => {
const textArea = textAreaRef.current;
if (!textArea) return;
const resizeTextArea = () => {
textArea.style.height = "auto";
const computedHeight = textArea.scrollHeight + "px";
textArea.style.height = computedHeight;
};
const handleInput = () => resizeTextArea();
// resize on mount
resizeTextArea();
textArea.addEventListener("input", handleInput);
return () => {
textArea.removeEventListener("input", handleInput);
};
}, [textAreaRef]);
};

View File

@ -109,7 +109,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
})}
>
<div className="h-full w-full flex flex-col gap-y-7 overflow-y-auto overflow-x-hidden">
<div className="w-full flex-shrink-0 ml-5">
<div className="relative w-full flex-shrink-0 pl-5">
<PageEditorTitle
editorRef={editorRef}
title={pageTitle}

View File

@ -1,8 +1,11 @@
import { useState } from "react";
import { observer } from "mobx-react";
// editor
import { EditorRefApi } from "@plane/document-editor";
// ui
import { TextArea } from "@plane/ui";
// helpers
import { cn } from "@/helpers/common.helper";
type Props = {
editorRef: React.RefObject<EditorRefApi>;
@ -13,27 +16,51 @@ type Props = {
export const PageEditorTitle: React.FC<Props> = observer((props) => {
const { editorRef, readOnly, title, updateTitle } = props;
// states
const [isLengthVisible, setIsLengthVisible] = useState(false);
return (
<>
{readOnly ? (
<h6 className="-mt-2 break-words bg-transparent text-4xl font-bold">{title}</h6>
) : (
<TextArea
onChange={(e) => updateTitle(e.target.value)}
className="-mt-2 w-full bg-custom-background text-4xl font-bold outline-none p-0 border-none resize-none rounded-none"
style={{
lineHeight: "1.2",
}}
placeholder="Untitled Page"
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
editorRef.current?.setFocusAtPosition(0);
}
}}
value={title}
/>
<>
<TextArea
onChange={(e) => updateTitle(e.target.value)}
className="-mt-2 w-full bg-custom-background text-4xl font-bold outline-none p-0 border-none resize-none rounded-none"
style={{
lineHeight: "1.2",
}}
placeholder="Untitled Page"
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
editorRef.current?.setFocusAtPosition(0);
}
}}
value={title}
maxLength={255}
onFocus={() => setIsLengthVisible(true)}
onBlur={() => setIsLengthVisible(false)}
/>
<div
className={cn(
"pointer-events-none absolute bottom-1 right-1 z-[2] rounded bg-custom-background-100 p-0.5 text-xs text-custom-text-200 opacity-0 transition-opacity",
{
"opacity-100": isLengthVisible,
}
)}
>
<span
className={cn({
"text-red-500": title.length === 0 || title.length > 255,
})}
>
{title.length}
</span>
/255
</div>
</>
)}
</>
);