show autocomplete

This commit is contained in:
2023-05-04 22:35:50 +02:00
parent 8e7b6d13af
commit 82e4f25b04
3 changed files with 85 additions and 16 deletions

View File

@@ -224,8 +224,8 @@ export default defineComponent({
this.$emit('click', { evt, data }, editor)
})
editor.model.document.on('contextedLinkAutocomplete', (evt, data) => {
this.$emit('contextedLinkAutocomplete', { evt, data })
editor.model.document.on('contextedLinkAutocomplete', (_, data) => {
this.$emit('contextedLinkAutocomplete', data)
})
},
},

View File

@@ -1,5 +1,7 @@
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'
import { TwoStepCaretMovement, inlineHighlight } from 'ckeditor5/src/typing'
import AttributeCommand from '@ckeditor/ckeditor5-basic-styles/src/attributecommand'
import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect'
const HIGHLIGHT_CLASS = 'ck-link_selected'
@@ -10,6 +12,10 @@ export default class ContextedLinkEditing extends Plugin {
const twoStepCaretMovementPlugin = this.editor.plugins.get(TwoStepCaretMovement)
twoStepCaretMovementPlugin.registerAttribute('contextedLink')
inlineHighlight(this.editor, 'contextedLink', 'a', HIGHLIGHT_CLASS)
this.editor.commands.add(
'autocomplete',
new AttributeCommand(this.editor, 'autocomplete')
)
}
afterInit() {
// const editor = this.editor
@@ -28,7 +34,7 @@ export default class ContextedLinkEditing extends Plugin {
// Extend the text node's schema to accept the abbreviation attribute.
schema.extend('$text', {
allowAttributes: ['abbreviation', 'contextedLink'],
allowAttributes: ['contextedLink', 'autocomplete'],
})
}
_defineConverters() {
@@ -62,10 +68,9 @@ export default class ContextedLinkEditing extends Plugin {
_addAutocomplete() {
// Copied from: node_modules/@ckeditor/ckeditor5-autoformat/src/inlineautoformatediting.js
const editor = this.editor
editor.model.document.on('change:data', (_, batch) => {
if (batch.isUndo || !batch.isLocal) {
return
}
// let autocomplete = false
editor.model.document.on('change', (_, batch) => {
if (batch.isUndo || !batch.isLocal) return
const model = editor.model
const selection = model.document.selection
// Do nothing if selection is not collapsed.
@@ -75,7 +80,7 @@ export default class ContextedLinkEditing extends Plugin {
// Typing is represented by only a single change.
if (
changes.length != 1 ||
entry.type !== 'insert' ||
(entry.type !== 'insert' && entry.type !== 'remove') ||
entry.name != '$text' ||
entry.length != 1
) {
@@ -84,21 +89,60 @@ export default class ContextedLinkEditing extends Plugin {
const focus = selection.focus
const block = focus?.parent
if (!block || !focus) return
const { text, range } = getTextAfterCode(
const { text } = getTextAfterCode(
model.createRange(model.createPositionAt(block, 0), focus),
model
)
const inputText = (text as string).split(']]').at(-1)
const autocompleteOpenMatch = (inputText as string).match(/(?<=\[\[).+/g)
const autocompleteCloseMatch = (inputText as string).match(/(?<=\[\[).+?(?=]])/g)
const openAutocomplete = autocompleteOpenMatch && !autocompleteCloseMatch
if (openAutocomplete) {
editor.model.document.fire('contextedLinkAutocomplete', autocompleteOpenMatch)
const autocompleteText = (inputText as string).match(/(?<=\[\[).*/g)
const cursorNodes = [focus.textNode, focus.nodeBefore, focus.nodeAfter]
const autocompleteNode: any = cursorNodes.find((node) =>
node?.hasAttribute('autocomplete')
)
interface AutocompleteEvent {
position?: any
autocompleteText?: string
show: boolean
}
if (Boolean(autocompleteText) || Boolean(autocompleteNode)) {
if (Boolean(autocompleteText) !== Boolean(autocompleteNode)) {
editor.execute('autocomplete')
if (autocompleteNode)
editor.model.document.fire('contextedLinkAutocomplete', { show: false })
} else {
const event: AutocompleteEvent = {
position: getNodePosition(
editor,
editor.model.createPositionFromPath(
autocompleteNode.root,
autocompleteNode.getPath()
)
),
autocompleteText: autocompleteNode.data,
show: true,
}
editor.model.document.fire('contextedLinkAutocomplete', event)
}
}
})
}
}
function getNodePosition(editor: any, modelPosition: any) {
try {
const mapper = editor.editing.mapper
const viewPosition = mapper.toViewPosition(modelPosition)
const viewRange = editor.editing.view.createRange(viewPosition)
const domConverter = editor.editing.view.domConverter
const rangeRects = Rect.getDomRangeRects(
domConverter.viewRangeToDom(viewRange)
).pop()
return rangeRects
} catch (e) {
console.log(e)
}
}
// function testOutputToRanges(start: any, arrays: any[], model: any) {
// return arrays
// .filter((array) => array[0] !== undefined && array[1] !== undefined)

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import CKEditor from '@/ckeditor/CKEditor'
import BalloonEditor from '@ckeditor/ckeditor5-editor-balloon/src/ballooneditor'
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials'
@@ -54,18 +55,35 @@ const editorConfig = {
},
}
const editorElement = ref<HTMLInputElement | null>(null)
const handleClick = ({ data }: { data: any }) => {
const noteTitle = data.domTarget.textContent as string
const note = getNoteByTitle(noteTitle)
if (note) activeNote.value = note
}
const showAutocomplete = ref(false)
const autocompleteStyle = ref<any>({})
const autocompleteText = ref('')
const handleAutocomplete = (event: any) => {
console.log(event.data)
const position = event.position
if (position && editorElement.value) {
const rect: any = editorElement.value?.getBoundingClientRect()
const fontSize = parseFloat(
window.getComputedStyle(editorElement.value, null).getPropertyValue('font-size')
)
autocompleteStyle.value = {
top: `${position.top - rect.top + fontSize}` + 'px',
left: `${position.left - rect.left}` + 'px',
}
}
autocompleteText.value = event.autocompleteText
showAutocomplete.value = event.show
}
</script>
<template>
<div>
<div class="relative" ref="editorElement">
<CKEditor
class="h-full"
:editor="editor"
@@ -74,6 +92,13 @@ const handleAutocomplete = (event: any) => {
@click="handleClick"
@contexted-link-autocomplete="handleAutocomplete"
></CKEditor>
<div
class="absolute mt-1 border-red-500 bg-primary text-white"
:style="autocompleteStyle"
v-if="showAutocomplete"
>
{{ autocompleteText }}
</div>
</div>
</template>
<style>