最终效果就是实现该输入框:
- 添加话题时,话题自动插入到输入框前面
- 多文本输入框左侧间距为话题的宽度
- 多行文本时,第二行紧接开头渲染
- 删除文本时,如果删除到话题,再次删除,话题被删除
- 首先构造div结构
const [hashtag, setHashtag] = useState(""); // 话题内容
const [textIndent, setTextIndent] = useState("0px"); // 动态缩进
const hashtagRef = useRef(null);
const [value, setValue] = useState("");
const [focus, setFocus] = useState(false);
<div className="topInput">
<div className="topiceShow" ref={hashtagRef}>
{hashtag}
</div>
<TextArea
value={value}
onChange={(e) => setValue(e.target.value)}
onInput={TextAreaInput}
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
onKeyDown={handleKeyDown}
autoSize={{ minRows: 3, maxRows: 5 }}
maxLength={1000}
style={{
flex: 1,
border: "none",
textIndent,
backgroundColor: focus ? "#ffffff" : "#f2f3f5",
transition: "none",
}}
/>
</div>
2.重点是在添加话题时,获取话题的宽度和你本身需要添加的间距
const selectTopice = (item: any) => {
setHashtag(`#${item.ActivityTitle}#`);
setValue(` ${value}`);
};
3.实时监听话题变化,给其宽度赋值
// 动态计算话题宽度
useEffect(() => {
if (hashtagRef.current) {
const hashtagWidth = hashtagRef.current.offsetWidth;
const extraPadding = 5; // 话题后的额外空隙
setTextIndent(`${hashtagWidth + extraPadding}px`);
}
}, [hashtag]);
4.现在宽度有了,如何使其两个div并行,且第二行紧接开始呢?
这就要用到css 的样式 text-index了
text-index是 CSS 中的一个属性,用于控制文本的首行缩进。它通常用于段落、列表、文本框等元素中,指定文本的第一行相对于其容器的缩进距离。这个属性的值可以是像素(px)、百分比(%)等单位。
5.所以我们来编写样式结构
.topInput {
position: relative;
.topiceShow {
font-size: 14px;
position: absolute;
left: 10px;
top: 5px;
z-index: 8888;
display: inline-block; /* 让内容宽度适应话题文本 */
}
}
6.话题设置为 position: absolute;然后设置TextArea的 textIndent为计算值,即可实现这样样式格式
7.但是如何实现删除呢,这就要用到onKeyDown事件了
const handleKeyDown = (e: any) => {
// 检测按键是否是 Backspace 并文本为空
const cursorPosition = e.target.selectionStart;
if (
(e.code === "Backspace" || e.code === "Delete") &&
cursorPosition === 0
) {
setHashtag(""); // 清空话题
}
};
8.至此,功能全部实现