vue2-bbl-editor

A production-ready, fully configurable rich text editor for Vue 2.7 built with Tiptap
3,870
Install
npm install vue2-bbl-editor
Latest Version:1.6.0
Vue:2
License:MIT
Last Updated:Mar 25, 2026
Links: npm
Maintainer: blubird

Vue 2 Premium BBL Editor

A production-ready, fully configurable rich text editor for Vue 2.7 applications. Built with Tiptap and designed for professional use with extensive customization options and advanced upload management system.

🚀 Features

  • ✅ Vue 2.7 Compatible - Full support for Vue 2.7+ applications
  • ✅ Production Ready - Thoroughly tested and optimized for production use
  • ✅ Fully Configurable - Extensive props for customizing every aspect
  • ✅ Rich Text Editing - Complete formatting options (bold, italic, underline, etc.)
  • ✅ Advanced Media - Resizable images and videos with upload support
  • ✅ Smart Tables - Interactive tables with cell controls and resizing
  • ✅ Upload Management - Comprehensive upload system with multiple adapters
  • ✅ Multiple Themes - Default, minimal, and dark themes included
  • ✅ TypeScript Support - Full TypeScript definitions included
  • ✅ Accessibility - WCAG compliant with keyboard navigation
  • ✅ Auto-save - Built-in auto-save functionality
  • ✅ Source Code View - Toggle between visual and HTML source editing
  • ✅ External Integration - Easy integration with existing projects

📦 Installation

NPM Installation

npm install vue2-bbl-editor

CDN for HTML Output Styling

For rendering editor HTML content in external projects (React, Vue, Angular, etc.), use the CDN:

<!-- Global CDN styles for HTML output rendering -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vue2-bbl-editor/dist/vue2-html-bbl-editor.css" />

Then wrap your editor HTML content with the .bbl-html-section class:

<div class="bbl-html-section">
  <!-- Your editor HTML content here -->
</div>

See External HTML Rendering Guide for detailed usage in different frameworks.

PREVIEW

🎯 Quick Start

import Vue from 'vue'
import PremiumBblEditor from 'vue2-bbl-editor'

// Register globally
Vue.use(PremiumBblEditor)

// Or register with options
Vue.use(PremiumBblEditor, {
  registerAllComponents: true // Registers all sub-components globally
})

HTML Output with CDN Styling

The editor automatically wraps HTML output with the bbl-html-section class for CDN styling compatibility:

// HTML output is automatically wrapped
const htmlContent = this.$refs.editor.getContent() 
// Returns: <div class="bbl-html-section">...your content...</div>

// To get unwrapped content
const unwrappedContent = this.$refs.editor.getContent('html', false)
// Returns: ...your content...

// Control wrapping via prop
<PremiumBblEditor 
  :wrap-with-content-class="false" 
  v-model="content" 
/>

Component Registration

// Composition API version (Vue 2.7+ has built-in Composition API)
import { PremiumBblEditor } from 'vue2-bbl-editor'

// Options API version (pure Vue 2)
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditor,
    // or
    PremiumBblEditorOptionsAPI
  }
}

Basic Usage

Composition API Version
<template>
  <div>
    <PremiumBblEditor
      v-model="content"
      placeholder="Start writing your content..."
      @input="handleInput"
      @ready="handleReady"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      content: '<p>Hello World!</p>'
    }
  },
  methods: {
    handleInput(content) {
      console.log('Content updated:', content)
    },
    handleReady(editor) {
      console.log('Editor is ready:', editor)
    }
  }
}
</script>
Options API Version
<template>
  <div>
    <PremiumBblEditorOptionsAPI
      v-model="content"
      placeholder="Start writing your content..."
      @input="handleInput"
      @ready="handleReady"
    />
  </div>
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<p>Hello World with Options API!</p>'
    }
  },
  methods: {
    handleInput(content) {
      console.log('Content updated:', content)
    },
    handleReady(editor) {
      console.log('Options API Editor is ready:', editor)
    }
  }
}
</script>

📋 Complete Component API

Props

