fix: @提及弹窗跟随光标位置
parent
0327034874
commit
8badd8f6db
|
|
@ -52,6 +52,50 @@ export default function ChatInput(props: {
|
||||||
const [mentionPos, setMentionPos] = useState<{ top: number; left: number } | null>(null);
|
const [mentionPos, setMentionPos] = useState<{ top: number; left: number } | null>(null);
|
||||||
const inputRef = useRef<TextAreaRef>(null);
|
const inputRef = useRef<TextAreaRef>(null);
|
||||||
|
|
||||||
|
const getCaretPos = (textarea: HTMLTextAreaElement, caretIndex: number) => {
|
||||||
|
const rect = textarea.getBoundingClientRect();
|
||||||
|
const cs = window.getComputedStyle(textarea);
|
||||||
|
const div = document.createElement('div');
|
||||||
|
|
||||||
|
div.style.position = 'absolute';
|
||||||
|
div.style.visibility = 'hidden';
|
||||||
|
div.style.top = '0';
|
||||||
|
div.style.left = '-9999px';
|
||||||
|
div.style.whiteSpace = 'pre-wrap';
|
||||||
|
div.style.wordWrap = 'break-word';
|
||||||
|
div.style.overflow = 'hidden';
|
||||||
|
div.style.boxSizing = cs.boxSizing;
|
||||||
|
div.style.width = rect.width + 'px';
|
||||||
|
div.style.fontFamily = cs.fontFamily;
|
||||||
|
div.style.fontSize = cs.fontSize;
|
||||||
|
div.style.fontWeight = cs.fontWeight;
|
||||||
|
div.style.fontStyle = cs.fontStyle;
|
||||||
|
div.style.letterSpacing = cs.letterSpacing;
|
||||||
|
div.style.textTransform = cs.textTransform;
|
||||||
|
div.style.lineHeight = cs.lineHeight;
|
||||||
|
div.style.padding = cs.padding;
|
||||||
|
div.style.border = cs.border;
|
||||||
|
div.style.tabSize = (cs as any).tabSize || '8';
|
||||||
|
|
||||||
|
const before = textarea.value.slice(0, caretIndex).replace(/ /g, '\u00a0');
|
||||||
|
div.textContent = before;
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = '\u200b';
|
||||||
|
div.appendChild(span);
|
||||||
|
|
||||||
|
document.body.appendChild(div);
|
||||||
|
const divRect = div.getBoundingClientRect();
|
||||||
|
const spanRect = span.getBoundingClientRect();
|
||||||
|
document.body.removeChild(div);
|
||||||
|
|
||||||
|
const lineHeight = Number.parseFloat(cs.lineHeight) || Number.parseFloat(cs.fontSize) * 1.2;
|
||||||
|
return {
|
||||||
|
top: rect.top + (spanRect.top - divRect.top) - textarea.scrollTop,
|
||||||
|
left: rect.left + (spanRect.left - divRect.left) - textarea.scrollLeft,
|
||||||
|
lineHeight
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 检测 @ 触发提及选择
|
// 检测 @ 触发提及选择
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
|
|
@ -82,10 +126,11 @@ export default function ChatInput(props: {
|
||||||
console.log('[@mention] cannot get textarea DOM from inputRef:', inputRef.current);
|
console.log('[@mention] cannot get textarea DOM from inputRef:', inputRef.current);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rect = textarea.getBoundingClientRect();
|
const caret = getCaretPos(textarea, cursorPos);
|
||||||
console.log('[@mention] textarea rect:', rect);
|
const top = Math.min(window.innerHeight - 8, caret.top + caret.lineHeight + 8);
|
||||||
setMentionPos({ top: rect.bottom, left: rect.left + 10 });
|
const left = Math.min(window.innerWidth - 160, Math.max(8, caret.left));
|
||||||
console.log('[@mention] show popover:', { top: rect.bottom, left: rect.left + 10, query });
|
setMentionPos({ top, left });
|
||||||
|
console.log('[@mention] show popover:', { top, left, query });
|
||||||
setShowMentionPopover(true);
|
setShowMentionPopover(true);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue