Skip to main content

Developing Plugins

On-Codemerge's flexible architecture allows for the development of custom plugins to extend the editor's functionality. This guide will walk you through the process of creating a new plugin.

Overview

A plugin in On-Codemerge is an independent module that interacts with the EditorCore to provide additional functionalities or customizations. Each plugin must implement the IEditorModule interface.

Implementing IEditorModule Interface

The IEditorModule interface ensures that your plugin has the necessary structure to integrate with the editor core. It typically includes initialization and other essential methods.

Example Interface Implementation

import type EditorCore, { IEditorModule } from 'on-codemerge';

export class MyCustomPlugin implements IEditorModule {
private core: EditorCore;

initialize(core: EditorCore): void {
this.core = core;
// Initialization logic here
}

destroy(): void {

}

// Additional methods specific to your plugin
}

initialize(core: EditorCore): void

The initialize method is a required method in a custom plugin class. It is called when the plugin is being initialized and provides access to the EditorCore instance, allowing the plugin to integrate with the editor and perform any necessary setup. Here's what this method typically does:

  • Receives the EditorCore instance as a parameter, which provides access to the editor's functionality and elements.

  • Sets up the plugin by configuring buttons, event listeners, or any other components necessary for its functionality within the editor.

  • Performs any initializations, such as creating UI elements, adding buttons to the toolbar, or registering event handlers.

  • This method is where you can define the behavior of your custom plugin within the editor.

destroy(): void

The destroy method is also a required method in a custom plugin class. It is called when the plugin needs to be removed or cleaned up, ensuring that it doesn't leave any lingering resources or event listeners. Here's what this method typically does:

  • Removes any event listeners that the plugin added to prevent memory leaks and unwanted behavior after the plugin is removed.

  • Cleans up any resources or DOM elements created by the plugin during its initialization.

  • Performs any necessary cleanup to revert any changes or configurations made by the plugin within the editor.

  • This method ensures that the plugin is properly disposed of when it's no longer needed in the editor.

Custom plugins in an editor framework often follow this initialize and destroy pattern to seamlessly integrate and remove their functionality from the editor. It's essential to implement these methods correctly to ensure that your plugin behaves as expected and doesn't cause issues when enabled or disabled in the editor.

Creating a Basic Plugin

Let's create a simple plugin that adds a custom button to the editor's toolbar.

Step 1: Define Your Plugin Class

import type EditorCore, { IEditorModule } from 'on-codemerge';


export class MyCustomButton implements IEditorModule {
private core: EditorCore;
private button: HTMLButtonElement | null = null;

initialize(core: EditorCore): void {
this.core = core;
this.addButton();
}

private addButton(): void {
this.button = document.createElement("button");
this.button.textContent = "My Button";
this.button.addEventListener("click", this.onButtonClick);
}

private onButtonClick = (): void => {
console.log("Button clicked!");
// Your button logic here
}

destroy(): void {
if (this.button) {
// Remove the event listener to prevent memory leaks
this.button.removeEventListener("click", this.onButtonClick);
// Set the button reference to null
this.button = null;
}
}

}

Step 2: Register Your Plugin with the Editor

import EditorCore from 'on-codemerge';
import { MyCustomButton } from 'path_to_my_custom_button';

document.addEventListener('DOMContentLoaded', () => {
const appElement = document.getElementById('app');
if (appElement) {
const editor = new EditorCore(appElement);
editor.registerModule(new MyCustomButton());
// ... other configurations and module registrations
}
});

Modifying Text and Inserting Content

Plugins can also modify the editor's text or insert new content. Below are examples illustrating these capabilities.

Text Modification

To modify text, you can manipulate the DOM elements within the editor. Here’s an example of a plugin that converts selected text to uppercase:

import type EditorCore, { IEditorModule } from 'on-codemerge';

export class UppercaseTextButton implements IEditorModule {
private core: EditorCore;
private button: HTMLButtonElement | null = null;

initialize(core: EditorCore): void {
this.core = core;
this.addButton();
}

private addButton(): void {
this.button = document.createElement("button");
this.button.textContent = "Uppercase";
this.button.addEventListener("click", this.onButtonClick);

this.core.toolbar.addElement(this.button);
}

private onButtonClick = (): void => {
const selectionRange = this.core.getCurrentSelection();
if (selectionRange && !selectionRange.collapsed) {
const selectedText = selectionRange.toString();
const uppercasedText = selectedText.toUpperCase();

const textNode = document.createTextNode(uppercasedText);
selectionRange.deleteContents();
selectionRange.insertNode(textNode);

// Update the editor content
this.core.setContent(this.core.editor.getEditorElement().innerHTML);
}
}

destroy(): void {
if (this.button) {
// Remove the event listener to prevent memory leaks
this.button.removeEventListener("click", this.onButtonClick);
// Set the button reference to null
this.button = null;
}
}
}