Content & Basic Configuration
Prop Type Default Description
value String '' Editor content (v-model)
placeholder String 'Start writing...' Placeholder text when editor is empty
editable Boolean true Whether editor content can be modified
outputFormat String 'html' Output format: 'html' or 'json'
autofocus Boolean false Auto-focus editor on mount
spellcheck Boolean true Enable browser spellcheck
Layout & Appearance
Prop Type Default Description
maxHeight String/Number null Maximum editor height (px or CSS value)
minHeight String/Number 200 Minimum editor height (px or CSS value)
theme String 'default' Theme: 'default', 'minimal', 'dark'
editorClass String/Array/Object '' Custom CSS classes for editor container
toolbarClass String/Array/Object '' Custom CSS classes for toolbar
contentClass String/Array/Object '' Custom CSS classes for content area
UI Component Visibility
Prop Type Default Description
showToolbar Boolean true Show/hide main toolbar
showBubbleMenu Boolean true Show/hide text selection bubble menu
showTableBubbleMenu Boolean true Show/hide table-specific bubble menu
showImageBubbleMenu Boolean true Show/hide image-specific bubble menu
showVideoBubbleMenu Boolean true Show/hide video-specific bubble menu
Toolbar Configuration
Prop Type Default Description
toolbarConfig Object See below Configure which toolbar buttons to show

Default Toolbar Configuration:

{
  // Text formatting
  bold: true,
  italic: true,
  underline: true,
  strike: true,
  code: true,
  
  // Text styling
  textColor: true,
  highlight: true,
  fontFamily: true,
  fontSize: true,
  
  // Alignment and spacing
  textAlign: true,
  lineHeight: true,
  
  // Structure
  headings: true,
  lists: true,
  blockquote: true,
  codeBlock: true,
  horizontalRule: true,
  
  // Media
  link: true,
  image: true,
  video: true,
  table: true,
  
  // Utilities
  clearFormatting: true,
  sourceCode: true,
  undo: true,
  redo: true
}
Extension Configuration
Prop Type Default Description
extensionConfig Object See below Configure extension behavior

Default Extension Configuration:

{
  image: {
    allowResize: true,
    allowAlignment: true,
    allowDelete: true,
    maxWidth: '100%',
    quality: 0.8
  },
  video: {
    allowResize: true,
    allowAlignment: true,
    allowDelete: true,
    maxWidth: '100%',
    autoplay: false
  },
  table: {
    resizable: true,
    allowRowControls: true,
    allowColumnControls: true,
    cellSelection: true
  },
  link: {
    openOnClick: false,
    autolink: true,
    linkOnPaste: true
  },
  textAlign: {
    types: ['heading', 'paragraph']
  }
}
Upload & Media Handling
Prop Type Default Description
uploadHandler Function null Custom upload handler function
imageUploadUrl String null URL endpoint for image uploads
videoUploadUrl String null URL endpoint for video uploads
maxFileSize Number 10485760 Maximum file size in bytes (10MB)
allowedImageTypes Array ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] Allowed image MIME types
allowedVideoTypes Array ['video/mp4', 'video/webm', 'video/ogg'] Allowed video MIME types
Local File Base64 Management
Prop Type Default Description
useBase64Upload Boolean false Force base64 conversion even when upload URLs are provided
base64Quality Number 0.8 Image compression quality (0.1 - 1.0)
base64MaxWidth Number 1920 Maximum width for compressed images (px)
base64MaxHeight Number 1080 Maximum height for compressed images (px)
enableImageCompression Boolean true Enable automatic image compression
preserveOriginalFileName Boolean true Preserve original file names in alt text
base64Prefix String 'data:' Custom prefix for base64 data URLs
Content Restrictions
Prop Type Default Description
maxContentLength Number null Maximum content length in characters
allowedHeadings Array [1, 2, 3, 4, 5, 6] Allowed heading levels
Styling Options
Prop Type Default Description
fontFamilies Array See below Available font families
fontSizes Array See below Available font sizes
lineHeights Array See below Available line heights

Default Font Options:

// Font Families
['Inter', 'Arial', 'Helvetica', 'Times New Roman', 'Georgia', 'Courier New', 'Monaco', 'Menlo']

// Font Sizes
['12px', '14px', '16px', '18px', '20px', '24px', '28px', '32px', '36px', '48px']

// Line Heights
['1', '1.2', '1.4', '1.6', '1.8', '2', '2.5', '3']
Auto-save Configuration
Prop Type Default Description
autoSave Boolean false Enable automatic content saving
autoSaveInterval Number 30000 Auto-save interval in milliseconds

Events

Event Payload Description
input content: string Emitted when content changes (v-model)
update content: string Emitted when content updates
ready editor: Editor Emitted when editor is ready for use
created editor: Editor Emitted when editor instance is created
focus - Emitted when editor gains focus
blur - Emitted when editor loses focus
destroyed - Emitted when editor is destroyed
auto-save content: string Emitted during auto-save
content-limit-exceeded {current: number, max: number} Emitted when content exceeds limit
image-compressed {originalSize: number, compressedSize: number, originalDimensions: object, compressedDimensions: object, compressionRatio: number} Emitted when image is compressed
error {type: string, message: string, ...} Emitted when errors occur

Methods

Access editor methods via template ref:

<template>
  <PremiumBblEditor
    ref="editor"
    v-model="content"
  />
</template>

<script>
export default {
  methods: {
    focusEditor() {
      this.$refs.editor.focus()
    },
    getContent() {
      return this.$refs.editor.getContent()
    },
    setContent(content) {
      this.$refs.editor.setContent(content)
    },
    executeCommand(command, options) {
      this.$refs.editor.executeCommand(command, options)
    }
  }
}
</script>

Available Methods:

  • focus() - Focus the editor
  • blur() - Blur the editor
  • getContent(format?) - Get editor content ('html' or 'json')
  • setContent(content, emitUpdate?) - Set editor content
  • executeCommand(command, options?) - Execute editor command

💡 Usage Examples

1. Basic Editor

Composition API Version
<template>
  <PremiumBblEditor
    v-model="content"
    placeholder="Start writing..."
    @input="handleInput"
    @ready="handleReady"
  />
</template>

<script>
export default {
  data() {
    return {
      content: '<p>Hello World!</p>'
    }
  },
  methods: {
    handleInput(content) {
      console.log('Content updated:', content)
    },
    handleReady(editor) {
      console.log('Editor is ready:', editor)
    }
  }
}
</script>
Options API Version
<template>
  <PremiumBblEditorOptionsAPI
    v-model="content"
    placeholder="Start writing..."
    @input="handleInput"
    @ready="handleReady"
  />
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<p>Hello World with Options API!</p>'
    }
  },
  methods: {
    handleInput(content) {
      console.log('Content updated:', content)
    },
    handleReady(editor) {
      console.log('Options API Editor is ready:', editor)
    }
  }
}
</script>

2. Minimal Configuration

Composition API Version
<template>
  <PremiumBblEditor
    v-model="content"
    theme="minimal"
    :toolbar-config="{
      bold: true,
      italic: true,
      underline: true,
      link: true,
      lists: true,
      undo: true,
      redo: true
    }"
    placeholder="Minimal editor..."
  />
</template>
Options API Version
<template>
  <PremiumBblEditorOptionsAPI
    v-model="content"
    theme="minimal"
    :toolbar-config="minimalToolbarConfig"
    placeholder="Minimal editor with Options API..."
  />
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<p>This is a minimal editor with limited features.</p>',
      minimalToolbarConfig: {
        bold: true,
        italic: true,
        underline: true,
        link: true,
        lists: true,
        undo: true,
        redo: true
      }
    }
  }
}
</script>

3. Advanced Configuration with Upload

Composition API Version
<template>
  <PremiumBblEditor
    v-model="content"
    :max-height="400"
    :auto-save="true"
    :auto-save-interval="15000"
    :upload-handler="customUploadHandler"
    :font-families="['Inter', 'Arial', 'Georgia']"
    :extension-config="{
      image: {
        allowResize: true,
        maxWidth: '800px'
      },
      table: {
        resizable: true,
        cellSelection: true
      }
    }"
    @auto-save="handleAutoSave"
    @ready="handleReady"
  />
</template>

<script>
export default {
  data() {
    return {
      content: '<h2>Advanced Editor</h2><p>With custom upload handling...</p>'
    }
  },
  methods: {
    async customUploadHandler(file) {
      const formData = new FormData()
      formData.append('file', file)
      
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData
      })
      
      const data = await response.json()
      return { src: data.url, alt: file.name }
    },
    
    handleAutoSave(content) {
      // Save to your backend
      this.saveToServer(content)
    },
    
    handleReady(editor) {
      console.log('Editor ready with advanced features')
    }
  }
}
</script>
Options API Version
<template>
  <PremiumBblEditorOptionsAPI
    v-model="content"
    :max-height="maxHeight"
    :auto-save="autoSave"
    :auto-save-interval="autoSaveInterval"
    :upload-handler="customUploadHandler"
    :font-families="fontFamilies"
    :extension-config="extensionConfig"
    @auto-save="handleAutoSave"
    @ready="handleReady"
  />
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<h2>Advanced Options API Editor</h2><p>With comprehensive configuration...</p>',
      maxHeight: 400,
      autoSave: true,
      autoSaveInterval: 15000,
      fontFamilies: ['Inter', 'Arial', 'Georgia'],
      extensionConfig: {
        image: {
          allowResize: true,
          maxWidth: '800px',
          quality: 0.9
        },
        table: {
          resizable: true,
          cellSelection: true,
          allowRowControls: true
        },
        link: {
          autolink: true,
          linkOnPaste: true
        }
      }
    }
  },
  methods: {
    async customUploadHandler(file) {
      try {
        const formData = new FormData()
        formData.append('file', file)
        
        const response = await fetch('/api/upload', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${this.getAuthToken()}`
          },
          body: formData
        })
        
        if (!response.ok) {
          throw new Error(`Upload failed: ${response.statusText}`)
        }
        
        const data = await response.json()
        return { 
          src: data.url, 
          alt: file.name,
          width: data.width,
          height: data.height
        }
      } catch (error) {
        console.error('Upload error:', error)
        throw error
      }
    },
    
    handleAutoSave(content) {
      console.log('Auto-saving content...')
      this.saveToServer(content)
    },
    
    handleReady(editor) {
      console.log('Options API Editor ready with advanced features')
      // Store editor reference for later use
      this.editor = editor
    },
    
    async saveToServer(content) {
      try {
        await fetch('/api/save', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${this.getAuthToken()}`
          },
          body: JSON.stringify({ content })
        })
        console.log('Content saved successfully')
      } catch (error) {
        console.error('Save failed:', error)
      }
    },
    
    getAuthToken() {
      return localStorage.getItem('authToken') || ''
    }
  }
}
</script>

4. Read-only Display

Composition API Version
<template>
  <PremiumBblEditor
    v-model="content"
    :editable="false"
    :show-toolbar="false"
    :show-bubble-menu="false"
  />
</template>

<script>
export default {
  data() {
    return {
      content: `
        <h3>Read-only Content</h3>
        <p>This content is displayed in <strong>read-only mode</strong>.</p>
        <blockquote>
          <p>Perfect for displaying formatted content without editing capabilities.</p>
        </blockquote>
      `
    }
  }
}
</script>
Options API Version
<template>
  <PremiumBblEditorOptionsAPI
    v-model="content"
    :editable="editable"
    :show-toolbar="showToolbar"
    :show-bubble-menu="showBubbleMenu"
    :show-table-bubble-menu="showTableBubbleMenu"
    :show-image-bubble-menu="showImageBubbleMenu"
    :show-video-bubble-menu="showVideoBubbleMenu"
  />
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: `
        <h3>Read-only Content (Options API)</h3>
        <p>This content is displayed in <strong>read-only mode</strong> using Options API.</p>
        <blockquote>
          <p>Perfect for displaying formatted content without editing capabilities.</p>
        </blockquote>
        <ul>
          <li>No toolbar visible</li>
          <li>No bubble menus</li>
          <li>Content cannot be modified</li>
        </ul>
      `,
      editable: false,
      showToolbar: false,
      showBubbleMenu: false,
      showTableBubbleMenu: false,
      showImageBubbleMenu: false,
      showVideoBubbleMenu: false
    }
  }
}
</script>

5. Custom Styling & Dark Theme

Composition API Version
<template>
  <PremiumBblEditor
    v-model="content"
    theme="dark"
    editor-class="custom-editor"
    toolbar-class="custom-toolbar"
    content-class="custom-content"
    placeholder="Dark themed editor..."
  />
</template>

<style>
.custom-editor {
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.custom-toolbar {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.custom-content {
  font-family: 'Georgia', serif;
}
</style>
Options API Version
<template>
  <PremiumBblEditorOptionsAPI
    v-model="content"
    :theme="theme"
    :editor-class="editorClass"
    :toolbar-class="toolbarClass"
    :content-class="contentClass"
    :placeholder="placeholder"
  />
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<p>This Options API editor uses custom styling and dark theme.</p>',
      theme: 'dark',
      placeholder: 'Dark themed Options API editor...',
      editorClass: 'custom-editor-options',
      toolbarClass: 'custom-toolbar-options',
      contentClass: 'custom-content-options'
    }
  }
}
</script>

<style scoped>
.custom-editor-options {
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  border: 2px solid #4f46e5;
}

.custom-toolbar-options {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-bottom: 1px solid #4f46e5;
}

.custom-content-options {
  font-family: 'Georgia', serif;
  line-height: 1.8;
}
</style>

6. Local Base64 File Management

Composition API Version
<template>
  <PremiumBblEditor
    v-model="content"
    :use-base64-upload="true"
    :enable-image-compression="true"
    :base64-quality="0.8"
    :base64-max-width="1920"
    :base64-max-height="1080"
    :preserve-original-file-name="true"
    placeholder="Upload images for local base64 conversion..."
    @image-compressed="handleImageCompressed"
    @ready="handleReady"
  />
