實現一個內容自帶行號的textarea

需求背景

最近重構項目的設計裏大量用到須要多行輸入的組件(支持複製粘貼)。例如輸入須要排除的IP列表,須要排除的關鍵詞列表等。在原先的實現裏直接採用了textarea,可是此次產品提出了要在每行前面自動帶上序號的要求。react

效果圖

實現方案

在一番思考以後,想到了如下三個方案:數組

    1. 不用textarea,使用多個input框來模擬。在每次按下enter鍵以後生成下一個input框和序號。
    1. 使用textarea,在按下enter鍵以後改變用戶輸入的文本,加上序號。
    1. 使用textarea,序號所在列使用絕對定位,監聽文本變化同步序號列。

簡單對比一下三個方案的優劣:bash

方案 優點 劣勢
不用考慮滾動帶來的定位問題,獲取結果比較方便,多個input天然對應一個結果數組 按下enter以後的事件處理要考慮當前索引的位置,要處理刪除行的狀況, 複製黏貼要作額外處理
不用考慮滾動帶來的定位問題 獲取結果時要去除行號,要處理刪除行的狀況,複製黏貼要作額外處理
不須要額外處理複製粘貼,只須要監聽textarea的change事件 要考慮絕對定位致使的樣式和滾動帶來的定位問題

進過對比和嘗試,決定使用方案三,下面討論一下具體的實現antd

具體實現

方案三隻須要經過監聽textareaonChange事件,經過換行符切割出結果數組,再根據數組生成出序號列就好了。難點在於如何讓序號列的每一行看起來和textarea的每一行處於同一行ui

textarea的每行文本高度僅能經過line-height來設置,所以須要將絕對定位的序號列的行高和textarea的行高設爲等值。spa

經過textareapadding-left空出位置來容納序號所在列。設計

監聽textareaonScroll事件給序號所在列動態設置top值來同步滾動。code

提一下換行符在js裏的轉義字符爲\norm

代碼

import React, { useState, FunctionComponent } from "react";
import { Input } from "antd"
import { throttle } from "lodash"

const { TextArea } = Input

export interface IAreaFormProps {
    defaultList?: string[];
    onChange?: (list: string[]) => void;
}

const AreaForm: FunctionComponent<IAreaFormProps> = (props) => {
    const { defaultList = [], onChange } = props;

    const [list, setList] = useState(defaultList);

    const [areaValue, setAreaValue] = useState(defaultList.join("\n"))

    const [top, setTop] = useState(0)

    const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
        const value = event.currentTarget.value
        const list = value ? value.split("\n") : []
        onChange && onChange(list)
        setList(list)
        setAreaValue(value)
    }


    const handleScroll = (event: React.UIEvent<HTMLTextAreaElement>) => {
        setTop(-event.currentTarget.scrollTop)
    }


    return (
        <div className="area-form">
            <div className="area-form__content">
                <ul className="area-form__index" style={{ top: top + 11 }}>
                    {
                        list.map((_, index) => (
                            <li key={index}>
                                <span>{index + 1}、</span>
                            </li>
                        ))
                    }
                </ul>
                <TextArea autoFocus onScroll={throttle(handleScroll)} value={areaValue} onChange={e => handleChange(e)} autoComplete="off"></TextArea>
            </div>
        </div>
    )

}

export default AreaForm;
複製代碼

若是你們有更好的解決方案,歡迎評論區告知。cdn

相關文章
相關標籤/搜索