rte-builder 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,13 +14,13 @@ A **universal, adapter-based** rich text editor library for React that supports
14
14
 
15
15
  ## Supported Editors
16
16
 
17
- | Editor | Status | Bundle Size | Description |
18
- |--------|--------|-------------|-------------|
19
- | **TipTap** | ✅ Included | ~280KB | ProseMirror-based, highly extensible |
20
- | **Slate.js** | ✅ Included | ~150KB | Completely customizable framework |
21
- | **Lexical** | ✅ Included | ~100KB | Meta's modern editor framework |
22
- | **Quill** | 📋 Backlog | ~50KB | Simple, lightweight editor |
23
- | **Draft.js** | 📋 Backlog | ~200KB | React-first by Facebook |
17
+ | Editor | Status | Bundle Size | Description |
18
+ | ------------ | ----------- | ----------- | ------------------------------------ |
19
+ | **TipTap** | ✅ Included | ~280KB | ProseMirror-based, highly extensible |
20
+ | **Slate.js** | ✅ Included | ~150KB | Completely customizable framework |
21
+ | **Lexical** | ✅ Included | ~100KB | Meta's modern editor framework |
22
+ | **Quill** | 📋 Backlog | ~50KB | Simple, lightweight editor |
23
+ | **Draft.js** | 📋 Backlog | ~200KB | React-first by Facebook |
24
24
 
25
25
  ## 🎯 Try the Demo
26
26
 
@@ -41,10 +41,10 @@ npm install rte-builder
41
41
  ### Basic Usage (TipTap - Default)
42
42
 
43
43
  ```tsx
44
- import { RichTextEditor } from 'rte-builder'
44
+ import { RichTextEditor } from "rte-builder";
45
45
 
46
46
  function App() {
47
- const [content, setContent] = useState('')
47
+ const [content, setContent] = useState("");
48
48
 
49
49
  return (
50
50
  <RichTextEditor
@@ -53,56 +53,62 @@ function App() {
53
53
  placeholder="Start typing..."
54
54
  height={400}
55
55
  />
56
- )
56
+ );
57
57
  }
58
58
  ```
59
59
 
60
60
  ### Using the Unified Editor (Recommended)
61
61
 
62
62
  ```tsx
63
- import { UnifiedEditor } from 'rte-builder'
64
- import type { UnifiedEditorRef } from 'rte-builder'
63
+ import { UnifiedEditor } from "rte-builder";
64
+ import type { UnifiedEditorRef } from "rte-builder";
65
65
 
66
66
  function App() {
67
- const editorRef = useRef<UnifiedEditorRef>(null)
68
- const [content, setContent] = useState('')
67
+ const editorRef = useRef<UnifiedEditorRef>(null);
68
+ const [content, setContent] = useState("");
69
69
 
70
70
  return (
71
71
  <UnifiedEditor
72
- editor="tiptap" // Optional: explicitly select editor
72
+ editor="tiptap" // Optional: explicitly select editor
73
73
  value={content}
74
74
  onChange={setContent}
75
- toolbar="full" // Use preset: 'full' | 'medium' | 'simple' | 'minimal'
75
+ toolbar="full" // Use preset: 'full' | 'medium' | 'simple' | 'minimal'
76
76
  showCharCounter
77
77
  charCounterMax={5000}
78
78
  />
79
- )
79
+ );
80
80
  }
81
81
  ```
82
82
 
83
83
  ### With Custom Toolbar
84
84
 
85
85
  ```tsx
86
- import { RichTextEditor, customizeToolbar } from 'rte-builder'
86
+ import { RichTextEditor, customizeToolbar } from "rte-builder";
87
87
 
88
88
  // Create custom toolbar from preset
89
- const myToolbar = customizeToolbar('medium', {
90
- remove: ['table', 'video'],
91
- add: ['emoji', 'fullscreen'],
92
- })
89
+ const myToolbar = customizeToolbar("medium", {
90
+ remove: ["table", "video"],
91
+ add: ["emoji", "fullscreen"],
92
+ });
93
93
 
94
94
  // Or define explicitly
95
95
  const myToolbar = [
96
- 'bold', 'italic', 'underline',
97
- 'separator',
98
- 'heading1', 'heading2',
99
- 'separator',
100
- 'bulletList', 'orderedList',
101
- 'separator',
102
- 'link', 'image',
103
- 'separator',
104
- 'undo', 'redo',
105
- ]
96
+ "bold",
97
+ "italic",
98
+ "underline",
99
+ "separator",
100
+ "heading1",
101
+ "heading2",
102
+ "separator",
103
+ "bulletList",
104
+ "orderedList",
105
+ "separator",
106
+ "link",
107
+ "image",
108
+ "separator",
109
+ "undo",
110
+ "redo",
111
+ ];
106
112
 
107
113
  function App() {
108
114
  return (
@@ -111,35 +117,35 @@ function App() {
111
117
  onChange={setContent}
112
118
  toolbarButtons={myToolbar}
113
119
  />
114
- )
120
+ );
115
121
  }
116
122
  ```
117
123
 
118
124
  ### With Media Picker Integration
119
125
 
120
126
  ```tsx
121
- import { RichTextEditor } from 'rte-builder'
122
- import type { MediaFile } from 'rte-builder'
127
+ import { RichTextEditor } from "rte-builder";
128
+ import type { MediaFile } from "rte-builder";
123
129
 
124
130
  function App() {
125
131
  const handleImagePicker = async (): Promise<MediaFile | null> => {
126
132
  // Open your media picker dialog
127
- const file = await openYourMediaPicker('image')
133
+ const file = await openYourMediaPicker("image");
128
134
 
129
135
  if (file) {
130
136
  return {
131
137
  url: file.url,
132
138
  name: file.name,
133
139
  alt: file.name,
134
- }
140
+ };
135
141
  }
136
- return null
137
- }
142
+ return null;
143
+ };
138
144
 
139
145
  const handleVideoPicker = async (): Promise<MediaFile | null> => {
140
- const file = await openYourMediaPicker('video')
141
- return file ? { url: file.url, name: file.name } : null
142
- }
146
+ const file = await openYourMediaPicker("video");
147
+ return file ? { url: file.url, name: file.name } : null;
148
+ };
143
149
 
144
150
  return (
145
151
  <RichTextEditor
@@ -148,7 +154,7 @@ function App() {
148
154
  onMediaPickerImage={handleImagePicker}
149
155
  onMediaPickerVideo={handleVideoPicker}
150
156
  />
151
- )
157
+ );
152
158
  }
153
159
  ```
154
160
 
@@ -156,69 +162,69 @@ function App() {
156
162
 
157
163
  ### Component Props
158
164
 
159
- | Prop | Type | Default | Description |
160
- |------|------|---------|-------------|
161
- | `value` | `string` | `''` | HTML content |
162
- | `onChange` | `(content: string) => void` | - | Content change handler |
163
- | `onBlur` | `() => void` | - | Blur event handler |
164
- | `onFocus` | `() => void` | - | Focus event handler |
165
- | `placeholder` | `string` | `'Start typing...'` | Placeholder text |
166
- | `height` | `number` | `400` | Editor height in pixels |
167
- | `minHeight` | `number` | `300` | Minimum height |
168
- | `maxHeight` | `number` | - | Maximum height |
169
- | `disabled` | `boolean` | `false` | Disable editing |
170
- | `readOnly` | `boolean` | `false` | Read-only mode |
171
- | `charCounterMax` | `number` | `-1` | Character limit (-1 = no limit) |
172
- | `showCharCounter` | `boolean` | `false` | Show character count |
173
- | `toolbarPreset` | `'full' \| 'medium' \| 'simple'` | `'full'` | Toolbar preset |
174
- | `toolbarButtons` | `ToolbarButton[]` | - | Custom toolbar buttons |
175
- | `className` | `string` | `''` | Additional CSS class |
176
- | `onMediaPickerImage` | `() => Promise<MediaFile \| null>` | - | Image picker callback |
177
- | `onMediaPickerVideo` | `() => Promise<MediaFile \| null>` | - | Video picker callback |
178
- | `enableCodeHighlight` | `boolean` | `true` | Enable syntax highlighting |
179
- | `defaultCodeLanguage` | `string` | `'javascript'` | Default code language |
165
+ | Prop | Type | Default | Description |
166
+ | --------------------- | ---------------------------------- | ------------------- | ------------------------------- |
167
+ | `value` | `string` | `''` | HTML content |
168
+ | `onChange` | `(content: string) => void` | - | Content change handler |
169
+ | `onBlur` | `() => void` | - | Blur event handler |
170
+ | `onFocus` | `() => void` | - | Focus event handler |
171
+ | `placeholder` | `string` | `'Start typing...'` | Placeholder text |
172
+ | `height` | `number` | `400` | Editor height in pixels |
173
+ | `minHeight` | `number` | `300` | Minimum height |
174
+ | `maxHeight` | `number` | - | Maximum height |
175
+ | `disabled` | `boolean` | `false` | Disable editing |
176
+ | `readOnly` | `boolean` | `false` | Read-only mode |
177
+ | `charCounterMax` | `number` | `-1` | Character limit (-1 = no limit) |
178
+ | `showCharCounter` | `boolean` | `false` | Show character count |
179
+ | `toolbarPreset` | `'full' \| 'medium' \| 'simple'` | `'full'` | Toolbar preset |
180
+ | `toolbarButtons` | `ToolbarButton[]` | - | Custom toolbar buttons |
181
+ | `className` | `string` | `''` | Additional CSS class |
182
+ | `onMediaPickerImage` | `() => Promise<MediaFile \| null>` | - | Image picker callback |
183
+ | `onMediaPickerVideo` | `() => Promise<MediaFile \| null>` | - | Video picker callback |
184
+ | `enableCodeHighlight` | `boolean` | `true` | Enable syntax highlighting |
185
+ | `defaultCodeLanguage` | `string` | `'javascript'` | Default code language |
180
186
 
181
187
  ### Ref Methods
182
188
 
183
189
  ```tsx
184
- const editorRef = useRef<EditorRef>(null)
190
+ const editorRef = useRef<EditorRef>(null);
185
191
 
186
192
  // Get content
187
- const html = editorRef.current?.getContent()
188
- const text = editorRef.current?.getText()
189
- const json = editorRef.current?.getJSON()
193
+ const html = editorRef.current?.getContent();
194
+ const text = editorRef.current?.getText();
195
+ const json = editorRef.current?.getJSON();
190
196
 
191
197
  // Set content
192
- editorRef.current?.setContent('<p>Hello World</p>')
198
+ editorRef.current?.setContent("<p>Hello World</p>");
193
199
 
194
200
  // Navigation
195
- editorRef.current?.focus()
196
- editorRef.current?.blur()
201
+ editorRef.current?.focus();
202
+ editorRef.current?.blur();
197
203
 
198
204
  // Insert
199
- editorRef.current?.insertHTML('<strong>Bold text</strong>')
200
- editorRef.current?.insertText('Plain text')
205
+ editorRef.current?.insertHTML("<strong>Bold text</strong>");
206
+ editorRef.current?.insertText("Plain text");
201
207
 
202
208
  // Clear
203
- editorRef.current?.clear()
209
+ editorRef.current?.clear();
204
210
 
205
211
  // State
206
- const empty = editorRef.current?.isEmpty()
207
- const chars = editorRef.current?.getCharacterCount()
208
- const words = editorRef.current?.getWordCount()
212
+ const empty = editorRef.current?.isEmpty();
213
+ const chars = editorRef.current?.getCharacterCount();
214
+ const words = editorRef.current?.getWordCount();
209
215
 
210
216
  // History
211
- editorRef.current?.undo()
212
- editorRef.current?.redo()
213
- const canUndo = editorRef.current?.canUndo()
214
- const canRedo = editorRef.current?.canRedo()
217
+ editorRef.current?.undo();
218
+ editorRef.current?.redo();
219
+ const canUndo = editorRef.current?.canUndo();
220
+ const canRedo = editorRef.current?.canRedo();
215
221
 
216
222
  // Actions
217
- editorRef.current?.toggleFullscreen()
218
- editorRef.current?.print()
223
+ editorRef.current?.toggleFullscreen();
224
+ editorRef.current?.print();
219
225
 
220
226
  // Native editor access
221
- const tiptapEditor = editorRef.current?.getNativeEditor()
227
+ const tiptapEditor = editorRef.current?.getNativeEditor();
222
228
  ```
223
229
 
224
230
  ### Toolbar Buttons
@@ -255,16 +261,16 @@ All available toolbar buttons:
255
261
  ### Toolbar Presets
256
262
 
257
263
  ```tsx
258
- import { toolbarPresets, getToolbarPreset } from 'rte-builder'
264
+ import { toolbarPresets, getToolbarPreset } from "rte-builder";
259
265
 
260
266
  // Available presets
261
- const full = getToolbarPreset('full') // All features (50+ buttons)
262
- const medium = getToolbarPreset('medium') // Standard features (30+ buttons)
263
- const simple = getToolbarPreset('simple') // Basic features (12 buttons)
264
- const minimal = getToolbarPreset('minimal') // Just essentials (7 buttons)
265
- const code = getToolbarPreset('code') // For technical docs
266
- const blog = getToolbarPreset('blog') // For blog posts
267
- const email = getToolbarPreset('email') // For email composition
267
+ const full = getToolbarPreset("full"); // All features (50+ buttons)
268
+ const medium = getToolbarPreset("medium"); // Standard features (30+ buttons)
269
+ const simple = getToolbarPreset("simple"); // Basic features (12 buttons)
270
+ const minimal = getToolbarPreset("minimal"); // Just essentials (7 buttons)
271
+ const code = getToolbarPreset("code"); // For technical docs
272
+ const blog = getToolbarPreset("blog"); // For blog posts
273
+ const email = getToolbarPreset("email"); // For email composition
268
274
  ```
269
275
 
270
276
  ## Editor Registry
@@ -278,21 +284,21 @@ import {
278
284
  isEditorAvailable,
279
285
  getAvailableAdapters,
280
286
  getEditorFeatures,
281
- } from 'rte-builder'
287
+ } from "rte-builder";
282
288
 
283
289
  // Check what's available
284
- const available = getAvailableAdapters()
285
- console.log(available.map(a => a.name))
290
+ const available = getAvailableAdapters();
291
+ console.log(available.map((a) => a.name));
286
292
 
287
293
  // Check specific editor
288
- if (isEditorAvailable('tiptap')) {
289
- console.log('TipTap is ready!')
294
+ if (isEditorAvailable("tiptap")) {
295
+ console.log("TipTap is ready!");
290
296
  }
291
297
 
292
298
  // Get feature comparison
293
- const features = getEditorFeatures('tiptap')
294
- console.log(features.tables) // true
295
- console.log(features.collaboration) // false
299
+ const features = getEditorFeatures("tiptap");
300
+ console.log(features.tables); // true
301
+ console.log(features.collaboration); // false
296
302
  ```
297
303
 
298
304
  ## Custom Extensions (TipTap)
@@ -309,10 +315,10 @@ import {
309
315
  Print,
310
316
  Indent,
311
317
  EMOJI_CATEGORIES,
312
- } from 'rte-builder'
318
+ } from "rte-builder";
313
319
 
314
320
  // Use with TipTap directly
315
- import { useEditor } from '@tiptap/react'
321
+ import { useEditor } from "@tiptap/react";
316
322
 
317
323
  const editor = useEditor({
318
324
  extensions: [
@@ -321,7 +327,7 @@ const editor = useEditor({
321
327
  LineHeight,
322
328
  Fullscreen,
323
329
  ],
324
- })
330
+ });
325
331
  ```
326
332
 
327
333
  ## Styling
@@ -344,31 +350,6 @@ The library includes comprehensive CSS. You can customize via CSS variables:
344
350
  }
345
351
  ```
346
352
 
347
- ## Migration from Froala
348
-
349
- See [MIGRATION_FROM_FROALA.md](./MIGRATION_FROM_FROALA.md) for a complete migration guide.
350
-
351
- **Quick comparison:**
352
-
353
- ```tsx
354
- // Before (Froala)
355
- <FroalaEditor
356
- model={content}
357
- onModelChange={setContent}
358
- config={{
359
- key: FROALA_LICENSE_KEY, // ❌ $399+/year
360
- placeholderText: 'Type...',
361
- }}
362
- />
363
-
364
- // After (RTE Builder)
365
- <RichTextEditor
366
- value={content}
367
- onChange={setContent}
368
- placeholder="Type..." // ✅ FREE (MIT)
369
- />
370
- ```
371
-
372
353
  ## Project Structure
373
354
 
374
355
  ```
@@ -398,12 +379,119 @@ rte-builder/
398
379
  - [x] **v1.1**: Generic adapter architecture
399
380
  - [x] **v1.2**: Slate.js adapter
400
381
  - [x] **v1.3**: Lexical adapter
401
- - [ ] **v2.0**: Collaborative editing
402
- - [ ] **v2.1**: Comments & annotations
403
- - [ ] **v2.2**: Version history
382
+ - [x] **v2.0**: Collaborative editing
383
+ - [x] **v2.1**: Comments & annotations
384
+ - [x] **v2.2**: Version history
404
385
  - [ ] **v2.3**: Quill adapter
405
386
  - [ ] **v2.4**: Draft.js adapter
406
387
 
388
+ ## Collaborative Editing (v2.0)
389
+
390
+ Enable real-time collaboration with presence indicators:
391
+
392
+ ```tsx
393
+ import { UnifiedEditor, CollaborationProvider, PresenceIndicator } from 'rte-builder'
394
+
395
+ function CollaborativeEditor() {
396
+ return (
397
+ <CollaborationProvider
398
+ config={{
399
+ provider: 'websocket',
400
+ serverUrl: 'wss://your-server.com/collab',
401
+ roomId: 'document-123',
402
+ user: {
403
+ id: 'user-1',
404
+ name: 'John Doe',
405
+ color: '#3b82f6',
406
+ },
407
+ }}
408
+ onStatusChange={(status) => console.log('Status:', status)}
409
+ onUsersChange={(users) => console.log('Users:', users)}
410
+ >
411
+ <PresenceIndicator />
412
+ <UnifiedEditor
413
+ value={content}
414
+ onChange={setContent}
415
+ />
416
+ </CollaborationProvider>
417
+ )
418
+ }
419
+ ```
420
+
421
+ ## Comments & Annotations (v2.1)
422
+
423
+ Add inline comments and annotations to your documents:
424
+
425
+ ```tsx
426
+ import { UnifiedEditor, CommentsProvider, CommentsPanel } from 'rte-builder'
427
+
428
+ function EditorWithComments() {
429
+ return (
430
+ <CommentsProvider
431
+ config={{
432
+ currentUser: {
433
+ id: 'user-1',
434
+ name: 'John Doe',
435
+ },
436
+ allowResolve: true,
437
+ allowReactions: true,
438
+ }}
439
+ onThreadsChange={(threads) => saveThreads(threads)}
440
+ >
441
+ <div style={{ display: 'flex' }}>
442
+ <UnifiedEditor
443
+ value={content}
444
+ onChange={setContent}
445
+ />
446
+ <CommentsPanel position="right" />
447
+ </div>
448
+ </CommentsProvider>
449
+ )
450
+ }
451
+ ```
452
+
453
+ ## Version History (v2.2)
454
+
455
+ Track and restore document versions:
456
+
457
+ ```tsx
458
+ import { UnifiedEditor, VersionHistoryProvider, VersionHistoryPanel } from 'rte-builder'
459
+
460
+ function EditorWithHistory() {
461
+ const editorRef = useRef(null)
462
+
463
+ return (
464
+ <VersionHistoryProvider
465
+ config={{
466
+ currentUser: {
467
+ id: 'user-1',
468
+ name: 'John Doe',
469
+ },
470
+ autoSave: true,
471
+ autoSaveInterval: 60000, // 1 minute
472
+ maxVersions: 100,
473
+ onRestore: (version) => {
474
+ editorRef.current?.setContent(version.content)
475
+ },
476
+ }}
477
+ getCurrentContent={() => ({
478
+ html: editorRef.current?.getContent() || '',
479
+ text: editorRef.current?.getText() || '',
480
+ })}
481
+ >
482
+ <div style={{ display: 'flex' }}>
483
+ <UnifiedEditor
484
+ ref={editorRef}
485
+ value={content}
486
+ onChange={setContent}
487
+ />
488
+ <VersionHistoryPanel position="right" />
489
+ </div>
490
+ </VersionHistoryProvider>
491
+ )
492
+ }
493
+ ```
494
+
407
495
  ## License
408
496
 
409
497
  MIT License - Free for commercial and personal use.
@@ -415,4 +503,3 @@ Contributions welcome! Please read our contributing guidelines.
415
503
  ## Support
416
504
 
417
505
  - GitHub Issues: Report bugs or request features
418
- - Documentation: See [EXAMPLES.md](./EXAMPLES.md) and [QUICKSTART.md](./QUICKSTART.md)