Open fihis opened 4 years ago
I have the same problem :(
I have the same problem :(
Who solved this problem?
Has anyone solved this issue?
When I press enter, it will start typing on a new line that is hidden, but once i type 2 characters, it suddenly jumps down so the text is visible again. Very odd behavior.
Has anyone solved this issue?
When I press enter, it will start typing on a new line that is hidden, but once i type 2 characters, it suddenly jumps down so the text is visible again. Very odd behavior.
it seems not
I solved this problem by attaching some hooks to the editor, and scrolling to the bottom of the div
when the number of lines change.
const [lines, setLines] = useState(0);
const editorRef = useRef(null);
const handleEditorChange = function(e) {
const prevLines = lines;
if(prevLines !== e.blocks.length) {
setLines(e.blocks.length);
}
};
useEffect(() => {
const scrollDiv = editorRef.current.querySelector(".wysiwyg-editor");
scrollDiv.scrollTop = scrollDiv.scrollHeight;
}, [lines]);
return(<div className="row" ref={editorRef}><Editor onChange={(e) => handleEditorChange(e)} {...props} /></div>);
@jtrost It is interesting decision. So i tried your variant. But class '.wysiwyg-editor' is not exist. I have only these: I tried also '.rdw-editor-wrapper' and '.DraftEditor-root' and others. But all they have EditorState with _immutable state. So i can't figure how to scroll down.
hi i am working on react native and used react-draft-wysiwyg for web version of my app and i amfacing the issue of autoscrolling on new line when i hit enter on the in bottom of editor the cursor moved to the the new line but scroll is moving down ward i have tried lot for this all the solution i saw on the internet @jpuri please help me out from this
@bikashsahucbnits you just now repeated the same about this thread, man.
Here's a partial solution. It solves the main case--no scrolling at the end of the text when you are at the bottom of the view. You can still trigger this behavior if you are at the bottom of the view but still in the middle of the scroll. Anyway, in case it's helpful--
const editorRef = useRef<null | HTMLDivElement>(null)
const bottomOffset = useRef("")
const editorContainingDivClass = ".public-DraftEditor-content"
const scrollIfNewElementAtEnd = () => {
if (editorRef.current) {
const scrollDiv = editorRef.current.querySelector(
editorContainingDivClass
)
if (scrollDiv) {
const newElement = scrollDiv.lastChild?.lastChild as HTMLElement
const currentBottom = newElement.dataset.offsetKey
if (
currentBottom !== "" &&
currentBottom !== bottomOffset.current
) {
bottomOffset.current = currentBottom ?? ""
newElement.scrollIntoView()
}
}
}
}
Here is a solution that works for me.
Packages that I use
"draft-js": "^0.11.7",
"react-draft-wysiwyg": "^1.14.7",
My styles for scrolling
.DraftEditor-root {
height: 200px;
overflow-y: auto;
}
.DraftEditor-editorContainer,
.public-DraftEditor-content {
height: 100%;
}
Some extracts from my .tsx file:
import { Editor } from 'react-draft-wysiwyg';
import { ContentState, EditorState, SelectionState } from 'draft-js';
Save ref for future usage
_editorReferece: any = null;
_setEditorReference = (ref: any) => {
if (!ref || !!this._editorReferece) {
return;
}
this._editorReferece = ref;
};
OnChange event handler
_onEditorStateChange = (editorState: EditorState) => {
const
currentContentState: ContentState = this.state.editorState.getCurrentContent(),
newContentState: ContentState = editorState.getCurrentContent(),
lastChangeType: string = editorState.getLastChangeType(),
enterWasClicked = currentContentState !== newContentState && lastChangeType === 'split-block';
this.setState({ editorState }, () => {
// It is important to adjust scroll position after state was set
if (enterWasClicked) {
this._scrollToCursor();
}
});
};
_scrollToCursor = () => {
if (!this._editorReferece) {
return;
}
// Find focused element using SelectionState
// Find out where focused element is located inside container
// If focused element is not visible, change position of the scroll
const
{ editorState } = this.state,
selectionState: SelectionState = editorState.getSelection(),
focusedBlockKey = selectionState.getStartKey(),
rootElement: HTMLElement = this._editorReferece, // element with class public-DraftEditor-content
scrollableContainer = rootElement!.parentElement!.parentElement as HTMLElement, // element with class DraftEditor-root
blocksWrapper = rootElement.lastChild as HTMLElement, // direct child of the element with class public-DraftEditor-content
blocks = [...blocksWrapper.children] as HTMLElement[],
focusedBlockIndex: number = blocks.findIndex((element: any) => element.dataset.offsetKey.startsWith(focusedBlockKey)),
focusedBlock: HTMLElement = blocks[focusedBlockIndex],
blockLineHeight = 20, // depends on block type, so has to be calculated
focusedBlockFirstLineBottom = focusedBlock.offsetTop + blockLineHeight,
maxVisiblePosition = scrollableContainer.scrollTop + scrollableContainer.clientHeight,
diff = focusedBlockFirstLineBottom - maxVisiblePosition;
if (diff > 0) {
scrollableContainer.scrollTop += diff + 5;
}
};
<Editor
editorState={this.state.editorState}
editorRef={this._setEditorReference}
onEditorStateChange={this._onEditorStateChange}
/>
For an unmanaged component, I did find a way to mostly solve the problem (the Editor correctly scroll down at a new last line, but still struggles autoscrolling when a new line added within the text overflows the editor <- to analyse). This is a pure empirical code, so please don't hesitate to correct it if you see any flaw :)
// The code bellow is to be added into the class/function rendering the <Editor />
const [recordedLastLineContent, seRecordedtLastLineContent] = useState(null);
let handleEditorChange = (editorState) => { // To pass to the <Editor /> as its onChange callback
let lastLineContent = editorState.getCurrentContent().blockMap.last();
if (recordedLastLineContent && lastLineContent !== recordedLastLineContent) { // Detects that a new line has been added
seRecordedtLastLineContent(lastLineContent);
const scrollableDiv = editorRef.parentNode.parentNode.parentNode; // editorRef: returned by editorRef callback of the <Editor />
setTimeout(() => { scrollableDiv.scrollTop = scrollableDiv.scrollHeight }, 20); // Give 20ms to the <Editor /> to update its own content before scrolling down
}
seRecordedtLastLineContent(lastLineContent);
}
Still have this problem
have you solved this problem? stuck on it
did anyone find a solution?
FINNALY solved it, just move ur dynamic import to ouside of all ur components
FINNALY solved it, just move ur dynamic import to ouside of all ur components
That's great, can you please elaborate more ?
FINNALY solved it, just move ur dynamic import to ouside of all ur components
That's great, can you please elaborate more ?
I was using next dynamic import inside the component like this:
import dynamic from "next/dynamic";
export function Prontuario({ notes, setNotes }) {
const Editor = dynamic(
() => import("react-draft-wysiwyg").then((mod) => mod.Editor),
{ ssr: false }
);
return (
<Editor
editorState={notes}
onEditorStateChange={setNotes}
// toolbarOnFocus
localization={{
locale: "pt",
}}
toolbar={{
options: ["inline", "list", "textAlign", "remove", "history"],
}}
/>
);
}
So a move up one line this code:
const Editor = dynamic(
() => import("react-draft-wysiwyg").then((mod) => mod.Editor),
{ ssr: false }
);
Ended up like this:
import dynamic from "next/dynamic";
const Editor = dynamic(
() => import("react-draft-wysiwyg").then((mod) => mod.Editor),
{ ssr: false }
);
export function Prontuario({ notes, setNotes }) {
return (
<Editor
editorState={notes}
onEditorStateChange={setNotes}
// toolbarOnFocus
localization={{
locale: "pt",
}}
toolbar={{
options: ["inline", "list", "textAlign", "remove", "history"],
}}
/>
);
}
Did anyone find out a solution for this issue?
Finally found out the solution for this issue,add this logic to Editor: onContentStateChange={() => { if (editorRef.current) { const scrollDiv = editorRef.current if (scrollDiv) { const newElement = scrollDiv.lastChild ?.lastChild as HTMLElement newElement?.scrollIntoView() } } }}
I did it like that: useEffect:
useEffect(() => {
const childNode = editorRef?.current?.wrapper?.parentElement?.childNodes[1];
const lineHeight = childNode?.children[0]?.children[0]?.children[0]?.children[0]?.lastChild?.clientHeight;
if (
childNode?.scrollHeight > 0 &&
childNode?.scrollHeight - childNode?.offsetHeight == childNode?.scrollTop + lineHeight
)
childNode?.scrollTo({ top: childNode?.scrollHeight - childNode?.offsetHeight });
}, [editorRef.current?.wrapper?.scrollHeight]);
Editor component
<Editor ref={editorRef} />
Hope it will be usefull. When you are adding a new line and the scroll is on the bottom of the container then it will scroll down, but if you are somewhere in the midle of the container then it won't. I think it should work like that.
Hey This Solution Works for me !!! Try this out
onContentStateChange={() => { const contentDiv = document.querySelector(".rdw-editor-main"); console.log(contentDiv.scrollHeight); contentDiv.scrollTop = contentDiv.scrollHeight; }}
Finally I found a solution with css. I just override the DraftEditor-editorContainer
class.
.DraftEditor-editorContainer {
height: 200px; // my text editor height
overflow: auto;
padding: 0px 14px;
}
How can I make editor's input to scroll down automatically on new line input? Like in example 6 at https://jpuri.github.io/react-draft-wysiwyg/#/demo. Now scroll stays on top and text cursor hides when goes below bottom border.