show autocomplete
This commit is contained in:
@@ -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)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user