2.5.4 • Published 4 months ago

vuetify-pro-tiptap v2.5.4

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

Vuetify Pro Tiptap

一个 Vue.js 的基于 tiptapvuetify 的 「所见即所得」 富文本编辑器

download version gzip Test LICENSE semantic-release: vue

English | 中文

Demo

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

👾Code Sandbox

👾Nuxt3 Code Sandbox

特色

  • 使用 vuetify 组件
  • 许多开箱即用的 extension (欢迎提交 issue 为新的 feature 留下建议)
  • 支持 markdown 语法
  • TypeScript 支持
  • 支持 i18n
  • Vuetify 3.x 和 Vue 3.x 支持

Vuetify 2.x

Vuetify 2.x 请使用最新版本的 vuetify-pro-tiptap@1.X.X

安装

NPM安装

pnpm add vuetify-pro-tiptap
# 或者
yarn add vuetify-pro-tiptap
# 或者
npm i vuetify-pro-tiptap -S

安装插件

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
  ]
})
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')

全局设置

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

你可以只使用需要的 extension,对应的菜单按钮将会按照你声明的顺序被添加。

所有可用的 extensions:

自定义主题

创建 github.scss

$value: 'github';

.vuetify-pro-tiptap-editor__content.markdown-theme-#{$value} {
  // 自定义样式
  &.__dark {
    // dark 模式下的自定义样式
  }
}

在 ts 中导入 github.scss

// import 'vuetify-pro-tiptap/style.css' // 导入全部(editor 和 markdown)样式
import 'vuetify-pro-tiptap/styles/editor.css' // 只使用 editor 样式,不使用 markdown 样式
import './styles/markdown/github.scss'

在组件中使用

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

自定义 extension

<script setup lang="ts">
import { ref } from 'vue'
import { mdiClose, mdiFileCodeOutline } 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>
import { Extension } from '@tiptap/core'

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

export interface PreviewOptions extends GeneralOptions {
  button: ButtonView
}

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

国际化

设置语言

你可以在安装插件的时候声明

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

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

或者调用方法动态修改

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

locale.setLang('en')

可用的语言:

  • en (默认)
  • zhHans
  • nl
  • de

加载新语言

当前语言暂未提供时,可以使用 setMessage 进行设置

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

locale.setMessage('zhHant', {
  // 国际化文本
})
locale.setLang('zhHant')

用法

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

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

名称类型默认值说明
modelValuestring''输入的值
markdownThemestring | false'default'markdown主题
output'html' | 'json' | 'text''html'输出格式
darkbooleanfalse是否为深色主题
densebooleanfalse是否为紧凑模式
outlinedbooleantrue将轮廓样式应用于输入
flatbooleantrue移除卡片的 elevation
disabledbooleanfalse禁用输入
labelstringundefined设置输入标签
hideToolbarbooleanfalse隐藏工具栏
disableToolbarbooleanfalse禁用工具栏
hideBubblebooleanfalse隐藏气泡菜单
removeDefaultWrapperbooleanfalse删除编辑器为空时默认的包装器
maxWidthstring | numberundefined输入框最大宽度
minHeightstring | numberundefined输入框最小高度
maxHeightstring | numberundefined输入框最大高度
extensionsAnyExtension[][]tiptap插件
editorClassstring | string[] | Record\<string, any>undefined编辑器class

Slots

名称说明
editor自定义编辑器的插槽
bottom自定义编辑器底部的插槽

Event

名称类型说明
update:modelValuestring | JSONContent编辑器 onUpdate 时处触发
update:markdownThemestring切换主题时触发
change{ editor: Editor, output: string | JSONContent }编辑器 onUpdate 时处触发
enter键盘输入回车时触发

VuetifyViewer

Props

名称类型默认值说明
valuestring | JSONContent''预览的值
darkbooleanfalse是否为深色主题
densebooleanfalse是否为紧凑模式
markdownThemestring | false'default'markdown主题
xssbooleantrue是否开启xss过滤
xssOptionsxss.IWhiteList内置默认规则xss过滤规则配置
maxWidthstring | numberundefined预览最大宽度
extensionsAnyExtension[][]tiptap插件

Slots

名称说明
before在顶部添加内容
after在底部添加内容

🏗 贡献代码

  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 master branch

📄 许可证

MIT

🌹 鸣谢

2.5.0

7 months ago

2.5.2

6 months ago

2.5.1

6 months ago

2.5.4

4 months ago

2.5.3

5 months ago

2.4.3

11 months ago

2.4.2

11 months ago

2.4.1

11 months ago

2.4.0

11 months ago

2.3.0

1 year ago

2.2.0

1 year ago

2.1.0

2 years ago

2.0.2

2 years ago

2.0.1

2 years ago

2.0.0

2 years ago

1.4.3

2 years ago

1.4.2

2 years ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.0

2 years ago

1.2.0

2 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago