npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue2-bbl-editor

v1.6.0

Published

A production-ready, fully configurable rich text editor for Vue 2.7 built with Tiptap

Readme

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

Global Registration (Recommended)

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