@overlap/rte 0.1.9 → 0.1.10
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 +409 -181
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @overlap/rte
|
|
2
2
|
|
|
3
|
-
A lightweight, extensible Rich Text Editor for React.
|
|
3
|
+
A lightweight, extensible Rich Text Editor for React -- zero extra dependencies beyond React itself.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,266 +10,494 @@ npm install @overlap/rte
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
- **Lightweight** -- only peer-depends on React, no heavy framework
|
|
14
|
+
- **Plugin system** -- compose exactly the toolbar you need
|
|
15
|
+
- **Settings object** -- configure features with a single object instead of assembling plugins manually
|
|
16
|
+
- **Contenteditable-based** -- uses native browser editing for performance
|
|
17
|
+
- **Full formatting** -- bold, italic, underline, strikethrough, subscript, superscript, inline code
|
|
18
|
+
- **Block formats** -- headings (h1-h6), bullet lists, numbered lists, checkbox lists, blockquote, code block
|
|
19
|
+
- **Tables** -- insert, row/column operations, context menu
|
|
20
|
+
- **Images** -- upload via callback, URL insert, drag & drop, paste
|
|
21
|
+
- **Links** -- floating editor, advanced attributes, custom fields
|
|
22
|
+
- **Colors** -- text color and background color pickers
|
|
23
|
+
- **Font sizes** -- configurable size dropdown
|
|
24
|
+
- **Alignment** -- left, center, right, justify
|
|
25
|
+
- **Indent / Outdent** -- nested list support via Tab / Shift+Tab
|
|
26
|
+
- **Undo / Redo** -- full history management
|
|
27
|
+
- **HTML import/export** -- `htmlToContent()` and `contentToHTML()`
|
|
28
|
+
- **Theming** -- CSS variables + theme prop for brand colors
|
|
29
|
+
- **Lexical compatible** -- correctly parses and renders HTML generated by Lexical editors
|
|
30
|
+
- **TypeScript** -- fully typed, ships `.d.ts`
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
22
33
|
|
|
23
34
|
```tsx
|
|
24
|
-
import React, { useState } from "react";
|
|
25
35
|
import { Editor, EditorContent } from "@overlap/rte";
|
|
26
|
-
import "@overlap/rte/dist/styles.css";
|
|
36
|
+
import "@overlap/rte/dist/styles.css";
|
|
27
37
|
|
|
28
38
|
function App() {
|
|
29
|
-
const [content, setContent] = useState<EditorContent
|
|
39
|
+
const [content, setContent] = useState<EditorContent>();
|
|
30
40
|
|
|
31
41
|
return (
|
|
32
42
|
<Editor
|
|
33
43
|
initialContent={content}
|
|
34
|
-
onChange={
|
|
35
|
-
|
|
36
|
-
console.log("Content:", newContent);
|
|
37
|
-
}}
|
|
38
|
-
placeholder="Text eingeben..."
|
|
44
|
+
onChange={setContent}
|
|
45
|
+
placeholder="Enter text..."
|
|
39
46
|
/>
|
|
40
47
|
);
|
|
41
48
|
}
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
**Note:**
|
|
51
|
+
> **Note:** Always import the CSS: `import '@overlap/rte/dist/styles.css'`
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
---
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
import React from "react";
|
|
50
|
-
import { Editor, Plugin, EditorAPI, ButtonProps } from "@overlap/rte";
|
|
55
|
+
## Settings-Based Configuration
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
Instead of assembling plugins manually, pass a **settings object** to control which features are enabled. This is the recommended approach for most use cases.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { Editor, EditorSettings, buildPluginsFromSettings } from "@overlap/rte";
|
|
61
|
+
import "@overlap/rte/dist/styles.css";
|
|
62
|
+
|
|
63
|
+
const settings: EditorSettings = {
|
|
64
|
+
format: {
|
|
65
|
+
bold: true,
|
|
66
|
+
italic: true,
|
|
67
|
+
underline: true,
|
|
68
|
+
strikethrough: true,
|
|
69
|
+
code: false,
|
|
70
|
+
subscript: true,
|
|
71
|
+
superscript: true,
|
|
72
|
+
bulletList: true,
|
|
73
|
+
numberedList: true,
|
|
74
|
+
quote: true,
|
|
75
|
+
codeBlock: false,
|
|
76
|
+
check: true,
|
|
77
|
+
typography: ["h1", "h2", "h3"],
|
|
78
|
+
colors: ["#000", "#ff0000", "#00aa00", "#0000ff"],
|
|
79
|
+
fontSize: true,
|
|
80
|
+
alignment: ["left", "center", "right", "justify", "indent", "outdent"],
|
|
81
|
+
},
|
|
82
|
+
link: {
|
|
83
|
+
external: true,
|
|
84
|
+
internal: true,
|
|
67
85
|
},
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
table: {
|
|
87
|
+
enabled: true,
|
|
88
|
+
},
|
|
89
|
+
image: {
|
|
90
|
+
enabled: true,
|
|
70
91
|
},
|
|
71
92
|
};
|
|
72
93
|
|
|
73
94
|
function App() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
// Option A: Let the Editor build plugins from settings automatically
|
|
96
|
+
return <Editor settings={settings} onImageUpload={handleUpload} />;
|
|
97
|
+
|
|
98
|
+
// Option B: Build plugins yourself for more control
|
|
99
|
+
const plugins = buildPluginsFromSettings(settings, {
|
|
100
|
+
onImageUpload: handleUpload,
|
|
101
|
+
linkCustomFields: [
|
|
102
|
+
{
|
|
103
|
+
key: "urlExtra",
|
|
104
|
+
label: "Anchor / Params",
|
|
105
|
+
placeholder: "?param=value or #anchor",
|
|
106
|
+
dataAttribute: "data-url-extra",
|
|
107
|
+
appendToHref: true,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
return <Editor plugins={plugins} />;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### EditorSettings Interface
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface EditorSettings {
|
|
119
|
+
format?: {
|
|
120
|
+
bold?: boolean;
|
|
121
|
+
italic?: boolean;
|
|
122
|
+
underline?: boolean;
|
|
123
|
+
strikethrough?: boolean;
|
|
124
|
+
code?: boolean; // inline code
|
|
125
|
+
subscript?: boolean;
|
|
126
|
+
superscript?: boolean;
|
|
127
|
+
bulletList?: boolean;
|
|
128
|
+
numberedList?: boolean;
|
|
129
|
+
quote?: boolean;
|
|
130
|
+
codeBlock?: boolean;
|
|
131
|
+
check?: boolean; // checkbox lists
|
|
132
|
+
typography?: string[]; // e.g. ["h1", "h2", "h3"]
|
|
133
|
+
colors?: string[]; // e.g. ["#000", "#ff0000"]
|
|
134
|
+
fontSize?: boolean;
|
|
135
|
+
alignment?: string[]; // e.g. ["left", "center", "right", "justify", "indent", "outdent"]
|
|
136
|
+
};
|
|
137
|
+
link?: {
|
|
138
|
+
external?: boolean;
|
|
139
|
+
internal?: boolean;
|
|
140
|
+
};
|
|
141
|
+
table?: {
|
|
142
|
+
enabled?: boolean;
|
|
143
|
+
};
|
|
144
|
+
image?: {
|
|
145
|
+
enabled?: boolean;
|
|
146
|
+
};
|
|
80
147
|
}
|
|
81
148
|
```
|
|
82
149
|
|
|
83
|
-
|
|
150
|
+
By default, **everything is enabled** (`defaultEditorSettings`). Disable features by setting them to `false` or removing values from arrays.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Manual Plugin Composition
|
|
155
|
+
|
|
156
|
+
For full control, assemble plugins yourself:
|
|
84
157
|
|
|
85
158
|
```tsx
|
|
86
|
-
import
|
|
87
|
-
|
|
159
|
+
import {
|
|
160
|
+
Editor,
|
|
161
|
+
boldPlugin,
|
|
162
|
+
italicPlugin,
|
|
163
|
+
underlinePlugin,
|
|
164
|
+
strikethroughPlugin,
|
|
165
|
+
subscriptPlugin,
|
|
166
|
+
superscriptPlugin,
|
|
167
|
+
codeInlinePlugin,
|
|
168
|
+
undoPlugin,
|
|
169
|
+
redoPlugin,
|
|
170
|
+
clearFormattingPlugin,
|
|
171
|
+
indentListItemPlugin,
|
|
172
|
+
outdentListItemPlugin,
|
|
173
|
+
createBlockFormatPlugin,
|
|
174
|
+
createTextColorPlugin,
|
|
175
|
+
createBackgroundColorPlugin,
|
|
176
|
+
createFontSizePlugin,
|
|
177
|
+
createAlignmentPlugin,
|
|
178
|
+
createAdvancedLinkPlugin,
|
|
179
|
+
createImagePlugin,
|
|
180
|
+
tablePlugin,
|
|
181
|
+
} from "@overlap/rte";
|
|
182
|
+
|
|
183
|
+
const plugins = [
|
|
184
|
+
undoPlugin,
|
|
185
|
+
redoPlugin,
|
|
186
|
+
createBlockFormatPlugin(["h1", "h2", "h3"], {
|
|
187
|
+
bulletList: true,
|
|
188
|
+
numberedList: true,
|
|
189
|
+
quote: true,
|
|
190
|
+
check: true,
|
|
191
|
+
codeBlock: false,
|
|
192
|
+
}),
|
|
193
|
+
boldPlugin,
|
|
194
|
+
italicPlugin,
|
|
195
|
+
underlinePlugin,
|
|
196
|
+
strikethroughPlugin,
|
|
197
|
+
createAdvancedLinkPlugin({ enableTarget: true }),
|
|
198
|
+
createTextColorPlugin(["#000", "#ff0000", "#00aa00", "#0000ff"]),
|
|
199
|
+
createBackgroundColorPlugin(["#ffff00", "#00ff00", "#ff00ff"]),
|
|
200
|
+
createFontSizePlugin([12, 14, 16, 18, 20, 24, 28, 32]),
|
|
201
|
+
createAlignmentPlugin(["left", "center", "right", "justify"]),
|
|
202
|
+
tablePlugin,
|
|
203
|
+
createImagePlugin(handleUpload),
|
|
204
|
+
subscriptPlugin,
|
|
205
|
+
superscriptPlugin,
|
|
206
|
+
codeInlinePlugin,
|
|
207
|
+
clearFormattingPlugin,
|
|
208
|
+
indentListItemPlugin,
|
|
209
|
+
outdentListItemPlugin,
|
|
210
|
+
];
|
|
88
211
|
|
|
89
212
|
function App() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Editor
|
|
94
|
-
plugins={allPlugins}
|
|
95
|
-
onChange={(content) => console.log(content)}
|
|
96
|
-
/>
|
|
97
|
-
);
|
|
213
|
+
return <Editor plugins={plugins} onChange={console.log} />;
|
|
98
214
|
}
|
|
99
215
|
```
|
|
100
216
|
|
|
101
|
-
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Advanced Link Plugin
|
|
220
|
+
|
|
221
|
+
The link plugin supports a floating editor with advanced attributes and extensible custom fields:
|
|
102
222
|
|
|
103
223
|
```tsx
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
224
|
+
import { createAdvancedLinkPlugin, LinkCustomField } from "@overlap/rte";
|
|
225
|
+
|
|
226
|
+
const linkPlugin = createAdvancedLinkPlugin({
|
|
227
|
+
enableTarget: true, // show "Open in new tab" checkbox
|
|
228
|
+
customFields: [
|
|
229
|
+
{
|
|
230
|
+
key: "urlExtra",
|
|
231
|
+
label: "Anchor / Params",
|
|
232
|
+
placeholder: "?param=value or #anchor",
|
|
233
|
+
dataAttribute: "data-url-extra",
|
|
234
|
+
appendToHref: true,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
key: "pageRef",
|
|
238
|
+
label: "Page Reference",
|
|
239
|
+
placeholder: "Select a page...",
|
|
240
|
+
dataAttribute: "data-page-ref",
|
|
241
|
+
disablesUrl: true,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
});
|
|
109
245
|
```
|
|
110
246
|
|
|
111
|
-
|
|
247
|
+
### LinkCustomField
|
|
112
248
|
|
|
113
|
-
|
|
249
|
+
| Property | Type | Description |
|
|
250
|
+
| --- | --- | --- |
|
|
251
|
+
| `key` | `string` | Unique identifier |
|
|
252
|
+
| `label` | `string` | Input label text |
|
|
253
|
+
| `placeholder` | `string?` | Input placeholder |
|
|
254
|
+
| `dataAttribute` | `string` | HTML data attribute stored on the `<a>` tag |
|
|
255
|
+
| `appendToHref` | `boolean?` | If true, value is appended to the `href` |
|
|
256
|
+
| `disablesUrl` | `boolean?` | If true, hides the URL field when this field has a value |
|
|
114
257
|
|
|
115
|
-
|
|
116
|
-
| ------------------ | ----------------------------------- | ------------------------------------------------------ |
|
|
117
|
-
| `initialContent` | `EditorContent?` | Initial editor content |
|
|
118
|
-
| `onChange` | `(content: EditorContent) => void?` | Callback on changes |
|
|
119
|
-
| `plugins` | `Plugin[]?` | Array of plugins (default: defaultPlugins) |
|
|
120
|
-
| `headings` | `string[]?` | Available heading levels (default: ["h1", "h2", "h3"]) |
|
|
121
|
-
| `placeholder` | `string?` | Placeholder text |
|
|
122
|
-
| `className` | `string?` | CSS class for container |
|
|
123
|
-
| `toolbarClassName` | `string?` | CSS class for toolbar |
|
|
124
|
-
| `editorClassName` | `string?` | CSS class for editor |
|
|
125
|
-
| `onImageUpload` | `(file: File) => Promise<string>?` | Callback for image uploads |
|
|
258
|
+
---
|
|
126
259
|
|
|
127
|
-
|
|
260
|
+
## Image Upload
|
|
128
261
|
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
|
|
262
|
+
```tsx
|
|
263
|
+
import { Editor, createImagePlugin } from "@overlap/rte";
|
|
264
|
+
|
|
265
|
+
async function handleUpload(file: File): Promise<string> {
|
|
266
|
+
const formData = new FormData();
|
|
267
|
+
formData.append("file", file);
|
|
268
|
+
const res = await fetch("/api/upload", { method: "POST", body: formData });
|
|
269
|
+
const { url } = await res.json();
|
|
270
|
+
return url;
|
|
132
271
|
}
|
|
133
272
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
273
|
+
// Via settings:
|
|
274
|
+
<Editor settings={{ image: { enabled: true } }} onImageUpload={handleUpload} />
|
|
275
|
+
|
|
276
|
+
// Via plugin:
|
|
277
|
+
<Editor plugins={[...otherPlugins, createImagePlugin(handleUpload)]} />
|
|
140
278
|
```
|
|
141
279
|
|
|
142
|
-
|
|
280
|
+
Images can be inserted by:
|
|
281
|
+
- Clicking the toolbar button (file picker or URL input)
|
|
282
|
+
- Pasting an image from the clipboard
|
|
283
|
+
- Dragging and dropping an image file
|
|
143
284
|
|
|
144
|
-
|
|
285
|
+
---
|
|
145
286
|
|
|
146
|
-
|
|
147
|
-
- `getSelection(): Selection | null` - Returns the current selection
|
|
148
|
-
- `getContent(): EditorContent` - Returns the current content
|
|
149
|
-
- `setContent(content: EditorContent): void` - Sets the content
|
|
150
|
-
- `insertBlock(type: string, attributes?: Record<string, string>): void` - Inserts a block
|
|
151
|
-
- `insertInline(type: string, attributes?: Record<string, string>): void` - Inserts an inline element
|
|
152
|
-
- `undo(): void` - Undoes the last action
|
|
153
|
-
- `redo(): void` - Redoes the last action
|
|
154
|
-
- `canUndo(): boolean` - Checks if undo is possible
|
|
155
|
-
- `canRedo(): boolean` - Checks if redo is possible
|
|
156
|
-
- `indentListItem(): void` - Indents a list item (creates sub-list)
|
|
157
|
-
- `outdentListItem(): void` - Outdents a list item
|
|
287
|
+
## Tables
|
|
158
288
|
|
|
159
|
-
|
|
289
|
+
```tsx
|
|
290
|
+
import { Editor, tablePlugin } from "@overlap/rte";
|
|
160
291
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
renderButton?: (props: ButtonProps) => React.ReactElement;
|
|
167
|
-
execute?: (editor: EditorAPI) => void;
|
|
168
|
-
isActive?: (editor: EditorAPI) => boolean;
|
|
169
|
-
canExecute?: (editor: EditorAPI) => boolean;
|
|
170
|
-
}
|
|
292
|
+
// Via settings:
|
|
293
|
+
<Editor settings={{ table: { enabled: true } }} />
|
|
294
|
+
|
|
295
|
+
// Via plugin:
|
|
296
|
+
<Editor plugins={[...otherPlugins, tablePlugin]} />
|
|
171
297
|
```
|
|
172
298
|
|
|
173
|
-
|
|
299
|
+
Right-click a table cell to access the context menu with row/column operations.
|
|
174
300
|
|
|
175
|
-
|
|
301
|
+
---
|
|
176
302
|
|
|
177
|
-
|
|
178
|
-
- **Undo/Redo** - History management
|
|
179
|
-
- **Block Format Dropdown** - Headings (h1-h3 by default), Lists (ul, ol), Blockquote
|
|
180
|
-
- **Clear Formatting** - Remove all formatting
|
|
181
|
-
- **Indent/Outdent** - List indentation (Tab/Shift+Tab)
|
|
303
|
+
## Theming
|
|
182
304
|
|
|
183
|
-
|
|
305
|
+
### CSS Variables
|
|
184
306
|
|
|
185
|
-
-
|
|
186
|
-
- `createFontSizePlugin` - Font size selector
|
|
187
|
-
- `createColorPlugin` - Text color picker
|
|
188
|
-
- `createBackgroundColorPlugin` - Background color picker
|
|
189
|
-
- `createImagePlugin` - Image upload
|
|
190
|
-
- `createHeadingsPlugin` - Custom heading levels (if not using default block format)
|
|
307
|
+
Override CSS variables on `.rte-container` or a parent element:
|
|
191
308
|
|
|
192
|
-
|
|
309
|
+
```css
|
|
310
|
+
.my-editor {
|
|
311
|
+
--rte-primary-color: #339192;
|
|
312
|
+
--rte-primary-light: rgba(51, 145, 146, 0.15);
|
|
313
|
+
--rte-border-color: #e0e0e0;
|
|
314
|
+
--rte-toolbar-bg: #fafafa;
|
|
315
|
+
--rte-content-bg: #ffffff;
|
|
316
|
+
--rte-button-hover-bg: rgba(51, 145, 146, 0.08);
|
|
317
|
+
--rte-border-radius: 8px;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
193
320
|
|
|
194
|
-
###
|
|
321
|
+
### Theme Prop
|
|
195
322
|
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
323
|
+
```tsx
|
|
324
|
+
<Editor
|
|
325
|
+
theme={{
|
|
326
|
+
primaryColor: "#339192",
|
|
327
|
+
borderColor: "#e0e0e0",
|
|
328
|
+
borderRadius: 8,
|
|
329
|
+
toolbarBg: "#fafafa",
|
|
330
|
+
contentBg: "#ffffff",
|
|
331
|
+
buttonHoverBg: "rgba(51, 145, 146, 0.08)",
|
|
332
|
+
}}
|
|
333
|
+
/>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### CSS Class Overrides
|
|
337
|
+
|
|
338
|
+
```css
|
|
339
|
+
.rte-container { /* outer wrapper */ }
|
|
340
|
+
.rte-toolbar { /* toolbar row */ }
|
|
341
|
+
.rte-toolbar-button { /* individual toolbar button */ }
|
|
342
|
+
.rte-toolbar-button-active { /* active state */ }
|
|
343
|
+
.rte-editor { /* contenteditable area */ }
|
|
206
344
|
```
|
|
207
345
|
|
|
208
|
-
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## HTML Import / Export
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
import { htmlToContent, contentToHTML, EditorAPI } from "@overlap/rte";
|
|
352
|
+
|
|
353
|
+
// Import HTML into the editor
|
|
354
|
+
const content = htmlToContent("<p>Hello <strong>world</strong></p>");
|
|
355
|
+
<Editor initialContent={content} />
|
|
209
356
|
|
|
210
|
-
|
|
357
|
+
// Export HTML from the editor
|
|
358
|
+
function MyEditor() {
|
|
359
|
+
const apiRef = useRef<EditorAPI>(null);
|
|
360
|
+
|
|
361
|
+
return (
|
|
362
|
+
<Editor
|
|
363
|
+
onEditorAPIReady={(api) => { apiRef.current = api; }}
|
|
364
|
+
onChange={() => {
|
|
365
|
+
const html = apiRef.current?.exportHtml();
|
|
366
|
+
console.log(html);
|
|
367
|
+
}}
|
|
368
|
+
/>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Editor Props
|
|
376
|
+
|
|
377
|
+
| Prop | Type | Description |
|
|
378
|
+
| --- | --- | --- |
|
|
379
|
+
| `initialContent` | `EditorContent?` | Initial editor content (JSON) |
|
|
380
|
+
| `onChange` | `(content: EditorContent) => void` | Called on every content change |
|
|
381
|
+
| `plugins` | `Plugin[]?` | Manual plugin array (overrides settings) |
|
|
382
|
+
| `settings` | `EditorSettings?` | Settings object to auto-build plugins |
|
|
383
|
+
| `settingsOptions` | `BuildPluginsOptions?` | Extra options when using settings (e.g. `onImageUpload`, `linkCustomFields`) |
|
|
384
|
+
| `placeholder` | `string?` | Placeholder text (default: `"Enter text..."`) |
|
|
385
|
+
| `className` | `string?` | CSS class for the outer container |
|
|
386
|
+
| `toolbarClassName` | `string?` | CSS class for the toolbar |
|
|
387
|
+
| `editorClassName` | `string?` | CSS class for the editor area |
|
|
388
|
+
| `headings` | `string[]?` | Heading levels when using default plugins |
|
|
389
|
+
| `fontSizes` | `number[]?` | Font sizes when using default plugins |
|
|
390
|
+
| `colors` | `string[]?` | Color palette when using default plugins |
|
|
391
|
+
| `onImageUpload` | `(file: File) => Promise<string>` | Image upload callback |
|
|
392
|
+
| `onEditorAPIReady` | `(api: EditorAPI) => void` | Callback when editor API is ready |
|
|
393
|
+
| `theme` | `object?` | Theme overrides (see Theming section) |
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## EditorAPI
|
|
398
|
+
|
|
399
|
+
Available via `onEditorAPIReady` callback or passed to plugin functions:
|
|
400
|
+
|
|
401
|
+
| Method | Description |
|
|
402
|
+
| --- | --- |
|
|
403
|
+
| `executeCommand(cmd, value?)` | Execute a `document.execCommand` |
|
|
404
|
+
| `getSelection()` | Get the current `Selection` |
|
|
405
|
+
| `getContent()` | Get content as `EditorContent` JSON |
|
|
406
|
+
| `setContent(content)` | Set content from `EditorContent` JSON |
|
|
407
|
+
| `exportHtml()` | Export current content as HTML string |
|
|
408
|
+
| `importHtml(html)` | Import HTML string into the editor |
|
|
409
|
+
| `insertBlock(type, attrs?)` | Insert a block element |
|
|
410
|
+
| `insertInline(type, attrs?)` | Insert an inline element |
|
|
411
|
+
| `undo()` / `redo()` | History navigation |
|
|
412
|
+
| `canUndo()` / `canRedo()` | Check history state |
|
|
413
|
+
| `indentListItem()` | Indent the current list item |
|
|
414
|
+
| `outdentListItem()` | Outdent the current list item |
|
|
415
|
+
| `clearFormatting()` | Remove all formatting from selection |
|
|
416
|
+
| `clearTextColor()` | Remove text color |
|
|
417
|
+
| `clearBackgroundColor()` | Remove background color |
|
|
418
|
+
| `clearFontSize()` | Remove font size |
|
|
419
|
+
| `clearLinks()` | Remove links (keep text) |
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Creating Custom Plugins
|
|
211
424
|
|
|
212
425
|
```typescript
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
426
|
+
import { Plugin, EditorAPI, ButtonProps } from "@overlap/rte";
|
|
427
|
+
|
|
428
|
+
const highlightPlugin: Plugin = {
|
|
429
|
+
name: "highlight",
|
|
430
|
+
type: "inline",
|
|
216
431
|
renderButton: (props: ButtonProps) => (
|
|
217
|
-
<button
|
|
218
|
-
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
432
|
+
<button
|
|
433
|
+
onClick={props.onClick}
|
|
434
|
+
className={`rte-toolbar-button ${props.isActive ? "rte-toolbar-button-active" : ""}`}
|
|
435
|
+
title="Highlight"
|
|
436
|
+
>
|
|
437
|
+
H
|
|
222
438
|
</button>
|
|
223
439
|
),
|
|
224
440
|
execute: (editor: EditorAPI) => {
|
|
225
|
-
editor.
|
|
441
|
+
editor.executeCommand("backColor", "#ffff00");
|
|
226
442
|
},
|
|
227
|
-
isActive: (
|
|
228
|
-
|
|
229
|
-
return false;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return true;
|
|
443
|
+
isActive: () => {
|
|
444
|
+
const sel = document.getSelection();
|
|
445
|
+
if (!sel || sel.rangeCount === 0) return false;
|
|
446
|
+
const el = sel.getRangeAt(0).commonAncestorContainer;
|
|
447
|
+
const parent = el.nodeType === Node.TEXT_NODE ? el.parentElement : el as HTMLElement;
|
|
448
|
+
return parent?.closest("[style*='background-color: rgb(255, 255, 0)']") !== null;
|
|
234
449
|
},
|
|
450
|
+
canExecute: () => true,
|
|
235
451
|
};
|
|
236
452
|
```
|
|
237
453
|
|
|
238
|
-
|
|
454
|
+
---
|
|
239
455
|
|
|
240
|
-
|
|
456
|
+
## Exports Overview
|
|
241
457
|
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
458
|
+
```typescript
|
|
459
|
+
// Components
|
|
460
|
+
export { Editor, Toolbar, Dropdown };
|
|
461
|
+
|
|
462
|
+
// Settings
|
|
463
|
+
export { EditorSettings, defaultEditorSettings, buildPluginsFromSettings, BuildPluginsOptions };
|
|
464
|
+
|
|
465
|
+
// Individual plugins
|
|
466
|
+
export { boldPlugin, italicPlugin, underlinePlugin, strikethroughPlugin };
|
|
467
|
+
export { subscriptPlugin, superscriptPlugin, codeInlinePlugin };
|
|
468
|
+
export { undoPlugin, redoPlugin, clearFormattingPlugin };
|
|
469
|
+
export { indentListItemPlugin, outdentListItemPlugin };
|
|
470
|
+
export { defaultPlugins };
|
|
471
|
+
|
|
472
|
+
// Plugin factories
|
|
473
|
+
export { createBlockFormatPlugin, BlockFormatOptions };
|
|
474
|
+
export { createTextColorPlugin, createBackgroundColorPlugin };
|
|
475
|
+
export { createFontSizePlugin };
|
|
476
|
+
export { createAlignmentPlugin };
|
|
477
|
+
export { createAdvancedLinkPlugin, LinkCustomField };
|
|
478
|
+
export { createImagePlugin };
|
|
479
|
+
export { tablePlugin };
|
|
480
|
+
|
|
481
|
+
// Utilities
|
|
482
|
+
export { htmlToContent, contentToHTML, contentToDOM, domToContent, createEmptyContent };
|
|
483
|
+
export { HistoryManager };
|
|
484
|
+
|
|
485
|
+
// Types
|
|
486
|
+
export { Plugin, EditorAPI, EditorContent, EditorNode, EditorProps, ButtonProps };
|
|
258
487
|
```
|
|
259
488
|
|
|
489
|
+
---
|
|
490
|
+
|
|
260
491
|
## Development
|
|
261
492
|
|
|
262
493
|
```bash
|
|
263
|
-
#
|
|
264
|
-
npm
|
|
265
|
-
|
|
266
|
-
# Build
|
|
267
|
-
npm run build
|
|
494
|
+
npm install # install dependencies
|
|
495
|
+
npm run build # production build
|
|
496
|
+
npm run dev # watch mode
|
|
268
497
|
|
|
269
|
-
#
|
|
270
|
-
npm run dev
|
|
498
|
+
cd example && npm run dev # run the showcase app
|
|
271
499
|
```
|
|
272
500
|
|
|
273
|
-
##
|
|
501
|
+
## License
|
|
274
502
|
|
|
275
503
|
MIT
|