Skip to content
Snippets Groups Projects
TextArea.tsx 5.12 KiB
Newer Older
insert's avatar
insert committed
// import classNames from "classnames";
// import { memo } from "preact/compat";
// import styles from "./TextArea.module.scss";
// import { useState, useEffect, useRef, useLayoutEffect } from "preact/hooks";
import styled, { css } from "styled-components";

insert's avatar
insert committed
export interface TextAreaProps {
insert's avatar
insert committed
    code?: boolean;
insert's avatar
insert committed
    padding?: number;
insert's avatar
insert committed
export default styled.textarea<TextAreaProps>`
insert's avatar
insert committed
    width: 100%;
    resize: none;
    display: block;
    border-radius: 4px;
insert's avatar
insert committed
    padding: ${ props => props.padding ?? 16 }px;
insert's avatar
insert committed

    color: var(--foreground);
    border: 2px solid transparent;
    background: var(--secondary-background);
    transition: border-color .2s ease-in-out;

    &:focus {
        outline: none;
        border: 2px solid var(--accent);
    }
insert's avatar
insert committed

insert's avatar
insert committed
    ${ props => props.code ? css`
        font-family: 'Fira Mono', 'Courier New', Courier, monospace;
    ` : css`
        font-family: 'Open Sans', sans-serif;
    ` }
`;
insert's avatar
insert committed

insert's avatar
insert committed
/*export interface TextAreaProps {
insert's avatar
insert committed
    id?: string;
    value: string;
    maxRows?: number;
    padding?: number;
    minHeight?: number;
    disabled?: boolean;
    maxLength?: number;
    className?: string;
    autoFocus?: boolean;
    forceFocus?: boolean;
    placeholder?: string;
    onKeyDown?: (ev: KeyboardEvent) => void;
    onKeyUp?: (ev: KeyboardEvent) => void;
    onChange: (
        value: string,
        ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>
    ) => void;
    onFocus?: (current: HTMLTextAreaElement) => void;
    onBlur?: () => void;
}

const lineHeight = 20;

insert's avatar
insert committed
export const TextAreaB = memo((props: TextAreaProps) => {
insert's avatar
insert committed
    const padding = props.padding ? props.padding * 2 : 0;

    const [height, setHeightState] = useState(
        props.minHeight ?? lineHeight + padding
    );
    const ghost = useRef<HTMLDivElement>();
    const ref = useRef<HTMLTextAreaElement>();

    function setHeight(h: number = lineHeight) {
        let newHeight = Math.min(
            Math.max(
                lineHeight,
                props.maxRows ? Math.min(h, props.maxRows * lineHeight) : h
            ),
            props.minHeight ?? Infinity
        );

        if (props.padding) newHeight += padding;
        if (height !== newHeight) {
            setHeightState(newHeight);
        }
    }

    function onChange(ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) {
        props.onChange(ev.currentTarget.value, ev);
    }

    useLayoutEffect(() => {
        setHeight(ghost.current.clientHeight);
    }, [ghost, props.value]);

    useEffect(() => {
        if (props.autoFocus) ref.current.focus();
    }, [props.value]);

    const inputSelected = () =>
        ["TEXTAREA", "INPUT"].includes(document.activeElement?.nodeName ?? "");

    useEffect(() => {
        if (props.forceFocus) {
            ref.current.focus();
        }

        if (props.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 = props.value;

        if (!props.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]);

    useEffect(() => {
        function focus(textarea_id: string) {
            if (props.id === textarea_id) {
                ref.current.focus();
            }
        }

        // InternalEventEmitter.addListener("focus_textarea", focus);
        // return () =>
            // InternalEventEmitter.removeListener("focus_textarea", focus);
    }, [ref]);

    return (
        <div className={classNames(styles.container, props.className)}>
            <textarea
                id={props.id}
                name={props.id}
                style={{ height }}
                value={props.value}
                onChange={onChange}
                disabled={props.disabled}
                maxLength={props.maxLength}
                className={styles.textarea}
                onKeyDown={props.onKeyDown}
                placeholder={props.placeholder}
                onContextMenu={e => e.stopPropagation()}
                onKeyUp={ev => {
                    setHeight(ghost.current.clientHeight);
                    props.onKeyUp && props.onKeyUp(ev);
                }}
                ref={ref}
                onFocus={() => props.onFocus && props.onFocus(ref.current)}
                onBlur={props.onBlur}
            />
            <div className={styles.hide}>
                <div className={styles.ghost} ref={ghost}>
                    {props.value
                        ? props.value
                              .split("\n")
                              .map(x => `${x}`)
                              .join("\n")
                        : undefined ?? "\n"}
                </div>
            </div>
        </div>
    );
insert's avatar
insert committed
});*/