</template>

<script>
export default {
  data() {
    return {
      content: '<p>Upload images to see base64 conversion with compression!</p>'
    }
  },
  methods: {
    handleImageCompressed(compressionData) {
      console.log('Image compressed:', {
        originalSize: compressionData.originalSize,
        compressedSize: compressionData.compressedSize,
        compressionRatio: compressionData.compressionRatio,
        savings: Math.round(((compressionData.originalSize - compressionData.compressedSize) / compressionData.originalSize) * 100) + '%'
      })
    },
    
    handleReady(editor) {
      console.log('Editor ready with base64 management')
    }
  }
}
</script>
Options API Version
<template>
  <PremiumBblEditorOptionsAPI
    v-model="content"
    :use-base64-upload="useBase64"
    :enable-image-compression="enableCompression"
    :base64-quality="quality"
    :base64-max-width="maxWidth"
    :base64-max-height="maxHeight"
    :preserve-original-file-name="preserveNames"
    placeholder="Options API with base64 management..."
    @image-compressed="onImageCompressed"
    @ready="onEditorReady"
  />
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<p>Options API editor with base64 file management!</p>',
      useBase64: true,
      enableCompression: true,
      quality: 0.8,
      maxWidth: 1920,
      maxHeight: 1080,
      preserveNames: true
    }
  },
  methods: {
    onImageCompressed(data) {
      console.log('Options API - Image compressed:', data)
      
      // Show compression statistics
      this.showCompressionStats(data)
    },
    
    onEditorReady(editor) {
      console.log('Options API Editor ready with base64 management')
      this.editorInstance = editor
    },
    
    showCompressionStats(data) {
      const savings = Math.round(((data.originalSize - data.compressedSize) / data.originalSize) * 100)
      alert(`Image compressed successfully!\nOriginal: ${this.formatFileSize(data.originalSize)}\nCompressed: ${this.formatFileSize(data.compressedSize)}\nSavings: ${savings}%`)
    },
    
    formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes'
      const k = 1024
      const sizes = ['Bytes', 'KB', 'MB', 'GB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
    }
  }
}
</script>

7. Event Handling & Method Access

Composition API Version
<template>
  <div>
    <PremiumBblEditor
      ref="editor"
      v-model="content"
      @input="handleInput"
      @focus="handleFocus"
      @blur="handleBlur"
      @ready="handleReady"
      @auto-save="handleAutoSave"
      @content-limit-exceeded="handleLimitExceeded"
    />
    
    <div class="editor-controls">
      <button @click="focusEditor">Focus Editor</button>
      <button @click="getEditorContent">Get Content</button>
      <button @click="setEditorContent">Set Content</button>
      <button @click="executeCustomCommand">Bold Text</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      content: '<p>Editor with comprehensive event handling...</p>'
    }
  },
  methods: {
    handleInput(content) {
      console.log('Content changed:', content.length, 'characters')
    },
    
    handleFocus() {
      console.log('Editor focused')
    },
    
    handleBlur() {
      console.log('Editor blurred')
    },
    
    handleReady(editor) {
      console.log('Editor ready:', editor)
    },
    
    handleAutoSave(content) {
      console.log('Auto-saving content...')
      // Save to backend
    },
    
    handleLimitExceeded({ current, max }) {
      alert(`Content limit exceeded: ${current}/${max} characters`)
    },
    
    // Method examples
    focusEditor() {
      this.$refs.editor.focus()
    },
    
    getEditorContent() {
      const content = this.$refs.editor.getContent()
      console.log('Current content:', content)
    },
    
    setEditorContent() {
      this.$refs.editor.setContent('<p>New content set programmatically!</p>')
    },
    
    executeCustomCommand() {
      this.$refs.editor.executeCommand('toggleBold')
    }
  }
}
</script>
Options API Version
<template>
  <div>
    <PremiumBblEditorOptionsAPI
      ref="editorOptionsAPI"
      v-model="content"
      :max-content-length="maxContentLength"
      :auto-save="autoSave"
      :auto-save-interval="autoSaveInterval"
      @input="handleInput"
      @focus="handleFocus"
      @blur="handleBlur"
      @ready="handleReady"
      @auto-save="handleAutoSave"
      @content-limit-exceeded="handleLimitExceeded"
      @error="handleError"
    />
    
    <div class="editor-controls">
      <button @click="focusEditor">Focus Editor</button>
      <button @click="blurEditor">Blur Editor</button>
      <button @click="getEditorContent">Get Content</button>
      <button @click="setEditorContent">Set Content</button>
      <button @click="executeCustomCommand">Toggle Bold</button>
      <button @click="checkActiveState">Check Bold State</button>
      <button @click="clearContent">Clear Content</button>
    </div>
    
    <div class="editor-status" v-if="editorStatus">
      <h4>Editor Status</h4>
      <p>Content Length: {{ contentLength }}</p>
      <p>Is Bold Active: {{ isBoldActive }}</p>
      <p>Last Action: {{ lastAction }}</p>
    </div>
  </div>
