Code Monkey home page Code Monkey logo

vuetify-pro-tiptap's Introduction

Vuetify Pro Tiptap

A WYSIWYG rich-text editor using tiptap and vuetify for Vue.js

download version gzip Test LICENSE semantic-release: vue

English | 中文

Demo

👉https://yikoyu.github.io/vuetify-pro-tiptap/

👾Code Sandbox

👾Nuxt3 Code Sandbox

Features

  • Use vuetify components
  • Many out of box extension (welcome to submit an issue for feature request)
  • Markdown support
  • TypeScript support
  • I18n support(en, zhHans, nl)
  • Vuetify 3.x and Vue 3.x support

Vuetify 2.x

For Vuetify 2.x please use the latest version of [email protected]

Installation

NPM

pnpm add vuetify-pro-tiptap
# or
yarn add vuetify-pro-tiptap
# or
npm i vuetify-pro-tiptap -S

Install plugin

tiptap.ts
import { markRaw } from 'vue'
import { VuetifyTiptap, VuetifyViewer, createVuetifyProTipTap } from 'vuetify-pro-tiptap'
import { BaseKit, Bold, Italic, Underline, Strike, Color, Highlight, Heading, TextAlign, FontFamily, FontSize, SubAndSuperScript, BulletList, OrderedList, TaskList, Indent, Link, Image, Video, Table, Blockquote, HorizontalRule, Code, CodeBlock, Clear, Fullscreen, History } from 'vuetify-pro-tiptap'
import 'vuetify-pro-tiptap/style.css'
import SelectImage from './components/SelectImage.vue'

export const vuetifyProTipTap = createVuetifyProTipTap({
  lang: 'zhHans',
  components: {
    VuetifyTiptap,
    VuetifyViewer
  },
  extensions: [
    BaseKit.configure({
      placeholder: {
        placeholder: 'Enter some text...'
      }
    }),
    Bold,
    Italic,
    Underline,
    Strike,
    Code.configure({ divider: true }),
    Heading,
    TextAlign,
    FontFamily,
    FontSize,
    Color,
    Highlight.configure({ divider: true }),
    SubAndSuperScript.configure({ divider: true }),
    Clear.configure({ divider: true }),
    BulletList,
    OrderedList,
    TaskList,
    Indent.configure({ divider: true }),
    Link,
    Image.configure({
      imageTabs: [{ name: 'SELECT', component: markRaw(SelectImage) }],
      // hiddenTabs: ['upload'],
      upload(file: File) {
        const url = URL.createObjectURL(file)
        console.log('mock upload api :>> ', url)
        return Promise.resolve(url)
      }
    }),
    Video,
    Table.configure({ divider: true }),
    Blockquote,
    HorizontalRule,
    CodeBlock.configure({ divider: true }),
    History.configure({ divider: true }),
    Fullscreen
  ]
})
main.ts
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
import 'vuetify/styles'
import App from './App.vue'

import { vuetifyProTipTap } from './tiptap'

const vuetify = createVuetify()

const app = createApp(App)
app.use(vuetify)
app.use(vuetifyProTipTap)

// fix warning injected property "decorationClasses" is a ref and will be auto-unwrapped
// https://github.com/ueberdosis/tiptap/issues/1719
app.config.unwrapInjectedRef = true

app.mount('#app')

Global Settings

import { markRaw } from 'vue'
import { VuetifyTiptap, VuetifyViewer, createVuetifyProTipTap, defaultBubbleList } from 'vuetify-pro-tiptap'
import { BaseKit, Image, Fullscreen } from 'vuetify-pro-tiptap'
import 'vuetify-pro-tiptap/style.css'
import SelectImage from './components/SelectImage.vue'

export const vuetifyProTipTap = createVuetifyProTipTap({
  // Set default lang
  lang: 'zhHans',
  // Set markdown theme 
  markdownTheme: 'github',
  // Global registration app.component
  components: {
    VuetifyTiptap,
    VuetifyViewer
  },
  // Global registration extensions
  extensions: [
    BaseKit.configure({
      placeholder: {
        placeholder: 'Enter some text...'
      },
      bubble: {
        // default config
        list: {
          image: [ 'float-left', 'float-none', 'float-right', 'divider', 'size-small', 'size-medium', 'size-large', 'divider', 'textAlign', 'divider', 'image', 'image-aspect-ratio', 'remove'],
          text: ['bold', 'italic', 'underline', 'strike', 'divider', 'color', 'highlight', 'textAlign', 'divider', 'link'],
          video: ['video', 'remove']
        },
        defaultBubbleList: editor => {
          // You can customize the bubble menu here
          return defaultBubbleList(editor) // default customize bubble list
        }
      }
    }),
    Image.configure({
      // Generate a VDivider after the button
      divider: true,
      // Custom image tabs
      imageTabs: [{ name: 'SELECT', component: markRaw(SelectImage) }],
      // hidden default tab
      hiddenTabs: ['upload'],
      // custom upload function
      upload(file) {
        const url = URL.createObjectURL(file)
        console.log('mock upload api :>> ', url)
        return Promise.resolve(url)
      }
    }),
    Fullscreen.configure({
      // Generate a VSpacer after the button
      spacer: true
    })
  ]
})

Extensions

You can use the necessary extensions. The corresponding command-buttons will be added by declaring the order of the extension.

All available extensions:

Custom theme

Create github.scss

$value: 'github';

.vuetify-pro-tiptap-editor__content.markdown-theme-#{$value} {
  // your custom styles
  &.__dark {
    // your dark mode custom styles
  }
}

Import github.scss in ts

// import 'vuetify-pro-tiptap/style.css' // import all(editor and markdown) styles
import 'vuetify-pro-tiptap/styles/editor.css' // only use editor style, not using markdown style
import './styles/markdown/github.scss'

In the component using

<template>
  <VuetifyTiptap v-model="content" markdown-theme="github" />
  <VuetifyViewer :value="content" markdown-theme="github" />
</template>

Custom extensions

PreviewActionButton.vue
<script setup lang="ts">
import { ref } from 'vue'
import { mdiFileCodeOutline, mdiClose } from '@mdi/js'
import type { Editor } from '@tiptap/vue-3'
import { ActionButton } from 'vuetify-pro-tiptap'

interface Props {
  editor: Editor
  tooltip?: string
  disabled?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  tooltip: undefined,
  disabled: false
})

const dialog = ref(false)
const maxWidth = ref<number>(900)
</script>

<template>
  <ActionButton tooltip="全屏" :disabled="disabled">
    <VIcon>{{ `svg:${mdiFileCodeOutline}` }}</VIcon>
    <VDialog v-model="dialog" fullscreen hide-overlay activator="parent">
      <VCard>
        <VToolbar dark color="primary">
          <VBtn icon dark @click="dialog = false">
            <VIcon>{{ `svg:${mdiClose}` }}</VIcon>
          </VBtn>
        </VToolbar>

        <VContainer>
          <VSheet class="mx-auto" :max-width="maxWidth">
            <VuetifyViewer :value="editor.getHTML()" />
          </VSheet>
        </VContainer>
      </VCard>
    </VDialog>
  </ActionButton>
</template>
preview.ts
import { Extension } from '@tiptap/core'

import PreviewActionButton from '../components/PreviewActionButton.vue'
import type { ButtonView, GeneralOptions } from 'vuetify-pro-tiptap'

export interface PreviewOptions extends GeneralOptions {
  button: ButtonView
}

export default Extension.create<PreviewOptions>({
  name: 'preview',
  addOptions() {
    return {
      divider: false,
      spacer: false,
      button: () => ({
        component: PreviewActionButton,
        componentProps: {}
      })
    }
  }
})

I18n

Setting language

You can declare when you install the plugin.

import { createVuetifyProTipTap } from 'vuetify-pro-tiptap'

const VuetifyProTipTap = createVuetifyProTipTap({
  lang: 'zhHans'
})

Or use setLang dynamic change

import { locale } from 'vuetify-pro-tiptap'

locale.setLang('en')

Available languages:

  • en (default)
  • zhHans
  • nl

Use unavailable language

Loading unavailable language, use setMessage for Settings

import { locale } from 'vuetify-pro-tiptap'

locale.setMessage('zhHant', {
  // i18n text
})
locale.setLang('zhHant')

Usage

<script setup lang="ts">
import { ref } from 'vue'
import { VuetifyTiptap, VuetifyViewer } from 'vuetify-pro-tiptap'
import 'vuetify-pro-tiptap/style.css'
import { BaseKit, Bold, Italic, Underline, Strike, Color, Highlight, Heading, Link, Image, Video, Table, Fullscreen, History } from 'vuetify-pro-tiptap'

const extensions = [
  BaseKit.configure({
    placeholder: {
      placeholder: 'Enter some text...'
    }
  }),
  Bold,
  Italic,
  Underline,
  Strike,
  Color,
  Highlight,
  Heading,
  Link,
  Image,
  Video,
  Table,
  Fullscreen,
  History
]

const content = ref('')
</script>

<template>
  <VApp id="app">
    <VContainer>
      <VuetifyTiptap v-model="content" label="Title" rounded :min-height="200" :max-height="465" :max-width="900" :extensions="extensions" />
      <VuetifyViewer :value="content" />
    </VContainer>
  </VApp>
</template>

Props

VuetifyTiptap

Props

Name Type Default Description
modelValue string | JSONContent '' The input’s value
markdownTheme string | false 'default' Markdown theme
output 'html' | 'json' | 'text' 'html' Output format
dark boolean false Applies the dark theme variant to the component.
dense boolean false Reduces the input height
outlined boolean true Applies the outlined style to the input
flat boolean true Removes the card’s elevation
disabled boolean false Disable the input
label string undefined Sets input label
hideToolbar boolean false Hidden the toolbar
disableToolbar boolean false Disable the toolbar
hideBubble boolean false Hidden the bubble menu
removeDefaultWrapper boolean false Default wrapper when the delete editor is empty
maxWidth string | number undefined Sets the maximum width for the component.
minHeight string | number undefined Sets the minimum height for the component.
maxHeight string | number undefined Sets the maximum height for the component.
extensions AnyExtension[] [] Tiptap the extensions
editorClass string | string[] | Record<string, any> undefined Editor class

Slots

Name Description
editor Slot to customize editor
bottom Slot to customize editor bottom

Event

Name Type Description
update:modelValue string | JSONContent Emitted when editor onUpdate
update:markdownTheme string Emitted when change theme
change { editor: Editor, output: string | JSONContent } Emitted when editor onUpdate
enter Keyboard enter return

VuetifyViewer

Props

Name Type Default Description
value string | JSONContent '' The preview’s value
dark boolean false Applies the dark theme variant to the component.
dense boolean false Reduces the input height
markdownTheme string | false 'default' Markdown theme
xss boolean true Enable xss filter
xssOptions xss.IWhiteList Default rule Xss filter rule config
maxWidth string | number undefined Sets the maximum width for the component.
extensions AnyExtension[] [] Tiptap the extensions

Slots

Name Description
before Add content at the before
after Add content at the after

🏗 Contributing

  1. 🍴Fork it
  2. 🔀Create your branch: git checkout -b your-branch
  3. 🎨Make your changes
  4. 📝Commit your changes with Semantic Commit Messages (recommended)
  5. 🚀Push to the branch: git push origin your-branch
  6. 🎉Submit a PR to develop branch

📄 License

MIT

Thanks

vuetify-pro-tiptap's People

Contributors

renovate[bot] avatar yikoyu avatar semantic-release-bot avatar mtdvlpr avatar kissmannchristoph avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.