Content Insertion

For inserting new content, like an image or a custom HTML element, you can use the insertHTMLIntoEditor method of EditorCore. Here's an example of inserting an image:

import type EditorCore, { IEditorModule } from 'on-codemerge';
export class InsertImageButton implements IEditorModule {
private core: EditorCore;
private button: HTMLButtonElement | null = null;

initialize(core: EditorCore): void {
this.core = core;
this.addButton();
}

private addButton(): void {
this.button = document.createElement("button");
this.button.textContent = "Insert Image";
this.button.addEventListener("click", this.onButtonClick);

this.core.toolbar.addElement(this.button);
}

private onButtonClick = (): void => {
const imageUrl = 'path_to_your_image.jpg'; // Replace with your image URL
const imgElement = `<img src="${imageUrl}" alt="Inserted Image"/>`;

this.core.insertHTMLIntoEditor(imgElement);
}

destroy(): void {
if (this.button) {
// Remove the event listener to prevent memory leaks
this.button.removeEventListener("click", this.onButtonClick);
// Set the button reference to null
this.button = null;
}
}
}

In these examples, the UppercaseTextButton plugin modifies the selected text to uppercase, and the InsertImageButton plugin inserts an image at the current cursor position. These examples demonstrate how plugins can interact with the editor's content and extend its functionality.

Remember, these plugins should be registered with the editor core, similar to the previous examples:

const editor = new EditorCore(appElement);
editor.registerModule(new UppercaseTextButton());
editor.registerModule(new InsertImageButton());

With these enhancements, the documentation now provides a more complete guide on creating plugins that can modify and insert content in the On-Codemerge editor. This will be immensely helpful for developers looking to extend the editor's capabilities.

Certainly, here's the part of the documentation related to the Footer class:

Footer Class Documentation

The Footer class is a fundamental component of the On-Codemerge editor that manages the footer panel's functionality and appearance. This documentation provides an overview of the Footer class and its usage within the editor.

Example Usage

import type EditorCore, { IEditorModule } from 'on-codemerge';
export class Plugin implements IEditorModule {
initialize(core: EditorCore): void {
const footer = core.footer;
footer.addButtonIcon('Click Me', '<i class="fas fa-check"></i>', () => {
// Handle button click action
alert('Button clicked!');
});
}
}

Toolbar Class Documentation

The Toolbar class is a fundamental component of the On-Codemerge editor that manages the toolbar's functionality and appearance. This documentation provides an overview of the Toolbar class and its usage within the editor.

Example Usage

import type EditorCore, { IEditorModule } from 'on-codemerge';
export class Plugin implements IEditorModule {
initialize(core: EditorCore): void {
const toolbar = core.toolbar;
toolbar.addButtonIcon('Click Me', '<i class="fas fa-check"></i>', () => {
// Handle button click action
alert('Button clicked!');
});
}
}

Editor Class Documentation

The Editor class is a core component of the On-Codemerge editor responsible for managing the editor's content and user interactions. This documentation provides an overview of the Editor class and its usage within the editor.

Example Usage

import type EditorCore, { IEditorModule } from 'on-codemerge';
export class Plugin implements IEditorModule {
initialize(core: EditorCore): void {
const editor = core.editor;
editor.setContent('<p>Hello, On-Codemerge editor!</p>');
core.subscribeToContentChange((text) => {
console.log(text)
});
}
}

EditorState Class Documentation

The EditorState class is responsible for managing the state and history of the editor's content, enabling undo and redo functionality. This documentation provides an overview of the EditorState class and its methods.

Example Usage

import type EditorCore, { IEditorModule } from 'on-codemerge';
export class Plugin implements IEditorModule {
initialize(core: EditorCore): void {
const editorState = core.state;
editorState.setContent('<p>Hello, On-Codemerge editor!</p>');
editorState.setContent('<p>Updated content</p>');
console.log(editorState.getContent()); // Output: <p>Updated content</p>

// Perform undo operation
editorState.undo();
console.log(editorState.getContent()); // Output: <p>Hello, On-Codemerge editor!</p>

// Perform redo operation
editorState.redo();
console.log(editorState.getContent()); // Output: <p>Updated content</p>

// Check if undo and redo are available
console.log(editorState.isUndo()); // Output: true
console.log(editorState.isRedo()); // Output: true
}
}

Certainly, here's the documentation for the EventManager class you provided:

EventManager Class Documentation

Example Usage