</template>

<script>
import { PremiumBblEditorOptionsAPI } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditorOptionsAPI
  },
  data() {
    return {
      content: '<p>Options API Editor with comprehensive event handling and method access...</p>',
      maxContentLength: 5000,
      autoSave: true,
      autoSaveInterval: 20000,
      editorInstance: null,
      editorStatus: true,
      contentLength: 0,
      isBoldActive: false,
      lastAction: 'None'
    }
  },
  methods: {
    handleInput(content) {
      this.contentLength = content.length
      this.lastAction = 'Content changed'
      console.log('Options API - Content changed:', content.length, 'characters')
    },
    
    handleFocus() {
      this.lastAction = 'Editor focused'
      console.log('Options API - Editor focused')
    },
    
    handleBlur() {
      this.lastAction = 'Editor blurred'
      console.log('Options API - Editor blurred')
    },
    
    handleReady(editor) {
      this.editorInstance = editor
      this.lastAction = 'Editor ready'
      console.log('Options API - Editor ready:', editor)
    },
    
    handleAutoSave(content) {
      this.lastAction = 'Auto-saved'
      console.log('Options API - Auto-saving content...')
      this.saveToServer(content)
    },
    
    handleLimitExceeded({ current, max }) {
      this.lastAction = `Content limit exceeded: ${current}/${max}`
      alert(`Content limit exceeded: ${current}/${max} characters`)
    },
    
    handleError(error) {
      this.lastAction = `Error: ${error.type}`
      console.error('Options API - Editor error:', error)
    },
    
    // Method examples using the Options API component
    focusEditor() {
      this.$refs.editorOptionsAPI.focus()
      this.lastAction = 'Focus method called'
    },
    
    blurEditor() {
      this.$refs.editorOptionsAPI.blur()
      this.lastAction = 'Blur method called'
    },
    
    getEditorContent() {
      const htmlContent = this.$refs.editorOptionsAPI.getContent('html')
      const jsonContent = this.$refs.editorOptionsAPI.getContent('json')
      
      console.log('HTML Content:', htmlContent)
      console.log('JSON Content:', jsonContent)
      this.lastAction = 'Get content method called'
    },
    
    setEditorContent() {
      const newContent = `
        <h3>New Content Set Programmatically</h3>
        <p>This content was set using the <strong>setContent</strong> method in Options API.</p>
        <ul>
          <li>Method called at: ${new Date().toLocaleTimeString()}</li>
          <li>Using Options API component</li>
        </ul>
      `
      this.$refs.editorOptionsAPI.setContent(newContent)
      this.lastAction = 'Set content method called'
    },
    
    executeCustomCommand() {
      this.$refs.editorOptionsAPI.executeCommand('toggleBold')
      this.checkActiveState()
      this.lastAction = 'Toggle bold command executed'
    },
    
    checkActiveState() {
      this.isBoldActive = this.$refs.editorOptionsAPI.isActive('bold')
      this.lastAction = 'Checked bold active state'
    },
    
    clearContent() {
      this.$refs.editorOptionsAPI.setContent('<p></p>')
      this.lastAction = 'Content cleared'
    },
    
    async saveToServer(content) {
      try {
        // Simulate API call
        await new Promise(resolve => setTimeout(resolve, 1000))
        console.log('Options API - Content saved to server')
      } catch (error) {
        console.error('Options API - Save failed:', error)
      }
    }
  }
}
</script>

