mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-25 16:40:58 -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;
|
flex-grow: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
// overflow: hidden;
|
// overflow: hidden;
|
||||||
font-size: 0.875rem;
|
font-size: var(--text-size);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -65,7 +65,7 @@ const Base = styled.div`
|
||||||
background: var(--message-box);
|
background: var(--message-box);
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
font-size: 0.875rem;
|
font-size: var(--text-size);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -75,7 +75,7 @@ const Blocked = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 14px 0;
|
padding: 14px 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-size: 0.875rem;
|
font-size: var(--text-size);
|
||||||
color: var(--tertiary-foreground);
|
color: var(--tertiary-foreground);
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
|
@ -423,10 +423,10 @@ function MessageBox({ channel, draft }: Props) {
|
||||||
autoFocus
|
autoFocus
|
||||||
hideBorder
|
hideBorder
|
||||||
maxRows={20}
|
maxRows={20}
|
||||||
padding={12}
|
|
||||||
id="message"
|
id="message"
|
||||||
value={draft ?? ""}
|
|
||||||
onKeyUp={onKeyUp}
|
onKeyUp={onKeyUp}
|
||||||
|
value={draft ?? ""}
|
||||||
|
padding="var(--message-box-padding)"
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (onKeyDown(e)) return;
|
if (onKeyDown(e)) return;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ export default styled.select`
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: var(--secondary-foreground);
|
color: var(--secondary-foreground);
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
font-size: 0.875rem;
|
font-size: var(--text-size);
|
||||||
border: none;
|
border: none;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
transition: outline-color 0.2s ease-in-out;
|
transition: outline-color 0.2s ease-in-out;
|
||||||
|
|
|
@ -2,8 +2,8 @@ import styled, { css } from "styled-components";
|
||||||
|
|
||||||
export interface TextAreaProps {
|
export interface TextAreaProps {
|
||||||
code?: boolean;
|
code?: boolean;
|
||||||
padding?: number;
|
padding?: string;
|
||||||
lineHeight?: number;
|
lineHeight?: string;
|
||||||
hideBorder?: boolean;
|
hideBorder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ export default styled.textarea<TextAreaProps>`
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
padding: ${(props) => props.padding ?? DEFAULT_TEXT_AREA_PADDING}px;
|
padding: ${(props) => (props.padding) ?? 'var(--textarea-padding)'};
|
||||||
line-height: ${(props) => props.lineHeight ?? DEFAULT_LINE_HEIGHT}px;
|
line-height: ${(props) => (props.lineHeight) ?? 'var(--textarea-line-height)'};
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.hideBorder &&
|
props.hideBorder &&
|
||||||
|
@ -31,7 +31,7 @@ export default styled.textarea<TextAreaProps>`
|
||||||
css`
|
css`
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: border-color 0.2s ease-in-out;
|
transition: border-color 0.2s ease-in-out;
|
||||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
|
border: var(--input-border-width) solid transparent;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
@ -40,7 +40,7 @@ export default styled.textarea<TextAreaProps>`
|
||||||
${(props) =>
|
${(props) =>
|
||||||
!props.hideBorder &&
|
!props.hideBorder &&
|
||||||
css`
|
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, {
|
import TextArea, {
|
||||||
DEFAULT_LINE_HEIGHT,
|
DEFAULT_LINE_HEIGHT,
|
||||||
|
@ -12,7 +14,7 @@ import { isTouchscreenDevice } from "./isTouchscreenDevice";
|
||||||
|
|
||||||
type TextAreaAutoSizeProps = Omit<
|
type TextAreaAutoSizeProps = Omit<
|
||||||
JSX.HTMLAttributes<HTMLTextAreaElement>,
|
JSX.HTMLAttributes<HTMLTextAreaElement>,
|
||||||
"style" | "value"
|
"style" | "value" | "onChange"
|
||||||
> &
|
> &
|
||||||
TextAreaProps & {
|
TextAreaProps & {
|
||||||
forceFocus?: boolean;
|
forceFocus?: boolean;
|
||||||
|
@ -22,8 +24,37 @@ type TextAreaAutoSizeProps = Omit<
|
||||||
value: string;
|
value: string;
|
||||||
|
|
||||||
id?: 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) {
|
export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
|
||||||
const {
|
const {
|
||||||
autoFocus,
|
autoFocus,
|
||||||
|
@ -39,19 +70,13 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
|
||||||
onChange,
|
onChange,
|
||||||
...textAreaProps
|
...textAreaProps
|
||||||
} = props;
|
} = 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 ref = useRef<HTMLTextAreaElement>();
|
||||||
|
const ghost = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
ref.current.style.height = ghost.current.clientHeight + 'px';
|
||||||
|
}, [ghost, props.value]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isTouchscreenDevice) return;
|
if (isTouchscreenDevice) return;
|
||||||
|
@ -101,18 +126,29 @@ export default function TextAreaAutoSize(props: TextAreaAutoSizeProps) {
|
||||||
}, [ref]);
|
}, [ref]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextArea
|
<Container>
|
||||||
ref={ref}
|
<TextArea
|
||||||
value={value}
|
ref={ref}
|
||||||
padding={padding}
|
value={value}
|
||||||
style={{ height }}
|
padding={padding}
|
||||||
hideBorder={hideBorder}
|
style={{ height: minHeight }}
|
||||||
lineHeight={lineHeight}
|
hideBorder={hideBorder}
|
||||||
|
lineHeight={lineHeight}
|
||||||
onChange={ev => {
|
onChange={(ev) => {
|
||||||
onChange && onChange(ev);
|
onChange && onChange(ev);
|
||||||
}}
|
}}
|
||||||
{...textAreaProps}
|
{...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 {
|
textarea {
|
||||||
resize: none;
|
resize: none;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
font-size: 0.875rem;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
font-size: var(--text-size);
|
||||||
background: var(--secondary-header);
|
background: var(--secondary-header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,9 +101,9 @@ export default function MessageEditor({ message, finish }: Props) {
|
||||||
<TextAreaAutoSize
|
<TextAreaAutoSize
|
||||||
forceFocus
|
forceFocus
|
||||||
maxRows={3}
|
maxRows={3}
|
||||||
padding={12}
|
|
||||||
value={content}
|
value={content}
|
||||||
maxLength={2000}
|
maxLength={2000}
|
||||||
|
padding="var(--message-box-padding)"
|
||||||
onChange={(ev) => {
|
onChange={(ev) => {
|
||||||
onChange(ev);
|
onChange(ev);
|
||||||
setContent(ev.currentTarget.value);
|
setContent(ev.currentTarget.value);
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
:root {
|
:root {
|
||||||
--ligatures: none;
|
--ligatures: none;
|
||||||
|
--text-size: 14px;
|
||||||
--font: "Open Sans";
|
--font: "Open Sans";
|
||||||
--app-height: 100vh;
|
--app-height: 100vh;
|
||||||
--codeblock-font: "Fira Code";
|
--codeblock-font: "Fira Code";
|
||||||
--sidebar-active: var(--secondary-background);
|
--sidebar-active: var(--secondary-background);
|
||||||
|
|
||||||
|
--input-border-width: 2px;
|
||||||
|
|
||||||
|
--textarea-padding: 16px;
|
||||||
|
--textarea-line-height: 20px;
|
||||||
|
--message-box-padding: 12px;
|
||||||
|
|
||||||
--bottom-navigation-height: 50px;
|
--bottom-navigation-height: 50px;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue