@crashbytes/contentful-richtext-editor 2.0.4 → 2.0.5
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 +156 -97
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,24 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Contentful Rich Text Editor
|
|
2
2
|
|
|
3
3
|
A modern, Tiptap-based rich text editor that's fully compatible with Contentful's rich text format. Provides the same editing experience as Contentful's native editor while maintaining perfect compatibility with Contentful's document structure.
|
|
4
4
|
|
|
5
5
|
## ✨ Features
|
|
6
6
|
|
|
7
7
|
- ✅ **Full Contentful Compatibility** - Seamless conversion between Contentful and Tiptap formats
|
|
8
|
-
- ✅ **
|
|
8
|
+
- ✅ **Automatic Configuration** - Reads Contentful field settings to auto-configure the editor *(NEW in v2.0)*
|
|
9
|
+
- ✅ **Modern UI** - Clean, intuitive interface matching Contentful's design
|
|
9
10
|
- ✅ **TypeScript Support** - Complete type safety with Contentful's rich text types
|
|
10
11
|
- ✅ **Extensible** - Built on Tiptap v2 for easy customization
|
|
11
12
|
- ✅ **Lightweight** - Tree-shakeable, only import what you need
|
|
12
13
|
- ✅ **Responsive** - Works on desktop and mobile devices
|
|
13
|
-
- ✅ **
|
|
14
|
+
- ✅ **Keyboard Shortcuts** - Built-in shortcuts for common actions *(NEW in v2.0)*
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
### Supported Features
|
|
17
|
+
|
|
18
|
+
- **Text Formatting**: Bold, italic, underline
|
|
19
|
+
- **Headings**: H1 through H6
|
|
20
|
+
- **Lists**: Ordered and unordered lists
|
|
21
|
+
- **Links**: Hyperlinks with URL validation
|
|
22
|
+
- **Tables**: Full table support with headers
|
|
23
|
+
- **Quotes**: Blockquotes
|
|
24
|
+
- **Embedded Content**: Callbacks for Contentful entries and assets
|
|
25
|
+
- **Inline Entries**: Support for inline embedded entries *(NEW in v2.0)*
|
|
26
|
+
- **Undo/Redo**: Full history support
|
|
27
|
+
|
|
28
|
+
## 📦 Installation
|
|
16
29
|
|
|
17
30
|
```bash
|
|
18
31
|
npm install @crashbytes/contentful-richtext-editor
|
|
19
32
|
```
|
|
20
33
|
|
|
21
|
-
##
|
|
34
|
+
## 🚀 Quick Start
|
|
35
|
+
|
|
36
|
+
### Basic Usage
|
|
22
37
|
|
|
23
38
|
```tsx
|
|
24
39
|
import React, { useState } from 'react';
|
|
@@ -41,7 +56,6 @@ function App() {
|
|
|
41
56
|
placeholder="Start writing your content..."
|
|
42
57
|
onChange={handleChange}
|
|
43
58
|
initialValue={content}
|
|
44
|
-
showBorder={true} // Control border visibility
|
|
45
59
|
/>
|
|
46
60
|
</div>
|
|
47
61
|
);
|
|
@@ -50,55 +64,41 @@ function App() {
|
|
|
50
64
|
export default App;
|
|
51
65
|
```
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
### Automatic Configuration (NEW in v2.0)
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
The editor can automatically configure itself based on your Contentful field settings:
|
|
56
70
|
|
|
57
71
|
```tsx
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
showBorder={false}
|
|
64
|
-
className="my-custom-editor"
|
|
65
|
-
/>
|
|
66
|
-
|
|
67
|
-
// Themed borderless editor
|
|
68
|
-
<ContentfulRichTextEditor
|
|
69
|
-
showBorder={false}
|
|
70
|
-
theme="minimal"
|
|
71
|
-
/>
|
|
72
|
-
```
|
|
72
|
+
import {
|
|
73
|
+
ContentfulRichTextEditor,
|
|
74
|
+
fetchContentfulFieldConfig
|
|
75
|
+
} from '@crashbytes/contentful-richtext-editor';
|
|
76
|
+
import '@crashbytes/contentful-richtext-editor/dist/index.css';
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
function AutoConfiguredEditor() {
|
|
79
|
+
const [fieldConfig, setFieldConfig] = useState();
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
// Fetch configuration from Contentful Management API
|
|
83
|
+
fetchContentfulFieldConfig(
|
|
84
|
+
'your-space-id',
|
|
85
|
+
'your-content-type-id',
|
|
86
|
+
'your-field-id',
|
|
87
|
+
'your-management-token'
|
|
88
|
+
).then(setFieldConfig);
|
|
89
|
+
}, []);
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
return (
|
|
92
|
+
<ContentfulRichTextEditor
|
|
93
|
+
fieldConfiguration={fieldConfig}
|
|
94
|
+
placeholder="Auto-configured based on Contentful settings!"
|
|
95
|
+
onChange={handleChange}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
85
98
|
}
|
|
86
99
|
```
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- **Text Formatting**: Bold, italic, underline
|
|
91
|
-
- **Headings**: H1 through H6
|
|
92
|
-
- **Lists**: Ordered and unordered lists
|
|
93
|
-
- **Links**: Hyperlinks with URL validation
|
|
94
|
-
- **Tables**: Full table support with headers
|
|
95
|
-
- **Quotes**: Blockquotes
|
|
96
|
-
- **Embedded Content**: Callbacks for Contentful entries and assets
|
|
97
|
-
- **Undo/Redo**: Full history support
|
|
98
|
-
|
|
99
|
-
## 🔧 Advanced Usage
|
|
100
|
-
|
|
101
|
-
### With Contentful Integration
|
|
101
|
+
### Advanced Usage with Embeds
|
|
102
102
|
|
|
103
103
|
```tsx
|
|
104
104
|
import { ContentfulRichTextEditor } from '@crashbytes/contentful-richtext-editor';
|
|
@@ -112,25 +112,31 @@ function ContentfulEditor() {
|
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
const handleEmbedAsset = async () => {
|
|
115
|
-
// Your logic to select a Contentful asset
|
|
115
|
+
// Your logic to select a Contentful asset
|
|
116
116
|
const asset = await openAssetSelector();
|
|
117
117
|
return asset;
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
const handleEmbedInlineEntry = async () => {
|
|
121
|
+
// NEW v2.0 - Your logic to select an inline entry
|
|
122
|
+
const entry = await openInlineEntrySelector();
|
|
123
|
+
return entry;
|
|
124
|
+
};
|
|
125
|
+
|
|
120
126
|
return (
|
|
121
127
|
<ContentfulRichTextEditor
|
|
122
128
|
placeholder="Write your travel tip..."
|
|
123
129
|
onChange={(doc) => saveToContentful(doc)}
|
|
124
130
|
onEmbedEntry={handleEmbedEntry}
|
|
125
131
|
onEmbedAsset={handleEmbedAsset}
|
|
132
|
+
onEmbedInlineEntry={handleEmbedInlineEntry}
|
|
126
133
|
theme="contentful"
|
|
127
|
-
showBorder={true}
|
|
128
134
|
/>
|
|
129
135
|
);
|
|
130
136
|
}
|
|
131
137
|
```
|
|
132
138
|
|
|
133
|
-
###
|
|
139
|
+
### Minimal Configuration
|
|
134
140
|
|
|
135
141
|
```tsx
|
|
136
142
|
<ContentfulRichTextEditor
|
|
@@ -143,25 +149,58 @@ function ContentfulEditor() {
|
|
|
143
149
|
/>
|
|
144
150
|
```
|
|
145
151
|
|
|
146
|
-
|
|
152
|
+
### With Initial Content
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { createEmptyDocument } from '@crashbytes/contentful-richtext-editor';
|
|
156
|
+
|
|
157
|
+
const initialContent = {
|
|
158
|
+
nodeType: 'document',
|
|
159
|
+
data: {},
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
nodeType: 'paragraph',
|
|
163
|
+
data: {},
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
nodeType: 'text',
|
|
167
|
+
value: 'Hello world!',
|
|
168
|
+
marks: [{ type: 'bold' }],
|
|
169
|
+
data: {}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
<ContentfulRichTextEditor
|
|
177
|
+
initialValue={initialContent}
|
|
178
|
+
onChange={handleChange}
|
|
179
|
+
/>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## 📋 API Reference
|
|
183
|
+
|
|
184
|
+
### ContentfulRichTextEditor Props
|
|
147
185
|
|
|
148
186
|
| Prop | Type | Default | Description |
|
|
149
187
|
|------|------|---------|-------------|
|
|
150
|
-
| `
|
|
188
|
+
| `availableHeadings` | `Array<1\|2\|3\|4\|5\|6>` | `[1,2,3,4,5,6]` | Available heading levels (fallback when no fieldConfiguration) |
|
|
189
|
+
| `availableMarks` | `Array<'bold'\|'italic'\|'underline'>` | `['bold','italic','underline']` | Available text formatting marks (fallback when no fieldConfiguration) |
|
|
190
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
191
|
+
| `disabledFeatures` | `Array<string>` | `[]` | Features to disable |
|
|
192
|
+
| `fieldConfiguration` | `ContentfulFieldConfiguration` | `undefined` | **NEW v2.0** - Contentful field validation config (takes precedence over manual settings) |
|
|
151
193
|
| `initialValue` | `Document` | `undefined` | Initial Contentful rich text document |
|
|
152
194
|
| `onChange` | `(document: Document) => void` | `undefined` | Callback when content changes |
|
|
153
|
-
| `onEmbedEntry` | `() => Promise<any> \| void` | `undefined` | Callback for embedding Contentful entries |
|
|
154
195
|
| `onEmbedAsset` | `() => Promise<any> \| void` | `undefined` | Callback for embedding Contentful assets |
|
|
155
|
-
| `
|
|
196
|
+
| `onEmbedEntry` | `() => Promise<any> \| void` | `undefined` | Callback for embedding Contentful entries |
|
|
197
|
+
| `onEmbedInlineEntry` | `() => Promise<any> \| void` | `undefined` | **NEW v2.0** - Callback for embedding inline entries |
|
|
156
198
|
| `placeholder` | `string` | `'Start writing...'` | Placeholder text |
|
|
157
199
|
| `readonly` | `boolean` | `false` | Whether editor is read-only |
|
|
158
|
-
| `
|
|
200
|
+
| `showBorder` | `boolean` | `true` | Whether to show border around the editor |
|
|
159
201
|
| `theme` | `'default' \| 'minimal' \| 'contentful'` | `'contentful'` | Visual theme |
|
|
160
|
-
| `disabledFeatures` | `Array<string>` | `[]` | Features to disable |
|
|
161
|
-
| `availableHeadings` | `Array<1 \| 2 \| 3 \| 4 \| 5 \| 6>` | `[1,2,3,4,5,6]` | Available heading levels |
|
|
162
|
-
| `availableMarks` | `Array<'bold' \| 'italic' \| 'underline'>` | `['bold','italic','underline']` | Available text formatting |
|
|
163
202
|
|
|
164
|
-
|
|
203
|
+
### Disabled Features
|
|
165
204
|
|
|
166
205
|
You can disable specific features by passing them in the `disabledFeatures` array:
|
|
167
206
|
|
|
@@ -175,6 +214,20 @@ You can disable specific features by passing them in the `disabledFeatures` arra
|
|
|
175
214
|
- `'table'` - Tables
|
|
176
215
|
- `'embed'` - Embedded content
|
|
177
216
|
|
|
217
|
+
### Configuration Types (NEW v2.0)
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
interface ContentfulFieldConfiguration {
|
|
221
|
+
validations?: Array<{
|
|
222
|
+
enabledMarks?: string[]; // ['bold', 'italic', 'underline', 'code']
|
|
223
|
+
enabledNodeTypes?: string[]; // ['paragraph', 'heading-1', 'unordered-list', ...]
|
|
224
|
+
}>;
|
|
225
|
+
settings?: {
|
|
226
|
+
helpText?: string;
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
178
231
|
## 🛠️ Utility Functions
|
|
179
232
|
|
|
180
233
|
```tsx
|
|
@@ -183,23 +236,47 @@ import {
|
|
|
183
236
|
tiptapToContentful,
|
|
184
237
|
validateContentfulDocument,
|
|
185
238
|
createEmptyDocument,
|
|
186
|
-
extractPlainText,
|
|
187
|
-
countWords,
|
|
188
|
-
findEmbeddedContent
|
|
239
|
+
extractPlainText, // NEW v2.0
|
|
240
|
+
countWords, // NEW v2.0
|
|
241
|
+
findEmbeddedContent, // NEW v2.0
|
|
242
|
+
sanitizeContentfulDocument // NEW v2.0
|
|
189
243
|
} from '@crashbytes/contentful-richtext-editor';
|
|
190
244
|
|
|
191
245
|
// Convert between formats
|
|
192
246
|
const tiptapJson = contentfulToTiptap(contentfulDocument);
|
|
193
247
|
const contentfulDoc = tiptapToContentful(tiptapJson);
|
|
194
248
|
|
|
195
|
-
// Validation
|
|
249
|
+
// Validation
|
|
196
250
|
const isValid = validateContentfulDocument(someDocument);
|
|
251
|
+
|
|
252
|
+
// Create empty document
|
|
197
253
|
const emptyDoc = createEmptyDocument();
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
254
|
+
|
|
255
|
+
// NEW v2.0 - Content analysis
|
|
256
|
+
const plainText = extractPlainText(contentfulDocument);
|
|
257
|
+
const wordCount = countWords(contentfulDocument);
|
|
258
|
+
const embedded = findEmbeddedContent(contentfulDocument);
|
|
259
|
+
|
|
260
|
+
// NEW v2.0 - Sanitize content
|
|
261
|
+
const sanitized = sanitizeContentfulDocument(
|
|
262
|
+
document,
|
|
263
|
+
['paragraph', 'heading-1', 'unordered-list'], // allowed nodes
|
|
264
|
+
['bold', 'italic'] // allowed marks
|
|
265
|
+
);
|
|
201
266
|
```
|
|
202
267
|
|
|
268
|
+
## ⌨️ Keyboard Shortcuts (NEW v2.0)
|
|
269
|
+
|
|
270
|
+
- `Cmd/Ctrl + B` - Bold
|
|
271
|
+
- `Cmd/Ctrl + I` - Italic
|
|
272
|
+
- `Cmd/Ctrl + U` - Underline
|
|
273
|
+
- `Cmd/Ctrl + K` - Add/edit link
|
|
274
|
+
- `Cmd/Ctrl + Shift + E` - Embed entry
|
|
275
|
+
- `Cmd/Ctrl + Shift + A` - Embed asset
|
|
276
|
+
- `Cmd/Ctrl + Shift + I` - Embed inline entry
|
|
277
|
+
- `Cmd/Ctrl + Z` - Undo
|
|
278
|
+
- `Cmd/Ctrl + Y` / `Cmd/Ctrl + Shift + Z` - Redo
|
|
279
|
+
|
|
203
280
|
## 🎨 Styling
|
|
204
281
|
|
|
205
282
|
The editor comes with default styles that match Contentful's design. Import the CSS:
|
|
@@ -208,8 +285,6 @@ The editor comes with default styles that match Contentful's design. Import the
|
|
|
208
285
|
import '@crashbytes/contentful-richtext-editor/dist/index.css';
|
|
209
286
|
```
|
|
210
287
|
|
|
211
|
-
### Custom Styling
|
|
212
|
-
|
|
213
288
|
You can override the default styles by targeting the CSS classes:
|
|
214
289
|
|
|
215
290
|
```css
|
|
@@ -225,25 +300,27 @@ You can override the default styles by targeting the CSS classes:
|
|
|
225
300
|
font-family: 'Your Font', sans-serif;
|
|
226
301
|
}
|
|
227
302
|
|
|
228
|
-
/*
|
|
229
|
-
.contentful-
|
|
230
|
-
|
|
231
|
-
|
|
303
|
+
/* NEW v2.0 - Inline entries */
|
|
304
|
+
.contentful-inline-embedded-entry {
|
|
305
|
+
background: #e8f4fd;
|
|
306
|
+
padding: 2px 4px;
|
|
307
|
+
border-radius: 3px;
|
|
308
|
+
font-weight: 600;
|
|
232
309
|
}
|
|
233
310
|
```
|
|
234
311
|
|
|
235
312
|
### Themes
|
|
236
313
|
|
|
237
|
-
|
|
314
|
+
**`contentful`** (default)
|
|
238
315
|
Matches Contentful's native editor appearance.
|
|
239
316
|
|
|
240
|
-
|
|
317
|
+
**`minimal`**
|
|
241
318
|
Clean, minimal design with reduced visual elements.
|
|
242
319
|
|
|
243
|
-
|
|
320
|
+
**`default`**
|
|
244
321
|
Standard rich text editor appearance with serif fonts.
|
|
245
322
|
|
|
246
|
-
##
|
|
323
|
+
## ⚛️ Next.js Integration
|
|
247
324
|
|
|
248
325
|
```tsx
|
|
249
326
|
// pages/editor.tsx or app/editor/page.tsx
|
|
@@ -260,14 +337,13 @@ export default function EditorPage() {
|
|
|
260
337
|
<ContentfulEditor
|
|
261
338
|
placeholder="Write something amazing..."
|
|
262
339
|
onChange={(doc) => console.log(doc)}
|
|
263
|
-
showBorder={false}
|
|
264
340
|
/>
|
|
265
341
|
</div>
|
|
266
342
|
);
|
|
267
343
|
}
|
|
268
344
|
```
|
|
269
345
|
|
|
270
|
-
##
|
|
346
|
+
## 🔷 TypeScript Support
|
|
271
347
|
|
|
272
348
|
This package is written in TypeScript and includes full type definitions. All Contentful rich text types are re-exported for convenience:
|
|
273
349
|
|
|
@@ -277,7 +353,9 @@ import type {
|
|
|
277
353
|
Block,
|
|
278
354
|
Inline,
|
|
279
355
|
Text,
|
|
280
|
-
ContentfulRichTextEditorProps
|
|
356
|
+
ContentfulRichTextEditorProps,
|
|
357
|
+
ContentfulFieldConfiguration, // NEW v2.0
|
|
358
|
+
ParsedEditorConfig // NEW v2.0
|
|
281
359
|
} from '@crashbytes/contentful-richtext-editor';
|
|
282
360
|
```
|
|
283
361
|
|
|
@@ -288,19 +366,6 @@ import type {
|
|
|
288
366
|
- Safari 13+
|
|
289
367
|
- Edge 80+
|
|
290
368
|
|
|
291
|
-
## 🔄 Migration
|
|
292
|
-
|
|
293
|
-
### From v2.0.3 to v2.0.4
|
|
294
|
-
No breaking changes! Simply update and optionally use the new `showBorder` prop:
|
|
295
|
-
|
|
296
|
-
```tsx
|
|
297
|
-
// Before
|
|
298
|
-
<ContentfulRichTextEditor />
|
|
299
|
-
|
|
300
|
-
// After (optional)
|
|
301
|
-
<ContentfulRichTextEditor showBorder={false} />
|
|
302
|
-
```
|
|
303
|
-
|
|
304
369
|
## 🤝 Contributing
|
|
305
370
|
|
|
306
371
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -321,12 +386,6 @@ MIT © [CrashBytes](https://github.com/CrashBytes)
|
|
|
321
386
|
- [@contentful/rich-text-types](https://www.npmjs.com/package/@contentful/rich-text-types) - Contentful rich text type definitions
|
|
322
387
|
- [@tiptap/react](https://www.npmjs.com/package/@tiptap/react) - The underlying editor framework
|
|
323
388
|
|
|
324
|
-
## 📈 Version History
|
|
325
|
-
|
|
326
|
-
- **v2.0.4** - Added `showBorder` prop for flexible styling, resolved publishing issues
|
|
327
|
-
- **v2.0.0** - Major release with full Contentful compatibility and modern architecture
|
|
328
|
-
- **v1.x** - Initial releases with basic functionality
|
|
329
|
-
|
|
330
389
|
---
|
|
331
390
|
|
|
332
391
|
Made with ❤️ for the Contentful community
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crashbytes/contentful-richtext-editor",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "A Tiptap-based rich text editor compatible with Contentful's rich text format",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"dist"
|
|
11
11
|
],
|
|
12
|
-
|
|
12
|
+
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|