nextext-editor 0.1.0 → 0.1.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 +329 -0
- package/dist/block/EditorBlock.d.ts +38 -0
- package/dist/components/Editor.d.ts +4 -31
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/components/block/EditorBlock.d.ts +0 -10
- /package/dist/{components/block → block}/index.d.ts +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# NextText Editor
|
|
2
|
+
|
|
3
|
+
> A modern, customizable rich text editor for React with shadcn/ui patterns, Tailwind CSS styling, and collaborative editing powered by Loro CRDT.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/nextext-editor)
|
|
6
|
+
|
|
7
|
+
## Preview
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🎨 **Rich Formatting** - Bold, italic, underline, strikethrough, inline code, text color, and highlighting
|
|
14
|
+
- 📝 **Block Types** - Headings (H1-H6), paragraphs, bullet lists, numbered lists, blockquotes, code blocks, horizontal rules
|
|
15
|
+
- 🖼️ **Media Support** - Image upload with drag-and-drop (max 5MB, auto-styled)
|
|
16
|
+
- 📊 **Tables** - Insert and edit tables with styled cells
|
|
17
|
+
- 🔄 **Built-in Collaboration** - Loro CRDT for conflict-free real-time editing
|
|
18
|
+
- 🎨 **Customizable Design** - shadcn/ui-inspired design tokens for easy theming
|
|
19
|
+
- 📱 **Responsive** - Works seamlessly on desktop and mobile
|
|
20
|
+
- ⌨️ **Keyboard Shortcuts** - Familiar shortcuts (Ctrl/Cmd+B, I, U, Z, Y)
|
|
21
|
+
- 🔍 **Live Preview** - View your content in HTML, Text, or JSON format
|
|
22
|
+
- 📊 **Word & Character Count** - Real-time statistics display
|
|
23
|
+
- 🎯 **TypeScript** - Fully typed for better DX
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install nextext-editor
|
|
29
|
+
# or
|
|
30
|
+
yarn add nextext-editor
|
|
31
|
+
# or
|
|
32
|
+
pnpm add nextext-editor
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Basic Usage
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { EditorBlock } from 'nextext-editor';
|
|
41
|
+
import 'nextext-editor/styles.css';
|
|
42
|
+
|
|
43
|
+
function App() {
|
|
44
|
+
return <EditorBlock placeholder="Start writing..." />;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### With Preview Panel
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { EditorBlock } from 'nextext-editor';
|
|
52
|
+
import 'nextext-editor/styles.css';
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<EditorBlock
|
|
57
|
+
showPreview
|
|
58
|
+
placeholder="Start writing..."
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Controlled Mode with External State
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { useState } from 'react';
|
|
68
|
+
import { EditorBlock } from 'nextext-editor';
|
|
69
|
+
import 'nextext-editor/styles.css';
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
const [content, setContent] = useState('<p>Initial content</p>');
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<EditorBlock
|
|
76
|
+
externalContent={content}
|
|
77
|
+
onContentChange={setContent}
|
|
78
|
+
showPreview
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Using Loro CRDT for Collaboration
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { useLoroEditor, EditorBlock } from 'nextext-editor';
|
|
88
|
+
import 'nextext-editor/styles.css';
|
|
89
|
+
|
|
90
|
+
function CollaborativeEditor() {
|
|
91
|
+
const { content, updateContent, loroDoc } = useLoroEditor();
|
|
92
|
+
|
|
93
|
+
// Share loroDoc with other clients for real-time sync
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<EditorBlock
|
|
97
|
+
externalContent={content}
|
|
98
|
+
onContentChange={updateContent}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Using Editor Directly (Headless)
|
|
105
|
+
|
|
106
|
+
If you want to build your own toolbar and UI, use the low-level `Editor` component:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { useState } from 'react';
|
|
110
|
+
import { Editor } from 'nextext-editor';
|
|
111
|
+
import 'nextext-editor/styles.css';
|
|
112
|
+
|
|
113
|
+
function CustomEditor() {
|
|
114
|
+
const [content, setContent] = useState('<p>Start typing...</p>');
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div>
|
|
118
|
+
<div className="my-custom-toolbar">
|
|
119
|
+
<button onClick={() => document.execCommand('bold')}>Bold</button>
|
|
120
|
+
<button onClick={() => document.execCommand('italic')}>Italic</button>
|
|
121
|
+
</div>
|
|
122
|
+
<Editor
|
|
123
|
+
content={content}
|
|
124
|
+
onContentChange={setContent}
|
|
125
|
+
placeholder="Write something..."
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Tech Stack
|
|
133
|
+
|
|
134
|
+
- **React 18** - UI framework
|
|
135
|
+
- **TypeScript** - Type safety
|
|
136
|
+
- **Loro CRDT** - Conflict-free collaboration
|
|
137
|
+
- **Tailwind CSS v4** - Styling
|
|
138
|
+
- **Lucide React** - Icons
|
|
139
|
+
- **Vite** - Build tool
|
|
140
|
+
|
|
141
|
+
## API Reference
|
|
142
|
+
|
|
143
|
+
### Editor Props (Core ContentEditable Component)
|
|
144
|
+
|
|
145
|
+
| Prop | Type | Default | Description |
|
|
146
|
+
|------|------|---------|-------------|
|
|
147
|
+
| `content` | `string` | **required** | HTML content to display |
|
|
148
|
+
| `onContentChange` | `(html: string) => void` | **required** | Called when content changes |
|
|
149
|
+
| `placeholder` | `string` | `"Start typing..."` | Placeholder text when empty |
|
|
150
|
+
| `className` | `string` | - | Custom class for the editable area |
|
|
151
|
+
| `tokens` | `EditorTokens` | `editorTokens` | Custom design tokens |
|
|
152
|
+
|
|
153
|
+
### EditorBlock Props (Complete Editor with Toolbar & Preview)
|
|
154
|
+
|
|
155
|
+
| Prop | Type | Default | Description |
|
|
156
|
+
|------|------|---------|-------------|
|
|
157
|
+
| `initialContent` | `string` | - | Initial HTML content |
|
|
158
|
+
| `showPreview` | `boolean` | `false` | Show live preview panel |
|
|
159
|
+
| `showToolbar` | `boolean` | `true` | Show formatting toolbar |
|
|
160
|
+
| `externalContent` | `string` | - | Controlled content (for controlled mode) |
|
|
161
|
+
| `onContentChange` | `(html: string) => void` | - | Content change callback |
|
|
162
|
+
| `className` | `string` | - | Custom container class |
|
|
163
|
+
| `placeholder` | `string` | `"Start typing..."` | Placeholder text |
|
|
164
|
+
| `tokens` | `EditorTokens` | `editorTokens` | Custom design tokens |
|
|
165
|
+
|
|
166
|
+
### useLoroEditor Hook
|
|
167
|
+
|
|
168
|
+
Returns an object with:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
{
|
|
172
|
+
content: string; // Current HTML content
|
|
173
|
+
format: TextFormat; // Current text format state
|
|
174
|
+
updateContent: (html: string) => void; // Update content
|
|
175
|
+
insertText: (text: string, position?: number) => void; // Insert text
|
|
176
|
+
deleteText: (start: number, length: number) => void; // Delete text
|
|
177
|
+
applyFormat: (start: number, end: number, format: TextFormat) => void; // Apply formatting
|
|
178
|
+
getSnapshot: () => Uint8Array; // Export Loro snapshot
|
|
179
|
+
loadSnapshot: (snapshot: Uint8Array) => void; // Import Loro snapshot
|
|
180
|
+
loroDoc: Loro | null; // Raw Loro document instance
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Design Tokens
|
|
185
|
+
|
|
186
|
+
Customize the editor's appearance by passing custom tokens:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { EditorBlock, editorTokens } from 'nextext-editor';
|
|
190
|
+
|
|
191
|
+
const customTokens = {
|
|
192
|
+
...editorTokens,
|
|
193
|
+
container: {
|
|
194
|
+
...editorTokens.container,
|
|
195
|
+
base: 'w-full bg-slate-900 rounded-xl border border-slate-700',
|
|
196
|
+
},
|
|
197
|
+
editor: {
|
|
198
|
+
...editorTokens.editor,
|
|
199
|
+
base: 'min-h-[500px] p-8 text-slate-100',
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
function App() {
|
|
204
|
+
return <EditorBlock tokens={customTokens} />;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Toolbar Actions
|
|
209
|
+
|
|
210
|
+
The editor supports these formatting actions:
|
|
211
|
+
|
|
212
|
+
### Text Formatting
|
|
213
|
+
- `bold` - Toggle bold text
|
|
214
|
+
- `italic` - Toggle italic text
|
|
215
|
+
- `underline` - Toggle underline
|
|
216
|
+
- `strikethrough` - Toggle strikethrough
|
|
217
|
+
- `code` - Toggle inline code
|
|
218
|
+
- `textColor` - Change text color
|
|
219
|
+
- `highlight` - Change background color
|
|
220
|
+
- `clearMarks` - Remove all formatting
|
|
221
|
+
|
|
222
|
+
### Block Types
|
|
223
|
+
- `paragraph` - Normal paragraph
|
|
224
|
+
- `h1`, `h2`, `h3`, `h4`, `h5`, `h6` - Headings
|
|
225
|
+
- `bulletList` - Unordered list
|
|
226
|
+
- `numberedList` - Ordered list
|
|
227
|
+
- `codeBlock` - Code block
|
|
228
|
+
- `blockquote` - Blockquote
|
|
229
|
+
- `clearNodes` - Reset to paragraph
|
|
230
|
+
|
|
231
|
+
### Content
|
|
232
|
+
- `image` - Upload image (max 5MB)
|
|
233
|
+
- `table` - Insert 3x3 table
|
|
234
|
+
- `horizontalRule` - Insert horizontal line
|
|
235
|
+
- `hardBreak` - Insert line break
|
|
236
|
+
|
|
237
|
+
### History
|
|
238
|
+
- `undo` - Undo last change
|
|
239
|
+
- `redo` - Redo last change
|
|
240
|
+
|
|
241
|
+
## Keyboard Shortcuts
|
|
242
|
+
|
|
243
|
+
- **Ctrl/Cmd + B** - Bold
|
|
244
|
+
- **Ctrl/Cmd + I** - Italic
|
|
245
|
+
- **Ctrl/Cmd + U** - Underline
|
|
246
|
+
- **Ctrl/Cmd + Z** - Undo
|
|
247
|
+
- **Ctrl/Cmd + Y** - Redo
|
|
248
|
+
|
|
249
|
+
## Architecture
|
|
250
|
+
|
|
251
|
+
The editor follows a clean component hierarchy:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
Editor (Core - in components/)
|
|
255
|
+
└── Low-level contentEditable component
|
|
256
|
+
└── Handles HTML editing, cursor management, keyboard events
|
|
257
|
+
|
|
258
|
+
EditorBlock (Complete Editor - in block/)
|
|
259
|
+
├── Uses Editor component
|
|
260
|
+
├── Adds Toolbar
|
|
261
|
+
├── Adds Preview panel
|
|
262
|
+
├── Adds word/character count
|
|
263
|
+
└── Integrates with Loro CRDT via useLoroEditor hook
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**File Structure:**
|
|
267
|
+
```
|
|
268
|
+
src/
|
|
269
|
+
├── components/
|
|
270
|
+
│ ├── Editor.tsx ← Core contentEditable (low-level)
|
|
271
|
+
│ ├── Toolbar.tsx
|
|
272
|
+
│ ├── Preview.tsx
|
|
273
|
+
│ └── ...
|
|
274
|
+
├── block/
|
|
275
|
+
│ └── EditorBlock.tsx ← Complete editor (high-level)
|
|
276
|
+
├── hooks/
|
|
277
|
+
│ └── useLoroEditor.ts
|
|
278
|
+
└── index.ts
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Exported Components
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
import {
|
|
285
|
+
Editor, // Core contentEditable component (low-level)
|
|
286
|
+
EditorBlock, // Complete editor with toolbar & preview (high-level)
|
|
287
|
+
Toolbar, // Formatting toolbar component
|
|
288
|
+
Preview, // Preview panel component
|
|
289
|
+
ColorPicker, // Color picker dropdown
|
|
290
|
+
HeadingSelector, // Heading selector dropdown
|
|
291
|
+
} from 'nextext-editor';
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Exported Utilities
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import {
|
|
298
|
+
useLoroEditor, // Loro CRDT hook
|
|
299
|
+
editorTokens, // Default design tokens
|
|
300
|
+
cn, // className utility (clsx + tailwind-merge)
|
|
301
|
+
} from 'nextext-editor';
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## TypeScript Types
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
import type {
|
|
308
|
+
TextFormat, // Text formatting state
|
|
309
|
+
EditorState, // Editor state
|
|
310
|
+
ToolbarAction, // Toolbar action types
|
|
311
|
+
PreviewMode, // Preview mode ('html' | 'text' | 'json')
|
|
312
|
+
EditorTokens, // Design tokens type
|
|
313
|
+
EditorBlockProps, // EditorBlock props
|
|
314
|
+
} from 'nextext-editor';
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
MIT © [NaveenChand](https://github.com/NaveenChand755)
|
|
320
|
+
|
|
321
|
+
## Contributing
|
|
322
|
+
|
|
323
|
+
Contributions are welcome! Please check out the [GitHub repository](https://github.com/NaveenChand755/hyper-text).
|
|
324
|
+
|
|
325
|
+
## Support
|
|
326
|
+
|
|
327
|
+
- 🐛 [Report Issues](https://github.com/NaveenChand755/hyper-text/issues)
|
|
328
|
+
- 💬 [Discussions](https://github.com/NaveenChand755/hyper-text/discussions)
|
|
329
|
+
- 📖 [Documentation](https://github.com/NaveenChand755/hyper-text#readme)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { editorTokens } from '../lib/tokens';
|
|
3
|
+
export interface EditorBlockProps {
|
|
4
|
+
/**
|
|
5
|
+
* Initial content (HTML string)
|
|
6
|
+
*/
|
|
7
|
+
initialContent?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Show preview panel
|
|
10
|
+
*/
|
|
11
|
+
showPreview?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Show toolbar
|
|
14
|
+
*/
|
|
15
|
+
showToolbar?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* External content control (for controlled component)
|
|
18
|
+
*/
|
|
19
|
+
externalContent?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Content change handler (for controlled component)
|
|
22
|
+
*/
|
|
23
|
+
onContentChange?: (content: string) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Custom class name for container
|
|
26
|
+
*/
|
|
27
|
+
className?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Placeholder text
|
|
30
|
+
*/
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Custom design tokens
|
|
34
|
+
*/
|
|
35
|
+
tokens?: typeof editorTokens;
|
|
36
|
+
}
|
|
37
|
+
export declare const EditorBlock: React.NamedExoticComponent<EditorBlockProps>;
|
|
38
|
+
export default EditorBlock;
|
|
@@ -1,38 +1,11 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { editorTokens } from '../lib/tokens';
|
|
3
3
|
export interface EditorProps {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
initialContent?: string;
|
|
8
|
-
/**
|
|
9
|
-
* Show preview panel
|
|
10
|
-
*/
|
|
11
|
-
showPreview?: boolean;
|
|
12
|
-
/**
|
|
13
|
-
* Show toolbar
|
|
14
|
-
*/
|
|
15
|
-
showToolbar?: boolean;
|
|
16
|
-
/**
|
|
17
|
-
* External content control (for controlled component)
|
|
18
|
-
*/
|
|
19
|
-
externalContent?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Content change handler (for controlled component)
|
|
22
|
-
*/
|
|
23
|
-
onContentChange?: (content: string) => void;
|
|
24
|
-
/**
|
|
25
|
-
* Custom class name for container
|
|
26
|
-
*/
|
|
27
|
-
className?: string;
|
|
28
|
-
/**
|
|
29
|
-
* Placeholder text
|
|
30
|
-
*/
|
|
4
|
+
content: string;
|
|
5
|
+
onContentChange: (content: string) => void;
|
|
31
6
|
placeholder?: string;
|
|
32
|
-
|
|
33
|
-
* Custom design tokens
|
|
34
|
-
*/
|
|
7
|
+
className?: string;
|
|
35
8
|
tokens?: typeof editorTokens;
|
|
36
9
|
}
|
|
37
|
-
export declare const Editor: React.
|
|
10
|
+
export declare const Editor: React.FC<EditorProps>;
|
|
38
11
|
export default Editor;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Editor } from './components/Editor';
|
|
2
|
-
export {
|
|
3
|
-
export
|
|
2
|
+
export type { EditorProps } from './components/Editor';
|
|
3
|
+
export { EditorBlock } from './block';
|
|
4
|
+
export type { EditorBlockProps } from './block';
|
|
4
5
|
export { Toolbar } from './components/Toolbar';
|
|
5
6
|
export { Preview } from './components/Preview';
|
|
6
7
|
export { ColorPicker } from './components/ColorPicker';
|