mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-10 01:03:36 -05:00
Fix: Textarea AutoSize did not resize correctly with long lines.
This commit is contained in:
parent
56b509a16a
commit
b69ba4ca28
7 changed files with 83 additions and 40 deletions
|
@ -151,7 +151,7 @@ export const MessageContent = styled.div`
|
|||
flex-grow: 1;
|
||||
display: flex;
|
||||
// overflow: hidden;
|
||||
font-size: 0.875rem;
|
||||
font-size: var(--text-size);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
|
|
@ -65,7 +65,7 @@ const Base = styled.div`
|
|||
background: var(--message-box);
|
||||
|
||||
textarea {
|
||||
font-size: 0.875rem;
|
||||
font-size: var(--text-size);
|
||||
background: transparent;
|
||||
}
|
||||
`;
|
||||
|
@ -75,7 +75,7 @@ const Blocked = styled.div`
|
|||
align-items: center;
|
||||
padding: 14px 0;
|
||||
user-select: none;
|
||||
font-size: 0.875rem;
|
||||
font-size: var(--text-size);
|
||||
color: var(--tertiary-foreground);
|
||||
|
||||
svg {
|
||||
|
@ -423,10 +423,10 @@ function MessageBox({ channel, draft }: Props) {
|
|||
autoFocus
|
||||
hideBorder
|
||||
maxRows={20}
|
||||
padding={12}
|
||||
id="message"
|
||||
value={draft ?? ""}
|
||||
onKeyUp={onKeyUp}
|
||||
value={draft ?? ""}
|
||||
padding="var(--message-box-padding)"
|
||||
onKeyDown={(e) => {
|
||||
if (onKeyDown(e)) return;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ export default styled.select`
|
|||
font-family: inherit;
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
font-size: 0.875rem;
|
||||
font-size: var(--text-size);
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: outline-color 0.2s ease-in-out;
|
||||
|
|
|
@ -2,8 +2,8 @@ import styled, { css } from "styled-components";
|
|||
|
||||
export interface TextAreaProps {
|
||||
code?: boolean;
|
||||
padding?: number;
|
||||
lineHeight?: number;
|
||||
padding?: string;
|
||||
lineHeight?: string;
|
||||
hideBorder?: boolean;
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,8 @@ export default styled.textarea<TextAreaProps>`
|
|||
display: block;
|
||||
color: var(--foreground);
|
||||
background: var(--secondary-background);
|
||||
padding: ${(props) => props.padding ?? DEFAULT_TEXT_AREA_PADDING}px;
|
||||
line-height: ${(props) => props.lineHeight ?? DEFAULT_LINE_HEIGHT}px;
|
||||
padding: ${(props) => (props.padding) ?? 'var(--textarea-padding)'};
|
||||
line-height: ${(props) => (props.lineHeight) ?? 'var(--textarea-line-height)'};
|
||||
|
||||
${(props) =>
|
||||
props.hideBorder &&
|
||||
|
@ -31,7 +31,7 @@ export default styled.textarea<TextAreaProps>`
|
|||
css`
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.2s ease-in-out;
|
||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
|
||||
border: var(--input-border-width) solid transparent;
|
||||
`}
|
||||
|
||||
&:focus {
|
||||
|
@ -40,7 +40,7 @@ export default styled.textarea<TextAreaProps>`
|
|||
${(props) =>
|
||||
!props.hideBorder &&
|
||||
css`
|
||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid var(--accent);
|
||||
border: var(--input-border-width) solid var(--accent);
|
||||
`}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { useEffect, useRef } from "preact/hooks";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useEffect, useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import TextArea, {
|
||||
DEFAULT_LINE_HEIGHT,
|
||||
|
@ -12,7 +14,7 @@ import { isTouchscreenDevice } from "./isTouchscreenDevice";
|
|||
|
||||
type TextAreaAutoSizeProps = Omit<
|
||||
JSX.HTMLAttributes<HTMLTextAreaElement>,
|
||||
"style" | "value"
|
||||
"style" | "value" | "onChange"
|
||||
> &
|
||||
TextAreaProps & {
|
||||
forceFocus?: boolean;
|
||||
|
@ -22,8 +24,37 @@ type TextAreaAutoSizeProps = Omit<
|
|||
value: string;
|
||||
|
||||
id?: string;
|
||||
|
||||
onChange?: (ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) => void;
|
||||
};
|
||||
|
||||
const Container = styled.div`
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const Ghost = styled.div<{ lineHeight: string, maxRows: number }>`
|
||||
flex: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
|
||||
top: 0;
|
||||
position: absolute;
|
||||
font-size: var(--text-size);
|
||||
line-height: ${(props) => props.lineHeight};
|
||||
|
||||
max-height: calc(calc( ${(props) => props.lineHeight} * ${ (props) => props.maxRows } ));
|
||||
}
|
||||
`;
|
||||
|
||||
export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
|
||||
const {
|
||||
autoFocus,
|
||||
|
@ -39,19 +70,13 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
|
|||
onChange,
|
||||
...textAreaProps
|
||||
} = props;
|
||||
const line = lineHeight ?? DEFAULT_LINE_HEIGHT;
|
||||
|
||||
const heightPadding =
|
||||
((padding ?? DEFAULT_TEXT_AREA_PADDING) +
|
||||
(hideBorder ? 0 : TEXT_AREA_BORDER_WIDTH)) *
|
||||
2;
|
||||
const height = Math.max(
|
||||
Math.min(value.split("\n").length, maxRows ?? Infinity) * line +
|
||||
heightPadding,
|
||||
minHeight ?? 0,
|
||||
);
|
||||
|
||||
const ref = useRef<HTMLTextAreaElement>();
|
||||
const ghost = useRef<HTMLDivElement>();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
ref.current.style.height = ghost.current.clientHeight + 'px';
|
||||
}, [ghost, props.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTouchscreenDevice) return;
|
||||
|
@ -101,18 +126,29 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
|
|||
}, [ref]);
|
||||
|
||||
return (
|
||||
<TextArea
|
||||
ref={ref}
|
||||
value={value}
|
||||
padding={padding}
|
||||
style={{ height }}
|
||||
hideBorder={hideBorder}
|
||||
lineHeight={lineHeight}
|
||||
|
||||
onChange={ev => {
|
||||
onChange && onChange(ev);
|
||||
}}
|
||||
{...textAreaProps}
|
||||
/>
|
||||
<Container>
|
||||
<TextArea
|
||||
ref={ref}
|
||||
value={value}
|
||||
padding={padding}
|
||||
style={{ height: minHeight }}
|
||||
hideBorder={hideBorder}
|
||||
lineHeight={lineHeight}
|
||||
onChange={(ev) => {
|
||||
onChange && onChange(ev);
|
||||
}}
|
||||
{...textAreaProps}
|
||||
/>
|
||||
<Ghost lineHeight={lineHeight ?? 'var(--textarea-line-height)'} maxRows={maxRows ?? 5}>
|
||||
<div ref={ghost} style={{ padding }}>
|
||||
{props.value
|
||||
? props.value
|
||||
.split("\n")
|
||||
.map((x) => `\u200e${x}`)
|
||||
.join("\n")
|
||||
: undefined ?? "\n"}
|
||||
</div>
|
||||
</Ghost>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ const EditorBase = styled.div`
|
|||
textarea {
|
||||
resize: none;
|
||||
padding: 12px;
|
||||
font-size: 0.875rem;
|
||||
border-radius: 3px;
|
||||
white-space: pre-wrap;
|
||||
font-size: var(--text-size);
|
||||
background: var(--secondary-header);
|
||||
}
|
||||
|
||||
|
@ -101,9 +101,9 @@ export default function MessageEditor({ message, finish }: Props) {
|
|||
<TextAreaAutoSize
|
||||
forceFocus
|
||||
maxRows={3}
|
||||
padding={12}
|
||||
value={content}
|
||||
maxLength={2000}
|
||||
padding="var(--message-box-padding)"
|
||||
onChange={(ev) => {
|
||||
onChange(ev);
|
||||
setContent(ev.currentTarget.value);
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
:root {
|
||||
--ligatures: none;
|
||||
--text-size: 14px;
|
||||
--font: "Open Sans";
|
||||
--app-height: 100vh;
|
||||
--codeblock-font: "Fira Code";
|
||||
--sidebar-active: var(--secondary-background);
|
||||
|
||||
--input-border-width: 2px;
|
||||
|
||||
--textarea-padding: 16px;
|
||||
--textarea-line-height: 20px;
|
||||
--message-box-padding: 12px;
|
||||
|
||||
--bottom-navigation-height: 50px;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue