rte-builder 1.0.0 → 2.0.2
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 +219 -132
- package/dist/index.css +997 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +538 -2
- package/dist/index.d.ts +538 -2
- package/dist/index.js +1453 -2196
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1433 -2189
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -10
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
|
|
18
|
-
|
|
19
|
-
| **TipTap**
|
|
20
|
-
| **Slate.js** | ✅ Included | ~150KB
|
|
21
|
-
| **Lexical**
|
|
22
|
-
| **Quill**
|
|
23
|
-
| **Draft.js** | 📋 Backlog
|
|
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
|
|
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
|
|
64
|
-
import type { UnifiedEditorRef } from
|
|
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"
|
|
72
|
+
editor="tiptap" // Optional: explicitly select editor
|
|
73
73
|
value={content}
|
|
74
74
|
onChange={setContent}
|
|
75
|
-
toolbar="full"
|
|
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
|
|
86
|
+
import { RichTextEditor, customizeToolbar } from "rte-builder";
|
|
87
87
|
|
|
88
88
|
// Create custom toolbar from preset
|
|
89
|
-
const myToolbar = customizeToolbar(
|
|
90
|
-
remove: [
|
|
91
|
-
add: [
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
122
|
-
import type { MediaFile } from
|
|
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(
|
|
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(
|
|
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
|
|
160
|
-
|
|
161
|
-
| `value`
|
|
162
|
-
| `onChange`
|
|
163
|
-
| `onBlur`
|
|
164
|
-
| `onFocus`
|
|
165
|
-
| `placeholder`
|
|
166
|
-
| `height`
|
|
167
|
-
| `minHeight`
|
|
168
|
-
| `maxHeight`
|
|
169
|
-
| `disabled`
|
|
170
|
-
| `readOnly`
|
|
171
|
-
| `charCounterMax`
|
|
172
|
-
| `showCharCounter`
|
|
173
|
-
| `toolbarPreset`
|
|
174
|
-
| `toolbarButtons`
|
|
175
|
-
| `className`
|
|
176
|
-
| `onMediaPickerImage`
|
|
177
|
-
| `onMediaPickerVideo`
|
|
178
|
-
| `enableCodeHighlight` | `boolean`
|
|
179
|
-
| `defaultCodeLanguage` | `string`
|
|
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(
|
|
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(
|
|
200
|
-
editorRef.current?.insertText(
|
|
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
|
|
264
|
+
import { toolbarPresets, getToolbarPreset } from "rte-builder";
|
|
259
265
|
|
|
260
266
|
// Available presets
|
|
261
|
-
const full = getToolbarPreset(
|
|
262
|
-
const medium = getToolbarPreset(
|
|
263
|
-
const simple = getToolbarPreset(
|
|
264
|
-
const minimal = getToolbarPreset(
|
|
265
|
-
const code = getToolbarPreset(
|
|
266
|
-
const blog = getToolbarPreset(
|
|
267
|
-
const email = getToolbarPreset(
|
|
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
|
|
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(
|
|
289
|
-
console.log(
|
|
294
|
+
if (isEditorAvailable("tiptap")) {
|
|
295
|
+
console.log("TipTap is ready!");
|
|
290
296
|
}
|
|
291
297
|
|
|
292
298
|
// Get feature comparison
|
|
293
|
-
const features = getEditorFeatures(
|
|
294
|
-
console.log(features.tables)
|
|
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
|
|
318
|
+
} from "rte-builder";
|
|
313
319
|
|
|
314
320
|
// Use with TipTap directly
|
|
315
|
-
import { useEditor } from
|
|
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
|
-
- [
|
|
402
|
-
- [
|
|
403
|
-
- [
|
|
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)
|