1
This commit is contained in:
parent
5851c98537
commit
3140456ee0
@ -144,6 +144,20 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
|||||||
highlightElement.style.overflowWrap = inputStyles.overflowWrap;
|
highlightElement.style.overflowWrap = inputStyles.overflowWrap;
|
||||||
highlightElement.style.verticalAlign = inputStyles.verticalAlign;
|
highlightElement.style.verticalAlign = inputStyles.verticalAlign;
|
||||||
highlightElement.style.boxSizing = inputStyles.boxSizing;
|
highlightElement.style.boxSizing = inputStyles.boxSizing;
|
||||||
|
|
||||||
|
// 关键:同步文本对齐属性,确保光标位置准确
|
||||||
|
highlightElement.style.textAlign = inputStyles.textAlign;
|
||||||
|
highlightElement.style.direction = inputStyles.direction;
|
||||||
|
highlightElement.style.textIndent = inputStyles.textIndent;
|
||||||
|
|
||||||
|
// 调试信息(仅开发环境)
|
||||||
|
if (process.env.NODE_ENV === 'development' && variant === 'textarea') {
|
||||||
|
console.log('[VariableInput] 样式同步:', {
|
||||||
|
fontSize: inputStyles.fontSize,
|
||||||
|
lineHeight: inputStyles.lineHeight,
|
||||||
|
padding: `${inputStyles.paddingTop} ${inputStyles.paddingRight} ${inputStyles.paddingBottom} ${inputStyles.paddingLeft}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 同步滚动位置(仅 textarea)
|
// 同步滚动位置(仅 textarea)
|
||||||
@ -154,14 +168,28 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
|||||||
highlightElement.scrollLeft = inputElement.scrollLeft;
|
highlightElement.scrollLeft = inputElement.scrollLeft;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始同步(延迟执行,确保 DOM 完全渲染)
|
// 初始同步(多次延迟确保 DOM 完全渲染,包括 Modal/Sheet 的样式应用)
|
||||||
syncStyles();
|
syncStyles();
|
||||||
setTimeout(syncStyles, 0);
|
setTimeout(syncStyles, 0);
|
||||||
|
setTimeout(syncStyles, 10);
|
||||||
|
setTimeout(syncStyles, 50);
|
||||||
|
setTimeout(syncStyles, 100);
|
||||||
|
|
||||||
// 监听窗口大小变化(可能影响字体大小)
|
// 监听窗口大小变化(可能影响字体大小)
|
||||||
const resizeObserver = new ResizeObserver(syncStyles);
|
const resizeObserver = new ResizeObserver(syncStyles);
|
||||||
resizeObserver.observe(inputElement);
|
resizeObserver.observe(inputElement);
|
||||||
|
|
||||||
|
// 监听父元素属性变化(例如 Modal/Sheet 的显示状态)
|
||||||
|
const mutationObserver = new MutationObserver(syncStyles);
|
||||||
|
let parent = inputElement.parentElement;
|
||||||
|
while (parent && parent !== document.body) {
|
||||||
|
mutationObserver.observe(parent, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['class', 'style'],
|
||||||
|
});
|
||||||
|
parent = parent.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
// 监听滚动事件(仅 textarea)
|
// 监听滚动事件(仅 textarea)
|
||||||
if (variant === 'textarea') {
|
if (variant === 'textarea') {
|
||||||
inputElement.addEventListener('scroll', syncScroll);
|
inputElement.addEventListener('scroll', syncScroll);
|
||||||
@ -169,6 +197,7 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
|
mutationObserver.disconnect();
|
||||||
if (variant === 'textarea') {
|
if (variant === 'textarea') {
|
||||||
inputElement.removeEventListener('scroll', syncScroll);
|
inputElement.removeEventListener('scroll', syncScroll);
|
||||||
}
|
}
|
||||||
@ -178,6 +207,40 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [variant]);
|
}, [variant]);
|
||||||
|
|
||||||
|
// 当 value 变化时,确保样式同步(特别是首次输入时)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!inputRef.current || !highlightRef.current || !value) return;
|
||||||
|
|
||||||
|
const syncCriticalStyles = () => {
|
||||||
|
const inputElement = inputRef.current;
|
||||||
|
const highlightContainer = highlightRef.current;
|
||||||
|
if (!inputElement || !highlightContainer) return;
|
||||||
|
|
||||||
|
const inputStyles = window.getComputedStyle(inputElement);
|
||||||
|
|
||||||
|
// 同步关键样式
|
||||||
|
highlightContainer.style.paddingTop = inputStyles.paddingTop;
|
||||||
|
highlightContainer.style.paddingBottom = inputStyles.paddingBottom;
|
||||||
|
highlightContainer.style.paddingLeft = inputStyles.paddingLeft;
|
||||||
|
highlightContainer.style.paddingRight = inputStyles.paddingRight;
|
||||||
|
highlightContainer.style.lineHeight = inputStyles.lineHeight;
|
||||||
|
highlightContainer.style.fontSize = inputStyles.fontSize;
|
||||||
|
highlightContainer.style.fontFamily = inputStyles.fontFamily;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 多次延迟确保样式完全应用
|
||||||
|
syncCriticalStyles();
|
||||||
|
const timer1 = setTimeout(syncCriticalStyles, 0);
|
||||||
|
const timer2 = setTimeout(syncCriticalStyles, 10);
|
||||||
|
const timer3 = setTimeout(syncCriticalStyles, 50);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer1);
|
||||||
|
clearTimeout(timer2);
|
||||||
|
clearTimeout(timer3);
|
||||||
|
};
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
// 监听滚动和窗口大小变化,更新下拉框位置
|
// 监听滚动和窗口大小变化,更新下拉框位置
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showSuggestions) return;
|
if (!showSuggestions) return;
|
||||||
@ -452,9 +515,14 @@ const VariableInput: React.FC<VariableInputProps> = ({
|
|||||||
alignItems: variant === 'input' ? 'center' : 'flex-start',
|
alignItems: variant === 'input' ? 'center' : 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ width: '100%', flex: variant === 'input' ? 'none' : undefined }}>
|
{/* Input 需要包装 div 避免 flex 影响文字,Textarea 直接渲染 */}
|
||||||
|
{variant === 'input' ? (
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
<HighlightLayer value={value} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<HighlightLayer value={value} />
|
<HighlightLayer value={value} />
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user