We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
记录使用 hooks 实现 mention 组件的历程 💻
measuring // 测量 measureLocation // 需要定位的位置 measureText // 用于搜索的本文 measurePrefix // 呼出 List 的符号 例如 '@' isESC // 使用 ESC 关闭 activeIndex // 用于键盘事件中上下移动搜索的列表
type Props = { setValue: (value: string) => void value: string trigger: string // '@' onSearch: (text: string) => void // 传递 MeasureText textareaRef?: React.RefObject<HTMLTextAreaElement> | null style?: React.CSSProperties className?: string onSelect?: (params: ItemType) => void MenuClassName?: string mountIn?: () => HTMLElement loading?: boolean } & Omit<HTMLTextareaProps, 'onSelect'>
MeasureList 在这仅代表呼出的搜索结果列表,很多人可能首先想到的是应该要确定 measureList 出现的位置,但其实我们只需要在 measureList 要出现的时候再计算其位置就可以,所以首先必须要确定 measureList 何时出现
规则:
当光标左侧有 measurePrefix 存在并且之间没有空格时, 也就是 measureText 不含有空格
isESC 为 false 时「下文详细解释」
// 获取光标左侧的文本 textareaValue.slice(0, textAreaRef.current!.selectionStart) // 获取距离光标最近的 MeasurePrefix 的位置, 也就是需要定位的位置 const lastMeasurePrefixLocation = selectionStartText.lastIndexOf(measurePrefix) // 如果 lastMeasurePrefixLocation 不等于 -1 的情况下 // 此时 mesurePrefix 位置与光标位置之间的文本即为 measurePrefix const measureText = selectionStartText.slice(lastMeasurePrefixLocation + measurePrefix.length)
当满足以上规则时 measuring 为 true
当 measureList 出现时,需要用 measurePrefix 此时的位置来对 measureList 进行定位
在 textarea 中做定位相对困难,因此我们采用 div 来模拟 textarea 中的文本情况去实现 measurePrefix 的定位
首先需要将 textarea 中影响文本样式的 style 应用到 div 中去
useEffect(() => { if (textAreaRef.current) { const textareaCssProperties = getComputedStyle(textAreaRef.current!) const style = { lineHeight: textareaCssProperties.lineHeight!, fontSize: textareaCssProperties.fontSize!, } setTextAreaStyle(style) } }, [textAreaRef.current && textAreaRef.current.style, props.style])
模拟的 div 如下
const measureContent = ` <div style={textAreaStyle} className=${classnames(props.className, Styles['measure-content'])}> <span dangerouslySetInnerHTML={{ __html: escape(value.slice(0, measureLocation)) }} /> <span ref={measurePrefixRef}>@</span> <span dangerouslySetInnerHTML={{ __html: escape(value.slice( measureLocation)) }} /> </div>`
// escape 函数主要是用来处理回车造成的文本换行,如果不转换成 <br> 的话,在 div 中文本会显示成一行,定位就会有错误。 const escape = function (text: string) { return text.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, '<br>') }
模拟 div 的样式需要额外添加一些属性来使 div 完全模拟 textarea 的行为,当然 text-area-autosize 的行为也支持
.measure-content position absolute top 0 right 0 bottom 0 left 0 z-index -1 overflow scroll white-space pre-wrap word-break break-all opacity 0
注意其共同的父 div 需要添加 position: relative
position: relative
当 measureText 中包含空格时
当选中 MeasureList 中的某一个 item 时「点击或 Enter」
当按下 ESC 键时
当光标的左侧无 measurePrefix 时
按下 ESC 键时,此时 measureText 中不会包含空格,因此依然满足 measuring 为 true 的情况,所以需要对 ESC 进行特殊处理:当 measuring 为 true 时按下 ESC 键时我会将 isESC 置为 true,而当 isESC 为 true 时 measuring 则至为 false。isESC 会在下次普通的 keyUp 事件中重新置为 false
原文链接
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Mention
State
Props
MeasureList 何时出现
规则:
当光标左侧有 measurePrefix 存在并且之间没有空格时, 也就是 measureText 不含有空格
isESC 为 false 时「下文详细解释」
获取 MeasureText
当满足以上规则时 measuring 为 true
MeasurePrefix 的定位
在 textarea 中做定位相对困难,因此我们采用 div 来模拟 textarea 中的文本情况去实现 measurePrefix 的定位
首先需要将 textarea 中影响文本样式的 style 应用到 div 中去
模拟的 div 如下
模拟 div 的样式需要额外添加一些属性来使 div 完全模拟 textarea 的行为,当然 text-area-autosize 的行为也支持
注意其共同的父 div 需要添加
position: relative
MeasureList 何时隐藏
当 measureText 中包含空格时
当选中 MeasureList 中的某一个 item 时「点击或 Enter」
当按下 ESC 键时
当光标的左侧无 measurePrefix 时
注意
按下 ESC 键时,此时 measureText 中不会包含空格,因此依然满足 measuring 为 true 的情况,所以需要对 ESC 进行特殊处理:当 measuring 为 true 时按下 ESC 键时我会将 isESC 置为 true,而当 isESC 为 true 时 measuring 则至为 false。isESC 会在下次普通的 keyUp 事件中重新置为 false
原文链接
The text was updated successfully, but these errors were encountered: