<template> <div class="aura-quill tw-rounded tw-border tw-border-solid tw-border-gray-300 tw-bg-white"> <div v-once ref="contentEl" v-html="content" /> </div> </template> <script lang="ts"> import { BlotConstructor } from 'parchment' import MagicUrl from 'quill-magic-url' import Quill from 'quill' // @ts-expect-error The module is for Quill 1.x, but it works with 2.x Quill.register('modules/magicUrl', MagicUrl) // we don’t want Quill to add target="_blank" to link elements // and need to perform all kind of type shenanigans to accomplish that :( const Link = Quill.import('formats/link') as { create(value: string): HTMLElement new (...args: any[]): typeof Link } Quill.register( 'formats/link', class CustomLink extends Link { static create(value: string) { const node = Link.create(value) node.removeAttribute('target') return node } } as unknown as BlotConstructor, ) </script> <script lang="ts" setup> import { onBeforeUnmount, onMounted, ref } from 'vue' import { useEventListener } from '@vueuse/core' defineOptions({ compatConfig: { MODE: 3 } }) const content = defineModel<string>({ required: true }) const props = defineProps<{ placeholder?: string }>() const emit = defineEmits<{ blur: [FocusEvent] }>() const contentEl = ref<HTMLElement>() const quill = ref<Quill>() let cleanupBlurListener: ReturnType<typeof useEventListener> function initializeQuill(contentEl: HTMLElement) { const quillInstance = new Quill(contentEl, { placeholder: props.placeholder, theme: 'bubble', modules: { magicUrl: true, toolbar: [ ['bold', 'italic', 'underline', 'strike'], ['link'], [{ list: 'ordered' }, { list: 'bullet' }], ], }, }) quillInstance.on('text-change', () => { content.value = quillInstance.getSemanticHTML() }) // bubble blur events from the editor field cleanupBlurListener = useEventListener( quillInstance.editor.scroll.domNode, 'blur', (event: FocusEvent) => { emit('blur', event) }, ) quill.value = quillInstance } onMounted(() => { initializeQuill(contentEl.value as HTMLElement) }) onBeforeUnmount(() => { quill.value = undefined cleanupBlurListener?.() }) </script> <style lang="postcss"> .aura-quill { --_aura-quill-bg: theme('colors.gray.800'); .ql-container { font-size: inherit; color: #495057; } .ql-tooltip { z-index: 100; background-color: var(--_aura-quill-bg) !important; } .ql-tooltip-arrow { border-bottom-color: var(--_aura-quill-bg) !important; } } </style>