I figured out how sync text value between clients using Yjs but I still can't handle cursor pos after text update. I use quasar framework so I can't apply one of this recomended text-editors by Yjs documentation. I do not need and special binding like characters size, color, bolding and on.
this is what I've got up to now
<q-input
:modelValue="article"
debounce="1000"
ref="editor"
type="textarea"
@update:modelValue=" (e) => {
updateText(article, e )
}"
@focus="showCursor()"
/>
<script setup lang='ts'>
/* main imports */
import { QInput } from 'quasar'
import { ref, nextTick } from 'vue'
/* yjs lib */
import * as Y from 'yjs'
import { SocketIOProvider } from 'y-socket.io'
/* fast-diff lib */
import diff, { Diff } from 'fast-diff'
/* Set Y instance and bind it with y.text extension */
const doc = new Y.Doc()
const doc_text = doc.getText('article')
/* Set provider to manage communication */
const provider = new SocketIOProvider(
'http://localhost:1234',
'example-document',
doc,
{
autoConnect: true,
}
)
/* Check if provider is connected */
const is_provider_connected = ref('')
provider.on('status', ({ status }: { status: string }) => {
is_provider_connected.value = status
article.value = doc_text.toString()
})
/* To get cursor position */
const editor = ref<QInput | null>(null)
function curetPos() {
return editor.value?.getNativeElement().selectionStart
}
function showCursor(){
setTimeout(()=>{
relPos.value = Y.createRelativePositionFromTypeIndex(doc_text, curetPos())
}, 1)
}
const relPos = ref()
const pos = ref()
/* intercept article change */
const article = ref('')
const diffResult = ref<Diff[]>()
function updateText(oldValue: string, newValue: string | number | null) {
let consumerIndex = 0
if (typeof newValue === 'string') {
diffResult.value = diff(oldValue, newValue)
diffResult.value.forEach((el) => {
if (el[0] === 0) {
consumerIndex += el[1].length
} else if (el[0] === 1) {
doc_text.insert(consumerIndex, el[1])
} else if (el[0] === -1) {
doc_text.delete(consumerIndex, el[1].length)
}
})
}
}
doc.on('update', () => {
article.value = doc_text.toString()
if(relPos.value){
pos.value= Y.createAbsolutePositionFromRelativePosition(relPos.value, doc)
nextTick(()=>{
editor.value?.getNativeElement().setSelectionRange(pos.value.index, pos.value.index)
})
}
})
</script>
So as you see the main problem is the setting cursor pos after update.
First because update model is earlier then dom-el re-render and second is that someone could changed value while you still write the text.