@f1softinternational/ng-f1-editor
v1.0.19
Published
Enterprise-grade Rich Text Editor for Angular 16+. Features include text formatting, tables, media uploads, code blocks with syntax highlighting, and extensible plugin architecture.
Maintainers
Readme
@f1softinternational/ng-f1-editor
Enterprise-grade Rich Text Editor for Angular 16+. A production-ready, feature-rich WYSIWYG editor with plugin architecture, theme support, and full TypeScript typings.
Features
- ✅ Angular 16+ - Standalone components, no NgModules
- ✅ Full TypeScript - Strict mode, complete typings
- ✅ Tree-shakable - Only import what you use
- ✅ SSR Safe - Works with Angular Universal
- ✅ AOT Compatible - Pre-compiled for production
- ✅ Accessible - ARIA attributes, keyboard navigation
- ✅ Themeable - Light/dark/custom themes via CSS variables
- ✅ Plugin Architecture - Extend with custom plugins
- ✅ Security - XSS protection with DOMPurify
Editor Features
| Category | Features | |----------|----------| | Text Formatting | Bold, italic, underline, strikethrough, subscript, superscript | | Typography | Headings H1-H6, font family, font size, text/background color | | Structure | Ordered/unordered lists, blockquotes, horizontal rules | | Tables | Create, resize, merge/split cells | | Code | Inline code, code blocks with syntax highlighting | | Media | Images (drag & drop, paste, resize), embeds | | Tools | Find/replace, word count, character count | | Modes | WYSIWYG, HTML source (extensible for Markdown) |
Installation
npm install @f1softinternational/ng-f1-editorQuick Start
1. Import the Component
import { Component } from '@angular/core';
import { RichEditorComponent, EditorConfig } from '@f1softinternational/ng-f1-editor';
@Component({
selector: 'app-my-editor',
standalone: true,
imports: [RichEditorComponent],
template: `
<f1-rich-editor
[config]="editorConfig"
(contentChange)="onContentChange($event)">
</f1-rich-editor>
`
})
export class MyEditorComponent {
editorConfig: EditorConfig = {
placeholder: 'Start writing...',
enableWordCount: true
};
onContentChange(event: { content: { html: string } }) {
console.log('Content:', event.content.html);
}
}2. With Reactive Forms
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { RichEditorComponent } from '@f1softinternational/ng-f1-editor';
@Component({
selector: 'app-form-editor',
standalone: true,
imports: [RichEditorComponent, ReactiveFormsModule],
template: `
<f1-rich-editor [formControl]="contentControl"></f1-rich-editor>
`
})
export class FormEditorComponent {
contentControl = new FormControl('<p>Hello World!</p>');
}Configuration
EditorConfig
interface EditorConfig {
// Content
initialContent?: string | object; // HTML string or JSON document
placeholder?: string; // Placeholder text
// Toolbar
toolbar?: {
show?: boolean; // Show/hide toolbar
position?: 'top' | 'bottom'; // Toolbar position
sticky?: boolean; // Sticky toolbar
groups?: ToolbarGroup[]; // Custom toolbar groups
};
// Features
enableWordCount?: boolean; // Show word count
enableCharacterCount?: boolean; // Show character count
enableMarkdown?: boolean; // Enable markdown mode
enableSourceMode?: boolean; // Enable HTML source mode
enableFindReplace?: boolean; // Enable find/replace
// Limits
maxLength?: number; // Maximum characters
minHeight?: number; // Minimum height (px)
maxHeight?: number; // Maximum height (px)
// Behavior
readonly?: boolean; // Read-only mode
debounceTime?: number; // Change event debounce (ms)
// Theme
theme?: {
name?: 'light' | 'dark' | 'custom';
customVariables?: Record<string, string>;
};
// Image Upload
imageUpload?: {
maxSize?: number; // Max file size in bytes
allowedTypes?: string[]; // Allowed MIME types
uploadHandler?: (file: File) => Promise<string>; // Custom upload
enableDragDrop?: boolean;
enablePaste?: boolean;
enableResize?: boolean;
};
// Code Blocks
codeBlock?: {
defaultLanguage?: string;
languages?: string[]; // Languages in selector
showLineNumbers?: boolean;
};
}Custom Toolbar
const editorConfig: EditorConfig = {
toolbar: {
groups: [
{ id: 'history', items: ['undo', 'redo'] },
{ id: 'format', items: ['bold', 'italic', 'underline'] },
{ id: 'heading', items: ['heading'] },
{ id: 'list', items: ['bulletList', 'orderedList'] },
{ id: 'insert', items: ['link', 'image', 'codeBlock'] }
]
}
};Available toolbar items: undo, redo, bold, italic, underline, strike, heading, bulletList, orderedList, alignLeft, alignCenter, alignRight, link, image, table, codeBlock, blockquote, horizontalRule, removeFormat
Theming
Using Built-in Themes
const editorConfig: EditorConfig = {
theme: { name: 'dark' }
};Custom Theme with CSS Variables
const editorConfig: EditorConfig = {
theme: {
name: 'custom',
customVariables: {
'--f1-color-primary': '#6366f1',
'--f1-color-background': '#1a1a2e',
'--f1-color-text': '#eaeaea'
}
}
};Available CSS Variables
/* Colors */
--f1-color-primary
--f1-color-background
--f1-color-text
--f1-color-border
--f1-color-text-secondary
--f1-color-background-secondary
/* Typography */
--f1-font-family
--f1-font-size-md
--f1-line-height
/* Editor */
--f1-editor-min-height
--f1-editor-padding
--f1-toolbar-height
--f1-toolbar-background
/* Spacing & Radius */
--f1-spacing-sm
--f1-spacing-md
--f1-radius-sm
--f1-radius-mdBackend Integration
Image Upload
import { EditorConfig } from '@f1softinternational/ng-f1-editor';
const editorConfig: EditorConfig = {
imageUpload: {
maxSize: 5 * 1024 * 1024, // 5MB
allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
uploadHandler: async (file: File): Promise<string> => {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: formData
});
const { url } = await response.json();
return url;
}
}
};Content Save Adapter
import { ContentSaveAdapter, EditorContent, SaveResult } from '@f1softinternational/ng-f1-editor';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class MyContentAdapter implements ContentSaveAdapter {
constructor(private http: HttpClient) {}
save(content: EditorContent): Observable<SaveResult> {
return this.http.post<SaveResult>('/api/documents', {
html: content.html,
json: content.json
});
}
}Creating Custom Plugins
import { EditorPlugin, PluginContext } from '@f1softinternational/ng-f1-editor';
import { Plugin } from 'prosemirror-state';
const myPlugin: EditorPlugin = {
name: 'my-custom-plugin',
priority: 100,
// Add custom toolbar buttons
toolbarItems: [
{
id: 'myButton',
label: 'My Action',
icon: 'star',
tooltip: 'Do something',
type: 'button',
command: 'myCommand'
}
],
// Register commands
commands: (context: PluginContext) => [
{
name: 'myCommand',
execute: (state, dispatch) => {
if (dispatch) {
// Modify document
dispatch(state.tr.insertText('Hello!'));
}
return true;
}
}
],
// Add keyboard shortcuts
keyboardShortcuts: [
{
key: 'Mod-Shift-h',
command: (state, dispatch) => {
// Handle shortcut
return true;
},
description: 'Insert greeting'
}
],
// Add ProseMirror plugins
plugins: (context: PluginContext) => [
new Plugin({
// Plugin configuration
})
],
// Lifecycle hooks
onInit: (context) => {
console.log('Plugin initialized');
},
onDestroy: () => {
console.log('Plugin destroyed');
}
};
// Use in config
const editorConfig: EditorConfig = {
plugins: [myPlugin]
};API Reference
RichEditorComponent
Inputs
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| config | EditorConfig | {} | Editor configuration |
| initialContent | string \| object | undefined | Initial content |
| plugins | EditorPlugin[] | [] | Custom plugins |
Outputs
| Output | Type | Description |
|--------|------|-------------|
| contentChange | ContentChangeEvent | Emitted on content change |
| focusChange | boolean | Emitted on focus/blur |
| ready | void | Emitted when editor is ready |
Methods
// Get content
const content = editorRef.getContent();
// Returns: { html: string, json: object, text: string }
// Set content
editorRef.setContent('<p>New content</p>');
// Focus editor
editorRef.focus();
// Clear content
editorRef.clear();
// Execute command
editorRef.executeCommand('bold');Browser Support
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Build & Publish
# Install dependencies
npm install
# Build library
npm run build
# Run tests
npm test
# Create package
cd dist/ng-f1-editor
npm pack
# Publish to NPM
npm publish --access publicPeer Dependencies
{
"@angular/core": ">=16.0.0",
"@angular/common": ">=16.0.0",
"rxjs": ">=7.0.0"
}License
This project is licensed under the MIT License.
Copyright © 2024 F1Soft International and F1 ARL Club
See the LICENSE file for details.
Authors
Code by: Rashim Dhaubanjar
With support from: Pukar Shrestha and Bikal Shrestha
