diff --git a/src/components/ui/TextArea.tsx b/src/components/ui/TextArea.tsx index ed15695f320fc7366588fee3320928f5649da7fb..cb4bc81ab0305f9014be9ce204d480916b78f634 100644 --- a/src/components/ui/TextArea.tsx +++ b/src/components/ui/TextArea.tsx @@ -4,15 +4,17 @@ // import { useState, useEffect, useRef, useLayoutEffect } from "preact/hooks"; import styled, { css } from "styled-components"; -interface Props { +export interface TextAreaProps { code?: boolean; + padding?: number; } -export default styled.textarea<Props>` +export default styled.textarea<TextAreaProps>` width: 100%; resize: none; display: block; border-radius: 4px; + padding: ${ props => props.padding ?? 16 }px; color: var(--foreground); border: 2px solid transparent; diff --git a/src/lib/TextAreaAutoSize.tsx b/src/lib/TextAreaAutoSize.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2b1fd73fe1c494e3991c8ce1fd3a156bd414c81a --- /dev/null +++ b/src/lib/TextAreaAutoSize.tsx @@ -0,0 +1,105 @@ +import styled from "styled-components"; +import TextArea, { TextAreaProps } from "../components/ui/TextArea"; +import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks"; + +type TextAreaAutoSizeProps = Omit<JSX.HTMLAttributes<HTMLTextAreaElement>, 'style' | 'value'> & TextAreaProps & { + autoFocus?: boolean, + minHeight?: number, + maxRows?: number, + value: string +}; + +const lineHeight = 20; + +const Ghost = styled.div` + width: 100%; + overflow: hidden; + position: relative; + + > div { + width: 100%; + white-space: pre-wrap; + + top: 0; + position: absolute; + visibility: hidden; + } +`; + +export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) { + const { autoFocus, minHeight, maxRows, value, padding, children, as, ...textAreaProps } = props; + + const heightPadding = (padding ?? 0) * 2; + const minimumHeight = (minHeight ?? lineHeight) + heightPadding; + + var height = Math.max(Math.min(value.split('\n').length, maxRows ?? Infinity) * lineHeight + heightPadding, minimumHeight); + const ref = useRef<HTMLTextAreaElement>(); + + /*function setHeight(h: number = lineHeight) { + let newHeight = Math.min( + Math.max( + lineHeight, + maxRows ? Math.min(h, maxRows * lineHeight) : h + ), + minHeight ?? Infinity + ); + + if (heightPadding) newHeight += heightPadding; + if (height !== newHeight) { + setHeightState(newHeight); + } + }*/ + + {/*useLayoutEffect(() => { + setHeight(ghost.current.clientHeight); + }, [ghost, value]);*/} + + useEffect(() => { + autoFocus && ref.current.focus(); + }, [value]); + + const inputSelected = () => + ["TEXTAREA", "INPUT"].includes(document.activeElement?.nodeName ?? ""); + + useEffect(() => { + /* if (props.forceFocus) { // figure out what needed force focus + ref.current.focus(); + } */ + + if (autoFocus && !inputSelected()) { + ref.current.focus(); + } + + // ? if you are wondering what this is + // ? it is a quick and dirty hack to fix + // ? value not setting correctly + // ? I have no clue what's going on + ref.current.value = value; + + if (!autoFocus) return; + function keyDown(e: KeyboardEvent) { + if ((e.ctrlKey && e.key !== "v") || e.altKey || e.metaKey) return; + if (e.key.length !== 1) return; + if (ref && !inputSelected()) { + ref.current.focus(); + } + } + + document.body.addEventListener("keydown", keyDown); + return () => document.body.removeEventListener("keydown", keyDown); + }, [ref]); + + return <> + <TextArea + ref={ref} + value={value} + padding={padding} + style={{ height }} + {...textAreaProps} /> + {/*<Ghost><div ref={ghost}> + { props.value.split('\n') + .map(x => `\u0020${x}`) + .join('\n') } + </div></Ghost>*/} + </>; +} diff --git a/src/pages/settings/panes/Profile.tsx b/src/pages/settings/panes/Profile.tsx index aab0ae0b5efff39802b7d2c7ff7972ea089228b4..148f1b4782292f6576d1b4cb1b1aa6bb03b41e3e 100644 --- a/src/pages/settings/panes/Profile.tsx +++ b/src/pages/settings/panes/Profile.tsx @@ -1,8 +1,8 @@ import styles from "./Panes.module.scss"; import Button from "../../../components/ui/Button"; import { Users } from "revolt.js/dist/api/objects"; -import TextArea from "../../../components/ui/TextArea"; import { IntlContext, Text, translate } from "preact-i18n"; +import TextAreaAutoSize from "../../../lib/TextAreaAutoSize"; import { useContext, useEffect, useState } from "preact/hooks"; import { FileUploader } from "../../../context/revoltjs/FileUploads"; import { useForceUpdate, useSelf } from "../../../context/revoltjs/hooks"; @@ -93,9 +93,9 @@ export function Profile() { <h3> <Text id="app.settings.pages.profile.info" /> </h3> - <TextArea - // maxRows={10} - // minHeight={200} + <TextAreaAutoSize + maxRows={10} + minHeight={200} maxLength={2000} value={profile?.content ?? ""} disabled={typeof profile === "undefined"}