最近重構項目的設計裏大量用到須要多行輸入的組件(支持複製粘貼)。例如輸入須要排除的IP列表,須要排除的關鍵詞列表等。在原先的實現裏直接採用了textarea
,可是此次產品提出了要在每行前面自動帶上序號的要求。react
在一番思考以後,想到了如下三個方案:數組
textarea
,使用多個input
框來模擬。在每次按下enter鍵以後生成下一個input
框和序號。textarea
,在按下enter鍵以後改變用戶輸入的文本,加上序號。textarea
,序號所在列使用絕對定位,監聽文本變化同步序號列。簡單對比一下三個方案的優劣:bash
方案 | 優點 | 劣勢 |
---|---|---|
一 | 不用考慮滾動帶來的定位問題,獲取結果比較方便,多個input天然對應一個結果數組 | 按下enter以後的事件處理要考慮當前索引的位置,要處理刪除行的狀況, 複製黏貼要作額外處理 |
二 | 不用考慮滾動帶來的定位問題 | 獲取結果時要去除行號,要處理刪除行的狀況,複製黏貼要作額外處理 |
三 | 不須要額外處理複製粘貼,只須要監聽textarea的change事件 | 要考慮絕對定位致使的樣式和滾動帶來的定位問題 |
進過對比和嘗試,決定使用方案三,下面討論一下具體的實現antd
方案三隻須要經過監聽textarea
的onChange
事件,經過換行符切割出結果數組,再根據數組生成出序號列就好了。難點在於如何讓序號列的每一行看起來和textarea
的每一行處於同一行。ui
textarea
的每行文本高度僅能經過line-height
來設置,所以須要將絕對定位的序號列的行高和textarea
的行高設爲等值。spa
經過textarea
的padding-left
空出位置來容納序號所在列。設計
監聽textarea
的onScroll
事件給序號所在列動態設置top
值來同步滾動。code
提一下換行符在js裏的轉義字符爲\n
。orm
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