Implement BesQuillService grammar-checking class

This commit is contained in:
Aljaž Grilc 2025-01-20 13:31:19 +01:00
parent cbeadb4d5a
commit 18d23633ff
2 changed files with 106 additions and 1 deletions

View File

@ -42,7 +42,7 @@
const quill = new Quill('#editor', {
theme: 'snow',
});
BesService.registerByElement(quill.root, new BesStatusIconEventSink())
BesQuillService.register(quill.root, quill, new BesStatusIconEventSink())
</script>
</body>

View File

@ -1136,6 +1136,110 @@ class BesCKService extends BesTreeService {
}
}
/**************************************************************************************************
*
* Quill editor grammar-checking service
*
*************************************************************************************************/
class BesQuillService extends BesTreeService {
/**
* Constructs class.
*
* @param {Element} hostElement The element in DOM tree we are providing grammar-checking service
* for
* @param {*} quillInstance Quill instance
* @param {*} eventSink Event sink for notifications
*/
constructor(hostElement, quillInstance, eventSink) {
super(hostElement, hostElement, eventSink)
this.quillInstance = quillInstance
this.onChangeData = this.onChangeData.bind(this)
this.quillInstance.on('text-change', delta => {
this.onChangeData(delta)
})
}
/**
* Registers grammar checking service.
*
* @param {Element} hostElement DOM element to register grammar checking service for
* @param {Quill} quillInstance Enable Quill tweaks
* @param {*} eventSink Event sink for notifications
* @returns {BesQuillService} Grammar checking service instance
*/
static register(hostElement, quillInstance, eventSink) {
let service = BesService.getServiceByElement(hostElement)
if (service) return service
service = new BesQuillService(hostElement, quillInstance, eventSink)
if (service.eventSink && 'register' in service.eventSink)
service.eventSink.register(service)
// Defer proofing giving user a chance to configure the service.
service.scheduleProofing(10)
return service
}
/**
* Unregisters grammar checking service.
*/
unregister() {
this.quillInstance.off('change:data', this.onChangeData)
if (this.timer) clearTimeout(this.timer)
super.unregister()
}
/**
* Called to report the text has changed
*/
onChangeData(delta) {
let index = 0
let reproofNeeded = false
delta.ops.forEach(op => {
if (op.retain) {
index += op.retain
if (op.attributes) {
reproofNeeded = true
}
} else if (op.insert) {
reproofNeeded = true
index += op.insert.length
} else if (op.delete) {
reproofNeeded = true
}
})
if (reproofNeeded) {
const [leaf, offset] = this.quillInstance.getLeaf(index)
let domElement = leaf.domNode
while (domElement && domElement.tagName !== 'P') {
domElement = domElement.parentNode
}
this.clearProofing(domElement)
setTimeout(() => {
this.repositionAllMarkup()
this.scheduleProofing(1000)
}, 0)
}
}
/**
* Replaces grammar checking match with a suggestion provided by grammar checking service.
*
* @param {*} el Block element/paragraph containing grammar checking rule match
* @param {*} match Grammar checking rule match
* @param {String} replacement Text to replace grammar checking match with
*/
replaceText(el, match, replacement) {
if (this.timer) clearTimeout(this.timer)
if (this.abortController) this.abortController.abort()
this.clearProofing(el)
match.range.deleteContents()
match.range.insertNode(document.createTextNode(replacement))
this.scheduleProofing(20)
}
}
/**************************************************************************************************
*
* Plain-text grammar-checking service
@ -2260,3 +2364,4 @@ window.BesCKService = BesCKService
window.BesDOMPlainTextService = BesDOMPlainTextService
window.BesTAService = BesTAService
window.BesPopup = BesPopup
window.BesQuillService = BesQuillService