Skip to content

Commit 8ad99ca

Browse files
fix: prevent outer scrollers from moving during inline editor input
1 parent 47e90b1 commit 8ad99ca

1 file changed

Lines changed: 38 additions & 1 deletion

File tree

‎src/editors/inline-editor/index.js‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const InlineEditor = forwardRef(({
2525
}, ref) => {
2626
const [slateValue, setSlateValue] = useState(value);
2727
const focusRangeRef = useRef(null);
28+
const containerRef = useRef(null);
2829

2930
const editor = useMemo(() => {
3031
const _editor = inlineEditor();
@@ -123,6 +124,42 @@ const InlineEditor = forwardRef(({
123124
// eslint-disable-next-line react-hooks/exhaustive-deps
124125
}, []);
125126

127+
// Keep outer scrollers still on input: the browser's native "scroll caret
128+
// into view" would otherwise scroll the page (e.g. the form body).
129+
useEffect(() => {
130+
if (!enableEdit) return;
131+
const container = containerRef.current;
132+
if (!container) return;
133+
134+
const getOuterScrollers = () => {
135+
const scrollers = [];
136+
let el = container.parentElement;
137+
while (el && el !== document.body && el !== document.documentElement) {
138+
const { overflowY, overflowX } = window.getComputedStyle(el);
139+
const scrollableY = (overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight;
140+
const scrollableX = (overflowX === 'auto' || overflowX === 'scroll') && el.scrollWidth > el.clientWidth;
141+
if (scrollableY || scrollableX) scrollers.push(el);
142+
el = el.parentElement;
143+
}
144+
return scrollers;
145+
};
146+
147+
const onBeforeInput = () => {
148+
const scrollers = getOuterScrollers();
149+
if (scrollers.length === 0) return;
150+
const positions = scrollers.map(el => ({ el, top: el.scrollTop, left: el.scrollLeft }));
151+
requestAnimationFrame(() => {
152+
positions.forEach(({ el, top, left }) => {
153+
if (el.scrollTop !== top) el.scrollTop = top;
154+
if (el.scrollLeft !== left) el.scrollLeft = left;
155+
});
156+
});
157+
};
158+
159+
container.addEventListener('beforeinput', onBeforeInput, true);
160+
return () => container.removeEventListener('beforeinput', onBeforeInput, true);
161+
}, [enableEdit]);
162+
126163
const handleClear = useCallback((targetEditor) => {
127164
if (targetEditor._id !== editor._id) return;
128165
editor.children = [{
@@ -171,7 +208,7 @@ const InlineEditor = forwardRef(({
171208
}, [enableEdit, editor, focusNode, handelEnableEdit]);
172209

173210
return (
174-
<div className="sf-simple-slate-editor-container">
211+
<div className="sf-simple-slate-editor-container" ref={containerRef}>
175212
{enableEdit && (<InlineToolbar editor={editor} isSupportFormula={isSupportFormula} isSupportColumn={!!columns} onExpandEditorToggle={onExpandEditorToggle} />)}
176213
<div className="sf-slate-editor-content" onClick={onEditorClick}>
177214
<Slate editor={editor} initialValue={slateValue} onChange={onChange}>

0 commit comments

Comments
 (0)