From d965b20ee26cad80274f08aac287bdccfb38951c Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 21 Jun 2021 14:20:29 +0100 Subject: [PATCH] TextAreaAutoSize test --- src/components/ui/TextArea.tsx | 6 +- src/lib/TextAreaAutoSize.tsx | 105 +++++++++++++++++++++++++++ src/pages/settings/panes/Profile.tsx | 8 +- 3 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 src/lib/TextAreaAutoSize.tsx diff --git a/src/components/ui/TextArea.tsx b/src/components/ui/TextArea.tsx index ed15695f..cb4bc81a 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` +export default styled.textarea` 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 00000000..2b1fd73f --- /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, '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(); + + /*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 <> +