Skip to content
Snippets Groups Projects
AHTMLEditor.vue 2.53 KiB
<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>