contexted link styling

This commit is contained in:
2023-04-30 14:57:43 +02:00
parent 787b5a4cb8
commit 2ff849219b
5 changed files with 83 additions and 33 deletions

View File

@@ -56,7 +56,15 @@ export default defineComponent({
},
},
emits: ['ready', 'destroy', 'blur', 'focus', 'input', 'update:modelValue'],
emits: [
'ready',
'destroy',
'blur',
'focus',
'input',
'update:modelValue',
'click',
],
data(): CKEditorComponentData {
return {
@@ -131,7 +139,7 @@ export default defineComponent({
editorConfig.initialData = this.modelValue
}
this.editor
this.editor
.create(this.$el, editorConfig)
.then((editor) => {
// Save the reference to the instance for further use.
@@ -209,6 +217,11 @@ export default defineComponent({
editor.editing.view.document.on('blur', (evt) => {
this.$emit('blur', evt, editor)
})
// Custom event
editor.editing.view.document.on('click', (evt, data) => {
this.$emit('click', { evt, data }, editor)
})
},
},

View File

@@ -0,0 +1,45 @@
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'
export default class ContextedLinkEditing extends Plugin {
init() {
this._defineSchema() // ADDED
this._defineConverters() // ADDED
}
_defineSchema() {
// ADDED
const schema = this.editor.model.schema
// Extend the text node's schema to accept the abbreviation attribute.
schema.extend('$text', {
allowAttributes: ['abbreviation', 'contextedLink'],
})
}
_defineConverters() {
// ADDED
const conversion = this.editor.conversion
// Conversion from a model attribute to a view element.
conversion.for('downcast').attributeToElement({
model: 'contextedLink',
// Callback function provides access to the model attribute value
// and the DowncastWriter.
view: (modelAttributeValue, conversionApi) => {
const { writer } = conversionApi
return writer.createAttributeElement('a', {
'data-contexted-link': modelAttributeValue,
})
},
})
conversion.for('upcast').elementToAttribute({
view: {
name: 'a',
key: 'data-contexted-link',
},
model: {
key: 'contextedLink',
},
converterPriority: 'high',
})
}
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import CKEditor from '@/components/CKEditor'
import CKEditor from '@/ckeditor/CKEditor'
import BalloonEditor from '@ckeditor/ckeditor5-editor-balloon/src/ballooneditor'
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials'
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold'
@@ -11,16 +11,18 @@ import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading'
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph'
import ListPlugin from '@ckeditor/ckeditor5-list/src/list'
import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat'
import ContextedPlugin from '@/ckeditor/ContextedPlugin'
import { mdToHtml } from '@/utils/markdown'
import { getNoteById } from '@/composables/useNotes'
import { activeNote, getNoteByTitle } from '@/composables/useNotes'
const props = defineProps<{
note: Note
}>()
const html = mdToHtml(props.note.content)
const editor = BalloonEditor
const editorData = mdToHtml(props.note.content, 'c@', getNoteById)
const editorData = html
const editorConfig = {
plugins: [
EssentialsPlugin,
@@ -33,6 +35,7 @@ const editorConfig = {
ParagraphPlugin,
ListPlugin,
AutoformatPlugin,
ContextedPlugin,
],
toolbar: {
@@ -50,6 +53,12 @@ const editorConfig = {
],
},
}
const handleClick = ({ data }: { data: any }) => {
const noteTitle = data.domTarget.textContent as string
const note = getNoteByTitle(noteTitle)
if (note) activeNote.value = note
}
</script>
<template>
<div>
@@ -58,6 +67,7 @@ const editorConfig = {
:editor="editor"
v-model="editorData"
:config="editorConfig"
@click="handleClick"
></CKEditor>
</div>
</template>
@@ -68,4 +78,7 @@ const editorConfig = {
outline: none !important;
box-shadow: none !important;
}
.ck-content a[data-contexted-link='true'] {
@apply cursor-pointer font-semibold text-primary hover:bg-gray-200;
}
</style>

View File

@@ -4,8 +4,6 @@ import { mdToHtml } from '@/utils/markdown'
import { getAllMatches } from '@/utils/helpers'
import shortid from 'shortid'
const contextedPrefix = 'c@'
const baseNotes = reactive<{ [noteId: string]: BaseNote }>({})
export const notes = computed<Note[]>(() => {
@@ -40,10 +38,14 @@ export const getNoteById = (noteId: string) => {
return notes.value.find((note) => note.id === noteId)
}
export const getNoteByTitle = (title: string) => {
return notes.value.find((note) => note.title === title)
}
export const findNotes = (query: string): Note[] => {
const removeMdFromText = (mdText: string): string => {
const div = document.createElement('div')
div.innerHTML = mdToHtml(mdText, contextedPrefix, getNoteById)
div.innerHTML = mdToHtml(mdText)
const textWithoutMd = div.textContent || div.innerText || ''
return textWithoutMd
}

View File

@@ -1,39 +1,16 @@
import { marked } from 'marked'
import DOMPurify from 'dompurify'
export function mdToHtml(
mdText: string,
contextedPrefix: string,
getNoteById: (id: string) => Note | undefined
) {
export function mdToHtml(mdText: string) {
const renderer = new marked.Renderer()
renderer.link = (href, _, text) => {
const isContextedLink = href?.startsWith(contextedPrefix)
if (isContextedLink) {
const re = new RegExp(`${contextedPrefix}([^]+)`)
const match = re.exec(href || '')
const contextedLinkOptions = match ? match[1].split(';') : []
const noteId = contextedLinkOptions[0] || ''
const note = getNoteById(noteId)
const contextedHref = ''
const contextedLink = `<a data-contexted-link="${noteId}" title="${note?.title}"${contextedHref}>${text}</a>`
return note?.title ? contextedLink : text
} else {
return `<a target="_blank" href="${href}">${text}</a>`
}
}
const html = DOMPurify.sanitize(marked.parse(mdText, { renderer }))
const re = /(\[\[)(.*?)(\]\])/g
const doc = new DOMParser().parseFromString(html, 'text/html')
doc.querySelectorAll('p, b, u, i, li, h1, h2, h3').forEach((element) => {
// if (element.childElementCount === 0) {
element.innerHTML = element.innerHTML.replace(re, (_, p1, p2, p3) => {
// const { id: noteId } = getters.getNoteByTitle(p2) || {}
return `${p1}<a data-contexted-link="true">${p2}</a>${p3}`
})
// }
})
return doc.body.innerHTML
}