A React-based WYSIWYG continuous HTML editor with rich text formatting and automatic page break insertion
npm install @kanaka-prabhath/html-editorgetHTMLContent(), setContent(), and insertContent() are async for automatic image conversion
bash
Install the editor library
npm install @kanaka-prabhath/html-editor
Install peer dependencies (if not already installed)
npm install react react-dom
`
๐ Quick Start
`jsx
import React from 'react';
import { HtmlEditor, DocumentProvider } from '@kanaka-prabhath/html-editor';
import '@kanaka-prabhath/html-editor/styles';
function App() {
return (
);
}
export default App;
`
๐ฏ Advanced Usage
$3
`jsx
import { useDocumentState, useDocumentActions } from '@kanaka-prabhath/html-editor';
function ZoomControls() {
const { zoomLevel } = useDocumentState();
const { setZoomLevel, zoomIn, zoomOut, resetZoom } = useDocumentActions();
return (
Current Zoom: {zoomLevel}%
);
}
`
$3
`jsx
import { createTableResizeOverlay, applyTableDimensions } from '@kanaka-prabhath/html-editor';
function TableEditor() {
const handleTableResize = (tableElement, newDimensions) => {
applyTableDimensions(tableElement, newDimensions);
};
return (
{/ Tables in the editor automatically get resize handles /}
{/ Use the utilities for programmatic table manipulation /}
);
}
`
$3
`jsx
function IndentationExample() {
const editorRef = useRef(null);
const insertIndentedContent = () => {
// Insert content with indentation
editorRef.current.insertContent('Indented paragraph
');
};
return (
);
}
`
$3
The editor supports comprehensive keyboard shortcuts:
Formatting:
- Ctrl+B - Bold
- Ctrl+I - Italic
- Ctrl+U - Underline
- Ctrl+A then formatting - Apply to selection
Navigation:
- Tab - Increase indent (at cursor or selection)
- Shift+Tab - Decrease indent (selection only)
- Ctrl+A - Select all
Zoom:
- Ctrl++ - Zoom in
- Ctrl+- - Zoom out
- Ctrl+0 - Reset zoom
Async Content Methods:
The getHTMLContent(), setContent(), and insertContent() methods are now asynchronous to handle automatic image conversion between blob URLs and base64 data URLs. This ensures optimal performance by storing images as blob URLs during editing while providing base64-encoded images for export.
Content Change Callbacks:
The editor provides two content change callbacks with different performance characteristics:
- onChange: Called on every content change with raw HTML (blob URLs). Use for real-time updates.
- onContentFinalized: Called after 2 seconds of inactivity with base64-converted HTML. Use for saving/exporting.
`jsx
function AutoSaveEditor() {
const handleContentChange = (html) => {
// Real-time updates with blob URLs (fast)
console.log('Content changed:', html.length, 'characters');
};
const handleContentFinalized = (html) => {
// Finalized content with base64 images (for saving)
localStorage.setItem('draft', html);
};
return (
onChange={handleContentChange}
onContentFinalized={handleContentFinalized}
/>
);
}
`
Key Benefits:
- Automatic Conversion: Images are automatically converted between formats
- Performance Optimized: Blob URLs for fast editing, base64 for export
- IndexedDB Storage: Images stored in IndexedDB for persistence
- Error Handling: Graceful handling of conversion failures
Usage Examples:
`jsx
// Export content with base64 images
const exportContent = async () => {
const htmlWithBase64 = await editorRef.current.getHTMLContent();
// htmlWithBase64 contains base64-encoded images ready for export
return htmlWithBase64;
};
// Import content with base64 images
const importContent = async (htmlWithBase64) => {
await editorRef.current.setContent(htmlWithBase64);
// Images are automatically converted to blob URLs and stored in IndexedDB
};
// Insert content at cursor with base64 images
const insertAtCursor = async (htmlWithBase64) => {
await editorRef.current.insertContent(htmlWithBase64);
// Images are automatically converted and stored
};
`
๐ง API Reference
$3
The main editor component with ref access to content methods.
Props:
- pageManagerComponent?: ReactNode - Custom page manager component
- onNavigatePage?: (pageIndex: number) => void - Page navigation callback
- onAddPage?: () => void - Page addition callback
- onDeletePage?: (pageIndex: number) => void - Page deletion callback
- onPageSizeChange?: (size: 'A4' | 'Letter' | 'Legal') => void - Page size change callback
- onPageMarginsChange?: (margins: PageMarginPreset | CustomMargins) => void - Page margins change callback
- onChange?: (htmlContent: string) => void - Content change callback (real-time with blob URLs)
- onContentFinalized?: (htmlContent: string) => void - Finalized content callback (debounced with base64 images)
- showSidebar?: boolean - Show/hide sidebar (default: true)
- showToolbar?: boolean - Show/hide toolbar (default: true)
- showPageManager?: boolean - Show/hide page manager (default: true)
Ref Methods:
- getHTMLContent(): Promise - Returns current HTML content with blob URLs converted to base64
- getSelectedHTMLContent(): string - Returns selected HTML content (supports images and tables)
- getPlainText(): string - Returns plain text content
- setContent(html: string): Promise - Set editor content programmatically, converting base64 images to blobs
- setPageSize(size: 'A4' | 'Letter' | 'Legal'): void - Set page size programmatically
- setPageMargins(margins: PageMarginPreset | CustomMargins): void - Set page margins programmatically
- insertContent(html: string): Promise - Insert content at cursor position, converting base64 images to blobs
$3
Context provider for document state management.
`jsx
`
$3
useDocumentState() - Access current document state
- Returns: { id, title, createdAt, updatedAt, pages, activePage, pageSize, zoomLevel, continuousContent, pageBoundaries, ... }
useDocumentActions() - Access document state actions
- updateContinuousContent(html: string) - Update continuous content
- setActivePage(index: number) - Set active page
- addPage() - Add new page
- deletePage(index: number) - Delete page at index
- setPageSize(size: string) - Set page size
- setZoomLevel(level: number) - Set zoom level (50-200)
- zoomIn() - Increase zoom by 5%
- zoomOut() - Decrease zoom by 5%
- resetZoom() - Reset zoom to 100%
$3
useFormatting() - Text formatting state and commands
- Returns: { currentFormat, formatText, applyFormatting }
useContinuousReflow(pageSize, editorRef, zoomLevel?) - Automatic content reflow engine
- Returns: { checkAndUpdateBoundaries, triggerAutoReflow, scrollToPage, getCurrentPage, positionCursorAtPage, removePageAndContent }
$3
- saveImage(file: File): Promise - Save image to IndexedDB, returns storage key
- getImage(key: string): Promise - Retrieve image data URL by key
- deleteImage(key: string): Promise - Delete image by key
- clearImages(): Promise - Clear all stored images
- getAllImageKeys(): Promise - Get all stored image keys
$3
- PAGE_SIZES - Object with A4, Letter, Legal dimensions
- getPageDimensions(size: string): {width: number, height: number} - Get page dimensions in pixels
- isValidPageSize(size: string): boolean - Validate page size
- getAvailablePageSizes(): string[] - Get available page sizes
- FONT_SIZE_MAP - Font size mappings
- pointsToPixels(points: number): number - Convert points to pixels
- pixelsToPoints(pixels: number): number - Convert pixels to points
- isValidFontSize(size: number): number - Validate and return valid font size
$3
- isResizableTable(element: HTMLElement): boolean - Check if element is a resizable table
- getTableStructure(table: HTMLTableElement) - Get table structure info
- calculateTableResizeDimensions(params): object - Calculate resize dimensions
- applyTableDimensions(table: HTMLTableElement, dimensions, type?) - Apply dimensions to table
- createTableResizeOverlay(table: HTMLTableElement) - Create resize overlay
- updateTableResizeOverlay(overlay: HTMLElement, table: HTMLTableElement) - Update overlay
$3
- isResizableImage(element: HTMLElement): boolean - Check if element is a resizable image
- getImageDimensions(img: HTMLImageElement) - Get image dimensions
- applyImageDimensions(img: HTMLImageElement, dimensions) - Apply dimensions to image
- createResizeOverlay(img: HTMLImageElement) - Create image resize overlay
- updateResizeOverlay(overlay: HTMLElement, img: HTMLImageElement) - Update overlay
- removeResizeOverlay() - Remove resize overlay
$3
PageMarginPreset:
- 'NORMAL' - 1" margins all sides
- 'NARROW' - 0.5" margins all sides
- 'MODERATE' - 1" top/bottom, 0.75" left/right
- 'WIDE' - 1" top/bottom, 2" left/right
- 'OFFICE_2003' - 1" top/bottom, 1.25" left/right
CustomMargins:
`typescript
{
top: number, // Top margin in inches
bottom: number, // Bottom margin in inches
left: number, // Left margin in inches
right: number // Right margin in inches
}
`
๐ Documentation
For complete documentation, check out our Wiki which includes:
- Getting Started
- API Reference
- Advanced Usage
- Architecture
- Troubleshooting
๐จ Styling
Import default styles:
`jsx
import '@kanaka-prabhath/html-editor/styles';
`
Or use your own CSS:
`css
.multi-page-editor {
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.editor-toolbar {
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
}
`
๐ Examples
$3
`jsx
import { useDocumentActions, useDocumentState } from '@kanaka-prabhath/html-editor';
function CustomPageManager() {
const { pages, activePage } = useDocumentState();
const { setActivePage, addPage } = useDocumentActions();
return (
{pages.map((_, index) => (
key={index}
onClick={() => setActivePage(index)}
className={index === activePage ? 'active' : ''}
>
Page {index + 1}
))}
);
}
// Usage
} />
`
$3
`jsx
function TemplateLoader() {
const editorRef = useRef(null);
const loadTemplate = async (template) => {
await editorRef.current.setContent(template);
};
const insertSignature = async () => {
await editorRef.current.insertContent('-- Document Signature
');
};
return (
);
}
`
๐งช Testing
The library includes a comprehensive test suite covering unit tests, integration tests, and end-to-end tests:
$3
Unit Tests (tests/unit/):
- Component exports and library structure
- Document context contract and state management
- Hook functionality (useContinuousReflow, useFormatting)
- Utility functions (table resize, image handling, font utilities)
- Page size and margin calculations
- Content normalization and validation
Integration Tests (tests/integration/):
- Page size dimensions and boundaries
- Zoom controls and keyboard shortcuts
- Cross-component interactions
End-to-End Tests (tests/e2e/):
- Core text editing workflows
- Image upload and manipulation
- Page content management
- Table creation and resizing
- Indentation system (Tab/Shift+Tab)
- UI specifications and responsive design
- Keyboard shortcuts and accessibility
$3
`bash
Run all tests (unit + integration)
npm test
Run end-to-end tests
npm run test:e2e
Run E2E tests with UI
npm run test:e2e:ui
Run E2E tests in debug mode
npm run test:e2e:debug
Run tests with coverage
npm run test:coverage
``