import type EditorCore, { IEditorModule } from 'on-codemerge';
export class Plugin implements IEditorModule {
initialize(core: EditorCore): void {
const eventManager = core.eventManager;

// Subscribe to an event
const myCallback = (data) => {
console.log(`Event occurred with data: ${data}`);
};

eventManager.subscribe('myEvent', myCallback);

// Publish the event with optional data
eventManager.publish('myEvent', 'Hello, Event!'); // Output: Event occurred with data: Hello, Event!

// Unsubscribe from the event
eventManager.destroy();

// Attempt to trigger the event after unsubscribing (no output)
eventManager.publish('myEvent', 'This won\'t be logged.');

}

}

To integrate the use of I18n and asynchronous loading of translations into your documentation, you can add a section in the Developing Plugins part. This section will guide developers on how to effectively utilize the internationalization capabilities of On-Codemerge in their custom plugins.

Utilizing Internationalization (I18n)

On-Codemerge's I18n class provides a robust internationalization framework, enabling plugins to support multiple languages. Here's how you can use I18n in your custom plugin for asynchronous loading and adding translations.

Example: Asynchronous Language Loading

import type EditorCore, { Observer, IEditorModule } from 'on-codemerge';

export class MyCustomPlugin implements IEditorModule, Observer {
private core: EditorCore;

initialize(core: EditorCore): void {
this.core = core;
this.loadLanguages();
}

async loadLanguages() {
await this.i18n.loadLanguage('en');
await this.i18n.loadLanguage('es');
// Set the default language
await this.i18n.setCurrentLanguage('en');
}

// Rest of your plugin implementation...
}

Example: Adding and Merging Translations

import type EditorCore, { Observer, IEditorModule } from 'on-codemerge';

export class MyCustomPlugin implements IEditorModule, Observer {
private core: EditorCore;

initialize(core: EditorCore): void {
this.core = core;
this.addTranslations();
}

addTranslations() {
// Custom translations for 'en'
const customEnTranslations = {
'my.custom.key': 'Custom English Text'
};

// Merge custom translations into the existing 'en' language pack
this.core.i18n.merge(customEnTranslations);
// You can repeat the above steps for other languages
}

public myPluginMethod() {
// Access the translation for a specific key
const translatedText = this.core.i18n.translate('my.custom.key');
console.log(translatedText); // Output will depend on the current language
}

// Rest of your plugin implementation...
}

Certainly! To demonstrate the asynchronous loading of translations using dynamic imports in the context of the On-Codemerge editor, you can include an example that showcases how to load language files dynamically based on user selection or application requirements. This approach is particularly useful for optimizing load times and resource management, as it allows for loading only the necessary translations on demand.

Here's an example you can include in the documentation:


Asynchronous Loading of Translations

In some scenarios, you may want to load translation files dynamically to reduce the initial load time or to load languages based on user preferences. The I18n class in On-Codemerge supports asynchronous loading of translation files using dynamic imports.

Example: Dynamic Import of Language Files

import type EditorCore, { Observer, IEditorModule } from 'on-codemerge';

export class MyCustomPlugin implements IEditorModule, Observer {
private core: EditorCore;

initialize(core: EditorCore): void {
this.core = core;
this.initialize();
}

async initialize() {
// Dynamically load the language based on user preference or other logic
const userLanguage = 'es'; // Example user language
await this.loadLanguage(userLanguage);
}

async loadLanguage(lang: string) {
try {
const languagePack = await import(`../locales/${lang}.json`);
this.core.i18n.languages[lang] = languagePack.default;
this.core.i18n.setCurrentLanguage(lang);
} catch (error) {
console.error(`Error loading language pack for ${lang}:`, error);
}
}

// Rest of your plugin implementation...
}

Best Practices

  • Encapsulation: Keep your plugin's internal logic encapsulated to avoid conflicts with other plugins or the core editor.
  • State Management: If your plugin maintains state, ensure it's managed efficiently to avoid performance issues.
  • Event Handling: Use the EventManager for handling and broadcasting events within the editor.
  • UI Consistency: Maintain UI consistency with the core editor. Use existing styles and UI elements where possible.
  • Performance: Optimize for performance, especially if your plugin interacts frequently with the editor's DOM.

Testing Your Plugin

  • Thoroughly test your plugin in various scenarios to ensure it behaves as expected.
  • Consider edge cases, such as how your plugin behaves with large texts or interacts with other plugins.

Documentation

  • Document your plugin's functionality, methods, and usage.
  • Provide examples and usage guides to help other developers integrate your plugin.

By following these guidelines, you can develop efficient and effective plugins that enhance the capabilities of the On-Codemerge editor. Your custom plugins can range from simple UI enhancements to complex features that significantly augment the editor's functionality.