Closed tiendatleo closed 1 year ago
Hi @tiendatleo, are you able to create a simplified sandbox? It's hard to tell what's going wrong without more details.
Dependencies installed:
- "@tiptap/extension-mention": "^2.0.0-beta.102"
- "@tiptap/starter-kit": "^2.0.0-beta.190"
- "@tiptap/vue-2": "^2.0.0-beta.84"
That happened to me as well. The bug is really hard to replicate honestly. But I attached the dependencies I've used if it should help.
What fixes the bug for me is to add in this snippet in the watchers:
Which is the same code mentioned in the documentation as well. But I've removed it as it didn't explain why and it didn't make sense to check same value in the watcher (which watchers should trigger whenever the dependent values' change)
Also, just generally speaking (and completely unrelated to this thread) if you it helps you, I find Tiptap's Vue 2 has weird reactivity issues and I'm doing weird things like stringifying and parsing the editor doc object as a prop.
The codes are very long and I can't bring it to a sandbox. Anyone facing to this issue?
Do not use emit and setContent by watch props at the same time. setContent makes the cursor move to the end of the editor.
Without being able to look at the code, it's difficult to provide help. Now that there were some good advices and the demo on model does not show the same behaviour, I would close the issue for now.
Feel free to let me know if the problem is still reproducible.
I can reproduce this issue with TipTap 2.0.3 on Safari 16.5 when I place the editor inside a custom element with a closed shadow root. Opening the shadow root on all ancestor elements fixes the problem. I don't really have the time to research this further, but it looks like upstream WebKit bug.
Do not use emit and setContent by watch props at the same time. setContent makes the cursor move to the end of the editor.
wow! so, how to update the editor content if we want to update editor content from parent component?
Do not use emit and setContent by watch props at the same time. setContent makes the cursor move to the end of the editor.
wow! so, how to update the editor content if we want to update editor content from parent component?
const setContent = (content: string) => {
editor.value?.chain().focus().setContent(JSON.parse(content)).run()
}
defineExpose({
setContent
})
Has anyone find a solution for this in React?
The same is happening to me, this is my code: I'm stuck please help, there is no more info about it other that this issue reported.
NoteContext.jsx
export const NoteContext = createContext()
export const NoteProvider = ({ children }) => {
const [noteId, setNoteId] = useState('')
const [noteContent, setNoteContent] = useState('')
const [debouncedNoteContent] = useDebounce(noteContent, 500)
const editor = useEditor({
extensions: [
StarterKit.configure({
codeBlock: false,
code: false,
hardBreak: false
}),
Underline,
Typography,
Placeholder.configure({
placeholder: 'Add note...'
}),
TextAlign.configure({
types: ['heading', 'paragraph'],
alignments: ['left', 'right', 'center', 'justify']
}),
CustomCodeBlock,
CustomCode,
CustomHardBreak
],
onUpdate: ({ editor }) => {
setNoteContent(editor.getHTML())
}
})
return (
<NoteContext.Provider value={{
noteId,
setNoteId,
editor,
noteContent,
setNoteContent,
debouncedNoteContent
}}
>
{children}
</NoteContext.Provider>
)
}
NoteContent.jsx
export default function NoteContent () {
const [user] = useAuthState(auth)
const {
noteId,
editor,
noteContent,
setNoteContent,
debouncedNoteContent
} = useContext(NoteContext)
const navigate = useNavigate()
const [values] = useListVals(ref(db, `note-it-db/${user.uid}/notes`))
const existingNote = useMemo(() => {
return values.find(note => note.id === noteId)
}, [values, noteId])
const createNewNote = (noteContent, user) => {
const newNoteId = uuidv4()
const newNoteData = {
id: newNoteId,
content: noteContent,
favorite: false,
lastModified: moment().format('DD-MM-YYYY hh:mm:ss')
}
set(ref(db, `note-it-db/${user.uid}/notes/${newNoteId}`), newNoteData)
return newNoteData
}
const updateNote = (existingNote, noteContent, user) => {
const updatedNoteData = {
...existingNote,
content: noteContent,
lastModified: moment().format('DD-MM-YYYY hh:mm:ss')
}
set(ref(db, `note-it-db/${user.uid}/notes/${existingNote.id}`), updatedNoteData)
return updatedNoteData
}
useEffect(() => {
if (editor && noteContent) {
const notePurified = DOMPurify.sanitize(noteContent)
editor.commands.setContent(notePurified, false, { preserveWhitespace: 'full' })
}
}, [editor, noteContent])
useEffect(() => {
if (noteId && existingNote) {
setNoteContent(existingNote.content)
}
}, [noteId, existingNote, setNoteContent])
useEffect(() => {
if (noteId && debouncedNoteContent.trim() && existingNote) {
updateNote(existingNote, debouncedNoteContent, user)
}
}, [noteId, debouncedNoteContent, existingNote, user])
useEffect(() => {
if (!noteId && debouncedNoteContent.trim()) {
const newNoteId = createNewNote(debouncedNoteContent, user).id
navigate(`/notes/${newNoteId}`)
}
}, [debouncedNoteContent, noteId, navigate, user])
return (
<Box style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%',
gap: '1rem'
}}
>
<EditorContent
className='note-content'
editor={editor}
/>
<NoteTools editor={editor} />
</Box>
)
}
I am also looking for a solution for this in React.
I was able to get something working but I'm not 100% sure it's the best way to go.
useEffect(() => {
if (content && editor) {
// Save cursor position
const { from, to } = editor.state.selection;
// Update content
editor.commands.setContent(content);
// Restore cursor position
const newFrom = Math.min(from, editor.state.doc.content.size);
const newTo = Math.min(to, editor.state.doc.content.size);
const textSelection = new TextSelection(
editor.state.doc.resolve(newFrom),
editor.state.doc.resolve(newTo)
);
editor.view.dispatch(editor.state.tr.setSelection(textSelection));
}
}, [content, editor]);
I faced this problem in React project. For me it was useful to set content only if (editor?.isEmpty). So I'm using setContent inside useEffect only to load initial state. Then I operate with useForm hook to set modified values.
P.S. I'm not logged in on the needed pc, so here is no code example. I'm commenting by phone.
@templeman15 Sorry I couldn't reply back to you, I haven't been able to fix this issue, your solution worked for me, I really appreciated it, thanks so much 🙌🏻 Just wondering how you discovered this information, specially the class TextSelection I haven't found anything related to that in the documentation.
Something I can show you is that I found that this also do the trick:
const newFrom = Math.min(from, editor.state.doc.content.size)
const newTo = Math.min(to, editor.state.doc.content.size)
editor.commands.setTextSelection({ from: newFrom, to: newTo })
@LeonidVE Thank you too, I tried this idea too and it worked, thanks
@templeman15 Sorry I couldn't reply back to you, I haven't been able to fix this issue, your solution worked for me, I really appreciated it, thanks so much 🙌🏻 Just wondering how you discovered this information, specially the class TextSelection I haven't found anything related to that in the documentation.
@SGLara I looked at the source some and then I asked ChatGPT (4) with some of the source and it pointed it out. 😄
Thanks guys!!
This solution worked without changing cursors etc, my example is much simpler tho, but I did have the same issue before ;) edit: value is a prop that is passed to this TipTap component
const editor: Editor | null = useEditor({
extensions: [StarterKit, Underline],
content: value,
editorProps: {
attributes: {
class: 'prose prose-sm lg:prose-lg w-full xl:prose-2xl focus:outline-none border bg-white border-radius-lg p-6 ',
},
},
onUpdate: ({ editor: currentEditor }) => {
onChange(currentEditor.getHTML())
},
})
useEffect(() => {
if (editor?.isEmpty) editor.commands.setContent(value)
}, [value, editor])
Thanks buddy for helping me. useEffect(() => { if (editor?.isEmpty) editor.commands.setContent(value) }, [value, editor])
This helped me to solve my issue
Hello Community,
If anyone is facing this issue with Vue watchers
and emit onUpdate
simultaneously, you can use this hack to get around this cursor issue easily. 🚀
modelValue(newValue) {
// ? If the new modelValue is different then editor HTML, set it
// ? This should only occur for once, if the modelValue fetch is delayed by a bit
if (this.editor && newValue && newValue !== this.editor.getHTML()) {
this.editor.commands.setContent(newValue, false, {
preserveWhitespace: "full",
});
}
}
Hi Guys,
The solution you proposed works but has a limitation: if you change the value outside the component and the value in the editor is not empty, it doesn't update.
The problem, as you mentioned, is that setContent
sets the cursor to the bottom, but it shouldn't enter that function because there's a check above it:
const isSame = editor.getHTML() === newValue;
if (isSame) {
return;
}
I wondered why it wasn't entering the if statement, so I investigated and discovered that getHTML() converts special characters while in newValue I found values like Ù, Œ, etc. To correct this error, you need to replicate what getHTML() does. So the solution I found is this:
const div = document.createElement('div');
div.innerHTML = newValue;
const isSame = editor.getHTML() === div.innerHTML;
if (isSame) {
return;
}
editor.commands.setContent(newValue, false);
That's why the problem doesn't always occur. In the end, it notes that the value is the same and doesn't enter the setContent.
What’s the bug you are facing?
I am facing an issue about the position of the cursor on production environment.
Which browser was this experienced in? Are any special extensions installed?
OS: macOS 12.3.1 Browser: Chrome Version 103.0.5060.53 (Official Build) (arm64) Dependencies: "@tiptap/core": "^2.0.0-beta.181", "@tiptap/extension-color": "^2.0.0-beta.12", "@tiptap/extension-image": "^2.0.0-beta.30", "@tiptap/extension-link": "^2.0.0-beta.35", "@tiptap/extension-table": "^2.0.0-beta.54", "@tiptap/extension-table-cell": "^2.0.0-beta.23", "@tiptap/extension-table-header": "^2.0.0-beta.25", "@tiptap/extension-table-row": "^2.0.0-beta.22", "@tiptap/extension-text-align": "^2.0.0-beta.31", "@tiptap/extension-text-style": "^2.0.0-beta.26", "@tiptap/extension-underline": "^2.0.0-beta.25", "@tiptap/starter-kit": "^2.0.0-beta.190", "@tiptap/vue-2": "^2.0.0-beta.84",
How can we reproduce the bug on our side?
After I typed a word, the cursor is moved to end of the editor. Video below.
Can you provide a CodeSandbox?
No response
What did you expect to happen?
The position of cursor is not changed
Anything to add? (optional)
https://user-images.githubusercontent.com/25786110/176377309-9f2522f1-9031-4df9-b3a5-48a1ab28dbb4.mov
Did you update your dependencies?
Are you sponsoring us?