<style scoped>
.editor-controls {
  margin: 20px 0;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.editor-controls button {
  padding: 8px 16px;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.editor-controls button:hover {
  background: #2563eb;
}

.editor-status {
  margin-top: 20px;
  padding: 15px;
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 6px;
}

.editor-status h4 {
  margin-top: 0;
  color: #495057;
}

.editor-status p {
  margin: 5px 0;
  font-size: 14px;
  color: #6c757d;
}
</style>

🔌 Upload System Integration

The editor includes a comprehensive upload management system for handling file uploads with multiple adapters.

Basic Upload Setup

<template>
  <PremiumBblEditor
    v-model="content"
    @ready="onEditorReady"
  />
</template>

<script>
import { 
  UploadManager, 
  LocalStorageAdapter, 
  integrateTipTapUpload,
  createUploadConfig
} from 'vue2-bbl-editor'

export default {
  data() {
    return {
      content: '<p>Try uploading images...</p>',
      uploadManager: null
    }
  },
  
  async created() {
    // Create upload configuration
    const config = createUploadConfig('localStorage', {
      validation: {
        maxSize: 5 * 1024 * 1024, // 5MB
        allowedTypes: ['image/jpeg', 'image/png', 'image/gif']
      }
    })
    
    // Initialize upload manager
    this.uploadManager = new UploadManager(config)
    
    // Register local storage adapter
    const adapter = new LocalStorageAdapter()
    this.uploadManager.registerAdapter(adapter)
    this.uploadManager.setDefaultAdapter('local-storage')
  },
  
  methods: {
    onEditorReady(editor) {
      // Integrate upload system with editor
      integrateTipTapUpload(editor, this.uploadManager, {
        onStart: () => console.log('Upload started'),
        onEnd: () => console.log('Upload finished')
      })
    }
  }
}
</script>

Custom Upload Adapter

// Create custom adapter for your API
class CustomApiAdapter {
  constructor(options) {
    this.name = 'custom-api'
    this.apiUrl = options.apiUrl
    this.headers = options.headers || {}
  }

  async upload(file, options) {
    const formData = new FormData()
    formData.append('file', file)

    const response = await fetch(this.apiUrl, {
      method: 'POST',
      headers: this.headers,
      body: formData
    })

    if (!response.ok) {
      throw new Error(`Upload failed: ${response.statusText}`)
    }

    const result = await response.json()
    return {
      url: result.url,
      size: file.size,
      metadata: {
        fileName: file.name,
        fileType: file.type
      }
    }
  }
}

// Register and use
const adapter = new CustomApiAdapter({
  apiUrl: '/api/upload',
  headers: { 'Authorization': 'Bearer your-token' }
})
uploadManager.registerAdapter(adapter)
uploadManager.setDefaultAdapter('custom-api')

Upload Event Handling

// Listen to upload events
uploadManager.on('progress', (progress) => {
  console.log(`Upload progress: ${progress.percentage}%`)
})

uploadManager.on('uploadCompleted', (event) => {
  console.log('Upload completed:', event.result.url)
})

uploadManager.on('uploadFailed', (event) => {
  console.error('Upload failed:', event.error.message)
})

🧩 Individual Component Usage

Import and use individual components for custom implementations:

import {
  PremiumBblEditor,
  PremiumBblEditorOptionsAPI,
  ToolbarMain,
  ToolbarGroup,
  ToolbarButton,
  EditorBubbleMenu,
  TableBubbleMenu,
  ImageBubbleMenu,
  VideoBubbleMenu,
  ImageModal,
  VideoModal,
  LinkModal,
  DiagnosticTool,
  DebugHelper,
  useEditor
} from 'vue2-bbl-editor'

Available Components

Main Components
  • PremiumBblEditor - Main editor component (Composition API)
  • PremiumBblEditorOptionsAPI - Main editor component (Options API)
  • DiagnosticTool - Built-in diagnostic and troubleshooting tool
  • DebugHelper - Development debugging helper
Toolbar Components
  • ToolbarMain - Complete toolbar component
  • ToolbarGroup - Toolbar button group container
  • ToolbarButton - Individual toolbar button
Menu Components
  • EditorBubbleMenu - Text selection bubble menu
  • TableBubbleMenu - Table-specific bubble menu
  • ImageBubbleMenu - Image-specific bubble menu
  • VideoBubbleMenu - Video-specific bubble menu
Modal Components
  • ImageModal - Image upload and configuration modal
  • VideoModal - Video upload and configuration modal
  • LinkModal - Link creation and editing modal
Composables
  • useEditor - Core editor composable for custom implementations

Custom Implementation Example

<template>
  <div class="custom-editor">
    <ToolbarMain
      v-if="editor"
      :editor="editor"
      :config="toolbarConfig"
      @execute-command="executeCommand"
    />
    
    <div class="editor-content">
      <editor-content :editor="editor" />
    </div>
    
    <EditorBubbleMenu
      v-if="editor"
      :editor="editor"
      :config="toolbarConfig"
    />
  </div>
</template>

<script>
import { EditorContent } from '@tiptap/vue-2'
import { ToolbarMain, EditorBubbleMenu, useEditor } from 'vue2-bbl-editor'

export default {
  components: {
    EditorContent,
    ToolbarMain,
    EditorBubbleMenu
  },
  
  setup() {
    const { editor, executeCommand } = useEditor({
      content: '<p>Custom editor implementation</p>',
      extensions: [
        // Your custom extensions
      ]
    })

    return {
      editor,
      executeCommand,
      toolbarConfig: {
        bold: true,
        italic: true,
        underline: true
      }
    }
  }
}
</script>

🔌 Extensions & Customization

The package includes custom extensions that enhance the editing experience:

Available Extensions

  • ResizableImage - Enhanced image handling with resize controls
  • ResizableVideo - Video embedding with resize functionality
  • CustomTableCell - Enhanced table cell with controls
  • CustomTableHeader - Enhanced table header with controls

Using Extensions Directly

import { ResizableImage, ResizableVideo } from 'vue2-bbl-editor'
import { Editor } from '@tiptap/vue-2'

const editor = new Editor({
  extensions: [
    ResizableImage.configure({
      allowResize: true,
      allowAlignment: true,
      maxWidth: '800px'
    }),
    ResizableVideo.configure({
      allowResize: true,
      autoplay: false
    })
  ]
})

🌐 External Project Integration

For detailed integration instructions for external projects, see our External Integration Guide.

Quick Integration

# Install in your existing project
npm install vue2-bbl-editor

# Install required dependencies (Vue 2.7+ has built-in Composition API)
npm install @tiptap/core @tiptap/vue-2 @tiptap/starter-kit
<template>
  <PremiumBblEditor
    v-model="content"
    @ready="onEditorReady"
  />
</template>

<script>
import { PremiumBblEditor } from 'vue2-bbl-editor'

export default {
  components: { PremiumBblEditor },
  data() {
    return {
      content: '<p>Start editing...</p>'
    }
  },
  methods: {
    onEditorReady(editor) {
      console.log('Editor ready in external project!')
    }
  }
}
</script>

🧪 Development & Testing

# Install dependencies
npm install

# Run development server
npm run serve

# Build library
npm run build:lib

# Run tests
npm test

# Lint code
npm run lint

Development Tools

The package includes built-in development and diagnostic tools:

<template>
  <div>
    <!-- Your editor -->
    <PremiumBblEditor v-model="content" />
    
    <!-- Development tools -->
    <DiagnosticTool v-if="isDevelopment" />
    <DebugHelper v-if="isDevelopment" />
  </div>
</template>

<script>
import { PremiumBblEditor, DiagnosticTool, DebugHelper } from 'vue2-bbl-editor'

export default {
  components: {
    PremiumBblEditor,
    DiagnosticTool,
    DebugHelper
  },
  computed: {
    isDevelopment() {
      return process.env.NODE_ENV === 'development'
    }
  }
}
</script>

🔧 Troubleshooting

Common Issues

  1. Editor not loading: Check if all required dependencies are installed
  2. Upload not working: Verify upload handler configuration
  3. Styling issues: Ensure CSS is properly imported
  4. TypeScript errors: Check type definitions import

Diagnostic Tools

Use the built-in diagnostic tool to identify issues:

<template>
  <div>
    <PremiumBblEditor v-model="content" />
    <DiagnosticTool />
  </div>
</template>

Debug Mode

Enable debug mode for detailed logging:

<template>
  <PremiumBblEditor
    v-model="content"
    :debug="true"
  />
</template>

For more troubleshooting help, see TROUBLESHOOTING.md.

📚 Documentation

📄 License

MIT License - see LICENSE file for details.

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📞 Support

🙏 Acknowledgments

Related Packages

vue2-premium-bbl-editor

A production-ready, fully configurable rich text editor for Vue 2.6 built with T...

3,495
@markweave/vue2

Vue 2 adapter for the Markweave Markdown-first WYSIWYG editor.

9,500 1
@orilight/editor

基于 Tiptap 3 的 Vue 3 富文本编辑器组件:Word 粘贴还原、图片 / 附件上传、表格与...

0
tiptap-vue-pro-core

Tiptap Vue Pro 的 UI 无关核心层:封装 Tiptap v3,提供 composable 与类型契约

3,174 8
tiptap-vue-pro-element-plus

Tiptap Vue Pro 的 Element Plus 适配:开箱即用的富文本编辑器组件

2,698 9

Version History

Version Released Vue License
1.6.0 ^2.7.0 MIT
1.5.8 ^2.6.0 MIT
1.5.7 ^2.6.0 MIT
1.5.6 ^2.6.0 MIT
1.5.5 ^2.6.0 MIT